Compare commits

..

1 Commits

Author SHA1 Message Date
(no author)
973a54719f This commit was manufactured by cvs2svn to create tag 'Last140MergeSource'.
git-svn-id: svn://10.0.0.236/tags/Last140MergeSource@10343 18797224-902f-48f8-a5cc-f745e15eee43
1998-09-18 02:28:51 +00:00
121 changed files with 11682 additions and 24580 deletions

View File

@@ -0,0 +1,5 @@
# This is a list of local files which get copied to the mozilla:dist directory
#
jsjava.h
nsILiveconnect.h

View File

@@ -0,0 +1,89 @@
#!gmake
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
DEPTH = ../../..
include $(DEPTH)/config/config.mk
ifdef NSJVM
DIRS = classes
endif
MODULE = java
LIBRARY_NAME = jsj
REQUIRES = java js applet nspr img util layer xpcom
CSRCS = jsj.c \
jsj_JSObject.c \
jsj_JavaArray.c \
jsj_JavaClass.c \
jsj_JavaMember.c \
jsj_JavaObject.c \
jsj_JavaPackage.c \
jsj_array.c \
jsj_class.c \
jsj_convert.c \
jsj_field.c \
jsj_hash.c \
jsj_method.c \
jsj_utils.c \
nsCLiveconnect.c \
nsCLiveconnectFactory.c
LLIBS=$(LIBNSPR) $(DIST)/lib/js$(MOZ_BITS)$(VERSION_NUMBER).lib \
$(DIST)/lib/xpcom32.lib
ifdef NSJVM
JNI_GEN = netscape.javascript.JSObject netscape.javascript.JSException
endif
EXPORTS = jsjava.h \
$(JNI_GEN_DIR)/netscape_javascript_JSObject.h \
$(JNI_GEN_DIR)/netscape_javascript_JSException.h \
nsILiveconnect.h \
$(NULL)
include $(DEPTH)/config/rules.mk
######################################################################
# Generate jsj_nodl.c (so that you can check it in)
# These make rules only works on IRIX...sigh
ifeq ($(OS_ARCH),IRIX)
jsj_nodl.c: $(OBJDIR)/stubs.o Makefile $(DEPTH)/config/nodl.pl
rm -f $@
$(PERL) $(DEPTH)/config/nodl.pl "jsj_nodl_tab" \
`nm -Bn $(OBJDIR)/stubs.o | egrep Java_.*_stub | awk '{print $$3;}'` > $@
endif
ifdef JAVA_OR_OJI
$(OBJDIR)/stubs.o: \
$(JNI_GEN_DIR)/netscape_javascript_JSObject.c \
$(JNI_GEN_DIR)/netscape_javascript_JSException.c \
$(NULL)
else
$(OBJDIR)/stubs.o:
endif

View File

@@ -0,0 +1,90 @@
#!gmake
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/config.mk
ifdef NSJVM
DIRS = classes
endif
MODULE = java
LIBRARY_NAME = jsj
REQUIRES = java js applet nspr img util layer xpcom caps oji plugin
CSRCS = jsj.c \
jsj_JSObject.c \
jsj_JavaArray.c \
jsj_JavaClass.c \
jsj_JavaMember.c \
jsj_JavaObject.c \
jsj_JavaPackage.c \
jsj_array.c \
jsj_class.c \
jsj_convert.c \
jsj_field.c \
jsj_hash.c \
jsj_method.c \
jsj_utils.c
nsCLiveconnect.c \
nsCLiveconnectFactory.c
ifdef NSJVM
JNI_GEN = netscape.javascript.JSObject netscape.javascript.JSException
endif
EXPORTS = $(srcdir)/jsjava.h \
$(srcdir)/$(JNI_GEN_DIR)/netscape_javascript_JSObject.h \
$(srcdir)/$(JNI_GEN_DIR)/netscape_javascript_JSException.h \
$(NULL)
include $(topsrcdir)/config/rules.mk
######################################################################
# Generate jsj_nodl.c (so that you can check it in)
# These make rules only works on IRIX...sigh
ifeq ($(OS_ARCH),IRIX)
jsj_nodl.c: $(OBJDIR)/stubs.o Makefile $(topsrcdir)/config/nodl.pl
rm -f $@
$(PERL) $(topsrcdir)/config/nodl.pl "jsj_nodl_tab" \
`nm -Bn $(OBJDIR)/stubs.o | egrep Java_.*_stub | awk '{print $$3;}'` > $@
endif
ifdef JAVA_OR_OJI
$(OBJDIR)/stubs.o: \
$(JRI_GEN_DIR)/netscape_javascript_JSObject.c \
$(JRI_GEN_DIR)/netscape_javascript_JSException.c \
$(NULL)
else
$(OBJDIR)/stubs.o:
endif

View File

@@ -0,0 +1,14 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class netscape_javascript_JSException */
#ifndef _Included_netscape_javascript_JSException
#define _Included_netscape_javascript_JSException
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,102 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class netscape_javascript_JSObject */
#ifndef _Included_netscape_javascript_JSObject
#define _Included_netscape_javascript_JSObject
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: netscape_javascript_JSObject
* Method: initClass
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_initClass
(JNIEnv *, jclass);
/*
* Class: netscape_javascript_JSObject
* Method: getMember
* Signature: (Ljava/lang/String;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_getMember
(JNIEnv *, jobject, jstring);
/*
* Class: netscape_javascript_JSObject
* Method: getSlot
* Signature: (I)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_getSlot
(JNIEnv *, jobject, jint);
/*
* Class: netscape_javascript_JSObject
* Method: setMember
* Signature: (Ljava/lang/String;Ljava/lang/Object;)V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_setMember
(JNIEnv *, jobject, jstring, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: setSlot
* Signature: (ILjava/lang/Object;)V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_setSlot
(JNIEnv *, jobject, jint, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: removeMember
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_removeMember
(JNIEnv *, jobject, jstring);
/*
* Class: netscape_javascript_JSObject
* Method: call
* Signature: (Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_call
(JNIEnv *, jobject, jstring, jobjectArray);
/*
* Class: netscape_javascript_JSObject
* Method: eval
* Signature: (Ljava/lang/String;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_eval
(JNIEnv *, jobject, jstring);
/*
* Class: netscape_javascript_JSObject
* Method: toString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_netscape_javascript_JSObject_toString
(JNIEnv *, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: getWindow
* Signature: (Ljava/applet/Applet;)Lnetscape/javascript/JSObject;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_getWindow
(JNIEnv *, jclass, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: finalize
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_finalize
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,65 @@
#!gmake
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
DEPTH = ../../../..
MODULE = java
#
# the idea is that the install phase for those modules will
# make their own signed jar files
# there are some exceptions here that we deal with
# stuff in modules and in ns/js/jsj
#
JMODS = netscape/javascript netscape/javascript/adapters
#
# JDIRS is dependant on JAVA_DESTPATH in config/rules.m[a]k.
# Be sure to touch that directory if you add a new directory to
# JDIRS, or else it will not build. FIXME
#
JDIRS = $(JMODS)
JAR_JSJ = jsj10.jar
JAR_JSJ_CLASSES = $(JMODS)
#
# jars to build at install time
#
JARS = $(JAR_JSJ)
include $(DEPTH)/config/rules.mk
JAVA_SOURCEPATH = $(DEPTH)/js/src/liveconnect/classes
doc::
$(JAVADOC) -d $(DIST)/doc netscape.javascript
natives_list:: FORCE
rm -rf $@
find . -name "*.class" -print | sed 's@\./\(.*\)\.class$$@\1@' | \
sed 's@/@.@g' | xargs $(JVH) -natives | sort > $@
check_natives:: natives_list
rm -f found_natives
nm -B ../$(OBJDIR)/*.o \
| egrep "Java.*_stub" | awk '{ print $$3; }' | sort > found_natives
diff found_natives natives_list
FORCE:

View File

@@ -0,0 +1,70 @@
#!gmake
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = java
#
# the idea is that the install phase for those modules will
# make their own signed jar files
# there are some exceptions here that we deal with
# stuff in modules and in ns/js/jsj
#
JMODS = netscape/javascript netscape/javascript/adapters
#
# JDIRS is dependant on JAVA_DESTPATH in config/rules.m[a]k.
# Be sure to touch that directory if you add a new directory to
# JDIRS, or else it will not build. FIXME
#
JDIRS = $(JMODS)
JAR_JSJ = jsj10.jar
JAR_JSJ_CLASSES = $(JMODS)
#
# jars to build at install time
#
JARS = $(JAR_JSJ)
include $(topsrcdir)/config/rules.mk
JAVA_SOURCEPATH = $(topsrcdir)/js/src/liveconnect/classes
doc::
$(JAVADOC) -d $(DIST)/doc netscape.javascript
natives_list:: FORCE
rm -rf $@
find . -name "*.class" -print | sed 's@\./\(.*\)\.class$$@\1@' | \
sed 's@/@.@g' | xargs $(JVH) -natives | sort > $@
check_natives:: natives_list
rm -f found_natives
nm -B ../$(OBJDIR)/*.o \
| egrep "Java.*_stub" | awk '{ print $$3; }' | sort > found_natives
diff found_natives natives_list
FORCE:

View File

@@ -0,0 +1,78 @@
#!gmake
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
IGNORE_MANIFEST=1
#
#//------------------------------------------------------------------------
#//
#// Makefile to build the JavaScript-Java tree
#//
#//------------------------------------------------------------------------
DEPTH = ..\..\..\..
JAVA_SOURCEPATH=$(DEPTH)\js\src\liveconnect\classes
#//------------------------------------------------------------------------
#//
#// Define the files necessary to build the target (ie. OBJS)
#//
#//------------------------------------------------------------------------
include <$(DEPTH)\config\config.mak>
JMOZ = \
netscape/javascript \
$(NULL)
all::
MODULE=java
JMODS=netscape/javascript
!ifdef JAVA_OR_NSJVM
JDIRS=$(JMODS)
JAR_JSJ_CLASSES=$(JMODS)
JAR_NAME = jsj10.jar
!endif
include <$(DEPTH)\config\rules.mak>
!ifdef JAVA_OR_NSJVM
install::
cd $(JAVA_DESTPATH)
@echo +++ building/updating $(JAR_NAME)
-for %i in ($(JAR_JSJ_CLASSES:/=\)) do @$(ZIP_PROG) $(ZIP_FLAGS) $(JAR_NAME) %i\*.class
cd $(MAKEDIR)
!ifdef REGENERATE
install::
cd $(JAVA_DESTPATH)
@echo +++ updating $(DEPTH)\nav-java\stubs\classes\$(JAR_NAME) for checkin
-for %i in ($(JAR_JSJ_CLASSES:/=\)) do @$(ZIP_PROG) $(ZIP_FLAGS) ..\..\nav-java\stubs\classes\$(JAR_NAME) %i\*.class
cd $(MAKEDIR)
!endif
!endif
javadoc:
-mkdir $(XPDIST)\javadoc 2> NUL
echo $(JAVADOC) -sourcepath . -d $(XPDIST)\javadoc $(JDIRS:/=.)
$(JAVADOC) -sourcepath . -d $(XPDIST)\javadoc $(JDIRS:/=.)

View File

@@ -0,0 +1,56 @@
/* Insert copyright and license here 19** */
package netscape.javascript;
/**
* JSException is an exception which is thrown when JavaScript code
* returns an error.
*/
public
class JSException extends Exception {
String filename;
int lineno;
String source;
int tokenIndex;
/**
* Constructs a JSException without a detail message.
* A detail message is a String that describes this particular exception.
*/
public JSException() {
super();
filename = "unknown";
lineno = 0;
source = "";
tokenIndex = 0;
}
/**
* Constructs a JSException with a detail message.
* A detail message is a String that describes this particular exception.
* @param s the detail message
*/
public JSException(String s) {
super(s);
filename = "unknown";
lineno = 0;
source = "";
tokenIndex = 0;
}
/**
* Constructs a JSException with a detail message and all the
* other info that usually comes with a JavaScript error.
* @param s the detail message
*/
public JSException(String s, String filename, int lineno,
String source, int tokenIndex) {
super(s);
this.filename = filename;
this.lineno = lineno;
this.source = source;
this.tokenIndex = tokenIndex;
}
}

View File

@@ -0,0 +1,150 @@
/* -*- Mode: C; tab-width: 4; -*- */
/* Insert copyright and license here 19** */
/* more doc todo:
* threads
* gc
*
*
*/
package netscape.javascript;
import java.applet.Applet;
/**
* JSObject allows Java to manipulate objects that are
* defined in JavaScript.
* Values passed from Java to JavaScript are converted as
* follows:<ul>
* <li>JSObject is converted to the original JavaScript object
* <li>Any other Java object is converted to a JavaScript wrapper,
* which can be used to access methods and fields of the java object.
* Converting this wrapper to a string will call the toString method
* on the original object, converting to a number will call the
* floatValue method if possible and fail otherwise. Converting
* to a boolean will try to call the booleanValue method in the
* same way.
* <li>Java arrays are wrapped with a JavaScript object that understands
* array.length and array[index]
* <li>A Java boolean is converted to a JavaScript boolean
* <li>Java byte, char, short, int, long, float, and double are converted
* to JavaScript numbers
* </ul>
* Values passed from JavaScript to Java are converted as follows:<ul>
* <li>objects which are wrappers around java objects are unwrapped
* <li>other objects are wrapped with a JSObject
* <li>strings, numbers and booleans are converted to String, Float,
* and Boolean objects respectively
* </ul>
* This means that all JavaScript values show up as some kind
* of java.lang.Object in Java. In order to make much use of them,
* you will have to cast them to the appropriate subclass of Object,
* e.g. <code>(String) window.getMember("name");</code> or
* <code>(JSObject) window.getMember("document");</code>.
*/
public final class JSObject {
/* the internal object data */
private int internal;
/**
* initialize
*/
private static native void initClass();
static {
// On MRJ, this property won't exist, because the library is preloaded.
String liveConnectLibrary = System.getProperty("netscape.jsj.dll", null);
if (liveConnectLibrary != null) {
System.loadLibrary(liveConnectLibrary);
initClass();
}
}
/**
* it is illegal to construct a JSObject manually
*/
private JSObject(int jsobj_addr) {
internal = jsobj_addr;
}
/**
* Retrieves a named member of a JavaScript object.
* Equivalent to "this.<i>name</i>" in JavaScript.
*/
public native Object getMember(String name);
/**
* Retrieves an indexed member of a JavaScript object.
* Equivalent to "this[<i>index</i>]" in JavaScript.
*/
// public Object getMember(int index) { return getSlot(index); }
public native Object getSlot(int index);
/**
* Sets a named member of a JavaScript object.
* Equivalent to "this.<i>name</i> = <i>value</i>" in JavaScript.
*/
public native void setMember(String name, Object value);
/**
* Sets an indexed member of a JavaScript object.
* Equivalent to "this[<i>index</i>] = <i>value</i>" in JavaScript.
*/
// public void setMember(int index, Object value) {
// setSlot(index, value);
// }
public native void setSlot(int index, Object value);
/**
* Removes a named member of a JavaScript object.
*/
public native void removeMember(String name);
/**
* Calls a JavaScript method.
* Equivalent to "this.<i>methodName</i>(<i>args</i>[0], <i>args</i>[1], ...)" in JavaScript.
*/
public native Object call(String methodName, Object args[]);
/**
* Evaluates a JavaScript expression. The expression is a string
* of JavaScript source code which will be evaluated in the context
* given by "this".
*/
public native Object eval(String s);
/**
* Converts a JSObject to a String.
*/
public native String toString();
// should use some sort of identifier rather than String
// is "property" the right word?
// native String[] listProperties();
/**
* get a JSObject for the window containing the given applet
*/
public static native JSObject getWindow(Applet applet);
/**
* Finalization decrements the reference count on the corresponding
* JavaScript object.
*/
protected native void finalize();
/**
* Override java.lang.Object.equals() because identity is not preserved
* with instances of JSObject.
*/
public boolean equals(Object obj) {
JSObject jsobj;
if (!(obj instanceof JSObject))
return false;
jsobj = (JSObject)obj;
return (internal == jsobj.internal);
}
}

View File

@@ -0,0 +1,37 @@
/* -*- Mode: Java; 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/* ** */
/**
* The JSProxy interface allows applets and plugins to
* share javascript contexts.
*/
package netscape.javascript;
import java.applet.Applet;
public interface JSProxy {
Object getMember(JSObject jso, String name);
Object getSlot(JSObject jso, int index);
void setMember(JSObject jso, String name, Object value);
void setSlot(JSObject jso, int index, Object value);
void removeMember(JSObject jso, String name);
Object call(JSObject jso, String methodName, Object args[]);
Object eval(JSObject jso, String s);
String toString(JSObject jso);
JSObject getWindow(Applet applet);
}

View File

@@ -0,0 +1,31 @@
package netscape.javascript;
/**
* Runs a JavaScript object with a run() method in a separate thread.
*/
public class JSRunnable implements Runnable {
private JSObject runnable;
public JSRunnable(JSObject runnable) {
this.runnable = runnable;
synchronized(this) {
new Thread(this).start();
try {
this.wait();
} catch (InterruptedException ie) {
}
}
}
public void run() {
try {
runnable.call("run", null);
synchronized(this) {
notifyAll();
}
} catch (Throwable t) {
System.err.println(t);
t.printStackTrace(System.err);
}
}
}

View File

@@ -0,0 +1,38 @@
/* -*- Mode: Java; 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/* ** */
package netscape.javascript;
import java.io.*;
public class JSUtil {
/* Return the stack trace of an exception or error as a String */
public static String getStackTrace(Throwable t) {
ByteArrayOutputStream captureStream;
PrintStream p;
captureStream = new ByteArrayOutputStream();
p = new PrintStream(captureStream);
t.printStackTrace(p);
p.flush();
return captureStream.toString();
}
}

View File

@@ -0,0 +1,876 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 Communicator client 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.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the top-level initialization code and the implementation of the
* public API.
*
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_private.h" /* LiveConnect internals */
#include "jsjava.h" /* LiveConnect external API */
/*
* At certain times during initialization, there may be no JavaScript context
* available to direct error reports to, in which case the error messages
* are sent to this function. The caller is responsible for free'ing
* the js_error_msg argument.
*/
static void
report_java_initialization_error(JNIEnv *jEnv, const char *js_error_msg)
{
const char *error_msg, *java_error_msg;
java_error_msg = NULL;
#if 0 /* This can never work here, because jsj_GetJavaErrorMessage relies on
jlThrowable_toString which is set up by the initialization that calls
this function. */
if (jEnv) {
java_error_msg = jsj_GetJavaErrorMessage(jEnv);
(*jEnv)->ExceptionClear(jEnv);
}
#endif
if (java_error_msg) {
error_msg = PR_smprintf("initialization error: %s (%s)\n",
js_error_msg, java_error_msg);
free((void*)java_error_msg);
} else {
error_msg = PR_smprintf("initialization error: %s\n",
js_error_msg);
}
jsj_LogError(error_msg);
}
/*
* Opaque JVM handles to Java classes and methods required for Java reflection.
* These are computed and cached during initialization.
*/
jclass jlObject; /* java.lang.Object */
jclass jlrMethod; /* java.lang.reflect.Method */
jclass jlrField; /* java.lang.reflect.Field */
jclass jlVoid; /* java.lang.Void */
jclass jlrConstructor; /* java.lang.reflect.Constructor */
jclass jlThrowable; /* java.lang.Throwable */
jclass jlSystem; /* java.lang.System */
jclass jlClass; /* java.lang.Class */
jclass jlBoolean; /* java.lang.Boolean */
jclass jlDouble; /* java.lang.Double */
jclass jlString; /* java.lang.String */
jclass njJSObject; /* netscape.javascript.JSObject */
jclass njJSException; /* netscape.javascript.JSException */
jclass njJSUtil; /* netscape.javascript.JSUtil */
jmethodID jlClass_getMethods; /* java.lang.Class.getMethods() */
jmethodID jlClass_getConstructors; /* java.lang.Class.getConstructors() */
jmethodID jlClass_getFields; /* java.lang.Class.getFields() */
jmethodID jlClass_getName; /* java.lang.Class.getName() */
jmethodID jlClass_getComponentType; /* java.lang.Class.getComponentType() */
jmethodID jlClass_getModifiers; /* java.lang.Class.getModifiers() */
jmethodID jlClass_isArray; /* java.lang.Class.isArray() */
jmethodID jlrMethod_getName; /* java.lang.reflect.Method.getName() */
jmethodID jlrMethod_getParameterTypes; /* java.lang.reflect.Method.getParameterTypes() */
jmethodID jlrMethod_getReturnType; /* java.lang.reflect.Method.getReturnType() */
jmethodID jlrMethod_getModifiers; /* java.lang.reflect.Method.getModifiers() */
jmethodID jlrConstructor_getParameterTypes; /* java.lang.reflect.Constructor.getParameterTypes() */
jmethodID jlrConstructor_getModifiers; /* java.lang.reflect.Constructor.getModifiers() */
jmethodID jlrField_getName; /* java.lang.reflect.Field.getName() */
jmethodID jlrField_getType; /* java.lang.reflect.Field.getType() */
jmethodID jlrField_getModifiers; /* java.lang.reflect.Field.getModifiers() */
jmethodID jlBoolean_Boolean; /* java.lang.Boolean constructor */
jmethodID jlBoolean_booleanValue; /* java.lang.Boolean.booleanValue() */
jmethodID jlDouble_Double; /* java.lang.Double constructor */
jmethodID jlDouble_doubleValue; /* java.lang.Double.doubleValue() */
jmethodID jlThrowable_toString; /* java.lang.Throwable.toString() */
jmethodID jlThrowable_getMessage; /* java.lang.Throwable.getMessage() */
jmethodID jlSystem_identityHashCode; /* java.lang.System.identityHashCode() */
jobject jlVoid_TYPE; /* java.lang.Void.TYPE value */
jmethodID njJSException_JSException; /* netscape.javascript.JSexception constructor */
jmethodID njJSObject_JSObject; /* netscape.javascript.JSObject constructor */
jmethodID njJSUtil_getStackTrace; /* netscape.javascript.JSUtil.getStackTrace() */
jfieldID njJSObject_internal; /* netscape.javascript.JSObject.internal */
jfieldID njJSException_lineno; /* netscape.javascript.JSException.lineno */
jfieldID njJSException_tokenIndex; /* netscape.javascript.JSException.tokenIndex */
jfieldID njJSException_source; /* netscape.javascript.JSException.source */
jfieldID njJSException_filename; /* netscape.javascript.JSException.filename */
/* Obtain a reference to a Java class */
#define LOAD_CLASS(qualified_name, class) \
{ \
jclass _##class = (*jEnv)->FindClass(jEnv, #qualified_name); \
if (_##class == 0) { \
(*jEnv)->ExceptionClear(jEnv); \
report_java_initialization_error(jEnv, \
"Can't load class " #qualified_name); \
return JS_FALSE; \
} \
class = (*jEnv)->NewGlobalRef(jEnv, _##class); \
}
/* Obtain a methodID reference to a Java method or constructor */
#define _LOAD_METHOD(qualified_class, method, mvar, signature, class, is_static)\
if (is_static) { \
class##_##mvar = \
(*jEnv)->GetStaticMethodID(jEnv, class, #method, signature); \
} else { \
class##_##mvar = \
(*jEnv)->GetMethodID(jEnv, class, #method, signature); \
} \
if (class##_##mvar == 0) { \
report_java_initialization_error(jEnv, \
"Can't get mid for " #qualified_class "." #method "()"); \
return JS_FALSE; \
}
/* Obtain a methodID reference to a Java instance method */
#define LOAD_METHOD(qualified_class, method, signature, class) \
_LOAD_METHOD(qualified_class, method, method, signature, class, JS_FALSE)
/* Obtain a methodID reference to a Java static method */
#define LOAD_STATIC_METHOD(qualified_class, method, signature, class) \
_LOAD_METHOD(qualified_class, method, method, signature, class, JS_TRUE)
/* Obtain a methodID reference to a Java constructor */
#define LOAD_CONSTRUCTOR(qualified_class, method, signature, class) \
_LOAD_METHOD(qualified_class,<init>, method, signature, class, JS_FALSE)
/* Obtain a fieldID reference to a Java instance or static field */
#define _LOAD_FIELDID(qualified_class, field, signature, class, is_static) \
if (is_static) { \
class##_##field = (*jEnv)->GetStaticFieldID(jEnv, class, #field, signature);\
} else { \
class##_##field = (*jEnv)->GetFieldID(jEnv, class, #field, signature);\
} \
if (class##_##field == 0) { \
report_java_initialization_error(jEnv, \
"Can't get fid for " #qualified_class "." #field); \
return JS_FALSE; \
}
/* Obtain a fieldID reference to a Java instance field */
#define LOAD_FIELDID(qualified_class, field, signature, class) \
_LOAD_FIELDID(qualified_class, field, signature, class, JS_FALSE)
/* Obtain the value of a static field in a Java class */
#define LOAD_FIELD_VAL(qualified_class, field, signature, class, type) \
{ \
jfieldID field_id; \
field_id = (*jEnv)->GetStaticFieldID(jEnv, class, #field, signature);\
if (field_id == 0) { \
report_java_initialization_error(jEnv, \
"Can't get fid for " #qualified_class "." #field); \
return JS_FALSE; \
} \
class##_##field = \
(*jEnv)->GetStatic##type##Field(jEnv, class, field_id); \
if (class##_##field == 0) { \
report_java_initialization_error(jEnv, \
"Can't read static field " #qualified_class "." #field); \
return JS_FALSE; \
} \
}
/* Obtain the value of a static field in a Java class, which is known to
contain an object value. */
#define LOAD_FIELD_OBJ(qualified_class, field, signature, class) \
LOAD_FIELD_VAL(qualified_class, field, signature, class, Object); \
class##_##field = (*jEnv)->NewGlobalRef(jEnv, class##_##field);
/*
* Load the Java classes, and the method and field descriptors required for Java reflection.
* Returns JS_TRUE on success, JS_FALSE on failure.
*/
static JSBool
init_java_VM_reflection(JSJavaVM *jsjava_vm, JNIEnv *jEnv)
{
/* Load Java system classes and method, including java.lang.reflect classes */
LOAD_CLASS(java/lang/Object, jlObject);
LOAD_CLASS(java/lang/Class, jlClass);
LOAD_CLASS(java/lang/reflect/Method, jlrMethod);
LOAD_CLASS(java/lang/reflect/Constructor, jlrConstructor);
LOAD_CLASS(java/lang/reflect/Field, jlrField);
LOAD_CLASS(java/lang/Throwable, jlThrowable);
LOAD_CLASS(java/lang/System, jlSystem);
LOAD_CLASS(java/lang/Boolean, jlBoolean);
LOAD_CLASS(java/lang/Double, jlDouble);
LOAD_CLASS(java/lang/String, jlString);
LOAD_CLASS(java/lang/Void, jlVoid);
LOAD_METHOD(java.lang.Class, getMethods, "()[Ljava/lang/reflect/Method;",jlClass);
LOAD_METHOD(java.lang.Class, getConstructors, "()[Ljava/lang/reflect/Constructor;",jlClass);
LOAD_METHOD(java.lang.Class, getFields, "()[Ljava/lang/reflect/Field;", jlClass);
LOAD_METHOD(java.lang.Class, getName, "()Ljava/lang/String;", jlClass);
LOAD_METHOD(java.lang.Class, isArray, "()Z", jlClass);
LOAD_METHOD(java.lang.Class, getComponentType, "()Ljava/lang/Class;", jlClass);
LOAD_METHOD(java.lang.Class, getModifiers, "()I", jlClass);
LOAD_METHOD(java.lang.reflect.Method, getName, "()Ljava/lang/String;", jlrMethod);
LOAD_METHOD(java.lang.reflect.Method, getParameterTypes, "()[Ljava/lang/Class;", jlrMethod);
LOAD_METHOD(java.lang.reflect.Method, getReturnType, "()Ljava/lang/Class;", jlrMethod);
LOAD_METHOD(java.lang.reflect.Method, getModifiers, "()I", jlrMethod);
LOAD_METHOD(java.lang.reflect.Constructor, getParameterTypes, "()[Ljava/lang/Class;", jlrConstructor);
LOAD_METHOD(java.lang.reflect.Constructor, getModifiers, "()I", jlrConstructor);
LOAD_METHOD(java.lang.reflect.Field, getName, "()Ljava/lang/String;", jlrField);
LOAD_METHOD(java.lang.reflect.Field, getType, "()Ljava/lang/Class;", jlrField);
LOAD_METHOD(java.lang.reflect.Field, getModifiers, "()I", jlrField);
LOAD_METHOD(java.lang.Throwable, toString, "()Ljava/lang/String;", jlThrowable);
LOAD_METHOD(java.lang.Throwable, getMessage, "()Ljava/lang/String;", jlThrowable);
LOAD_METHOD(java.lang.Double, doubleValue, "()D", jlDouble);
LOAD_METHOD(java.lang.Boolean, booleanValue, "()Z", jlBoolean);
LOAD_STATIC_METHOD(java.lang.System, identityHashCode, "(Ljava/lang/Object;)I", jlSystem);
LOAD_CONSTRUCTOR(java.lang.Boolean, Boolean, "(Z)V", jlBoolean);
LOAD_CONSTRUCTOR(java.lang.Double, Double, "(D)V", jlDouble);
LOAD_FIELD_OBJ(java.lang.Void, TYPE, "Ljava/lang/Class;", jlVoid);
return JS_TRUE;
}
#if XP_MAC
/**
* Workaround for the fact that MRJ loads a different instance of the shared library.
*/
#include "netscape_javascript_JSObject.h"
static JSObject_RegisterNativeMethods(JNIEnv* jEnv)
{
// Manually load the required native methods.
static JNINativeMethod nativeMethods[] = {
"initClass", "()V", (void*)&Java_netscape_javascript_JSObject_initClass,
"getMember", "(Ljava/lang/String;)Ljava/lang/Object;", (void*)&Java_netscape_javascript_JSObject_getMember,
"getSlot", "(I)Ljava/lang/Object;", (void*)&Java_netscape_javascript_JSObject_getSlot,
"setMember", "(Ljava/lang/String;Ljava/lang/Object;)V", (void*)&Java_netscape_javascript_JSObject_setMember,
"setSlot", "(ILjava/lang/Object;)V", (void*)&Java_netscape_javascript_JSObject_setSlot,
"removeMember", "(Ljava/lang/String;)V", (void*)&Java_netscape_javascript_JSObject_removeMember,
"call", "(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;", (void*)&Java_netscape_javascript_JSObject_call,
"eval", "(Ljava/lang/String;)Ljava/lang/Object;", (void*)&Java_netscape_javascript_JSObject_eval,
"toString", "()Ljava/lang/String;", (void*)&Java_netscape_javascript_JSObject_toString,
"getWindow", "(Ljava/applet/Applet;)Lnetscape/javascript/JSObject;", (void*)&Java_netscape_javascript_JSObject_getWindow,
"finalize", "()V", (void*)&Java_netscape_javascript_JSObject_finalize,
/* "equals", "(Ljava/lang/Object;)Z", (void*)&Java_netscape_javascript_JSObject_equals */
};
(*jEnv)->RegisterNatives(jEnv, njJSObject, nativeMethods, sizeof(nativeMethods) / sizeof(JNINativeMethod));
if ((*jEnv)->ExceptionOccurred(jEnv)) {
report_java_initialization_error(jEnv, "Couldn't initialize JSObject native methods.");
(*jEnv)->ExceptionClear(jEnv);
}
/* call the initClass method, since we nailed the static initializer for testing. */
Java_netscape_javascript_JSObject_initClass(jEnv, njJSObject);
}
#endif
/* Load Netscape-specific Java extension classes, methods, and fields */
static JSBool
init_netscape_java_classes(JSJavaVM *jsjava_vm, JNIEnv *jEnv)
{
LOAD_CLASS(netscape/javascript/JSObject, njJSObject);
LOAD_CLASS(netscape/javascript/JSException, njJSException);
LOAD_CLASS(netscape/javascript/JSUtil, njJSUtil);
#if XP_MAC
JSObject_RegisterNativeMethods(jEnv);
#endif
LOAD_CONSTRUCTOR(netscape.javascript.JSObject,
JSObject, "(I)V", njJSObject);
LOAD_CONSTRUCTOR(netscape.javascript.JSException,
JSException, "(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;I)V",
njJSException);
LOAD_FIELDID(netscape.javascript.JSObject,
internal, "I", njJSObject);
LOAD_FIELDID(netscape.javascript.JSException,
lineno, "I", njJSException);
LOAD_FIELDID(netscape.javascript.JSException,
tokenIndex, "I", njJSException);
LOAD_FIELDID(netscape.javascript.JSException,
source, "Ljava/lang/String;", njJSException);
LOAD_FIELDID(netscape.javascript.JSException,
filename, "Ljava/lang/String;", njJSException);
LOAD_STATIC_METHOD(netscape.javascript.JSUtil,
getStackTrace, "(Ljava/lang/Throwable;)Ljava/lang/String;",
njJSUtil);
return JS_TRUE;
}
JSJavaVM *jsjava_vm_list = NULL;
/*
* Called once per Java VM, this function initializes the classes, fields, and
* methods required for Java reflection. If java_vm is NULL, a new Java VM is
* created, using the provided classpath in addition to any default classpath.
* The classpath argument is ignored, however, if java_vm_arg is non-NULL.
*/
JSJavaVM *
JSJ_ConnectToJavaVM(SystemJavaVM *java_vm_arg, void* initargs)
{
SystemJavaVM* java_vm;
JSJavaVM *jsjava_vm;
JNIEnv *jEnv;
PR_ASSERT(JSJ_callbacks);
PR_ASSERT(JSJ_callbacks->attach_current_thread);
PR_ASSERT(JSJ_callbacks->detach_current_thread);
PR_ASSERT(JSJ_callbacks->get_java_vm);
jsjava_vm = (JSJavaVM*)malloc(sizeof(JSJavaVM));
if (!jsjava_vm)
return NULL;
memset(jsjava_vm, 0, sizeof(JSJavaVM));
java_vm = java_vm_arg;
/* If a Java VM was passed in, try to attach to it on the current thread. */
if (java_vm) {
jEnv = JSJ_callbacks->attach_current_thread(java_vm);
if (jEnv == NULL) {
jsj_LogError("Failed to attach to Java VM thread\n");
free(jsjava_vm);
return NULL;
}
}
else {
PRBool ok;
PR_ASSERT(JSJ_callbacks->create_java_vm);
PR_ASSERT(JSJ_callbacks->destroy_java_vm);
ok = JSJ_callbacks->create_java_vm(&java_vm, &jEnv, initargs);
if (!ok || java_vm == NULL) {
jsj_LogError("Failed to create Java VM\n");
free(jsjava_vm);
return NULL;
}
/* Remember that we created the VM so that we know to destroy it later */
jsjava_vm->jsj_created_java_vm = JS_TRUE;
}
jsjava_vm->java_vm = java_vm;
jsjava_vm->main_thread_env = jEnv;
/* Load the Java classes, and the method and field descriptors required for
Java reflection. */
if (!init_java_VM_reflection(jsjava_vm, jEnv)) {
JSJ_DisconnectFromJavaVM(jsjava_vm);
return NULL;
}
/*
* JVM initialization for netscape.javascript.JSObject is performed
* independently of the other classes that are initialized in
* init_java_VM_reflection, because we allow it to fail. In the case
* of failure, LiveConnect is still operative, but only when calling
* from JS to Java and not vice-versa.
*/
init_netscape_java_classes(jsjava_vm, jEnv);
/* Put this VM on the list of all created VMs */
jsjava_vm->next = jsjava_vm_list;
jsjava_vm_list = jsjava_vm;
return jsjava_vm;
}
JSJCallbacks *JSJ_callbacks = NULL;
/* Called once to set up callbacks for all instances of LiveConnect */
void
JSJ_Init(JSJCallbacks *callbacks)
{
PR_ASSERT(callbacks);
JSJ_callbacks = callbacks;
}
/*
* Initialize the provided JSContext by setting up the JS classes necessary for
* reflection and by defining JavaPackage objects for the default Java packages
* as properties of global_obj. Additional packages may be pre-defined by
* setting the predefined_packages argument. (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.)
*/
JSBool
JSJ_InitJSContext(JSContext *cx, JSObject *global_obj,
JavaPackageDef *predefined_packages)
{
/* Initialize the JavaScript classes used for reflection */
if (!jsj_init_JavaObject(cx, global_obj))
return JS_FALSE;
/* if (!jsj_init_JavaMember(cx, global_obj))
return JS_FALSE; */
if (!jsj_init_JavaPackage(cx, global_obj, predefined_packages))
return JS_FALSE;
if (!jsj_init_JavaClass(cx, global_obj))
return JS_FALSE;
if (!jsj_init_JavaArray(cx, global_obj))
return JS_FALSE;
if (!jsj_init_JavaMember(cx, global_obj))
return JS_FALSE;
return JS_TRUE;
}
/* Eliminate a reference to a Java class */
#define UNLOAD_CLASS(qualified_name, class) \
if (class) { \
(*jEnv)->DeleteGlobalRef(jEnv, class); \
class = NULL; \
}
/*
* This routine severs the connection to a Java VM, freeing all related resources.
* It shouldn't be called until the global scope has been cleared in all related
* JSContexts (so that all LiveConnect objects are finalized) and a JavaScript
* GC is performed. Otherwise, accessed to free'ed memory could result.
*/
void
JSJ_DisconnectFromJavaVM(JSJavaVM *jsjava_vm)
{
JNIEnv *jEnv;
SystemJavaVM *java_vm;
JSJavaVM *j, **jp;
java_vm = jsjava_vm->java_vm;
jEnv = jsjava_vm->main_thread_env;
/* Drop all references to Java objects and classes */
jsj_DiscardJavaObjReflections(jEnv);
jsj_DiscardJavaClassReflections(jEnv);
if (jsjava_vm->jsj_created_java_vm) {
(void)JSJ_callbacks->destroy_java_vm(java_vm, jEnv);
} else {
UNLOAD_CLASS(java/lang/Object, jlObject);
UNLOAD_CLASS(java/lang/Class, jlClass);
UNLOAD_CLASS(java/lang/reflect/Method, jlrMethod);
UNLOAD_CLASS(java/lang/reflect/Constructor, jlrConstructor);
UNLOAD_CLASS(java/lang/reflect/Field, jlrField);
UNLOAD_CLASS(java/lang/Throwable, jlThrowable);
UNLOAD_CLASS(java/lang/System, jlSystem);
UNLOAD_CLASS(java/lang/Boolean, jlBoolean);
UNLOAD_CLASS(java/lang/Double, jlDouble);
UNLOAD_CLASS(java/lang/String, jlString);
UNLOAD_CLASS(java/lang/Void, jlVoid);
UNLOAD_CLASS(netscape/javascript/JSObject, njJSObject);
UNLOAD_CLASS(netscape/javascript/JSException, njJSException);
UNLOAD_CLASS(netscape/javascript/JSUtil, njJSUtil);
}
/* Remove this VM from the list of all JSJavaVM objects. */
for (jp = &jsjava_vm_list; (j = *jp) != NULL; jp = &j->next) {
if (j == jsjava_vm) {
*jp = jsjava_vm->next;
break;
}
}
PR_ASSERT(j);
free(jsjava_vm);
}
static JSJavaThreadState *thread_list = NULL;
static JSJavaThreadState *
new_jsjava_thread_state(JSJavaVM *jsjava_vm, const char *thread_name, JNIEnv *jEnv)
{
JSJavaThreadState *jsj_env;
jsj_env = (JSJavaThreadState *)malloc(sizeof(JSJavaThreadState));
if (!jsj_env)
return NULL;
memset(jsj_env, 0, sizeof(JSJavaThreadState));
jsj_env->jEnv = jEnv;
jsj_env->jsjava_vm = jsjava_vm;
if (thread_name)
jsj_env->name = strdup(thread_name);
/* THREADSAFETY - need to protect against races */
jsj_env->next = thread_list;
thread_list = jsj_env;
return jsj_env;
}
static JSJavaThreadState *
find_jsjava_thread(JNIEnv *jEnv)
{
JSJavaThreadState *e, **p, *jsj_env;
jsj_env = NULL;
/* THREADSAFETY - need to protect against races in manipulating the thread list */
/* Search for the thread state among the list of all created
LiveConnect threads */
for (p = &thread_list; (e = *p) != NULL; p = &(e->next)) {
if (e->jEnv == jEnv) {
jsj_env = e;
*p = jsj_env->next;
break;
}
}
/* Move a found thread to head of list for faster search next time. */
if (jsj_env)
thread_list = jsj_env;
return jsj_env;
}
PR_IMPLEMENT(JSJavaThreadState *)
JSJ_AttachCurrentThreadToJava(JSJavaVM *jsjava_vm, const char *name, JNIEnv **java_envp)
{
JNIEnv *jEnv;
SystemJavaVM *java_vm;
JSJavaThreadState *jsj_env;
/* Try to attach a Java thread to the current native thread */
java_vm = jsjava_vm->java_vm;
jEnv = JSJ_callbacks->attach_current_thread(java_vm);
if (jEnv == NULL)
return NULL;
/* If we found an existing thread state, just return it. */
jsj_env = find_jsjava_thread(jEnv);
if (jsj_env)
return jsj_env;
/* Create a new wrapper around the thread/VM state */
jsj_env = new_jsjava_thread_state(jsjava_vm, name, jEnv);
if (java_envp)
*java_envp = jEnv;
return jsj_env;
}
static JSJavaVM *
map_java_vm_to_jsjava_vm(SystemJavaVM *java_vm)
{
JSJavaVM *v;
for (v = jsjava_vm_list; v; v = v->next) {
if (v->java_vm == java_vm)
return v;
}
return NULL;
}
/*
* Unfortunately, there's no standard means to associate any private data with
* a JNI thread environment, so we need to use the Java environment pointer as
* the key in a lookup table that maps it to a JSJavaThreadState structure,
* where we store all our per-thread private data. If no existing thread state
* is found, a new one is created.
*
* If an error occurs, returns NULL and sets the errp argument to an error
* message, which the caller is responsible for free'ing.
*/
JSJavaThreadState *
jsj_MapJavaThreadToJSJavaThreadState(JNIEnv *jEnv, char **errp)
{
JSJavaThreadState *jsj_env;
SystemJavaVM *java_vm;
JSJavaVM *jsjava_vm;
/* If we found an existing thread state, just return it. */
jsj_env = find_jsjava_thread(jEnv);
if (jsj_env)
return jsj_env;
/* No one set up a LiveConnect thread state for a given Java thread.
Invoke the callback to create one on-the-fly. */
/* First, figure out which Java VM is calling us */
java_vm = JSJ_callbacks->get_java_vm(jEnv);
if (jsjava_vm == NULL)
return NULL;
/* Get our private JavaVM data */
jsjava_vm = map_java_vm_to_jsjava_vm(java_vm);
if (!jsjava_vm) {
*errp = PR_smprintf("Total weirdness: No JSJavaVM wrapper ever created "
"for JavaVM 0x%08x", java_vm);
return NULL;
}
jsj_env = new_jsjava_thread_state(jsjava_vm, NULL, jEnv);
if (!jsj_env)
return NULL;
return jsj_env;
}
/*
* This function is used to specify a particular JSContext as *the* JavaScript
* execution environment to be used when LiveConnect is accessed from the given
* Java thread, i.e. by using one of the methods of netscape.javascript.JSObject.
* (There can only be one such JS context for a given Java thread. To
* multiplex JSContexts among a single thread, this function must be called
* before Java is invoked on that thread.) The return value is the previous
* context associated with the given Java thread.
*/
PR_IMPLEMENT(JSContext *)
JSJ_SetDefaultJSContextForJavaThread(JSContext *cx, JSJavaThreadState *jsj_env)
{
JSContext *old_context;
old_context = jsj_env->cx;
jsj_env->cx = cx;
return old_context;
}
PR_IMPLEMENT(JSBool)
JSJ_DetachCurrentThreadFromJava(JSJavaThreadState *jsj_env)
{
SystemJavaVM *java_vm;
JNIEnv* jEnv;
JSJavaThreadState *e, **p;
/* Disassociate the current native thread from its corresponding Java thread */
java_vm = jsj_env->jsjava_vm->java_vm;
jEnv = jsj_env->jEnv;
if (!JSJ_callbacks->detach_current_thread(java_vm, jEnv))
return JS_FALSE;
/* Destroy the LiveConnect execution environment passed in */
jsj_ClearPendingJSErrors(jsj_env);
/* THREADSAFETY - need to protect against races */
for (p = &thread_list; (e = *p) != NULL; p = &(e->next)) {
if (e == jsj_env) {
*p = jsj_env->next;
break;
}
}
free(jsj_env);
return JS_TRUE;
}
JSBool
JSJ_ConvertJavaObjectToJSValue(JSContext *cx, jobject java_obj, jsval *vp)
{
JNIEnv *jEnv;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
return jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, vp);
}
/*===========================================================================*/
#ifndef MOZILLA_CLIENT
/* The convenience functions below present a complete, but simplified
LiveConnect API which is designed to handle the special case of a single
Java-VM, single-threaded operation, and use of only one JSContext. */
/* We can get away with global variables in our single-threaded,
single-JSContext case. */
static JSJavaVM * the_jsj_vm = NULL;
static JSContext * the_cx = NULL;
static JSJavaThreadState * the_jsj_thread = NULL;
static JSObject * the_global_js_obj = NULL;
/* Trivial implementation of callback function */
static JSJavaThreadState *
default_map_js_context_to_jsj_thread(JSContext *cx, char **errp)
{
return the_jsj_thread;
}
/* Trivial implementation of callback function */
static JSContext *
default_map_jsj_thread_to_js_context(JSJavaThreadState *jsj_env, JNIEnv *jEnv, char **errp)
{
return the_cx;
}
/* Trivial implementation of callback function */
static JSObject *
default_map_java_object_to_js_object(JNIEnv *jEnv, jobject hint, char **errp)
{
return the_global_js_obj;
}
static PRBool PR_CALLBACK
default_create_java_vm(SystemJavaVM* *jvm, JNIEnv* *initialEnv, void* initargs)
{
jint err;
const char* user_classpath = (const char*)initargs;
/* No Java VM supplied, so create our own */
JDK1_1InitArgs vm_args;
/* Magic constant indicates JRE version 1.1 */
vm_args.version = 0x00010001;
JNI_GetDefaultJavaVMInitArgs(&vm_args);
/* Prepend the classpath argument to the default JVM classpath */
if (user_classpath) {
#ifdef XP_UNIX
const char *full_classpath = PR_smprintf("%s:%s", user_classpath, vm_args.classpath);
#else
const char *full_classpath = PR_smprintf("%s;%s", user_classpath, vm_args.classpath);
#endif
if (!full_classpath) {
return PR_FALSE;
}
vm_args.classpath = (char*)full_classpath;
}
err = JNI_CreateJavaVM((JavaVM**)jvm, initialEnv, &vm_args);
return err == 0;
}
static PRBool PR_CALLBACK
default_destroy_java_vm(SystemJavaVM* jvm, JNIEnv* initialEnv)
{
JavaVM* java_vm = (JavaVM*)jvm;
jint err = (*java_vm)->DestroyJavaVM(java_vm);
return err == 0;
}
static JNIEnv* PR_CALLBACK
default_attach_current_thread(SystemJavaVM* jvm)
{
JavaVM* java_vm = (JavaVM*)jvm;
JNIEnv* env = NULL;
jint err = (*java_vm)->AttachCurrentThread(java_vm, &env, NULL);
return env;
}
static PRBool PR_CALLBACK
default_detach_current_thread(SystemJavaVM* jvm, JNIEnv* env)
{
JavaVM* java_vm = (JavaVM*)jvm;
/* assert that env is the JNIEnv of the current thread */
jint err = (*java_vm)->DetachCurrentThread(java_vm);
return err == 0;
}
static SystemJavaVM* PR_CALLBACK
default_get_java_vm(JNIEnv* env)
{
JavaVM* java_vm = NULL;
jint err = (*env)->GetJavaVM(env, &java_vm);
return (SystemJavaVM*)java_vm;
}
/* Trivial implementations of callback functions */
JSJCallbacks jsj_default_callbacks = {
default_map_jsj_thread_to_js_context,
default_map_js_context_to_jsj_thread,
default_map_java_object_to_js_object,
NULL,
NULL,
NULL,
NULL,
default_create_java_vm,
default_destroy_java_vm,
default_attach_current_thread,
default_detach_current_thread,
default_get_java_vm
};
/*
* Initialize the provided JSContext by setting up the JS classes necessary for
* reflection and by defining JavaPackage objects for the default Java packages
* as properties of global_obj. If java_vm is NULL, a new Java VM is
* created, using the provided classpath in addition to any default classpath.
* The classpath argument is ignored, however, if java_vm is non-NULL.
*/
JSBool
JSJ_SimpleInit(JSContext *cx, JSObject *global_obj, SystemJavaVM *java_vm, const char *classpath)
{
JNIEnv *jEnv;
PR_ASSERT(!the_jsj_vm);
the_jsj_vm = JSJ_ConnectToJavaVM(java_vm, (void*)classpath);
if (!the_jsj_vm)
return JS_FALSE;
JSJ_Init(&jsj_default_callbacks);
if (!JSJ_InitJSContext(cx, global_obj, NULL))
goto error;
the_cx = cx;
the_global_js_obj = global_obj;
the_jsj_thread = JSJ_AttachCurrentThreadToJava(the_jsj_vm, "main thread", &jEnv);
if (!the_jsj_thread)
goto error;
JSJ_SetDefaultJSContextForJavaThread(cx, the_jsj_thread);
return JS_TRUE;
error:
JSJ_SimpleShutdown();
return JS_FALSE;
}
/*
* Free up all LiveConnect resources. Destroy the Java VM if it was
* created by LiveConnect.
*/
PR_IMPLEMENT(void)
JSJ_SimpleShutdown()
{
PR_ASSERT(the_jsj_vm);
JSJ_DisconnectFromJavaVM(the_jsj_vm);
the_jsj_vm = NULL;
the_cx = NULL;
the_global_js_obj = NULL;
the_jsj_thread = NULL;
}
#endif /* MOZILLA_CLIENT */

View File

@@ -0,0 +1,76 @@
/* -*- 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.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
// Version stamp for this .DLL
#include <windows.h>
#include <ver.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION 4 // major, minor, release (alpha 1), build #
PRODUCTVERSION 4
FILEFLAGSMASK 0
FILEFLAGS 0 // final version
FILEOS VOS_DOS_WINDOWS16
FILETYPE VFT_DLL
FILESUBTYPE 0 // not used
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904E4" // Lang=US English, CharSet=Windows Multilingual
BEGIN
VALUE "CompanyName", "Netscape Communications Corporation\0"
VALUE "FileDescription", "Netscape 16-bit JavaScript-Java Module\0"
VALUE "FileVersion", "4.0\0"
VALUE "InternalName", "JSJ1640\0"
VALUE "LegalCopyright", "Copyright Netscape Communications. 1994-96\0"
VALUE "LegalTrademarks", "Netscape, Mozilla\0"
VALUE "OriginalFilename","JSJ1640.DLL\0"
VALUE "ProductName", "NETSCAPE\0"
VALUE "ProductVersion", "4.0\0"
END
END
END

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,403 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 Communicator client 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.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the definition of the JavaScript JavaArray class.
* Instances of JavaArray are used to reflect Java arrays.
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_private.h" /* LiveConnect internals */
/* Shorthands for ASCII (7-bit) decimal and hex conversion. */
#define JS7_ISDEC(c) (((c) >= '0') && ((c) <= '9'))
#define JS7_UNDEC(c) ((c) - '0')
/*
* Convert any jsval v to an integer jsval if ToString(v)
* contains a base-10 integer that fits into 31 bits.
* Otherwise return v.
*/
static jsval
try_convert_to_jsint(JSContext *cx, jsval idval)
{
const jschar *cp;
JSString *jsstr;
jsstr = JS_ValueToString(cx, idval);
if (!jsstr)
return idval;
cp = JS_GetStringChars(jsstr);
if (JS7_ISDEC(*cp)) {
jsuint index = JS7_UNDEC(*cp++);
jsuint oldIndex = 0;
jsuint c = 0;
if (index != 0) {
while (JS7_ISDEC(*cp)) {
oldIndex = index;
c = JS7_UNDEC(*cp);
index = 10*index + c;
cp++;
}
}
if (*cp == 0 &&
(oldIndex < (JSVAL_INT_MAX / 10) ||
(oldIndex == (JSVAL_INT_MAX / 10) && c < (JSVAL_INT_MAX % 10)))) {
return INT_TO_JSVAL(index);
}
}
return idval;
}
static JSBool
access_java_array_element(JSContext *cx,
JNIEnv *jEnv,
JSObject *obj,
jsid id,
jsval *vp,
JSBool do_assignment)
{
jsval idval;
jarray java_array;
JavaClassDescriptor *class_descriptor;
JavaObjectWrapper *java_wrapper;
jsize array_length, index;
JavaSignature *array_component_signature;
/* printf("In JavaArray_getProperty\n"); */
java_wrapper = JS_GetPrivate(cx, obj);
if (!java_wrapper) {
const char *property_name;
if (JS_IdToValue(cx, id, &idval) && JSVAL_IS_STRING(idval) &&
(property_name = JS_GetStringBytes(JSVAL_TO_STRING(idval))) != NULL) {
if (!strcmp(property_name, "constructor")) {
*vp = JSVAL_VOID;
return JS_TRUE;
}
}
JS_ReportError(cx, "illegal operation on JavaArray prototype object");
return JS_FALSE;
}
class_descriptor = java_wrapper->class_descriptor;
java_array = java_wrapper->java_obj;
PR_ASSERT(class_descriptor->type == JAVA_SIGNATURE_ARRAY);
JS_IdToValue(cx, id, &idval);
if (!JSVAL_IS_INT(idval))
idval = try_convert_to_jsint(cx, idval);
if (!JSVAL_IS_INT(idval)) {
/*
* Usually, properties of JavaArray objects are indexed by integers, but
* Java arrays also inherit all the methods of java.lang.Object, so a
* string-valued property is also possible.
*/
if (JSVAL_IS_STRING(idval)) {
const char *member_name;
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
if (do_assignment) {
JSVersion version = JS_GetVersion(cx);
if (!JSVERSION_IS_ECMA(version)) {
JS_ReportError(cx, "Attempt to write to invalid Java array "
"element \"%s\"", member_name);
return JS_FALSE;
} else {
*vp = JSVAL_VOID;
return JS_TRUE;
}
} else {
if (!strcmp(member_name, "length")) {
array_length = jsj_GetJavaArrayLength(cx, jEnv, java_array);
if (array_length < 0)
return JS_FALSE;
if (vp)
*vp = INT_TO_JSVAL(array_length);
return JS_TRUE;
}
/* Check to see if we're reflecting a Java array method */
return JavaObject_getPropertyById(cx, obj, id, vp);
}
}
JS_ReportError(cx, "invalid Java array index expression");
return JS_FALSE;
}
index = JSVAL_TO_INT(idval);
#if 0
array_length = jsj_GetJavaArrayLength(cx, jEnv, java_array);
if (array_length < 0)
return JS_FALSE;
/* Just let Java throw an exception instead of checking array bounds here */
if (index < 0 || index >= array_length) {
JS_ReportError(cx, "Java array index %d out of range", index);
return JS_FALSE;
}
#endif
array_component_signature = class_descriptor->array_component_signature;
if (!vp)
return JS_TRUE;
if (do_assignment) {
return jsj_SetJavaArrayElement(cx, jEnv, java_array, index,
array_component_signature, *vp);
} else {
return jsj_GetJavaArrayElement(cx, jEnv, java_array, index,
array_component_signature, vp);
}
}
PR_STATIC_CALLBACK(JSBool)
JavaArray_getPropertyById(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
return access_java_array_element(cx, jEnv, obj, id, vp, JS_FALSE);
}
PR_STATIC_CALLBACK(JSBool)
JavaArray_setPropertyById(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
return access_java_array_element(cx, jEnv, obj, id, vp, JS_TRUE);
}
static JSBool
JavaArray_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
JSObject **objp, JSProperty **propp
#if defined JS_THREADSAFE && defined DEBUG
, const char *file, uintN line
#endif
)
{
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
return access_java_array_element(cx, jEnv, obj, id, NULL, JS_FALSE);
}
static JSBool
JavaArray_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
JSPropertyOp getter, JSPropertyOp setter,
uintN attrs, JSProperty **propp)
{
JS_ReportError(cx, "Cannot define a new property in a JavaArray");
return JS_FALSE;
}
static JSBool
JavaArray_getAttributes(JSContext *cx, JSObject *obj, jsid id,
JSProperty *prop, uintN *attrsp)
{
/* We don't maintain JS property attributes for Java class members */
*attrsp = JSPROP_PERMANENT|JSPROP_ENUMERATE;
return JS_FALSE;
}
static JSBool
JavaArray_setAttributes(JSContext *cx, JSObject *obj, jsid id,
JSProperty *prop, uintN *attrsp)
{
/* We don't maintain JS property attributes for Java class members */
if (*attrsp != (JSPROP_PERMANENT|JSPROP_ENUMERATE)) {
PR_ASSERT(0);
return JS_FALSE;
}
/* Silently ignore all setAttribute attempts */
return JS_TRUE;
}
static JSBool
JavaArray_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
JSVersion version = JS_GetVersion(cx);
*vp = JSVAL_FALSE;
if (!JSVERSION_IS_ECMA(version)) {
JS_ReportError(cx, "Properties of JavaArray objects may not be deleted");
return JS_FALSE;
} else {
/* Attempts to delete permanent properties are silently ignored
by ECMAScript. */
return JS_TRUE;
}
}
static JSBool
JavaArray_defaultValue(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
/* printf("In JavaArray_defaultValue()\n"); */
return JavaObject_convert(cx, obj, JSTYPE_STRING, vp);
}
static JSBool
JavaArray_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
jsval *statep, jsid *idp)
{
JavaObjectWrapper *java_wrapper;
JNIEnv *jEnv;
jsize array_length, index;
java_wrapper = JS_GetPrivate(cx, obj);
/* Check for prototype object */
if (!java_wrapper) {
*statep = JSVAL_NULL;
if (idp)
*idp = INT_TO_JSVAL(0);
return JS_TRUE;
}
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
array_length = jsj_GetJavaArrayLength(cx, jEnv, java_wrapper->java_obj);
if (array_length < 0)
return JS_FALSE;
switch(enum_op) {
case JSENUMERATE_INIT:
*statep = INT_TO_JSVAL(0);
if (idp)
*idp = INT_TO_JSVAL(array_length);
return JS_TRUE;
case JSENUMERATE_NEXT:
index = JSVAL_TO_INT(*statep);
if (index < array_length) {
JS_ValueToId(cx, INT_TO_JSVAL(index), idp);
index++;
*statep = INT_TO_JSVAL(index);
return JS_TRUE;
}
/* Fall through ... */
case JSENUMERATE_DESTROY:
*statep = JSVAL_NULL;
return JS_TRUE;
default:
PR_ASSERT(0);
return JS_FALSE;
}
}
static JSBool
JavaArray_checkAccess(JSContext *cx, JSObject *obj, jsid id,
JSAccessMode mode, jsval *vp, uintN *attrsp)
{
switch (mode) {
case JSACC_WATCH:
JS_ReportError(cx, "Cannot place watchpoints on JavaArray object properties");
return JS_FALSE;
case JSACC_IMPORT:
JS_ReportError(cx, "Cannot export a JavaArray object's properties");
return JS_FALSE;
default:
return JS_TRUE;
}
}
JSObjectOps JavaArray_ops = {
/* Mandatory non-null function pointer members. */
NULL, /* newObjectMap */
NULL, /* destroyObjectMap */
JavaArray_lookupProperty,
JavaArray_defineProperty,
JavaArray_getPropertyById, /* getProperty */
JavaArray_setPropertyById, /* setProperty */
JavaArray_getAttributes,
JavaArray_setAttributes,
JavaArray_deleteProperty,
JavaArray_defaultValue,
JavaArray_newEnumerate,
JavaArray_checkAccess,
/* Optionally non-null members start here. */
NULL, /* thisObject */
NULL, /* dropProperty */
NULL, /* call */
NULL, /* construct */
NULL, /* xdrObject */
NULL /* hasInstance */
};
static JSObjectOps *
JavaArray_getObjectOps(JSContext *cx, JSClass *clazz)
{
return &JavaArray_ops;
}
JSClass JavaArray_class = {
"JavaArray", JSCLASS_HAS_PRIVATE,
NULL, NULL, NULL, NULL,
NULL, NULL, JavaObject_convert, JavaObject_finalize,
JavaArray_getObjectOps,
};
extern PR_IMPORT_DATA(JSObjectOps) js_ObjectOps;
/* Initialize the JS JavaArray class */
JSBool
jsj_init_JavaArray(JSContext *cx, JSObject *global_obj)
{
JavaArray_ops.newObjectMap = js_ObjectOps.newObjectMap;
JavaArray_ops.destroyObjectMap = js_ObjectOps.destroyObjectMap;
if (!JS_InitClass(cx, global_obj,
0, &JavaArray_class, 0, 0,
0, 0, 0, 0))
return JS_FALSE;
return JS_TRUE;
}

View File

@@ -0,0 +1,586 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 Communicator client 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.
*/
/* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the native code implementation of JS's JavaClass class.
*
* A JavaClass is JavaScript's representation of a Java class.
* Its parent JS object is always a JavaPackage object. A JavaClass is not an
* exact reflection of Java's corresponding java.lang.Class object. Rather,
* the properties of a JavaClass are the static methods and properties of the
* corresponding Java class.
*
* Note that there is no runtime equivalent to the JavaClass class in Java.
* (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 first-class object that can be referenced simply as "java.lang.String".)
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_private.h" /* LiveConnect internals */
static JSBool
JavaClass_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
char *name;
JSString *str;
JavaClassDescriptor *class_descriptor;
class_descriptor = JS_GetPrivate(cx, obj);
if (!class_descriptor)
return JS_FALSE;
switch(type) {
case JSTYPE_STRING:
/* Convert '/' to '.' so that it looks like Java language syntax. */
if (!class_descriptor->name)
break;
name = PR_smprintf("[JavaClass %s]", class_descriptor->name);
if (!name) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
str = JS_NewString(cx, name, strlen(name));
if (!str) {
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);
return JS_TRUE;
default:
break;
}
return JS_TRUE;
}
static JSBool
lookup_static_member_by_id(JSContext *cx, JNIEnv *jEnv, JSObject *obj,
JavaClassDescriptor **class_descriptorp,
jsid id, JavaMemberDescriptor **memberp)
{
jsval idval;
JavaMemberDescriptor *member_descriptor;
const char *member_name;
JavaClassDescriptor *class_descriptor;
class_descriptor = JS_GetPrivate(cx, obj);
if (!class_descriptor) {
*class_descriptorp = NULL;
*memberp = NULL;
return JS_TRUE;
}
if (class_descriptorp)
*class_descriptorp = class_descriptor;
member_descriptor = jsj_LookupJavaStaticMemberDescriptorById(cx, jEnv, class_descriptor, id);
if (!member_descriptor) {
JS_IdToValue(cx, id, &idval);
if (!JSVAL_IS_STRING(idval)) {
JS_ReportError(cx, "invalid JavaClass property expression. "
"(methods and fields of a JavaClass object can only be identified by their name)");
return JS_FALSE;
}
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
/* Why do we have to do this ? */
if (!strcmp(member_name, "prototype")) {
*memberp = NULL;
return JS_TRUE;
}
JS_ReportError(cx, "Java class %s has no public static field or method named \"%s\"",
class_descriptor->name, member_name);
return JS_FALSE;
}
if (memberp)
*memberp = member_descriptor;
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
JavaClass_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
jsval idval;
jclass java_class;
const char *member_name;
JavaClassDescriptor *class_descriptor;
JavaMemberDescriptor *member_descriptor;
JNIEnv *jEnv;
/* printf("In JavaClass_getProperty\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (!lookup_static_member_by_id(cx, jEnv, obj, &class_descriptor, id, &member_descriptor))
return JS_FALSE;
if (!member_descriptor) {
*vp = JSVAL_VOID;
return JS_TRUE;
}
java_class = class_descriptor->java_class;
if (member_descriptor->field) {
if (!member_descriptor->methods) {
return jsj_GetJavaFieldValue(cx, jEnv, member_descriptor->field, java_class, vp);
} else {
PR_ASSERT(0);
}
} else {
JSFunction *function;
/* TODO - eliminate JSFUN_BOUND_METHOD */
JS_IdToValue(cx, id, &idval);
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
function = JS_NewFunction(cx, jsj_JavaStaticMethodWrapper, 0,
JSFUN_BOUND_METHOD, obj, member_name);
if (!function)
return JS_FALSE;
*vp = OBJECT_TO_JSVAL(JS_GetFunctionObject(function));
}
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
JavaClass_setPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
jclass java_class;
const char *member_name;
JavaClassDescriptor *class_descriptor;
JavaMemberDescriptor *member_descriptor;
jsval idval;
JNIEnv *jEnv;
/* printf("In JavaClass_setProperty\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (!lookup_static_member_by_id(cx, jEnv, obj, &class_descriptor, id, &member_descriptor))
return JS_FALSE;
/* Check for the case where there is a method with the given name, but no field
with that name */
if (!member_descriptor->field)
goto no_such_field;
/* Silently fail if field value is final (immutable), as required by ECMA spec */
if (member_descriptor->field->modifiers & ACC_FINAL)
return JS_TRUE;
java_class = class_descriptor->java_class;
return jsj_SetJavaFieldValue(cx, jEnv, member_descriptor->field, java_class, *vp);
no_such_field:
JS_IdToValue(cx, id, &idval);
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
JS_ReportError(cx, "No static field named \"%s\" in Java class %s",
member_name, class_descriptor->name);
return JS_FALSE;
}
/*
* Free the private native data associated with the JavaPackage object.
*/
PR_STATIC_CALLBACK(void)
JavaClass_finalize(JSContext *cx, JSObject *obj)
{
JNIEnv *jEnv;
JavaClassDescriptor *class_descriptor = JS_GetPrivate(cx, obj);
if (!class_descriptor)
return;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return;
/* printf("Finalizing %s\n", class_descriptor->name); */
jsj_ReleaseJavaClassDescriptor(cx, jEnv, class_descriptor);
}
static JSBool
JavaClass_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
JSObject **objp, JSProperty **propp
#if defined JS_THREADSAFE && defined DEBUG
, const char *file, uintN line
#endif
)
{
JNIEnv *jEnv;
JSErrorReporter old_reporter;
/* printf("In JavaClass_lookupProperty()\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
old_reporter = JS_SetErrorReporter(cx, NULL);
if (lookup_static_member_by_id(cx, jEnv, obj, NULL, id, NULL)) {
*objp = obj;
*propp = (JSProperty*)1;
} else {
*objp = NULL;
*propp = NULL;
}
JS_SetErrorReporter(cx, old_reporter);
return JS_TRUE;
}
static JSBool
JavaClass_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
JSPropertyOp getter, JSPropertyOp setter,
uintN attrs, JSProperty **propp)
{
JS_ReportError(cx, "Cannot define a new property in a JavaClass");
return JS_FALSE;
}
static JSBool
JavaClass_getAttributes(JSContext *cx, JSObject *obj, jsid id,
JSProperty *prop, uintN *attrsp)
{
/* We don't maintain JS property attributes for Java class members */
*attrsp = JSPROP_PERMANENT|JSPROP_ENUMERATE;
return JS_FALSE;
}
static JSBool
JavaClass_setAttributes(JSContext *cx, JSObject *obj, jsid id,
JSProperty *prop, uintN *attrsp)
{
/* We don't maintain JS property attributes for Java class members */
if (*attrsp != (JSPROP_PERMANENT|JSPROP_ENUMERATE)) {
PR_ASSERT(0);
return JS_FALSE;
}
/* Silently ignore all setAttribute attempts */
return JS_TRUE;
}
static JSBool
JavaClass_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
JSVersion version = JS_GetVersion(cx);
*vp = JSVAL_FALSE;
if (!JSVERSION_IS_ECMA(version)) {
JS_ReportError(cx, "Properties of JavaClass objects may not be deleted");
return JS_FALSE;
} else {
/* Attempts to delete permanent properties are silently ignored
by ECMAScript. */
return JS_TRUE;
}
}
static JSBool
JavaClass_defaultValue(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
/* printf("In JavaClass_defaultValue()\n"); */
return JavaClass_convert(cx, obj, JSTYPE_STRING, vp);
}
static JSBool
JavaClass_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
jsval *statep, jsid *idp)
{
JavaMemberDescriptor *member_descriptor;
JavaClassDescriptor *class_descriptor;
JNIEnv *jEnv;
class_descriptor = JS_GetPrivate(cx, obj);
/* Check for prototype JavaClass object */
if (!class_descriptor) {
*statep = JSVAL_NULL;
if (idp)
*idp = INT_TO_JSVAL(0);
return JS_TRUE;
}
switch(enum_op) {
case JSENUMERATE_INIT:
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
member_descriptor = jsj_GetClassStaticMembers(cx, jEnv, class_descriptor);
*statep = PRIVATE_TO_JSVAL(member_descriptor);
if (idp)
*idp = INT_TO_JSVAL(class_descriptor->num_instance_members);
return JS_TRUE;
case JSENUMERATE_NEXT:
member_descriptor = JSVAL_TO_PRIVATE(*statep);
if (member_descriptor) {
*idp = member_descriptor->id;
*statep = PRIVATE_TO_JSVAL(member_descriptor->next);
return JS_TRUE;
}
/* Fall through ... */
case JSENUMERATE_DESTROY:
*statep = JSVAL_NULL;
return JS_TRUE;
default:
PR_ASSERT(0);
return JS_FALSE;
}
}
static JSBool
JavaClass_checkAccess(JSContext *cx, JSObject *obj, jsid id,
JSAccessMode mode, jsval *vp, uintN *attrsp)
{
switch (mode) {
case JSACC_WATCH:
JS_ReportError(cx, "Cannot place watchpoints on JavaClass object properties");
return JS_FALSE;
case JSACC_IMPORT:
JS_ReportError(cx, "Cannot export a JavaClass object's properties");
return JS_FALSE;
default:
return JS_TRUE;
}
}
/*
* Implement the JavaScript instanceof operator for JavaClass objects by using
* the equivalent Java instanceof operation.
*/
static JSBool
JavaClass_hasInstance(JSContext *cx, JSObject *obj, jsval candidate_jsval,
JSBool *has_instancep)
{
JavaClassDescriptor *class_descriptor;
JavaObjectWrapper *java_wrapper;
JSClass *js_class;
JSBool has_instance;
JSObject *candidate_obj;
jclass java_class;
jobject java_obj;
JNIEnv *jEnv;
has_instance = JS_FALSE;
class_descriptor = JS_GetPrivate(cx, obj);
if (!class_descriptor) {
JS_ReportError(cx, "illegal operation on JavaClass prototype object");
return JS_FALSE;
}
/*
* Make sure that the thing to the left of the instanceof operator is a
* Java object.
*/
if (!JSVAL_IS_OBJECT(candidate_jsval))
goto done;
candidate_obj = JSVAL_TO_OBJECT(candidate_jsval);
js_class = JS_GetClass(candidate_obj);
if ((js_class != &JavaObject_class) && (js_class != &JavaArray_class))
goto done;
java_class = class_descriptor->java_class;
java_wrapper = JS_GetPrivate(cx, candidate_obj);
if (!java_wrapper) {
JS_ReportError(cx, "illegal operation on prototype object");
return JS_FALSE;
}
java_obj = java_wrapper->java_obj;
/* Get JNI pointer */
jsj_MapJSContextToJSJThread(cx, &jEnv);
has_instance = (*jEnv)->IsInstanceOf(jEnv, java_obj, java_class);
done:
*has_instancep = has_instance;
return JS_TRUE;
}
JSObjectOps JavaClass_ops = {
/* Mandatory non-null function pointer members. */
NULL, /* newObjectMap */
NULL, /* destroyObjectMap */
JavaClass_lookupProperty,
JavaClass_defineProperty,
JavaClass_getPropertyById, /* getProperty */
JavaClass_setPropertyById, /* setProperty */
JavaClass_getAttributes,
JavaClass_setAttributes,
JavaClass_deleteProperty,
JavaClass_defaultValue,
JavaClass_newEnumerate,
JavaClass_checkAccess,
/* Optionally non-null members start here. */
NULL, /* thisObject */
NULL, /* dropProperty */
jsj_JavaConstructorWrapper, /* call */
jsj_JavaConstructorWrapper, /* construct */
NULL, /* xdrObject */
JavaClass_hasInstance, /* hasInstance */
};
static JSObjectOps *
JavaClass_getObjectOps(JSContext *cx, JSClass *clazz)
{
return &JavaClass_ops;
}
JSClass JavaClass_class = {
"JavaClass", JSCLASS_HAS_PRIVATE,
NULL, NULL, NULL, NULL,
NULL, NULL, JavaClass_convert, JavaClass_finalize,
JavaClass_getObjectOps,
};
static JSObject *
jsj_new_JavaClass(JSContext *cx, JNIEnv *jEnv, JSObject* parent_obj,
JavaClassDescriptor *class_descriptor)
{
JSObject *JavaClass_obj;
JavaClass_obj = JS_NewObject(cx, &JavaClass_class, 0, parent_obj);
if (!JavaClass_obj)
return NULL;
JS_SetPrivate(cx, JavaClass_obj, (void *)class_descriptor);
#ifdef DEBUG
/* printf("JavaClass \'%s\' created\n", class_descriptor->name); */
#endif
return JavaClass_obj;
}
JSObject *
jsj_define_JavaClass(JSContext *cx, JNIEnv *jEnv, JSObject* parent_obj,
const char *simple_class_name,
jclass java_class)
{
JavaClassDescriptor *class_descriptor;
JSObject *JavaClass_obj;
class_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, java_class);
if (!class_descriptor)
return NULL;
JavaClass_obj = jsj_new_JavaClass(cx, jEnv, parent_obj, class_descriptor);
if (!JavaClass_obj)
return NULL;
if (!JS_DefineProperty(cx, parent_obj, simple_class_name,
OBJECT_TO_JSVAL(JavaClass_obj), 0, 0,
JSPROP_PERMANENT|JSPROP_READONLY|JSPROP_ENUMERATE))
return NULL;
return JavaClass_obj;
}
/*
* The getClass() native JS method is defined as a property of the global
* object. Given a JavaObject it returns the corresponding JavaClass. This
* is useful for accessing static methods and fields.
*
* js> getClass(new java.lang.String("foo"))
* [JavaClass java.lang.String]
*/
static JSBool
getClass(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSObject *obj_arg, *JavaClass_obj;
JavaObjectWrapper *java_wrapper;
JavaClassDescriptor *class_descriptor;
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (argc != 1 ||
!JSVAL_IS_OBJECT(argv[0]) ||
!(obj_arg = JSVAL_TO_OBJECT(argv[0])) ||
(!JS_InstanceOf(cx, obj_arg, &JavaObject_class, 0) &&
!JS_InstanceOf(cx, obj_arg, &JavaArray_class, 0))) {
JS_ReportError(cx, "getClass expects a Java object argument");
return JS_FALSE;
}
java_wrapper = JS_GetPrivate(cx, obj_arg);
if (!java_wrapper) {
JS_ReportError(cx, "getClass called on prototype object");
return JS_FALSE;
}
class_descriptor = java_wrapper->class_descriptor;
JavaClass_obj = jsj_new_JavaClass(cx, jEnv, NULL, class_descriptor);
if (!JavaClass_obj)
return JS_FALSE;
*rval = OBJECT_TO_JSVAL(JavaClass_obj);
return JS_TRUE;
}
extern PR_IMPORT_DATA(JSObjectOps) js_ObjectOps;
JSBool
jsj_init_JavaClass(JSContext *cx, JSObject *global_obj)
{
JavaClass_ops.newObjectMap = js_ObjectOps.newObjectMap;
JavaClass_ops.destroyObjectMap = js_ObjectOps.destroyObjectMap;
/* Define JavaClass class */
if (!JS_InitClass(cx, global_obj, 0, &JavaClass_class, 0, 0, 0, 0, 0, 0))
return JS_FALSE;
if (!JS_DefineFunction(cx, global_obj, "getClass", getClass, 0,
JSPROP_READONLY))
return JS_FALSE;
return jsj_InitJavaClassReflectionsTable();
}

View File

@@ -0,0 +1,134 @@
/* -*- Mode: C; tab-width: 8 -*-
* Copyright (C) 1998 Netscape Communications Corporation, All Rights Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the native code implementation of JS's JavaMember class.
* JavaMember's are a strange beast required only to handle the special case
* of a public field and a public method that appear in the same class and
* have the same name. When such a field/method is used in Java, the compiler
* can statically determine from context whether the field or the method is
* being referenced, but that is not possible with JavaScript. For example:
*
* ambiguousVal = javaObj.fieldOrMethod; // ambiguousVal is a JavaMember object
* a = ambiguousVal(); // ambiguousVal used as a method value
* b = ambiguousVal + 4; // ambiguousVal used as a field value
*
* A JavaMember instance carries both the captured value of the Java field and
* the method value until the context that the value is to be used in is known,
* at which point conversion forces the use of one or the other.
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_private.h" /* LiveConnect internals */
/* Private, native portion of a JavaMember */
typedef struct JavaMethodOrFieldValue {
jsval method_val;
jsval field_val;
} JavaMethodOrFieldValue;
JSObject *
jsj_CreateJavaMember(JSContext *cx, jsval method_val, jsval field_val)
{
JavaMethodOrFieldValue *member_val;
JSObject *JavaMember_obj;
member_val = (JavaMethodOrFieldValue *)JS_malloc(cx, sizeof(*member_val));
if (!member_val)
return NULL;
JavaMember_obj = JS_NewObject(cx, &JavaMember_class, 0, 0);
if (!JavaMember_obj) {
JS_free(cx, member_val);
return NULL;
}
JS_SetPrivate(cx, JavaMember_obj, (void *)member_val);
member_val->method_val = method_val;
JS_AddRoot(cx, &member_val->method_val);
member_val->field_val = field_val;
if (JSVAL_IS_GCTHING(field_val))
JS_AddRoot(cx, &member_val->field_val);
return JavaMember_obj;
}
static void
JavaMember_finalize(JSContext *cx, JSObject *obj)
{
JavaMethodOrFieldValue *member_val;
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return;
member_val = JS_GetPrivate(cx, obj);
if (!member_val)
return;
JS_RemoveRoot(cx, &member_val->method_val);
if (JSVAL_IS_GCTHING(member_val->method_val))
JS_RemoveRoot(cx, &member_val->method_val);
JS_free(cx, member_val);
}
static JSBool
JavaMember_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
JavaMethodOrFieldValue *member_val;
member_val = JS_GetPrivate(cx, obj);
if (!member_val) {
if (type == JSTYPE_OBJECT) {
*vp = OBJECT_TO_JSVAL(obj);
return JS_TRUE;
}
JS_ReportError(cx, "illegal operation on JavaObject prototype object");
return JS_FALSE;
}
switch (type) {
case JSTYPE_VOID:
case JSTYPE_STRING:
case JSTYPE_NUMBER:
case JSTYPE_BOOLEAN:
case JSTYPE_OBJECT:
*vp = member_val->field_val;
return JS_TRUE;
case JSTYPE_FUNCTION:
*vp = member_val->method_val;
return JS_TRUE;
default:
PR_ASSERT(0);
return JS_FALSE;
}
}
JSClass JavaMember_class = {
"JavaMember", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JavaMember_convert, JavaMember_finalize
};
extern PR_IMPORT_DATA(JSObjectOps) js_ObjectOps;
JSBool
jsj_init_JavaMember(JSContext *cx, JSObject *global_obj)
{
if (!JS_InitClass(cx, global_obj,
0, &JavaMember_class, 0, 0,
0, 0,
0, 0))
return JS_FALSE;
return JS_TRUE;
}

View File

@@ -0,0 +1,686 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 Communicator client 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.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the native code implementation of JS's JavaObject class.
*
* An instance of JavaObject is the JavaScript reflection of a Java object.
*
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_private.h" /* LiveConnect internals */
#include "jsj_hash.h" /* Hash table with Java object as key */
/*
* This is a hash table that maps from Java objects to JS objects.
* It is used to ensure that the same JS object is obtained when a Java
* object is reflected more than once, so that JS object equality tests
* work in the expected manner, i.e. the "==" and "===" operators.
*
* The table entry keys are Java objects (of type jobject) and the entry values
* are JSObject pointers. Because the jobject type is an opaque handle and
* not necessarily a pointer, the hashing and key comparison functions must
* invoke the appropriate JVM functions.
*
* When the corresponding JS object instance is finalized, the entry is
* removed from the table, and a Java GC root for the Java object is removed.
*/
static JSJHashTable *java_obj_reflections = NULL;
#ifdef JS_THREADSAFE
static PRMonitor *java_obj_reflections_monitor = NULL;
#endif
static JSBool
init_java_obj_reflections_table()
{
java_obj_reflections =
JSJ_NewHashTable(512, jsj_HashJavaObject, jsj_JavaObjectComparator,
NULL, NULL, NULL);
if (!java_obj_reflections)
return JS_FALSE;
#ifdef JS_THREADSAFE
java_obj_reflections_monitor = PR_NewNamedMonitor("java_obj_reflections");
if (!java_obj_reflections_monitor) {
PR_HashTableDestroy(java_obj_reflections);
return JS_FALSE;
}
#endif
return JS_TRUE;
}
JSObject *
jsj_WrapJavaObject(JSContext *cx,
JNIEnv *jEnv,
jobject java_obj,
jclass java_class)
{
JSJHashNumber hash_code;
JSClass *js_class;
JSObject *js_wrapper_obj;
JavaObjectWrapper *java_wrapper;
JavaClassDescriptor *class_descriptor;
JSJHashEntry *he, **hep;
js_wrapper_obj = NULL;
hash_code = jsj_HashJavaObject((void*)java_obj, (void*)jEnv);
#ifdef JS_THREADSAFE
PR_EnterMonitor(java_obj_reflections_monitor);
#endif
hep = JSJ_HashTableRawLookup(java_obj_reflections,
hash_code, java_obj, (void*)jEnv);
he = *hep;
if (he) {
js_wrapper_obj = (JSObject *)he->value;
if (js_wrapper_obj)
goto done;
}
/* No existing reflection found. Construct a new one */
class_descriptor = jsj_GetJavaClassDescriptor(cx, jEnv, java_class);
if (!class_descriptor)
goto done;
if (class_descriptor->type == JAVA_SIGNATURE_ARRAY) {
js_class = &JavaArray_class;
} else {
PR_ASSERT(class_descriptor->type == JAVA_SIGNATURE_CLASS);
js_class = &JavaObject_class;
}
/* Create new JS object to reflect Java object */
js_wrapper_obj = JS_NewObject(cx, js_class, NULL, NULL);
if (!js_wrapper_obj)
goto done;
/* Create private, native portion of JavaObject */
java_wrapper =
(JavaObjectWrapper *)JS_malloc(cx, sizeof(JavaObjectWrapper));
if (!java_wrapper) {
jsj_ReleaseJavaClassDescriptor(cx, jEnv, class_descriptor);
goto done;
}
JS_SetPrivate(cx, js_wrapper_obj, java_wrapper);
java_wrapper->class_descriptor = class_descriptor;
java_obj = (*jEnv)->NewGlobalRef(jEnv, java_obj);
java_wrapper->java_obj = java_obj;
if (!java_obj)
goto out_of_memory;
/* Add the JavaObject to the hash table */
he = JSJ_HashTableRawAdd(java_obj_reflections, hep, hash_code,
java_obj, js_wrapper_obj, (void*)jEnv);
if (!he) {
(*jEnv)->DeleteGlobalRef(jEnv, java_obj);
goto out_of_memory;
}
done:
#ifdef JS_THREADSAFE
PR_ExitMonitor(java_obj_reflections_monitor);
#endif
return js_wrapper_obj;
out_of_memory:
/* No need to free js_wrapper_obj, as it will be finalized by GC. */
JS_ReportOutOfMemory(cx);
js_wrapper_obj = NULL;
goto done;
}
static void
remove_java_obj_reflection_from_hashtable(jobject java_obj, JNIEnv *jEnv)
{
JSJHashNumber hash_code;
JSJHashEntry *he, **hep;
hash_code = jsj_HashJavaObject((void*)java_obj, (void*)jEnv);
#ifdef JS_THREADSAFE
PR_EnterMonitor(java_obj_reflections_monitor);
#endif
hep = JSJ_HashTableRawLookup(java_obj_reflections, hash_code,
java_obj, (void*)jEnv);
he = *hep;
PR_ASSERT(he);
if (he)
JSJ_HashTableRawRemove(java_obj_reflections, hep, he, (void*)jEnv);
#ifdef JS_THREADSAFE
PR_ExitMonitor(java_obj_reflections_monitor);
#endif
}
void
JavaObject_finalize(JSContext *cx, JSObject *obj)
{
JavaObjectWrapper *java_wrapper;
jobject java_obj;
JNIEnv *jEnv;
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return;
java_wrapper = JS_GetPrivate(cx, obj);
if (!java_wrapper)
return;
java_obj = java_wrapper->java_obj;
if (java_obj) {
remove_java_obj_reflection_from_hashtable(java_obj, jEnv);
(*jEnv)->DeleteGlobalRef(jEnv, java_obj);
}
jsj_ReleaseJavaClassDescriptor(cx, jEnv, java_wrapper->class_descriptor);
JS_free(cx, java_wrapper);
}
/* Trivial helper for jsj_DiscardJavaObjReflections(), below */
static PRIntn
enumerate_remove_java_obj(JSJHashEntry *he, PRIntn i, void *arg)
{
JNIEnv *jEnv = (JNIEnv*)arg;
jobject java_obj;
JavaObjectWrapper *java_wrapper;
JSObject *java_wrapper_obj;
java_wrapper_obj = (JSObject *)he->value;
java_wrapper = JS_GetPrivate(NULL, java_wrapper_obj);
java_obj = java_wrapper->java_obj;
(*jEnv)->DeleteGlobalRef(jEnv, java_obj);
java_wrapper->java_obj = NULL;
return HT_ENUMERATE_REMOVE;
}
/* This shutdown routine discards all JNI references to Java objects
that have been reflected into JS, even if there are still references
to them from JS. */
void
jsj_DiscardJavaObjReflections(JNIEnv *jEnv)
{
if (java_obj_reflections) {
JSJ_HashTableEnumerateEntries(java_obj_reflections,
enumerate_remove_java_obj,
(void*)jEnv);
JSJ_HashTableDestroy(java_obj_reflections);
java_obj_reflections = NULL;
}
}
PR_CALLBACK JSBool
JavaObject_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
JavaObjectWrapper *java_wrapper;
JavaClassDescriptor *class_descriptor;
jobject java_obj;
JNIEnv *jEnv;
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
java_wrapper = JS_GetPrivate(cx, obj);
if (!java_wrapper) {
if (type == JSTYPE_OBJECT) {
*vp = OBJECT_TO_JSVAL(obj);
return JS_TRUE;
}
JS_ReportError(cx, "illegal operation on JavaObject prototype object");
return JS_FALSE;
}
java_obj = java_wrapper->java_obj;
class_descriptor = java_wrapper->class_descriptor;
switch (type) {
case JSTYPE_OBJECT:
*vp = OBJECT_TO_JSVAL(obj);
return JS_TRUE;
case JSTYPE_FUNCTION:
JS_ReportError(cx, "can't convert Java object to function");
return JS_FALSE;
case JSTYPE_VOID:
case JSTYPE_STRING:
/* Either extract a C-string from the java.lang.String object
or call the Java toString() method */
return jsj_ConvertJavaObjectToJSString(cx, jEnv, class_descriptor, java_obj, vp);
case JSTYPE_NUMBER:
/* Call Java doubleValue() method, if applicable */
return jsj_ConvertJavaObjectToJSNumber(cx, jEnv, class_descriptor, java_obj, vp);
case JSTYPE_BOOLEAN:
/* Call booleanValue() method, if applicable */
return jsj_ConvertJavaObjectToJSBoolean(cx, jEnv, class_descriptor, java_obj, vp);
default:
PR_ASSERT(0);
return JS_FALSE;
}
}
static JSBool
lookup_member_by_id(JSContext *cx, JNIEnv *jEnv, JSObject *obj,
JavaObjectWrapper **java_wrapperp,
jsid id,
JavaMemberDescriptor **member_descriptorp)
{
jsval idval;
JavaObjectWrapper *java_wrapper;
JavaMemberDescriptor *member_descriptor;
const char *member_name, *property_name;
JavaClassDescriptor *class_descriptor;
java_wrapper = JS_GetPrivate(cx, obj);
if (!java_wrapper) {
if (JS_IdToValue(cx, id, &idval) && JSVAL_IS_STRING(idval) &&
(property_name = JS_GetStringBytes(JSVAL_TO_STRING(idval))) != NULL) {
if (!strcmp(property_name, "constructor")) {
*java_wrapperp = NULL;
*member_descriptorp = NULL;
return JS_TRUE;
}
}
JS_ReportError(cx, "illegal operation on JavaObject prototype object");
return JS_FALSE;
}
class_descriptor = java_wrapper->class_descriptor;
PR_ASSERT(class_descriptor->type == JAVA_SIGNATURE_CLASS ||
class_descriptor->type == JAVA_SIGNATURE_ARRAY);
member_descriptor = jsj_LookupJavaMemberDescriptorById(cx, jEnv, class_descriptor, id);
if (!member_descriptor) {
JS_IdToValue(cx, id, &idval);
if (!JSVAL_IS_STRING(idval)) {
JS_ReportError(cx, "invalid JavaObject property expression. "
"(methods and field properties of a JavaObject object can only be strings)");
return JS_FALSE;
}
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
JS_ReportError(cx, "Java class %s has no public instance field or "
"method named \"%s\"",
class_descriptor->name, member_name);
return JS_FALSE;
}
/* Success. Handle the multiple return values */
if (java_wrapperp)
*java_wrapperp = java_wrapper;
if (member_descriptorp)
*member_descriptorp = member_descriptor;
return JS_TRUE;
}
PR_CALLBACK JSBool
JavaObject_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
jobject java_obj;
JavaMemberDescriptor *member_descriptor;
JavaObjectWrapper *java_wrapper;
JNIEnv *jEnv;
JSObject *funobj;
jsval field_val, method_val;
JSBool success;
/* printf("In JavaObject_getProperty\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (!lookup_member_by_id(cx, jEnv, obj, &java_wrapper, id, &member_descriptor))
return JS_FALSE;
/* Handle access to "constructor" property of prototype object with
silent failure. */
if (!member_descriptor) {
*vp = JSVAL_VOID;
return JS_TRUE;
}
java_obj = java_wrapper->java_obj;
field_val = method_val = JSVAL_VOID;
/* If a field member, get the value of the field */
if (member_descriptor->field) {
success = jsj_GetJavaFieldValue(cx, jEnv, member_descriptor->field, java_obj, &field_val);
if (!success)
return JS_FALSE;
}
/* If a method member, build a wrapper around the Java method */
if (member_descriptor->methods) {
/* Create a function object with this JavaObject as its parent, so that
JSFUN_BOUND_METHOD binds it as the default 'this' for the function. */
funobj = JS_CloneFunctionObject(cx, member_descriptor->invoke_func_obj, obj);
if (!funobj)
return JS_FALSE;
method_val = OBJECT_TO_JSVAL(funobj);
}
#if TEST_JAVAMEMBER
/* Always create a JavaMember object, even though it's inefficient */
obj = jsj_CreateJavaMember(cx, method_val, field_val);
if (!obj)
return JS_FALSE;
*vp = OBJECT_TO_JSVAL(obj);
#else /* !TEST_JAVAMEMBER */
if (member_descriptor->field) {
if (!member_descriptor->methods) {
/* Return value of Java field */
*vp = field_val;
} else {
/* Handle special case of access to a property that could refer
to either a Java field or a method that share the same name.
In Java, such ambiguity is not possible because the compiler
can statically determine which is being accessed. */
obj = jsj_CreateJavaMember(cx, method_val, field_val);
if (!obj)
return JS_FALSE;
*vp = OBJECT_TO_JSVAL(obj);
}
} else {
/* Return wrapper around Java method */
*vp = method_val;
}
#endif /* !TEST_JAVAMEMBER */
return JS_TRUE;
}
PR_STATIC_CALLBACK(JSBool)
JavaObject_setPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
jobject java_obj;
const char *member_name;
JavaObjectWrapper *java_wrapper;
JavaClassDescriptor *class_descriptor;
JavaMemberDescriptor *member_descriptor;
jsval idval;
JNIEnv *jEnv;
/* printf("In JavaObject_setProperty\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
if (!lookup_member_by_id(cx, jEnv, obj, &java_wrapper, id, &member_descriptor))
return JS_FALSE;
/* Check for the case where there is a method with the give name, but no field
with that name */
if (!member_descriptor->field)
goto no_such_field;
/* Silently fail if field value is final (immutable), as required by ECMA spec */
if (member_descriptor->field->modifiers & ACC_FINAL)
return JS_TRUE;
java_obj = java_wrapper->java_obj;
return jsj_SetJavaFieldValue(cx, jEnv, member_descriptor->field, java_obj, *vp);
no_such_field:
JS_IdToValue(cx, id, &idval);
member_name = JS_GetStringBytes(JSVAL_TO_STRING(idval));
class_descriptor = java_wrapper->class_descriptor;
JS_ReportError(cx, "No instance field named \"%s\" in Java class %s",
member_name, class_descriptor->name);
return JS_FALSE;
}
static JSBool
JavaObject_lookupProperty(JSContext *cx, JSObject *obj, jsid id,
JSObject **objp, JSProperty **propp
#if defined JS_THREADSAFE && defined DEBUG
, const char *file, uintN line
#endif
)
{
JNIEnv *jEnv;
JSErrorReporter old_reporter;
/* printf("In JavaObject_lookupProperty()\n"); */
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
old_reporter = JS_SetErrorReporter(cx, NULL);
if (lookup_member_by_id(cx, jEnv, obj, NULL, id, NULL)) {
*objp = obj;
*propp = (JSProperty*)1;
} else {
*objp = NULL;
*propp = NULL;
}
JS_SetErrorReporter(cx, old_reporter);
return JS_TRUE;
}
static JSBool
JavaObject_defineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
JSPropertyOp getter, JSPropertyOp setter,
uintN attrs, JSProperty **propp)
{
JS_ReportError(cx, "Cannot define a new property in a JavaObject");
return JS_FALSE;
}
static JSBool
JavaObject_getAttributes(JSContext *cx, JSObject *obj, jsid id,
JSProperty *prop, uintN *attrsp)
{
/* We don't maintain JS property attributes for Java class members */
*attrsp = JSPROP_PERMANENT|JSPROP_ENUMERATE;
return JS_FALSE;
}
static JSBool
JavaObject_setAttributes(JSContext *cx, JSObject *obj, jsid id,
JSProperty *prop, uintN *attrsp)
{
/* We don't maintain JS property attributes for Java class members */
if (*attrsp != (JSPROP_PERMANENT|JSPROP_ENUMERATE)) {
PR_ASSERT(0);
return JS_FALSE;
}
/* Silently ignore all setAttribute attempts */
return JS_TRUE;
}
static JSBool
JavaObject_deleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
JSVersion version = JS_GetVersion(cx);
*vp = JSVAL_FALSE;
if (!JSVERSION_IS_ECMA(version)) {
JS_ReportError(cx, "Properties of JavaObject objects may not be deleted");
return JS_FALSE;
} else {
/* Attempts to delete permanent properties are silently ignored
by ECMAScript. */
return JS_TRUE;
}
}
static JSBool
JavaObject_defaultValue(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
{
/* printf("In JavaObject_defaultValue()\n"); */
return JavaObject_convert(cx, obj, type, vp);
}
static JSBool
JavaObject_newEnumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
jsval *statep, jsid *idp)
{
JavaObjectWrapper *java_wrapper;
JavaMemberDescriptor *member_descriptor;
JavaClassDescriptor *class_descriptor;
JNIEnv *jEnv;
java_wrapper = JS_GetPrivate(cx, obj);
/* Check for prototype object */
if (!java_wrapper) {
*statep = JSVAL_NULL;
if (idp)
*idp = INT_TO_JSVAL(0);
return JS_TRUE;
}
class_descriptor = java_wrapper->class_descriptor;
switch(enum_op) {
case JSENUMERATE_INIT:
/* Get the Java per-thread environment pointer for this JSContext */
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
member_descriptor = jsj_GetClassInstanceMembers(cx, jEnv, class_descriptor);
*statep = PRIVATE_TO_JSVAL(member_descriptor);
if (idp)
*idp = INT_TO_JSVAL(class_descriptor->num_instance_members);
return JS_TRUE;
case JSENUMERATE_NEXT:
member_descriptor = JSVAL_TO_PRIVATE(*statep);
if (member_descriptor) {
*idp = member_descriptor->id;
*statep = PRIVATE_TO_JSVAL(member_descriptor->next);
return JS_TRUE;
}
/* Fall through ... */
case JSENUMERATE_DESTROY:
*statep = JSVAL_NULL;
return JS_TRUE;
default:
PR_ASSERT(0);
return JS_FALSE;
}
}
static JSBool
JavaObject_checkAccess(JSContext *cx, JSObject *obj, jsid id,
JSAccessMode mode, jsval *vp, uintN *attrsp)
{
switch (mode) {
case JSACC_WATCH:
JS_ReportError(cx, "Cannot place watchpoints on JavaObject object properties");
return JS_FALSE;
case JSACC_IMPORT:
JS_ReportError(cx, "Cannot export a JavaObject object's properties");
return JS_FALSE;
default:
return JS_TRUE;
}
}
JSObjectOps JavaObject_ops = {
/* Mandatory non-null function pointer members. */
NULL, /* newObjectMap */
NULL, /* destroyObjectMap */
JavaObject_lookupProperty,
JavaObject_defineProperty,
JavaObject_getPropertyById, /* getProperty */
JavaObject_setPropertyById, /* setProperty */
JavaObject_getAttributes,
JavaObject_setAttributes,
JavaObject_deleteProperty,
JavaObject_defaultValue,
JavaObject_newEnumerate,
JavaObject_checkAccess,
/* Optionally non-null members start here. */
NULL, /* thisObject */
NULL, /* dropProperty */
NULL, /* call */
NULL, /* construct */
NULL, /* xdrObject */
NULL, /* hasInstance */
};
static JSObjectOps *
JavaObject_getObjectOps(JSContext *cx, JSClass *clazz)
{
return &JavaObject_ops;
}
JSClass JavaObject_class = {
"JavaObject", JSCLASS_HAS_PRIVATE,
NULL, NULL, NULL, NULL,
NULL, NULL, JavaObject_convert, JavaObject_finalize,
JavaObject_getObjectOps,
};
extern PR_IMPORT_DATA(JSObjectOps) js_ObjectOps;
JSBool
jsj_init_JavaObject(JSContext *cx, JSObject *global_obj)
{
JavaObject_ops.newObjectMap = js_ObjectOps.newObjectMap;
JavaObject_ops.destroyObjectMap = js_ObjectOps.destroyObjectMap;
if (!JS_InitClass(cx, global_obj,
0, &JavaObject_class, 0, 0,
0, 0,
0, 0))
return JS_FALSE;
return init_java_obj_reflections_table();
}

View File

@@ -0,0 +1,500 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 Communicator client 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.
*/
/* 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)
{
JSObject *package_obj;
JavaPackage_Private *package;
package_obj = JS_DefineObject(cx, parent_obj, obj_name, &JavaPackage_class, 0,
JSPROP_PERMANENT | JSPROP_READONLY);
if (!package_obj)
return NULL;
/* Attach private, native data to the JS object */
package = (JavaPackage_Private *)JS_malloc(cx, sizeof(JavaPackage_Private));
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_ReportError(cx, "illegal attempt to add property to "
"JavaPackage prototype object");
return JS_FALSE;
}
JS_ReportError(cx, "You may not add properties to a JavaPackage object");
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;
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 = PR_smprintf("%s%s%s", path, (path[0] ? "/" : ""), subPath);
if (!newPath) {
JS_ReportOutOfMemory(cx);
return JS_FALSE;
}
jsj_MapJSContextToJSJThread(cx, &jEnv);
if (!jEnv)
return JS_FALSE;
/*
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);
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);
/* beard: this has to be done here, so built-in classes will be defined. */
/* Painful hack for pre_define_java_packages() */
if (quiet_resolve_failure) {
ok = JS_FALSE;
goto out;
}
/*
* 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 *package;
package = JS_GetPrivate(cx, obj);
if (package->flags & PKG_SYSTEM) {
char *msg, *cp;
msg = PR_smprintf("No Java system package with name \"%s\" was identified "
"and no Java class with that name exists either",
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_ReportError(cx, msg);
free((char*)msg);
}
ok = JS_FALSE;
goto out;
}
}
if (!define_JavaPackage(cx, obj, subPath, newPath, 0)) {
ok = JS_FALSE;
goto out;
}
#ifdef DEBUG
/* printf("JavaPackage \'%s\' created\n", newPath); */
#endif
}
out:
free(newPath);
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 = PR_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) {
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
};
JavaPackageDef
standard_java_packages[] = {
{"java", NULL, PKG_USER},
{"java.applet", NULL, PKG_USER},
{"java.awt", NULL, PKG_USER},
{"java.awt.datatransfer",
NULL, PKG_SYSTEM},
{"java.awt.event", NULL, PKG_SYSTEM},
{"java.awt.image", NULL, PKG_SYSTEM},
{"java.awt.peer", NULL, PKG_SYSTEM},
{"java.beans", NULL, PKG_USER},
{"java.io", NULL, PKG_SYSTEM},
{"java.lang", NULL, PKG_USER},
{"java.lang.reflect", NULL, PKG_SYSTEM},
{"java.math", NULL, PKG_SYSTEM},
{"java.net", NULL, PKG_USER},
{"java.rmi", NULL, PKG_USER},
{"java.rmi.dgc", NULL, PKG_USER},
{"java.rmi.user", NULL, PKG_USER},
{"java.rmi.registry", NULL, PKG_USER},
{"java.rmi.server", NULL, PKG_USER},
{"java.security", NULL, PKG_USER},
{"java.security.acl", NULL, PKG_SYSTEM},
{"java.security.interfaces",
NULL, PKG_SYSTEM},
{"java.sql", NULL, PKG_USER},
{"java.text", NULL, PKG_USER},
{"java.text.resources", NULL, PKG_SYSTEM},
{"java.util", NULL, PKG_USER},
{"java.util.zip", NULL, PKG_SYSTEM},
{"netscape", NULL, PKG_USER},
{"netscape.applet", NULL, PKG_SYSTEM},
{"netscape.application",NULL, PKG_SYSTEM},
{"netscape.debug", NULL, PKG_SYSTEM},
{"netscape.javascript", NULL, PKG_SYSTEM},
{"netscape.ldap", NULL, PKG_SYSTEM},
{"netscape.misc", NULL, PKG_SYSTEM},
{"netscape.net", NULL, PKG_SYSTEM},
{"netscape.plugin", NULL, PKG_SYSTEM},
{"netscape.util", NULL, PKG_SYSTEM},
{"netscape.secfile", NULL, PKG_SYSTEM},
{"netscape.security", NULL, PKG_SYSTEM},
{"netscape.WAI", NULL, PKG_SYSTEM},
{"sun", NULL, PKG_USER},
{"Packages", "", PKG_USER},
{NULL, NULL, 0}
};
/*
* 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++) {
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(package_name, "."); 1; simple_name = strtok(NULL, ".")) {
jsval v;
if (!simple_name) {
JS_ReportError(cx, "Package %s defined twice ?", package_name);
goto error;
}
/* 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;
} else {
/* New package objects should only be created at the terminal
sub-package in a fully-qualified package-name */
if (strtok(NULL, ".")) {
JS_ReportError(cx, "Illegal predefined package definition for %s",
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);
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}
};
/*
* 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;
}

View File

@@ -0,0 +1,180 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 Communicator client 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.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the code for reading and writing elements of a Java array.
*/
#include "jsj_private.h" /* LiveConnect internals */
/*
* Read the Java value at a given index into a Java array and convert it
* to a JS value. The array_component_signature describes the type of
* the resulting Java value, which can be a primitive type or an object type.
* More specifically it can be an array type in the case of multidimensional
* arrays.
*/
JSBool
jsj_GetJavaArrayElement(JSContext *cx, JNIEnv *jEnv, jarray java_array, jsize index,
JavaSignature *array_component_signature,
jsval *vp)
{
jvalue java_value;
JavaSignatureChar component_type;
#define GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Type,member) \
(*jEnv)->Get##Type##ArrayRegion(jEnv, java_array, index, 1, \
&java_value.member); \
if ((*jEnv)->ExceptionOccurred(jEnv)) { \
jsj_ReportJavaError(cx, jEnv, "Error reading element of " \
"Java primitive array"); \
return JS_FALSE; \
}
component_type = array_component_signature->type;
switch(component_type) {
case JAVA_SIGNATURE_BYTE:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Byte,b);
break;
case JAVA_SIGNATURE_CHAR:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Char,c);
break;
case JAVA_SIGNATURE_SHORT:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Short,s);
break;
case JAVA_SIGNATURE_INT:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Int,i);
break;
case JAVA_SIGNATURE_BOOLEAN:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Boolean,z);
break;
case JAVA_SIGNATURE_LONG:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Long,j);
break;
case JAVA_SIGNATURE_FLOAT:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Float,f);
break;
case JAVA_SIGNATURE_DOUBLE:
GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Double,d);
break;
case JAVA_SIGNATURE_CLASS:
case JAVA_SIGNATURE_ARRAY:
java_value.l = (*jEnv)->GetObjectArrayElement(jEnv, java_array, index);
if ((*jEnv)->ExceptionOccurred(jEnv)) {
jsj_ReportJavaError(cx, jEnv, "Error reading Java object array");
return JS_FALSE;
}
break;
#undef GET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY
default:
PR_ASSERT(0); /* Unknown java type signature */
return JS_FALSE;
}
return jsj_ConvertJavaValueToJSValue(cx, jEnv, array_component_signature, &java_value, vp);
}
JSBool
jsj_SetJavaArrayElement(JSContext *cx, JNIEnv *jEnv, jarray java_array, jsize index,
JavaSignature *array_component_signature,
jsval js_val)
{
int dummy_cost;
jvalue java_value;
JavaSignatureChar component_type;
JSBool is_local_ref;
if (!jsj_ConvertJSValueToJavaValue(cx, jEnv, js_val, array_component_signature,
&dummy_cost, &java_value, &is_local_ref))
return JS_FALSE;
#define SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Type,member) \
(*jEnv)->Set##Type##ArrayRegion(jEnv, java_array, index, 1, \
&java_value.member); \
if ((*jEnv)->ExceptionOccurred(jEnv)) { \
jsj_ReportJavaError(cx, jEnv, "Error assigning to element of " \
"Java primitive array"); \
return JS_FALSE; \
}
component_type = array_component_signature->type;
switch(component_type) {
case JAVA_SIGNATURE_BYTE:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Byte,b);
break;
case JAVA_SIGNATURE_CHAR:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Char,c);
break;
case JAVA_SIGNATURE_SHORT:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Short,s);
break;
case JAVA_SIGNATURE_INT:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Int,i);
break;
case JAVA_SIGNATURE_BOOLEAN:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Boolean,z);
break;
case JAVA_SIGNATURE_LONG:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Long,j);
break;
case JAVA_SIGNATURE_FLOAT:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Float,f);
break;
case JAVA_SIGNATURE_DOUBLE:
SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY(Double,d);
break;
case JAVA_SIGNATURE_CLASS:
case JAVA_SIGNATURE_ARRAY:
(*jEnv)->SetObjectArrayElement(jEnv, java_array, index, java_value.l);
if (is_local_ref) \
(*jEnv)->DeleteLocalRef(jEnv, java_value.l);
if ((*jEnv)->ExceptionOccurred(jEnv)) {
jsj_ReportJavaError(cx, jEnv, "Error assigning to Java object array");
return JS_FALSE;
}
break;
#undef SET_ELEMENT_FROM_PRIMITIVE_JAVA_ARRAY
default:
PR_ASSERT(0); /* Unknown java type signature */
return JS_FALSE;
}
return JS_TRUE;
}

View File

@@ -0,0 +1,605 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 Communicator client 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.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the code that constructs and manipulates JavaClassDescriptor
* structs, which are the native wrappers for Java classes.
* JavaClassDescriptors are used to describe the signatures of methods and
* fields. There is a JavaClassDescriptor associated with the reflection of
* each Java Object.
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_private.h" /* LiveConnect internals */
#include "jsj_hash.h" /* Hash tables */
/* A one-to-one mapping between all referenced java.lang.Class objects and
their corresponding JavaClassDescriptor objects */
static JSJHashTable *java_class_reflections;
/*
* Given a JVM handle to a java.lang.Class object, malloc a C-string
* containing the UTF8 encoding of the fully qualified name of the class.
* It's the caller's responsibility to free the returned string.
*
* If an error occurs, NULL is returned and the error reporter called.
*/
const char *
jsj_GetJavaClassName(JSContext *cx, JNIEnv *jEnv, jclass java_class)
{
jstring java_class_name_jstr;
const char *java_class_name;
/* Get java.lang.String object containing class name */
java_class_name_jstr =
(*jEnv)->CallObjectMethod(jEnv, java_class, jlClass_getName);
if (!java_class_name_jstr)
goto error;
/* Convert to UTF8 encoding and copy */
java_class_name = jsj_DupJavaStringUTF(cx, jEnv, java_class_name_jstr);
if (!java_class_name)
return NULL;
return java_class_name;
error:
jsj_UnexpectedJavaError(cx, jEnv, "Can't get Java class name using"
"java.lang.Class.getName()");
return NULL;
}
/*
* Convert in-place a string of the form "java.lang.String" into "java/lang/String".
* Though the former style is conventionally used by Java programmers, the latter is
* what the JNI functions require.
*/
void
jsj_MakeJNIClassname(char * class_name)
{
char * c;
for (c = class_name; *c; c++)
if (*c == '.')
*c = '/';
}
/*
* Classify an instance of java.lang.Class as either one of the primitive
* types, e.g. int, char, etc., as an array type or as a non-array object type
* (subclass of java.lang.Object) by returning the appropriate enum member.
*
*/
static JavaSignatureChar
get_signature_type(JSContext *cx, JavaClassDescriptor *class_descriptor)
{
JavaSignatureChar type;
const char *java_class_name;
/* Get UTF8 encoding of class name */
java_class_name = class_descriptor->name;
PR_ASSERT(java_class_name);
if (!java_class_name)
return JAVA_SIGNATURE_UNKNOWN;
if (!strcmp(java_class_name, "byte"))
type = JAVA_SIGNATURE_BYTE;
else if (!strcmp(java_class_name, "char"))
type = JAVA_SIGNATURE_CHAR;
else if (!strcmp(java_class_name, "float"))
type = JAVA_SIGNATURE_FLOAT;
else if (!strcmp(java_class_name, "double"))
type = JAVA_SIGNATURE_DOUBLE;
else if (!strcmp(java_class_name, "int"))
type = JAVA_SIGNATURE_INT;
else if (!strcmp(java_class_name, "long"))
type = JAVA_SIGNATURE_LONG;
else if (!strcmp(java_class_name, "short"))
type = JAVA_SIGNATURE_SHORT;
else if (!strcmp(java_class_name, "boolean"))
type = JAVA_SIGNATURE_BOOLEAN;
else if (!strcmp(java_class_name, "void"))
type = JAVA_SIGNATURE_VOID;
else
/* Well, I guess it's a Java class, then. */
type = JAVA_SIGNATURE_CLASS;
return type;
}
static JSBool
is_java_array_class(JNIEnv *jEnv, jclass java_class)
{
return (*jEnv)->CallBooleanMethod(jEnv, java_class, jlClass_isArray);
}
/*
* Return the class of a Java array's component type. This is not the same
* as the array's element type. For example, the component type of an array
* of type SomeType[][][] is SomeType[][], but its element type is SomeType.
*
* If an error occurs, NULL is returned and an error reported.
*/
static jclass
get_java_array_component_class(JSContext *cx, JNIEnv *jEnv, jclass java_class)
{
jclass result;
result = (*jEnv)->CallObjectMethod(jEnv, java_class, jlClass_getComponentType);
if (!result) {
jsj_UnexpectedJavaError(cx, jEnv,
"Can't get Java array component class using "
"java.lang.Class.getComponentType()");
return NULL;
}
return result;
}
/*
* Given a Java class, fill in the signature structure that describes the class.
* If an error occurs, JS_FALSE is returned and the error reporter called.
*/
static JSBool
compute_java_class_signature(JSContext *cx, JNIEnv *jEnv, JavaSignature *signature)
{
jclass java_class = signature->java_class;
if (is_java_array_class(jEnv, java_class)) {
jclass component_class;
signature->type = JAVA_SIGNATURE_ARRAY;
component_class = get_java_array_component_class(cx, jEnv, java_class);
if (!component_class)
return JS_FALSE;
signature->array_component_signature =
jsj_GetJavaClassDescriptor(cx, jEnv, component_class);
if (!signature->array_component_signature)
return JS_FALSE;
} else {
signature->type = get_signature_type(cx, signature);
}
return JS_TRUE;
}
/*
* Convert a JavaSignature object into a string format as used by
* the JNI functions, e.g. java.lang.Object ==> "Ljava/lang/Object;"
* The caller is responsible for freeing the resulting string.
*
* If an error is encountered, NULL is returned and an error reported.
*/
const char *
jsj_ConvertJavaSignatureToString(JSContext *cx, JavaSignature *signature)
{
char *sig;
if (signature->type == JAVA_SIGNATURE_CLASS) {
/* A non-array object class */
sig = PR_smprintf("L%s;", signature->name);
if (sig)
jsj_MakeJNIClassname(sig);
} else if (signature->type == JAVA_SIGNATURE_ARRAY) {
/* An array class */
const char *component_signature_string;
component_signature_string =
jsj_ConvertJavaSignatureToString(cx, signature->array_component_signature);
if (!component_signature_string)
return NULL;
sig = PR_smprintf("[%s", component_signature_string);
JS_free(cx, (char*)component_signature_string);
} else {
/* A primitive class */
sig = PR_smprintf("%c", (char)signature->type);
}
if (!sig) {
JS_ReportOutOfMemory(cx);
return NULL;
}
return sig;
}
/*
* Convert a JavaSignature object into a human-readable string format as seen
* in Java source files, e.g. "byte", or "int[][]" or "java.lang.String".
* The caller is responsible for freeing the resulting string.
*
* If an error is encountered, NULL is returned and an error reported.
*/
const char *
jsj_ConvertJavaSignatureToHRString(JSContext *cx,
JavaSignature *signature)
{
char *sig;
JavaSignature *acs;
if (signature->type == JAVA_SIGNATURE_ARRAY) {
/* An array class */
const char *component_signature_string;
acs = signature->array_component_signature;
component_signature_string =
jsj_ConvertJavaSignatureToHRString(cx, acs);
if (!component_signature_string)
return NULL;
sig = PR_smprintf("%s[]", component_signature_string);
JS_free(cx, (char*)component_signature_string);
} else {
/* A primitive class or a non-array object class */
sig = JS_strdup(cx, signature->name);
}
if (!sig) {
JS_ReportOutOfMemory(cx);
return NULL;
}
return sig;
}
static void
destroy_java_member_descriptor(JSContext *cx, JNIEnv *jEnv, JavaMemberDescriptor *member_descriptor)
{
JavaMethodSpec *method, *next_method;
if (member_descriptor->field)
jsj_DestroyFieldSpec(cx, jEnv, member_descriptor->field);
method = member_descriptor->methods;
while (method) {
next_method = method->next;
jsj_DestroyMethodSpec(cx, jEnv, method);
method = next_method;
}
if (member_descriptor->invoke_func_obj)
JS_RemoveRoot(cx, &member_descriptor->invoke_func_obj);
}
static void
destroy_class_member_descriptors(JSContext *cx, JNIEnv *jEnv, JavaMemberDescriptor *member_descriptor)
{
JavaMemberDescriptor *next_member;
while (member_descriptor) {
next_member = member_descriptor->next;
destroy_java_member_descriptor(cx, jEnv, member_descriptor);
member_descriptor = next_member;
}
}
static void
destroy_class_descriptor(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor)
{
JS_FREE_IF(cx, (char *)class_descriptor->name);
if (class_descriptor->java_class) {
JSJ_HashTableRemove(java_class_reflections,
class_descriptor->java_class, (void*)jEnv);
(*jEnv)->DeleteGlobalRef(jEnv, class_descriptor->java_class);
}
if (class_descriptor->array_component_signature)
jsj_ReleaseJavaClassDescriptor(cx, jEnv, class_descriptor->array_component_signature);
destroy_class_member_descriptors(cx, jEnv, class_descriptor->instance_members);
destroy_class_member_descriptors(cx, jEnv, class_descriptor->static_members);
destroy_class_member_descriptors(cx, jEnv, class_descriptor->constructors);
JS_free(cx, class_descriptor);
}
static JavaClassDescriptor *
new_class_descriptor(JSContext *cx, JNIEnv *jEnv, jclass java_class)
{
JavaClassDescriptor *class_descriptor;
class_descriptor = (JavaClassDescriptor *)JS_malloc(cx, sizeof(JavaClassDescriptor));
if (!class_descriptor)
return NULL;
memset(class_descriptor, 0, sizeof(JavaClassDescriptor));
class_descriptor->name = jsj_GetJavaClassName(cx, jEnv, java_class);
if (!class_descriptor->name)
goto error;
java_class = (*jEnv)->NewGlobalRef(jEnv, java_class);
if (!java_class) {
jsj_UnexpectedJavaError(cx, jEnv, "Unable to reference Java class");
goto error;
}
class_descriptor->java_class = java_class;
if (!compute_java_class_signature(cx, jEnv, class_descriptor))
goto error;
class_descriptor->modifiers =
(*jEnv)->CallIntMethod(jEnv, java_class, jlClass_getModifiers);
class_descriptor->ref_count = 1;
if (!JSJ_HashTableAdd(java_class_reflections, java_class, class_descriptor,
(void*)jEnv))
goto error;
return class_descriptor;
error:
destroy_class_descriptor(cx, jEnv, class_descriptor);
return NULL;
}
/* Trivial helper for jsj_DiscardJavaClassReflections(), below */
static PRIntn
enumerate_remove_java_class(JSJHashEntry *he, PRIntn i, void *arg)
{
JNIEnv *jEnv = (JNIEnv*)arg;
jclass java_class;
JavaClassDescriptor *class_descriptor;
class_descriptor = (JavaClassDescriptor*)he->value;
java_class = class_descriptor->java_class;
(*jEnv)->DeleteGlobalRef(jEnv, java_class);
class_descriptor->java_class = NULL;
return HT_ENUMERATE_REMOVE;
}
/* This shutdown routine discards all JNI references to Java objects
that have been reflected into JS, even if there are still references
to them from JS. */
void
jsj_DiscardJavaClassReflections(JNIEnv *jEnv)
{
if (java_class_reflections) {
JSJ_HashTableEnumerateEntries(java_class_reflections,
enumerate_remove_java_class,
(void*)jEnv);
JSJ_HashTableDestroy(java_class_reflections);
java_class_reflections = NULL;
}
}
extern JavaClassDescriptor *
jsj_GetJavaClassDescriptor(JSContext *cx, JNIEnv *jEnv, jclass java_class)
{
JavaClassDescriptor *class_descriptor;
class_descriptor = JSJ_HashTableLookup(java_class_reflections,
(const void *)java_class,
(void*)jEnv);
if (!class_descriptor)
return new_class_descriptor(cx, jEnv, java_class);
PR_ASSERT(class_descriptor->ref_count > 0);
class_descriptor->ref_count++;
return class_descriptor;
}
void
jsj_ReleaseJavaClassDescriptor(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor)
{
if (!--class_descriptor->ref_count)
destroy_class_descriptor(cx, jEnv, class_descriptor);
}
static JSBool
reflect_java_methods_and_fields(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
JSBool reflect_statics_only)
{
JavaMemberDescriptor *member_descriptor;
if (reflect_statics_only)
class_descriptor->static_members_reflected = JS_TRUE;
else
class_descriptor->instance_members_reflected = JS_TRUE;
if (!jsj_ReflectJavaMethods(cx, jEnv, class_descriptor, reflect_statics_only))
return JS_FALSE;
if (!jsj_ReflectJavaFields(cx, jEnv, class_descriptor, reflect_statics_only))
return JS_FALSE;
if (reflect_statics_only) {
member_descriptor = class_descriptor->static_members;
while (member_descriptor) {
class_descriptor->num_static_members++;
member_descriptor = member_descriptor->next;
}
} else {
member_descriptor = class_descriptor->instance_members;
while (member_descriptor) {
class_descriptor->num_instance_members++;
member_descriptor = member_descriptor->next;
}
}
return JS_TRUE;
}
JavaMemberDescriptor *
jsj_GetClassStaticMembers(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor)
{
if (!class_descriptor->static_members_reflected)
reflect_java_methods_and_fields(cx, jEnv, class_descriptor, JS_TRUE);
return class_descriptor->static_members;
}
JavaMemberDescriptor *
jsj_GetClassInstanceMembers(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor)
{
if (!class_descriptor->instance_members_reflected)
reflect_java_methods_and_fields(cx, jEnv, class_descriptor, JS_FALSE);
return class_descriptor->instance_members;
}
JavaMemberDescriptor *
jsj_LookupJavaStaticMemberDescriptorById(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jsid id)
{
JavaMemberDescriptor *member_descriptor;
member_descriptor = jsj_GetClassStaticMembers(cx, jEnv, class_descriptor);
while (member_descriptor) {
if (id == member_descriptor->id)
return member_descriptor;
member_descriptor = member_descriptor->next;
}
return NULL;
}
JavaMemberDescriptor *
jsj_GetJavaStaticMemberDescriptor(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jstring member_name_jstr)
{
JavaMemberDescriptor *member_descriptor;
jsid id;
if (!JavaStringToId(cx, jEnv, member_name_jstr, &id))
return NULL;
member_descriptor = jsj_LookupJavaStaticMemberDescriptorById(cx, jEnv, class_descriptor, id);
if (member_descriptor)
return member_descriptor;
member_descriptor = JS_malloc(cx, sizeof(JavaMemberDescriptor));
if (!member_descriptor)
return NULL;
memset(member_descriptor, 0, sizeof(JavaMemberDescriptor));
member_descriptor->name = jsj_DupJavaStringUTF(cx, jEnv, member_name_jstr);
if (!member_descriptor->name) {
JS_free(cx, member_descriptor);
return NULL;
}
member_descriptor->id = id;
member_descriptor->next = class_descriptor->static_members;
class_descriptor->static_members = member_descriptor;
return member_descriptor;
}
JavaMemberDescriptor *
jsj_GetJavaClassConstructors(JSContext *cx,
JavaClassDescriptor *class_descriptor)
{
JavaMemberDescriptor *member_descriptor;
if (class_descriptor->constructors)
return class_descriptor->constructors;
member_descriptor = JS_malloc(cx, sizeof(JavaMemberDescriptor));
if (!member_descriptor)
return NULL;
memset(member_descriptor, 0, sizeof(JavaMemberDescriptor));
member_descriptor->name = JS_strdup(cx, "<init>");
if (!member_descriptor->name) {
JS_free(cx, member_descriptor);
return NULL;
}
class_descriptor->constructors = member_descriptor;
return member_descriptor;
}
JavaMemberDescriptor *
jsj_LookupJavaClassConstructors(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor)
{
if (!class_descriptor->static_members_reflected)
reflect_java_methods_and_fields(cx, jEnv, class_descriptor, JS_TRUE);
return class_descriptor->constructors;
}
JavaMemberDescriptor *
jsj_LookupJavaMemberDescriptorById(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jsid id)
{
JavaMemberDescriptor *member_descriptor;
member_descriptor = jsj_GetClassInstanceMembers(cx, jEnv, class_descriptor);
while (member_descriptor) {
if (id == member_descriptor->id)
return member_descriptor;
member_descriptor = member_descriptor->next;
}
return NULL;
}
JavaMemberDescriptor *
jsj_GetJavaMemberDescriptor(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jstring member_name_jstr)
{
JavaMemberDescriptor *member_descriptor;
jsid id;
if (!JavaStringToId(cx, jEnv, member_name_jstr, &id))
return NULL;
member_descriptor = jsj_LookupJavaMemberDescriptorById(cx, jEnv, class_descriptor, id);
if (member_descriptor)
return member_descriptor;
member_descriptor = JS_malloc(cx, sizeof(JavaMemberDescriptor));
if (!member_descriptor)
return NULL;
memset(member_descriptor, 0, sizeof(JavaMemberDescriptor));
member_descriptor->name = jsj_DupJavaStringUTF(cx, jEnv, member_name_jstr);
if (!member_descriptor->name) {
JS_free(cx, member_descriptor);
return NULL;
}
member_descriptor->id = id;
member_descriptor->next = class_descriptor->instance_members;
class_descriptor->instance_members = member_descriptor;
return member_descriptor;
}
JSBool
jsj_InitJavaClassReflectionsTable()
{
java_class_reflections =
JSJ_NewHashTable(64, jsj_HashJavaObject, jsj_JavaObjectComparator,
NULL, NULL, NULL);
if (!java_class_reflections)
return JS_FALSE;
return JS_TRUE;
}

View File

@@ -0,0 +1,806 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 Communicator client 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.
*/
/* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* Below is the code that converts between Java and JavaScript values of all
* types.
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_private.h" /* LiveConnect internals */
/* Floating-point double utilities, stolen from jsnum.h */
#ifdef IS_LITTLE_ENDIAN
#define JSDOUBLE_HI32(x) (((uint32 *)&(x))[1])
#define JSDOUBLE_LO32(x) (((uint32 *)&(x))[0])
#else
#define JSDOUBLE_HI32(x) (((uint32 *)&(x))[0])
#define JSDOUBLE_LO32(x) (((uint32 *)&(x))[1])
#endif
#define JSDOUBLE_HI32_SIGNBIT 0x80000000
#define JSDOUBLE_HI32_EXPMASK 0x7ff00000
#define JSDOUBLE_HI32_MANTMASK 0x000fffff
#define JSDOUBLE_IS_NaN(x) \
((JSDOUBLE_HI32(x) & JSDOUBLE_HI32_EXPMASK) == JSDOUBLE_HI32_EXPMASK && \
(JSDOUBLE_LO32(x) || (JSDOUBLE_HI32(x) & JSDOUBLE_HI32_MANTMASK)))
#define JSDOUBLE_IS_INFINITE(x) \
((JSDOUBLE_HI32(x) & ~JSDOUBLE_HI32_SIGNBIT) == JSDOUBLE_HI32_EXPMASK && \
!JSDOUBLE_LO32(x))
static JSBool
convert_js_obj_to_JSObject_wrapper(JSContext *cx, JNIEnv *jEnv, JSObject *js_obj,
JavaSignature *signature,
int *cost, jobject *java_value)
{
if (!njJSObject) {
if (java_value)
JS_ReportError(cx, "Couldn't convert JavaScript object to an "
"instance of netscape.javascript.JSObject "
"because that class could not be loaded.");
return JS_FALSE;
}
if (!(*jEnv)->IsAssignableFrom(jEnv, njJSObject, signature->java_class))
return JS_FALSE;
if (!java_value)
return JS_TRUE;
*java_value = jsj_WrapJSObject(cx, jEnv, js_obj);
return (*java_value != NULL);
}
jstring
jsj_ConvertJSStringToJavaString(JSContext *cx, JNIEnv *jEnv, JSString *js_str)
{
jstring result;
result = (*jEnv)->NewString(jEnv, JS_GetStringChars(js_str),
JS_GetStringLength(js_str));
if (!result) {
jsj_UnexpectedJavaError(cx, jEnv, "Couldn't construct instance "
"of java.lang.String");
}
return result;
}
/*
* Convert a JS value to an instance of java.lang.Object or one of its subclasses,
* performing any necessary type coercion. If non-trivial coercion is required,
* the cost value is incremented. If the java_value pass-by-reference argument
* is non-NULL, the resulting Java value is stored there.
*
* Returns JS_TRUE if the conversion is possible, JS_FALSE otherwise
*/
JSBool
jsj_ConvertJSValueToJavaObject(JSContext *cx, JNIEnv *jEnv, jsval v, JavaSignature *signature,
int *cost, jobject *java_value, JSBool *is_local_refp)
{
JSString *jsstr;
jclass target_java_class;
PR_ASSERT(signature->type == JAVA_SIGNATURE_CLASS ||
signature->type == JAVA_SIGNATURE_ARRAY);
/* Initialize to default case, in which no new Java object is
synthesized to perform the conversion and, therefore, no JNI local
references are being held. */
*is_local_refp = JS_FALSE;
/* Get the Java type of the target value */
target_java_class = signature->java_class;
if (JSVAL_IS_OBJECT(v)) {
JSObject *js_obj = JSVAL_TO_OBJECT(v);
/* JS null is always assignable to a Java object */
if (!js_obj) {
if (java_value)
*java_value = NULL;
return JS_TRUE;
}
if (JS_InstanceOf(cx, js_obj, &JavaObject_class, 0) ||
JS_InstanceOf(cx, js_obj, &JavaArray_class, 0)) {
/* The source value is a Java object wrapped inside a JavaScript
object. Unwrap the JS object and return the original Java
object if it's class makes it assignment-compatible with the
target class using Java's assignability rules. */
JavaObjectWrapper *java_wrapper = JS_GetPrivate(cx, js_obj);
jobject java_obj = java_wrapper->java_obj;
if ((*jEnv)->IsInstanceOf(jEnv, java_obj, target_java_class)) {
if (java_value)
*java_value = java_obj;
return JS_TRUE;
}
#ifdef LIVECONNECT_IMPROVEMENTS
/* Don't allow wrapped Java objects to be converted to strings */
goto conversion_error;
#else
/* Fall through, to attempt conversion to a Java string */
#endif
} else if (JS_InstanceOf(cx, js_obj, &JavaClass_class, 0)) {
/* We're dealing with the reflection of a Java class */
JavaClassDescriptor *java_class_descriptor = JS_GetPrivate(cx, js_obj);
/* Check if target type is java.lang.Class class */
if ((*jEnv)->IsAssignableFrom(jEnv, jlClass, target_java_class)) {
if (java_value)
*java_value = java_class_descriptor->java_class;
return JS_TRUE;
}
/* Check if target type is netscape.javascript.JSObject wrapper class */
if (convert_js_obj_to_JSObject_wrapper(cx, jEnv, js_obj, signature, cost, java_value)) {
if (*java_value)
*is_local_refp = JS_TRUE;
return JS_TRUE;
}
/* Fall through, to attempt conversion to a Java string */
} else if (JS_InstanceOf(cx, js_obj, &JavaMember_class, 0)) {
if (!JS_ConvertValue(cx, v, JSTYPE_OBJECT, &v))
return JS_FALSE;
return jsj_ConvertJSValueToJavaObject(cx, jEnv, v, signature, cost,
java_value, is_local_refp);
} else {
/* Otherwise, see if the target type is the netscape.javascript.JSObject
wrapper class or one of its subclasses, in which case a
reference is passed to the original JS object by wrapping it
inside an instance of netscape.javascript.JSObject */
if (convert_js_obj_to_JSObject_wrapper(cx, jEnv, js_obj, signature, cost, java_value)) {
return JS_TRUE;
}
/* Fall through, to attempt conversion to a Java string */
}
} else if (JSVAL_IS_NUMBER(v)) {
/* JS numbers, integral or not, can be converted to instances of java.lang.Double */
if ((*jEnv)->IsAssignableFrom(jEnv, jlDouble, target_java_class)) {
if (java_value) {
jsdouble d;
if (!JS_ValueToNumber(cx, v, &d))
goto conversion_error;
*java_value = (*jEnv)->NewObject(jEnv, jlDouble, jlDouble_Double, d);
if (*java_value) {
*is_local_refp = JS_TRUE;
} else {
jsj_UnexpectedJavaError(cx, jEnv,
"Couldn't construct instance of java.lang.Double");
return JS_FALSE;
}
}
#ifdef LIVECONNECT_IMPROVEMENTS
(*cost)++;
#endif
return JS_TRUE;
}
/* Fall through, to attempt conversion to a java.lang.String ... */
} else if (JSVAL_IS_BOOLEAN(v)) {
/* JS boolean values can be converted to instances of java.lang.Boolean */
if ((*jEnv)->IsAssignableFrom(jEnv, jlBoolean, target_java_class)) {
if (java_value) {
JSBool b;
if (!JS_ValueToBoolean(cx, v, &b))
goto conversion_error;
*java_value =
(*jEnv)->NewObject(jEnv, jlBoolean, jlBoolean_Boolean, b);
if (*java_value) {
*is_local_refp = JS_TRUE;
} else {
jsj_UnexpectedJavaError(cx, jEnv, "Couldn't construct instance "
"of java.lang.Boolean");
return JS_FALSE;
}
}
#ifdef LIVECONNECT_IMPROVEMENTS
(*cost)++;
#endif
return JS_TRUE;
}
/* Fall through, to attempt conversion to a java.lang.String ... */
}
/* If no other conversion is possible, see if the target type is java.lang.String */
if ((*jEnv)->IsAssignableFrom(jEnv, jlString, target_java_class)) {
#ifdef LIVECONNECT_IMPROVEMENTS
JSBool is_string = JSVAL_IS_STRING(v);
#endif
/* Convert to JS string, if necessary, and then to a Java Unicode string */
jsstr = JS_ValueToString(cx, v);
if (jsstr) {
if (java_value) {
*java_value = jsj_ConvertJSStringToJavaString(cx, jEnv, jsstr);
if (*java_value) {
*is_local_refp = JS_TRUE;
} else {
return JS_FALSE;
}
}
#ifdef LIVECONNECT_IMPROVEMENTS
if (!is_string)
(*cost)++;
#endif
return JS_TRUE;
}
}
conversion_error:
return JS_FALSE;
}
/* Valid ranges for Java numeric types */
#define jbyte_MAX_VALUE 127.0
#define jbyte_MIN_VALUE -128.0
#define jchar_MAX_VALUE 65535.0
#define jchar_MIN_VALUE 0.0
#define jshort_MAX_VALUE 32767.0
#define jshort_MIN_VALUE -32768.0
#define jint_MAX_VALUE 2147483647.0
#define jint_MIN_VALUE -2147483648.0
#define jlong_MAX_VALUE 9223372036854775807.0
#define jlong_MIN_VALUE -9223372036854775808.0
/* Utility macro for jsj_ConvertJSValueToJavaValue(), below */
#define JSVAL_TO_INTEGRAL_JVALUE(type_name, member_name, member_type, jsval, java_value) \
if (!JSVAL_IS_NUMBER(v)) { \
if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v)) \
goto conversion_error; \
(*cost)++; \
} \
{ \
member_type member_name; \
\
if (JSVAL_IS_INT(v)) { \
jsint ival = JSVAL_TO_INT(v); \
member_name = (member_type) ival; \
\
/* Check to see if the jsval's magnitude is too large to be \
representable in the target java type */ \
if (member_name != ival) \
goto numeric_conversion_error; \
} else { \
jdouble dval = *JSVAL_TO_DOUBLE(v); \
\
/* NaN becomes zero when converted to integral value */ \
if (JSDOUBLE_IS_NaN(dval)) \
member_name = 0; \
\
/* Unrepresentably large numbers, including infinities, */ \
/* cause an error. */ \
else if ((dval > member_type ## _MAX_VALUE) || \
(dval < member_type ## _MIN_VALUE)) { \
goto numeric_conversion_error; \
} else \
member_name = (member_type) dval; \
\
/* Don't allow a non-integral number to be converted \
to an integral type */ \
/* Actually, we have to allow this for LC1 compatibility */ \
/* if ((jdouble)member_name != dval) \
(*cost)++; */ \
} \
if (java_value) \
java_value->member_name = member_name; \
}
#if XP_MAC
/* on MRJ jlong is typedef'd to wide, which is a struct. */
#include <Math64.h>
static jsint jlong_to_jsint(jlong lvalue)
{
SInt64 val = WideToSInt64(lvalue);
return S32Set(val);
}
static jlong jsint_to_jlong(jsint ivalue)
{
SInt64 val = S64Set(ivalue);
wide wval =SInt64ToWide(val);
return *(jlong*)&wval;
}
static jdouble jlong_to_jdouble(jlong lvalue)
{
SInt64 val = WideToSInt64(lvalue);
return SInt64ToLongDouble(val);
}
static jlong jdouble_to_jlong(jdouble dvalue)
{
SInt64 val = LongDoubleToSInt64(dvalue);
wide wval = SInt64ToWide(val);
return *(jlong*)&wval;
}
/* Mac utility macro for jsj_ConvertJSValueToJavaValue(), below */
#define JSVAL_TO_JLONG_JVALUE(member_name, member_type, jsvalue, java_value) \
if (!JSVAL_IS_NUMBER(jsvalue)) { \
if (!JS_ConvertValue(cx, jsvalue, JSTYPE_NUMBER, &jsvalue)) \
goto conversion_error; \
(*cost)++; \
} \
{ \
member_type member_name; \
\
if (JSVAL_IS_INT(jsvalue)) { \
jsint ival = JSVAL_TO_INT(jsvalue); \
member_name = jsint_to_jlong(ival); \
\
} else { \
jdouble dval = *JSVAL_TO_DOUBLE(jsvalue); \
\
/* NaN becomes zero when converted to integral value */ \
if (JSDOUBLE_IS_NaN(dval)) \
member_name = jsint_to_jlong(0); \
\
/* Unrepresentably large numbers, including infinities, */ \
/* cause an error. */ \
else if ((dval > member_type ## _MAX_VALUE) || \
(dval < member_type ## _MIN_VALUE)) { \
goto numeric_conversion_error; \
} else \
member_name = jdouble_to_jlong(dval); \
\
/* Don't allow a non-integral number to be converted \
to an integral type */ \
/* Actually, we have to allow this for LC1 compatibility */ \
/*if (jlong_to_jdouble(member_name) != dval) \
(*cost)++;*/ \
} \
if (java_value) \
java_value->member_name = member_name; \
}
#else
#define jlong_to_jdouble(lvalue) ((jdouble) lvalue)
#endif
/*
* Convert a JS value to a Java value of the given type signature. The cost
* variable is incremented if coercion is required, e.g. the source value is
* a string, but the target type is a boolean.
*
* Returns JS_FALSE if no conversion is possible, either because the jsval has
* a type that is wholly incompatible with the Java value, or because a scalar
* jsval can't be represented in a variable of the target type without loss of
* precision, e.g. the source value is "4.2" but the destination type is byte.
* If conversion is not possible and java_value is non-NULL, the JS error
* reporter is called with an appropriate message.
*/
JSBool
jsj_ConvertJSValueToJavaValue(JSContext *cx, JNIEnv *jEnv, jsval v,
JavaSignature *signature,
int *cost, jvalue *java_value, JSBool *is_local_refp)
{
JavaSignatureChar type;
JSBool success = JS_FALSE;
/* Initialize to default case, in which no new Java object is
synthesized to perform the conversion and, therefore, no JNI local
references are being held. */
*is_local_refp = JS_FALSE;
type = signature->type;
switch (type) {
case JAVA_SIGNATURE_BOOLEAN:
if (!JSVAL_IS_BOOLEAN(v)) {
if (!JS_ConvertValue(cx, v, JSTYPE_BOOLEAN, &v))
goto conversion_error;
(*cost)++;
}
if (java_value)
java_value->z = (jboolean)(JSVAL_TO_BOOLEAN(v) == JS_TRUE);
break;
case JAVA_SIGNATURE_SHORT:
JSVAL_TO_INTEGRAL_JVALUE(short, s, jshort, v, java_value);
break;
case JAVA_SIGNATURE_BYTE:
JSVAL_TO_INTEGRAL_JVALUE(byte, b, jbyte, v, java_value);
break;
case JAVA_SIGNATURE_CHAR:
/* A one-character string can be converted into a character */
if (JSVAL_IS_STRING(v) && (JS_GetStringLength(JSVAL_TO_STRING(v)) == 1)) {
v = INT_TO_JSVAL(*JS_GetStringChars(JSVAL_TO_STRING(v)));
}
JSVAL_TO_INTEGRAL_JVALUE(char, c, jchar, v, java_value);
break;
case JAVA_SIGNATURE_INT:
JSVAL_TO_INTEGRAL_JVALUE(int, i, jint, v, java_value);
break;
case JAVA_SIGNATURE_LONG:
#if XP_MAC
JSVAL_TO_JLONG_JVALUE(j, jlong, v, java_value);
#else
JSVAL_TO_INTEGRAL_JVALUE(long, j, jlong, v, java_value);
#endif
break;
case JAVA_SIGNATURE_FLOAT:
if (!JSVAL_IS_NUMBER(v)) {
if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v))
goto conversion_error;
(*cost)++;
}
if (java_value) {
if (JSVAL_IS_INT(v))
java_value->f = (jfloat) JSVAL_TO_INT(v);
else
java_value->f = (jfloat) *JSVAL_TO_DOUBLE(v);
}
break;
case JAVA_SIGNATURE_DOUBLE:
if (!JSVAL_IS_NUMBER(v)) {
if (!JS_ConvertValue(cx, v, JSTYPE_NUMBER, &v))
goto conversion_error;
(*cost)++;
}
if (java_value) {
if (JSVAL_IS_INT(v))
java_value->d = (jdouble) JSVAL_TO_INT(v);
else
java_value->d = (jdouble) *JSVAL_TO_DOUBLE(v);
}
break;
case JAVA_SIGNATURE_CLASS:
case JAVA_SIGNATURE_ARRAY:
if (!jsj_ConvertJSValueToJavaObject(cx, jEnv, v, signature, cost,
&java_value->l, is_local_refp))
goto conversion_error;
break;
default:
PR_ASSERT(0);
return JS_FALSE;
}
/* Success */
return JS_TRUE;
numeric_conversion_error:
success = JS_TRUE;
/* Fall through ... */
conversion_error:
if (java_value) {
const char *jsval_string;
JSString *jsstr;
jsval_string = NULL;
jsstr = JS_ValueToString(cx, v);
if (jsstr)
jsval_string = JS_GetStringBytes(jsstr);
if (!jsval_string)
jsval_string = "";
JS_ReportError(cx, "Unable to convert JavaScript value %s to "
"Java value of type %s",
jsval_string, signature->name);
return JS_FALSE;
}
return success;
}
/*
* A utility routine to create a JavaScript Unicode string from a
* java.lang.String (Unicode) string.
*/
JSString *
jsj_ConvertJavaStringToJSString(JSContext *cx, JNIEnv *jEnv, jstring java_str)
{
JSString *js_str;
jboolean is_copy;
const jchar *ucs2_str;
jchar *copy_ucs2_str;
jsize ucs2_str_len, num_bytes;
ucs2_str_len = (*jEnv)->GetStringLength(jEnv, java_str);
ucs2_str = (*jEnv)->GetStringChars(jEnv, java_str, &is_copy);
if (!ucs2_str) {
jsj_UnexpectedJavaError(cx, jEnv,
"Unable to extract native Unicode from Java string");
return NULL;
}
js_str = NULL;
/* The string data passed into JS_NewUCString() is
not copied, so make a copy of the Unicode character vector. */
num_bytes = ucs2_str_len * sizeof(jchar);
copy_ucs2_str = (jchar*)JS_malloc(cx, num_bytes);
if (!copy_ucs2_str)
goto done;
memcpy(copy_ucs2_str, ucs2_str, num_bytes);
js_str = JS_NewUCString(cx, (jschar*)copy_ucs2_str, ucs2_str_len);
done:
(*jEnv)->ReleaseStringChars(jEnv, java_str, ucs2_str);
return js_str;
}
/*
* Attempt to obtain a JS string representation of a Java object.
* The java_obj argument must be of type java.lang.Object or a subclass.
* If java_obj is a Java string, it's value is simply extracted and
* copied into a JS string. Otherwise, the toString() method is called
* on java_obj.
*/
JSBool
jsj_ConvertJavaObjectToJSString(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jobject java_obj, jsval *vp)
{
JSString *js_str;
jstring java_str;
jmethodID toString;
/* Create a Java string, unless java_obj is already a java.lang.String */
if ((*jEnv)->IsInstanceOf(jEnv, java_obj, jlString)) {
java_str = java_obj;
} else {
jclass java_class;
java_class = class_descriptor->java_class;
toString = (*jEnv)->GetMethodID(jEnv, java_class, "toString",
"()Ljava/lang/String;");
if (!toString) {
/* All Java objects have a toString method */
jsj_UnexpectedJavaError(cx, jEnv, "No toString() method for class %s!",
class_descriptor->name);
return JS_FALSE;
}
java_str = (*jEnv)->CallObjectMethod(jEnv, java_obj, toString);
if (!java_str) {
jsj_ReportJavaError(cx, jEnv, "toString() method failed");
return JS_FALSE;
}
}
/* Extract Unicode from java.lang.String instance and convert to JS string */
js_str = jsj_ConvertJavaStringToJSString(cx, jEnv, java_str);
if (!js_str)
return JS_FALSE;
*vp = STRING_TO_JSVAL(js_str);
return JS_TRUE;
}
/*
* Convert a Java object to a number by attempting to call the
* doubleValue() method on a Java object to get a double result.
* This usually only works on instances of java.lang.Double, but the code
* is generalized to work with any Java object that supports this method.
*
* Returns JS_TRUE if the call was successful.
* Returns JS_FALSE if conversion is not possible or an error occurs.
*/
JSBool
jsj_ConvertJavaObjectToJSNumber(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jobject java_obj, jsval *vp)
{
jdouble d;
jmethodID doubleValue;
jclass java_class;
java_class = class_descriptor->java_class;
doubleValue = (*jEnv)->GetMethodID(jEnv, java_class, "doubleValue", "()D");
if (!doubleValue) {
/* There is no doubleValue() method for the object. Try toString()
instead and the JS engine will attempt to convert the result to
a number. */
(*jEnv)->ExceptionClear(jEnv);
return jsj_ConvertJavaObjectToJSString(cx, jEnv, class_descriptor,
java_obj, vp);
}
d = (*jEnv)->CallDoubleMethod(jEnv, java_obj, doubleValue);
if ((*jEnv)->ExceptionOccurred(jEnv)) {
jsj_UnexpectedJavaError(cx, jEnv, "doubleValue() method failed");
return JS_FALSE;
}
return JS_NewDoubleValue(cx, d, vp);
}
/*
* Convert a Java object to a boolean by attempting to call the
* booleanValue() method on a Java object to get a boolean result.
* This usually only works on instances of java.lang.Boolean, but the code
* is generalized to work with any Java object that supports this method.
*
* Returns JS_TRUE if the call was successful.
* Returns JS_FALSE if conversion is not possible or an error occurs.
*/
extern JSBool
jsj_ConvertJavaObjectToJSBoolean(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jobject java_obj, jsval *vp)
{
jboolean b;
jmethodID booleanValue;
jclass java_class;
/* Null converts to false. */
if (!java_obj) {
*vp = JSVAL_FALSE;
return JS_TRUE;
}
java_class = class_descriptor->java_class;
booleanValue = (*jEnv)->GetMethodID(jEnv, java_obj, "booleanValue", "()Z");
/* Non-null Java object does not have a booleanValue() method, so
it converts to true. */
if (!booleanValue) {
(*jEnv)->ExceptionClear(jEnv);
*vp = JSVAL_TRUE;
return JS_TRUE;
}
b = (*jEnv)->CallBooleanMethod(jEnv, java_obj, booleanValue);
if ((*jEnv)->ExceptionOccurred(jEnv)) {
jsj_UnexpectedJavaError(cx, jEnv, "booleanValue() method failed");
return JS_FALSE;
}
*vp = BOOLEAN_TO_JSVAL(b);
return JS_TRUE;
}
/*
* Reflect a Java object into a JS value. The source object, java_obj, must
* be of type java.lang.Object or a subclass and may, therefore, be an array.
*/
JSBool
jsj_ConvertJavaObjectToJSValue(JSContext *cx, JNIEnv *jEnv,
jobject java_obj, jsval *vp)
{
jclass java_class;
JSObject *js_obj;
/* A null in Java-land is also null in JS */
if (!java_obj) {
*vp = JSVAL_NULL;
return JS_TRUE;
}
java_class = (*jEnv)->GetObjectClass(jEnv, java_obj);
/*
* If it's an instance of netscape.javascript.JSObject, i.e. a wrapper
* around a JS object that has been passed into the Java world, unwrap
* it to obtain the original JS object.
*/
if (njJSObject && (*jEnv)->IsInstanceOf(jEnv, java_obj, njJSObject)) {
#ifdef PRESERVE_JSOBJECT_IDENTITY
js_obj = (JSObject *)((*jEnv)->GetIntField(jEnv, java_obj, njJSObject_internal));
#else
js_obj = jsj_UnwrapJSObjectWrapper(jEnv, java_obj);
#endif
PR_ASSERT(js_obj);
if (!js_obj)
return JS_FALSE;
*vp = OBJECT_TO_JSVAL(js_obj);
return JS_TRUE;
}
/*
* Instances of java.lang.String are wrapped so we can call methods on
* them, but they convert to a JS string if used in a string context.
*/
/* TODO - let's get rid of this annoying "feature" */
/* otherwise, wrap it inside a JavaObject */
js_obj = jsj_WrapJavaObject(cx, jEnv, java_obj, java_class);
if (!js_obj)
return JS_FALSE;
*vp = OBJECT_TO_JSVAL(js_obj);
return JS_TRUE;
}
/*
* Convert a Java value (primitive or object) to a JS value.
*
* This is usually an infallible operation, but JS_FALSE is returned
* on an out-of-memory condition and the error reporter is called.
*/
JSBool
jsj_ConvertJavaValueToJSValue(JSContext *cx, JNIEnv *jEnv,
JavaSignature *signature,
jvalue *java_value,
jsval *vp)
{
int32 ival32;
switch (signature->type) {
case JAVA_SIGNATURE_VOID:
*vp = JSVAL_VOID;
return JS_TRUE;
case JAVA_SIGNATURE_BYTE:
*vp = INT_TO_JSVAL((jsint)java_value->b);
return JS_TRUE;
case JAVA_SIGNATURE_CHAR:
*vp = INT_TO_JSVAL((jsint)java_value->c);
return JS_TRUE;
case JAVA_SIGNATURE_SHORT:
*vp = INT_TO_JSVAL((jsint)java_value->s);
return JS_TRUE;
case JAVA_SIGNATURE_INT:
ival32 = java_value->i;
if (INT_FITS_IN_JSVAL(ival32)) {
*vp = INT_TO_JSVAL((jsint) ival32);
return JS_TRUE;
} else {
return JS_NewDoubleValue(cx, ival32, vp);
}
case JAVA_SIGNATURE_BOOLEAN:
*vp = BOOLEAN_TO_JSVAL((JSBool) java_value->z);
return JS_TRUE;
case JAVA_SIGNATURE_LONG:
return JS_NewDoubleValue(cx, jlong_to_jdouble(java_value->j), vp);
case JAVA_SIGNATURE_FLOAT:
return JS_NewDoubleValue(cx, java_value->f, vp);
case JAVA_SIGNATURE_DOUBLE:
return JS_NewDoubleValue(cx, java_value->d, vp);
case JAVA_SIGNATURE_CLASS:
case JAVA_SIGNATURE_ARRAY:
return jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_value->l, vp);
default:
PR_ASSERT(0);
return JS_FALSE;
}
}

View File

@@ -0,0 +1,383 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 Communicator client 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.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the code used to reflect Java fields as properties of
* JavaObject objects and the code to access those fields.
*
*/
#include <stdlib.h>
#include "jsj_private.h" /* LiveConnect internals */
/*
* Add a single field, described by java_field, to the JavaMemberDescriptor
* named by field_name within the given JavaClassDescriptor.
*
* Returns JS_TRUE on success. Otherwise, returns JS_FALSE and reports an error.
*/
static JSBool
add_java_field_to_class_descriptor(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jstring field_name_jstr,
jobject java_field, /* a java.lang.reflect.Field */
jint modifiers)
{
jclass fieldType;
jfieldID fieldID;
jclass java_class;
JSBool is_static_field;
JavaMemberDescriptor *member_descriptor = NULL;
const char *sig_cstr = NULL;
const char *field_name = NULL;
JavaSignature *signature = NULL;
JavaFieldSpec *field_spec = NULL;
is_static_field = modifiers & ACC_STATIC;
if (is_static_field) {
member_descriptor = jsj_GetJavaStaticMemberDescriptor(cx, jEnv, class_descriptor, field_name_jstr);
} else {
member_descriptor = jsj_GetJavaMemberDescriptor(cx, jEnv, class_descriptor, field_name_jstr);
}
if (!member_descriptor)
goto error;
field_spec = (JavaFieldSpec*)JS_malloc(cx, sizeof(JavaFieldSpec));
if (!field_spec)
goto error;
field_spec->modifiers = modifiers;
/* Get the Java class corresponding to the type of the field */
fieldType = (*jEnv)->CallObjectMethod(jEnv, java_field, jlrField_getType);
if (!fieldType) {
jsj_UnexpectedJavaError(cx, jEnv,
"Unable to determine type of field using"
" java.lang.reflect.Field.getType()");
goto error;
}
signature = jsj_GetJavaClassDescriptor(cx, jEnv, fieldType);
if (!signature)
goto error;
field_spec->signature = signature;
field_name = jsj_DupJavaStringUTF(cx, jEnv, field_name_jstr);
if (!field_name)
goto error;
field_spec->name = field_name;
/* Compute the JNI-style (string-based) signature of the field type */
sig_cstr = jsj_ConvertJavaSignatureToString(cx, signature);
if (!sig_cstr)
goto error;
/* Compute the JNI fieldID and cache it for quick field access */
java_class = class_descriptor->java_class;
if (is_static_field)
fieldID = (*jEnv)->GetStaticFieldID(jEnv, java_class, field_name, sig_cstr);
else
fieldID = (*jEnv)->GetFieldID(jEnv, java_class, field_name, sig_cstr);
if (!fieldID) {
jsj_UnexpectedJavaError(cx, jEnv,
"Can't get Java field ID for class %s, field %s (sig=%s)",
class_descriptor->name, field_name, sig_cstr);
goto error;
}
field_spec->fieldID = fieldID;
JS_free(cx, (char*)sig_cstr);
member_descriptor->field = field_spec;
/* Success */
return JS_TRUE;
error:
if (field_spec) {
JS_FREE_IF(cx, (char*)field_spec->name);
JS_free(cx, field_spec);
}
JS_FREE_IF(cx, (char*)sig_cstr);
if (signature)
jsj_ReleaseJavaClassDescriptor(cx, jEnv, signature);
return JS_FALSE;
}
/*
* Free up a JavaFieldSpec and all its resources.
*/
void
jsj_DestroyFieldSpec(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field)
{
JS_FREE_IF(cx, (char*)field->name);
jsj_ReleaseJavaClassDescriptor(cx, jEnv, field->signature);
JS_free(cx, field);
}
/*
* Add a JavaMemberDescriptor to the collection of members in class_descriptor
* for every public field of the identified Java class. (A separate collection
* is kept in class_descriptor for static and instance members.)
* If reflect_only_static_fields is set, instance fields are not reflected. If
* it isn't set, only instance fields are reflected and static fields are not
* reflected.
*
* Returns JS_TRUE on success. Otherwise, returns JS_FALSE and reports an error.
*/
JSBool
jsj_ReflectJavaFields(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor,
JSBool reflect_only_static_fields)
{
int i;
JSBool ok;
jint modifiers;
jobject java_field;
jstring field_name_jstr;
jarray joFieldArray;
jsize num_fields;
jclass java_class;
/* Get a java array of java.lang.reflect.Field objects, by calling
java.lang.Class.getFields(). */
java_class = class_descriptor->java_class;
joFieldArray = (*jEnv)->CallObjectMethod(jEnv, java_class, jlClass_getFields);
if (!joFieldArray) {
jsj_UnexpectedJavaError(cx, jEnv,
"Can't determine Java object's fields "
"using java.lang.Class.getFields()");
return JS_FALSE;
}
/* Iterate over the class fields */
num_fields = (*jEnv)->GetArrayLength(jEnv, joFieldArray);
for (i = 0; i < num_fields; i++) {
/* Get the i'th reflected field */
java_field = (*jEnv)->GetObjectArrayElement(jEnv, joFieldArray, i);
if (!java_field) {
jsj_UnexpectedJavaError(cx, jEnv, "Can't access a Field[] array");
return JS_FALSE;
}
/* Get the field modifiers, e.g. static, public, private, etc. */
modifiers = (*jEnv)->CallIntMethod(jEnv, java_field, jlrField_getModifiers);
if ((*jEnv)->ExceptionOccurred(jEnv)) {
jsj_UnexpectedJavaError(cx, jEnv,
"Can't access a Field's modifiers using"
"java.lang.reflect.Field.getModifiers()");
return JS_FALSE;
}
/* Don't allow access to private or protected Java fields. */
if (!(modifiers & ACC_PUBLIC))
continue;
/* Reflect all instance fields or all static fields, but not both */
if (reflect_only_static_fields != ((modifiers & ACC_STATIC) != 0))
continue;
/* Determine the unqualified name of the field */
field_name_jstr = (*jEnv)->CallObjectMethod(jEnv, java_field, jlrField_getName);
if (!field_name_jstr) {
jsj_UnexpectedJavaError(cx, jEnv,
"Can't obtain a Field's name"
"java.lang.reflect.Field.getName()");
return JS_FALSE;
}
/* Add a JavaFieldSpec object to the JavaClassDescriptor */
ok = add_java_field_to_class_descriptor(cx, jEnv, class_descriptor, field_name_jstr,
java_field, modifiers);
if (!ok)
return JS_FALSE;
}
/* Success */
return JS_TRUE;
}
/*
* Read the value of a Java field and return it as a JavaScript value.
* If the field is static, then java_obj is a Java class, otherwise
* it's a Java instance object.
*
* Returns JS_TRUE on success. Otherwise, returns JS_FALSE and reports an error.
*/
JSBool
jsj_GetJavaFieldValue(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field_spec,
jobject java_obj, jsval *vp)
{
JSBool is_static_field;
jvalue java_value;
JavaSignature *signature;
JavaSignatureChar field_type;
jfieldID fieldID = field_spec->fieldID;
is_static_field = field_spec->modifiers & ACC_STATIC;
#define GET_JAVA_FIELD(Type,member) \
PR_BEGIN_MACRO \
if (is_static_field) \
java_value.member = \
(*jEnv)->GetStatic##Type##Field(jEnv, java_obj, fieldID); \
else \
java_value.member = \
(*jEnv)->Get##Type##Field(jEnv, java_obj, fieldID); \
if ((*jEnv)->ExceptionOccurred(jEnv)) { \
jsj_UnexpectedJavaError(cx, jEnv, "Error reading Java field"); \
return JS_FALSE; \
} \
PR_END_MACRO
signature = field_spec->signature;
field_type = signature->type;
switch(field_type) {
case JAVA_SIGNATURE_BYTE:
GET_JAVA_FIELD(Byte,b);
break;
case JAVA_SIGNATURE_CHAR:
GET_JAVA_FIELD(Char,c);
break;
case JAVA_SIGNATURE_SHORT:
GET_JAVA_FIELD(Short,s);
break;
case JAVA_SIGNATURE_INT:
GET_JAVA_FIELD(Int,i);
break;
case JAVA_SIGNATURE_BOOLEAN:
GET_JAVA_FIELD(Boolean,z);
break;
case JAVA_SIGNATURE_LONG:
GET_JAVA_FIELD(Long,j);
break;
case JAVA_SIGNATURE_FLOAT:
GET_JAVA_FIELD(Float,f);
break;
case JAVA_SIGNATURE_DOUBLE:
GET_JAVA_FIELD(Double,d);
break;
case JAVA_SIGNATURE_CLASS:
case JAVA_SIGNATURE_ARRAY:
GET_JAVA_FIELD(Object,l);
break;
#undef GET_JAVA_FIELD
default:
PR_ASSERT(0); /* Unknown java type signature */
return JS_FALSE;
}
return jsj_ConvertJavaValueToJSValue(cx, jEnv, signature, &java_value, vp);
}
JSBool
jsj_SetJavaFieldValue(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field_spec,
jclass java_obj, jsval js_val)
{
JSBool is_static_field, is_local_ref;
int dummy_cost;
jvalue java_value;
JavaSignature *signature;
JavaSignatureChar field_type;
jfieldID fieldID = field_spec->fieldID;
is_static_field = field_spec->modifiers & ACC_STATIC;
#define SET_JAVA_FIELD(Type,member) \
PR_BEGIN_MACRO \
if (is_static_field) { \
(*jEnv)->SetStatic##Type##Field(jEnv, java_obj, fieldID, \
java_value.member); \
} else { \
(*jEnv)->Set##Type##Field(jEnv, java_obj, fieldID,java_value.member);\
} \
if ((*jEnv)->ExceptionOccurred(jEnv)) { \
jsj_UnexpectedJavaError(cx, jEnv, "Error assigning to Java field"); \
return JS_FALSE; \
} \
PR_END_MACRO
signature = field_spec->signature;
if (!jsj_ConvertJSValueToJavaValue(cx, jEnv, js_val, signature, &dummy_cost,
&java_value, &is_local_ref))
return JS_FALSE;
field_type = signature->type;
switch(field_type) {
case JAVA_SIGNATURE_BYTE:
SET_JAVA_FIELD(Byte,b);
break;
case JAVA_SIGNATURE_CHAR:
SET_JAVA_FIELD(Char,c);
break;
case JAVA_SIGNATURE_SHORT:
SET_JAVA_FIELD(Short,s);
break;
case JAVA_SIGNATURE_INT:
SET_JAVA_FIELD(Int,i);
break;
case JAVA_SIGNATURE_BOOLEAN:
SET_JAVA_FIELD(Boolean,z);
break;
case JAVA_SIGNATURE_LONG:
SET_JAVA_FIELD(Long,j);
break;
case JAVA_SIGNATURE_FLOAT:
SET_JAVA_FIELD(Float,f);
break;
case JAVA_SIGNATURE_DOUBLE:
SET_JAVA_FIELD(Double,d);
break;
case JAVA_SIGNATURE_CLASS:
case JAVA_SIGNATURE_ARRAY:
SET_JAVA_FIELD(Object,l);
if (is_local_ref)
(*jEnv)->DeleteLocalRef(jEnv, java_value.l);
break;
#undef SET_JAVA_FIELD
default:
PR_ASSERT(0); /* Unknown java type signature */
return JS_FALSE;
}
return JS_TRUE;
}

View File

@@ -0,0 +1,487 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 Communicator client 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.
*/
/*
* This is a copy of the NSPR hash-table library, but it has been slightly
* modified to allow an additional argument to be passed into the hash
* key-comparision function. This is used to maintain thread-safety by
* passing in a JNIEnv pointer to the key-comparison function rather
* than storing it in a global. All types,function names, etc. have
* been renamed from their original NSPR names to protect the innocent.
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_hash.h"
#include "prtypes.h"
#ifdef NSPR20
# include "prlog.h"
# include "prbit.h"
#else
# include "prassert.h"
#endif
/* Compute the number of buckets in ht */
#define NBUCKETS(ht) (1 << (JSJ_HASH_BITS - (ht)->shift))
/* The smallest table has 16 buckets */
#define MINBUCKETSLOG2 4
#define MINBUCKETS (1 << MINBUCKETSLOG2)
/* Compute the maximum entries given n buckets that we will tolerate, ~90% */
#define OVERLOADED(n) ((n) - ((n) >> 3))
/* Compute the number of entries below which we shrink the table by half */
#define UNDERLOADED(n) (((n) > MINBUCKETS) ? ((n) >> 2) : 0)
/*
** Stubs for default hash allocator ops.
*/
static void *
DefaultAllocTable(void *pool, size_t size)
{
#if defined(XP_MAC)
#pragma unused (pool)
#endif
return malloc(size);
}
static void
DefaultFreeTable(void *pool, void *item)
{
#if defined(XP_MAC)
#pragma unused (pool)
#endif
free(item);
}
static JSJHashEntry *
DefaultAllocEntry(void *pool, const void *key)
{
#if defined(XP_MAC)
#pragma unused (pool,key)
#endif
return malloc(sizeof(JSJHashEntry));
}
static void
DefaultFreeEntry(void *pool, JSJHashEntry *he, PRUintn flag)
{
#if defined(XP_MAC)
#pragma unused (pool)
#endif
if (flag == HT_FREE_ENTRY)
free(he);
}
static JSJHashAllocOps defaultHashAllocOps = {
DefaultAllocTable, DefaultFreeTable,
DefaultAllocEntry, DefaultFreeEntry
};
PR_IMPLEMENT(JSJHashTable *)
JSJ_NewHashTable(PRUint32 n, JSJHashFunction keyHash,
JSJHashComparator keyCompare, JSJHashComparator valueCompare,
JSJHashAllocOps *allocOps, void *allocPriv)
{
JSJHashTable *ht;
PRUint32 nb;
if (n <= MINBUCKETS) {
n = MINBUCKETSLOG2;
} else {
n = PR_CeilingLog2(n);
if ((PRInt32)n < 0)
return 0;
}
if (!allocOps) allocOps = &defaultHashAllocOps;
ht = (*allocOps->allocTable)(allocPriv, sizeof *ht);
if (!ht)
return 0;
memset(ht, 0, sizeof *ht);
ht->shift = JSJ_HASH_BITS - n;
n = 1 << n;
#if defined(XP_PC) && !defined(_WIN32)
if (n > 16000) {
(*allocOps->freeTable)(allocPriv, ht);
return 0;
}
#endif /* WIN16 */
nb = n * sizeof(JSJHashEntry *);
ht->buckets = (*allocOps->allocTable)(allocPriv, nb);
if (!ht->buckets) {
(*allocOps->freeTable)(allocPriv, ht);
return 0;
}
memset(ht->buckets, 0, nb);
ht->keyHash = keyHash;
ht->keyCompare = keyCompare;
ht->valueCompare = valueCompare;
ht->allocOps = allocOps;
ht->allocPriv = allocPriv;
return ht;
}
PR_IMPLEMENT(void)
JSJ_HashTableDestroy(JSJHashTable *ht)
{
PRUint32 i, n;
JSJHashEntry *he, *next;
JSJHashAllocOps *allocOps = ht->allocOps;
void *allocPriv = ht->allocPriv;
n = NBUCKETS(ht);
for (i = 0; i < n; i++) {
for (he = ht->buckets[i]; he; he = next) {
next = he->next;
(*allocOps->freeEntry)(allocPriv, he, HT_FREE_ENTRY);
}
}
#ifdef DEBUG
memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]);
#endif
(*allocOps->freeTable)(allocPriv, ht->buckets);
#ifdef DEBUG
memset(ht, 0xDB, sizeof *ht);
#endif
(*allocOps->freeTable)(allocPriv, ht);
}
/*
** Multiplicative hash, from Knuth 6.4.
*/
#define GOLDEN_RATIO 0x9E3779B9U
PR_IMPLEMENT(JSJHashEntry **)
JSJ_HashTableRawLookup(JSJHashTable *ht, JSJHashNumber keyHash, const void *key, void *arg)
{
JSJHashEntry *he, **hep, **hep0;
JSJHashNumber h;
#ifdef HASHMETER
ht->nlookups++;
#endif
h = keyHash * GOLDEN_RATIO;
h >>= ht->shift;
hep = hep0 = &ht->buckets[h];
while ((he = *hep) != 0) {
if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key, arg)) {
/* Move to front of chain if not already there */
if (hep != hep0) {
*hep = he->next;
he->next = *hep0;
*hep0 = he;
}
return hep0;
}
hep = &he->next;
#ifdef HASHMETER
ht->nsteps++;
#endif
}
return hep;
}
PR_IMPLEMENT(JSJHashEntry *)
JSJ_HashTableRawAdd(JSJHashTable *ht, JSJHashEntry **hep,
JSJHashNumber keyHash, const void *key, void *value,
void *arg)
{
PRUint32 i, n;
JSJHashEntry *he, *next, **oldbuckets;
PRUint32 nb;
/* Grow the table if it is overloaded */
n = NBUCKETS(ht);
if (ht->nentries >= OVERLOADED(n)) {
#ifdef HASHMETER
ht->ngrows++;
#endif
ht->shift--;
oldbuckets = ht->buckets;
#if defined(XP_PC) && !defined(_WIN32)
if (2 * n > 16000)
return 0;
#endif /* WIN16 */
nb = 2 * n * sizeof(JSJHashEntry *);
ht->buckets = (*ht->allocOps->allocTable)(ht->allocPriv, nb);
if (!ht->buckets) {
ht->buckets = oldbuckets;
return 0;
}
memset(ht->buckets, 0, nb);
for (i = 0; i < n; i++) {
for (he = oldbuckets[i]; he; he = next) {
next = he->next;
hep = JSJ_HashTableRawLookup(ht, he->keyHash, he->key, arg);
PR_ASSERT(*hep == 0);
he->next = 0;
*hep = he;
}
}
#ifdef DEBUG
memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
#endif
(*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
hep = JSJ_HashTableRawLookup(ht, keyHash, key, arg);
}
/* Make a new key value entry */
he = (*ht->allocOps->allocEntry)(ht->allocPriv, key);
if (!he)
return 0;
he->keyHash = keyHash;
he->key = key;
he->value = value;
he->next = *hep;
*hep = he;
ht->nentries++;
return he;
}
PR_IMPLEMENT(JSJHashEntry *)
JSJ_HashTableAdd(JSJHashTable *ht, const void *key, void *value, void *arg)
{
JSJHashNumber keyHash;
JSJHashEntry *he, **hep;
keyHash = (*ht->keyHash)(key, arg);
hep = JSJ_HashTableRawLookup(ht, keyHash, key, arg);
if ((he = *hep) != 0) {
/* Hit; see if values match */
if ((*ht->valueCompare)(he->value, value, arg)) {
/* key,value pair is already present in table */
return he;
}
if (he->value)
(*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_VALUE);
he->value = value;
return he;
}
return JSJ_HashTableRawAdd(ht, hep, keyHash, key, value, arg);
}
PR_IMPLEMENT(void)
JSJ_HashTableRawRemove(JSJHashTable *ht, JSJHashEntry **hep, JSJHashEntry *he, void *arg)
{
PRUint32 i, n;
JSJHashEntry *next, **oldbuckets;
PRUint32 nb;
*hep = he->next;
(*ht->allocOps->freeEntry)(ht->allocPriv, he, HT_FREE_ENTRY);
/* Shrink table if it's underloaded */
n = NBUCKETS(ht);
if (--ht->nentries < UNDERLOADED(n)) {
#ifdef HASHMETER
ht->nshrinks++;
#endif
ht->shift++;
oldbuckets = ht->buckets;
nb = n * sizeof(JSJHashEntry*) / 2;
ht->buckets = (*ht->allocOps->allocTable)(ht->allocPriv, nb);
if (!ht->buckets) {
ht->buckets = oldbuckets;
return;
}
memset(ht->buckets, 0, nb);
for (i = 0; i < n; i++) {
for (he = oldbuckets[i]; he; he = next) {
next = he->next;
hep = JSJ_HashTableRawLookup(ht, he->keyHash, he->key, arg);
PR_ASSERT(*hep == 0);
he->next = 0;
*hep = he;
}
}
#ifdef DEBUG
memset(oldbuckets, 0xDB, n * sizeof oldbuckets[0]);
#endif
(*ht->allocOps->freeTable)(ht->allocPriv, oldbuckets);
}
}
PR_IMPLEMENT(PRBool)
JSJ_HashTableRemove(JSJHashTable *ht, const void *key, void *arg)
{
JSJHashNumber keyHash;
JSJHashEntry *he, **hep;
keyHash = (*ht->keyHash)(key, arg);
hep = JSJ_HashTableRawLookup(ht, keyHash, key, arg);
if ((he = *hep) == 0)
return PR_FALSE;
/* Hit; remove element */
JSJ_HashTableRawRemove(ht, hep, he, arg);
return PR_TRUE;
}
PR_IMPLEMENT(void *)
JSJ_HashTableLookup(JSJHashTable *ht, const void *key, void *arg)
{
JSJHashNumber keyHash;
JSJHashEntry *he, **hep;
keyHash = (*ht->keyHash)(key, arg);
hep = JSJ_HashTableRawLookup(ht, keyHash, key, arg);
if ((he = *hep) != 0) {
return he->value;
}
return 0;
}
/*
** Iterate over the entries in the hash table calling func for each
** entry found. Stop if "f" says to (return value & PR_ENUMERATE_STOP).
** Return a count of the number of elements scanned.
*/
PR_IMPLEMENT(int)
JSJ_HashTableEnumerateEntries(JSJHashTable *ht, JSJHashEnumerator f, void *arg)
{
JSJHashEntry *he, **hep;
PRUint32 i, nbuckets;
int rv, n = 0;
JSJHashEntry *todo = 0;
nbuckets = NBUCKETS(ht);
for (i = 0; i < nbuckets; i++) {
hep = &ht->buckets[i];
while ((he = *hep) != 0) {
rv = (*f)(he, n, arg);
n++;
if (rv & (HT_ENUMERATE_REMOVE | HT_ENUMERATE_UNHASH)) {
*hep = he->next;
if (rv & HT_ENUMERATE_REMOVE) {
he->next = todo;
todo = he;
}
} else {
hep = &he->next;
}
if (rv & HT_ENUMERATE_STOP) {
goto out;
}
}
}
out:
hep = &todo;
while ((he = *hep) != 0) {
JSJ_HashTableRawRemove(ht, hep, he, arg);
}
return n;
}
#ifdef HASHMETER
#include <math.h>
#include <stdio.h>
PR_IMPLEMENT(void)
JSJ_HashTableDumpMeter(JSJHashTable *ht, JSJHashEnumerator dump, FILE *fp)
{
double mean, variance;
PRUint32 nchains, nbuckets;
PRUint32 i, n, maxChain, maxChainLen;
JSJHashEntry *he;
variance = 0;
nchains = 0;
maxChainLen = 0;
nbuckets = NBUCKETS(ht);
for (i = 0; i < nbuckets; i++) {
he = ht->buckets[i];
if (!he)
continue;
nchains++;
for (n = 0; he; he = he->next)
n++;
variance += n * n;
if (n > maxChainLen) {
maxChainLen = n;
maxChain = i;
}
}
mean = (double)ht->nentries / nchains;
variance = fabs(variance / nchains - mean * mean);
fprintf(fp, "\nHash table statistics:\n");
fprintf(fp, " number of lookups: %u\n", ht->nlookups);
fprintf(fp, " number of entries: %u\n", ht->nentries);
fprintf(fp, " number of grows: %u\n", ht->ngrows);
fprintf(fp, " number of shrinks: %u\n", ht->nshrinks);
fprintf(fp, " mean steps per hash: %g\n", (double)ht->nsteps
/ ht->nlookups);
fprintf(fp, "mean hash chain length: %g\n", mean);
fprintf(fp, " standard deviation: %g\n", sqrt(variance));
fprintf(fp, " max hash chain length: %u\n", maxChainLen);
fprintf(fp, " max hash chain: [%u]\n", maxChain);
for (he = ht->buckets[maxChain], i = 0; he; he = he->next, i++)
if ((*dump)(he, i, fp) != HT_ENUMERATE_NEXT)
break;
}
#endif /* HASHMETER */
PR_IMPLEMENT(int)
JSJ_HashTableDump(JSJHashTable *ht, JSJHashEnumerator dump, FILE *fp)
{
int count;
count = JSJ_HashTableEnumerateEntries(ht, dump, fp);
#ifdef HASHMETER
JSJ_HashTableDumpMeter(ht, dump, fp);
#endif
return count;
}
PR_IMPLEMENT(JSJHashNumber)
JSJ_HashString(const void *key)
{
JSJHashNumber h;
const unsigned char *s;
h = 0;
for (s = key; *s; s++)
h = (h >> 28) ^ (h << 4) ^ *s;
return h;
}
PR_IMPLEMENT(int)
JSJ_CompareStrings(const void *v1, const void *v2)
{
return strcmp(v1, v2) == 0;
}
PR_IMPLEMENT(int)
JSJ_CompareValues(const void *v1, const void *v2)
{
return v1 == v2;
}

View File

@@ -0,0 +1,141 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 Communicator client 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.
*/
/*
* This is a copy of the NSPR hash-table library, but it has been slightly
* modified to allow an additional argument to be passed into the hash
* key-comparision function. This is used to maintain thread-safety by
* passing in a JNIEnv pointer to the key-comparison function rather
* than storing it in a global. All types,function names, etc. have
* been renamed from their original NSPR names to protect the innocent.
*/
#ifndef jsj_hash_h___
#define jsj_hash_h___
/*
* API to portable hash table code.
*/
#include <stddef.h>
#include <stdio.h>
#include "prtypes.h"
PR_BEGIN_EXTERN_C
typedef struct JSJHashEntry JSJHashEntry;
typedef struct JSJHashTable JSJHashTable;
typedef PRUint32 JSJHashNumber;
#define JSJ_HASH_BITS 32
typedef JSJHashNumber (*JSJHashFunction)(const void *key, void *arg);
typedef PRIntn (*JSJHashComparator)(const void *v1, const void *v2, void *arg);
typedef PRIntn (*JSJHashEnumerator)(JSJHashEntry *he, PRIntn i, void *arg);
/* Flag bits in JSJHashEnumerator's return value */
#define HT_ENUMERATE_NEXT 0 /* continue enumerating entries */
#define HT_ENUMERATE_STOP 1 /* stop enumerating entries */
#define HT_ENUMERATE_REMOVE 2 /* remove and free the current entry */
#define HT_ENUMERATE_UNHASH 4 /* just unhash the current entry */
typedef struct JSJHashAllocOps {
void * (*allocTable)(void *pool, size_t size);
void (*freeTable)(void *pool, void *item);
JSJHashEntry * (*allocEntry)(void *pool, const void *key);
void (*freeEntry)(void *pool, JSJHashEntry *he, PRUintn flag);
} JSJHashAllocOps;
#define HT_FREE_VALUE 0 /* just free the entry's value */
#define HT_FREE_ENTRY 1 /* free value and entire entry */
struct JSJHashEntry {
JSJHashEntry *next; /* hash chain linkage */
JSJHashNumber keyHash; /* key hash function result */
const void *key; /* ptr to opaque key */
void *value; /* ptr to opaque value */
};
struct JSJHashTable {
JSJHashEntry **buckets; /* vector of hash buckets */
PRUint32 nentries; /* number of entries in table */
PRUint32 shift; /* multiplicative hash shift */
JSJHashFunction keyHash; /* key hash function */
JSJHashComparator keyCompare; /* key comparison function */
JSJHashComparator valueCompare; /* value comparison function */
JSJHashAllocOps *allocOps; /* allocation operations */
void *allocPriv; /* allocation private data */
#ifdef HASHMETER
PRUint32 nlookups; /* total number of lookups */
PRUint32 nsteps; /* number of hash chains traversed */
PRUint32 ngrows; /* number of table expansions */
PRUint32 nshrinks; /* number of table contractions */
#endif
};
/*
* Create a new hash table.
* If allocOps is null, use default allocator ops built on top of malloc().
*/
PR_EXTERN(JSJHashTable *)
JSJ_NewHashTable(PRUint32 n, JSJHashFunction keyHash,
JSJHashComparator keyCompare, JSJHashComparator valueCompare,
JSJHashAllocOps *allocOps, void *allocPriv);
PR_EXTERN(void)
JSJ_HashTableDestroy(JSJHashTable *ht);
/* Low level access methods */
PR_EXTERN(JSJHashEntry **)
JSJ_HashTableRawLookup(JSJHashTable *ht, JSJHashNumber keyHash, const void *key, void *arg);
PR_EXTERN(JSJHashEntry *)
JSJ_HashTableRawAdd(JSJHashTable *ht, JSJHashEntry **hep, JSJHashNumber keyHash,
const void *key, void *value, void *arg);
PR_EXTERN(void)
JSJ_HashTableRawRemove(JSJHashTable *ht, JSJHashEntry **hep, JSJHashEntry *he, void *arg);
/* Higher level access methods */
PR_EXTERN(JSJHashEntry *)
JSJ_HashTableAdd(JSJHashTable *ht, const void *key, void *value, void *arg);
PR_EXTERN(PRBool)
JSJ_HashTableRemove(JSJHashTable *ht, const void *key, void *arg);
PR_EXTERN(PRIntn)
JSJ_HashTableEnumerateEntries(JSJHashTable *ht, JSJHashEnumerator f, void *arg);
PR_EXTERN(void *)
JSJ_HashTableLookup(JSJHashTable *ht, const void *key, void *arg);
PR_EXTERN(PRIntn)
JSJ_HashTableDump(JSJHashTable *ht, JSJHashEnumerator dump, FILE *fp);
/* General-purpose C string hash function. */
PR_EXTERN(JSJHashNumber)
JSJ_HashString(const void *key);
/* Compare strings using strcmp(), return true if equal. */
PR_EXTERN(int)
JSJ_CompareStrings(const void *v1, const void *v2);
/* Stub function just returns v1 == v2 */
PR_EXTERN(PRIntn)
JSJ_CompareValues(const void *v1, const void *v2);
PR_END_EXTERN_C
#endif /* jsj_hash_h___ */

File diff suppressed because it is too large Load Diff

View File

View File

@@ -0,0 +1,552 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 Communicator client 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.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* Declarations of private (internal) functions/data/types for
* JavaScript <==> Java communication.
*
*/
#ifndef _JSJAVA_PVT_H
#define _JSJAVA_PVT_H
#include "prtypes.h"
/* NSPR1 compatibility definitions */
#ifdef NSPR20
# include "prprf.h"
# include "prlog.h"
# include "plhash.h" /* NSPR hash-tables */
#else
# include "prprintf.h"
# include "prassert.h"
# include "prhash.h" /* NSPR hash-tables */
# define PRHashNumber prhashcode
#endif
#ifdef XP_MAC
# include "prosdep.h"
#endif
#include "jsj_hash.h" /* Hash tables */
#include "jni.h" /* Java Native Interface */
#include "jsapi.h" /* JavaScript engine API */
#include "jsjava.h" /* LiveConnect public API */
/*************************** Type Declarations ******************************/
/* Forward type declarations */
typedef struct JavaMemberDescriptor JavaMemberDescriptor;
typedef struct JavaMethodSpec JavaMethodSpec;
typedef struct JavaClassDescriptor JavaClassDescriptor;
typedef struct JavaClassDescriptor JavaSignature;
typedef struct CapturedJSError CapturedJSError;
typedef struct JavaMemberVal JavaMemberVal;
/*
* This enum uses the same character encoding used by the JDK to encode
* Java type signatures, but the enum is easier to debug/compile with.
*/
typedef enum {
JAVA_SIGNATURE_ARRAY = '[',
JAVA_SIGNATURE_BYTE = 'B',
JAVA_SIGNATURE_CHAR = 'C',
JAVA_SIGNATURE_CLASS = 'L',
JAVA_SIGNATURE_FLOAT = 'F',
JAVA_SIGNATURE_DOUBLE = 'D',
JAVA_SIGNATURE_INT = 'I',
JAVA_SIGNATURE_LONG = 'J',
JAVA_SIGNATURE_SHORT = 'S',
JAVA_SIGNATURE_VOID = 'V',
JAVA_SIGNATURE_BOOLEAN = 'Z',
JAVA_SIGNATURE_UNKNOWN = 0
} JavaSignatureChar;
/* The signature of a Java method consists of the signatures of all its
arguments and its return type signature. */
typedef struct JavaMethodSignature {
jsize num_args; /* Length of arg_signatures array */
JavaSignature ** arg_signatures; /* Array of argument signatures */
JavaSignature * return_val_signature; /* Return type signature */
} JavaMethodSignature;
/* A descriptor for the reflection of a single Java field */
typedef struct JavaFieldSpec {
jfieldID fieldID; /* JVM opaque access handle for field */
JavaSignature * signature; /* Java type of field */
int modifiers; /* Bitfield indicating field qualifiers */
const char * name; /* UTF8; TODO - Should support Unicode field names */
} JavaFieldSpec;
/* A descriptor for the reflection of a single Java method.
Each overloaded method has a separate corresponding JavaMethodSpec. */
struct JavaMethodSpec {
jmethodID methodID; /* JVM opaque access handle for method */
JavaMethodSignature signature;
const char * name; /* UTF8; TODO - Should support Unicode method names */
JavaMethodSpec * next; /* next method in chain of overloaded methods */
};
/*
* A descriptor for the reflection of a single member of a Java object.
* This can represent one or more Java methods and/or a single field.
* (When there is more than one method attached to a single JavaMemberDescriptor
* they are overloaded methods sharing the same simple name.) This same
* descriptor type is used for both static or instance members.
*/
struct JavaMemberDescriptor {
const char * name; /* simple name of field and/or method */
jsid id; /* hashed name for quick JS property lookup */
JavaFieldSpec * field; /* field with the given name, if any */
JavaMethodSpec * methods; /* Overloaded methods which share the same name, if any */
JavaMemberDescriptor * next; /* next descriptor in same defining class */
JSObject * invoke_func_obj; /* If non-null, JSFunction obj to invoke method */
};
/* This is the native portion of a reflected Java class */
struct JavaClassDescriptor {
const char * name; /* Name of class, e.g. "java/lang/Byte" */
JavaSignatureChar type; /* class category: primitive type, object, array */
jclass java_class; /* Opaque JVM handle to corresponding java.lang.Class */
int num_instance_members;
int num_static_members;
JSBool instance_members_reflected;
JavaMemberDescriptor * instance_members;
JSBool static_members_reflected;
JavaMemberDescriptor * static_members;
JavaMemberDescriptor * constructors;
int modifiers; /* Class declaration qualifiers,
e.g. abstract, private */
int ref_count; /* # of references to this struct */
JavaSignature * array_component_signature; /* Only non-NULL for array classes */
};
/* This is the native portion of a reflected Java method or field */
struct JavaMemberVal {
jsval field_val; /* Captured value of Java field */
jsval invoke_method_func_val; /* JSFunction wrapper around Java method invoker */
JavaMemberDescriptor * descriptor;
JavaMemberVal * next;
};
/* This is the native portion of a reflected Java object */
typedef struct JavaObjectWrapper {
jobject java_obj; /* Opaque JVM ref to Java object */
JavaClassDescriptor * class_descriptor; /* Java class info */
} JavaObjectWrapper;
/* These are definitions of the Java class/method/field modifier bits.
These really shouldn't be hard-coded here. Rather,
they should be read from java.lang.reflect.Modifier */
#define ACC_PUBLIC 0x0001 /* visible to everyone */
#define ACC_STATIC 0x0008 /* instance variable is static */
#define ACC_FINAL 0x0010 /* no further subclassing,overriding */
#define ACC_INTERFACE 0x0200 /* class is an interface */
#define ACC_ABSTRACT 0x0400 /* no definition provided */
/* A JSJavaVM structure must be created for each Java VM that is accessed
via LiveConnect */
struct JSJavaVM {
/* TODO - all LiveConnect global variables should be migrated into this
structure in order to allow more than one LiveConnect-enabled
Java VM to exist within the same process. */
SystemJavaVM * java_vm;
JNIEnv * main_thread_env; /* Main-thread Java environment */
JSBool jsj_created_java_vm;
int num_attached_threads;
JSJavaVM * next; /* next VM among all created VMs */
};
/* Per-thread state that encapsulates the connection to the Java VM */
struct JSJavaThreadState {
const char * name; /* Thread name, for debugging */
JSJavaVM * jsjava_vm; /* All per-JVM state */
JNIEnv * jEnv; /* Per-thread opaque handle to Java VM */
CapturedJSError * pending_js_errors; /* JS errors to be thrown as Java exceptions */
JSContext * cx; /* current JS context for thread */
JSJavaThreadState * next; /* next thread state among all created threads */
};
struct JavaToJSSavedState {
JSErrorReporter error_reporter;
JSJavaThreadState* java_jsj_env;
};
typedef struct JavaToJSSavedState JavaToJSSavedState;
/******************************** Globals ***********************************/
extern JSJCallbacks *JSJ_callbacks;
/* JavaScript classes that reflect Java objects */
extern JSClass JavaObject_class;
extern JSClass JavaArray_class;
extern JSClass JavaClass_class;
extern JSClass JavaMember_class;
/*
* Opaque JVM handles to Java classes, methods and objects required for
* Java reflection. These are computed and cached during initialization.
* TODO: These should be moved inside the JSJavaVM struct
*/
extern jclass jlObject; /* java.lang.Object */
extern jclass jlrConstructor; /* java.lang.reflect.Constructor */
extern jclass jlThrowable; /* java.lang.Throwable */
extern jclass jlSystem; /* java.lang.System */
extern jclass jlClass; /* java.lang.Class */
extern jclass jlBoolean; /* java.lang.Boolean */
extern jclass jlDouble; /* java.lang.Double */
extern jclass jlString; /* java.lang.String */
extern jclass njJSObject; /* netscape.javascript.JSObject */
extern jclass njJSException; /* netscape.javascript.JSException */
extern jclass njJSUtil; /* netscape.javascript.JSUtil */
extern jmethodID jlClass_getMethods; /* java.lang.Class.getMethods() */
extern jmethodID jlClass_getConstructors; /* java.lang.Class.getConstructors() */
extern jmethodID jlClass_getFields; /* java.lang.Class.getFields() */
extern jmethodID jlClass_getName; /* java.lang.Class.getName() */
extern jmethodID jlClass_getComponentType; /* java.lang.Class.getComponentType() */
extern jmethodID jlClass_getModifiers; /* java.lang.Class.getModifiers() */
extern jmethodID jlClass_isArray; /* java.lang.Class.isArray() */
extern jmethodID jlrMethod_getName; /* java.lang.reflect.Method.getName() */
extern jmethodID jlrMethod_getParameterTypes; /* java.lang.reflect.Method.getParameterTypes() */
extern jmethodID jlrMethod_getReturnType; /* java.lang.reflect.Method.getReturnType() */
extern jmethodID jlrMethod_getModifiers; /* java.lang.reflect.Method.getModifiers() */
extern jmethodID jlrConstructor_getParameterTypes; /* java.lang.reflect.Constructor.getParameterTypes() */
extern jmethodID jlrConstructor_getModifiers; /* java.lang.reflect.Constructor.getModifiers() */
extern jmethodID jlrField_getName; /* java.lang.reflect.Field.getName() */
extern jmethodID jlrField_getType; /* java.lang.reflect.Field.getType() */
extern jmethodID jlrField_getModifiers; /* java.lang.reflect.Field.getModifiers() */
extern jmethodID jlThrowable_getMessage; /* java.lang.Throwable.getMessage() */
extern jmethodID jlThrowable_toString; /* java.lang.Throwable.toString() */
extern jmethodID jlBoolean_Boolean; /* java.lang.Boolean constructor */
extern jmethodID jlBoolean_booleanValue; /* java.lang.Boolean.booleanValue() */
extern jmethodID jlDouble_Double; /* java.lang.Double constructor */
extern jmethodID jlDouble_doubleValue; /* java.lang.Double.doubleValue() */
extern jmethodID jlSystem_identityHashCode; /* java.lang.System.identityHashCode() */
extern jobject jlVoid_TYPE; /* java.lang.Void.TYPE value */
extern jmethodID njJSException_JSException; /* netscape.javascipt.JSexception constructor */
extern jmethodID njJSObject_JSObject; /* netscape.javascript.JSObject constructor */
extern jmethodID njJSUtil_getStackTrace; /* netscape.javascript.JSUtil.getStackTrace() */
extern jfieldID njJSObject_internal; /* netscape.javascript.JSObject.internal */
extern jfieldID njJSException_lineno; /* netscape.javascript.JSException.lineno */
extern jfieldID njJSException_tokenIndex; /* netscape.javascript.JSException.tokenIndex */
extern jfieldID njJSException_source; /* netscape.javascript.JSException.source */
extern jfieldID njJSException_filename; /* netscape.javascript.JSException.filename */
/**************** Java <==> JS conversions and Java types *******************/
extern JSBool
jsj_ComputeJavaClassSignature(JSContext *cx,
JavaSignature *signature,
jclass java_class);
extern const char *
jsj_ConvertJavaSignatureToString(JSContext *cx, JavaSignature *signature);
extern const char *
jsj_ConvertJavaSignatureToHRString(JSContext *cx,
JavaSignature *signature);
extern JavaMethodSignature *
jsj_InitJavaMethodSignature(JSContext *cx, JNIEnv *jEnv, jobject method,
JavaMethodSignature *method_signature);
extern const char *
jsj_ConvertJavaMethodSignatureToString(JSContext *cx,
JavaMethodSignature *method_signature);
extern const char *
jsj_ConvertJavaMethodSignatureToHRString(JSContext *cx,
const char *method_name,
JavaMethodSignature *method_signature);
extern void
jsj_PurgeJavaMethodSignature(JSContext *cx, JNIEnv *jEnv, JavaMethodSignature *signature);
extern JSBool
jsj_ConvertJSValueToJavaValue(JSContext *cx, JNIEnv *jEnv, jsval v, JavaSignature *signature,
int *cost, jvalue *java_value, JSBool *is_local_refp);
extern JSBool
jsj_ConvertJSValueToJavaObject(JSContext *cx, JNIEnv *jEnv, jsval v, JavaSignature *signature,
int *cost, jobject *java_value, JSBool *is_local_refp);
extern jstring
jsj_ConvertJSStringToJavaString(JSContext *cx, JNIEnv *jEnv, JSString *js_str);
extern JSBool
jsj_ConvertJavaValueToJSValue(JSContext *cx, JNIEnv *jEnv, JavaSignature *signature,
jvalue *java_value, jsval *vp);
extern JSBool
jsj_ConvertJavaObjectToJSValue(JSContext *cx, JNIEnv *jEnv,
jobject java_obj, jsval *vp);
extern JSString *
jsj_ConvertJavaStringToJSString(JSContext *cx, JNIEnv *jEnv, jstring java_str);
extern JSBool
jsj_ConvertJavaObjectToJSString(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jobject java_obj, jsval *vp);
extern JSBool
jsj_ConvertJavaObjectToJSNumber(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jobject java_obj, jsval *vp);
extern JSBool
jsj_ConvertJavaObjectToJSBoolean(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jobject java_obj, jsval *vp);
extern JSJavaThreadState *
jsj_enter_js(JNIEnv *jEnv, jobject java_wrapper_obj,
JSContext **cxp, JSObject **js_objp, JavaToJSSavedState* saved_state);
extern JSBool
jsj_exit_js(JSContext *cx, JSJavaThreadState *jsj_env, JavaToJSSavedState* original_state);
extern JavaClassDescriptor *
jsj_get_jlObject_descriptor(JSContext *cx, JNIEnv *jEnv);
extern JSBool
jsj_remove_js_obj_reflection_from_hashtable(JSContext *cx, JSObject *js_obj);
extern JSBool
jsj_init_js_obj_reflections_table();
/************************ Java package reflection **************************/
extern JSBool
jsj_init_JavaPackage(JSContext *, JSObject *,
JavaPackageDef *predefined_packages);
/************************* Java class reflection ***************************/
extern JSBool
jsj_init_JavaClass(JSContext *cx, JSObject *global_obj);
extern void
jsj_DiscardJavaClassReflections(JNIEnv *jEnv);
extern const char *
jsj_GetJavaClassName(JSContext *cx, JNIEnv *jEnv, jclass java_class);
extern JavaClassDescriptor *
jsj_GetJavaClassDescriptor(JSContext *cx, JNIEnv *jEnv, jclass java_class);
extern void
jsj_ReleaseJavaClassDescriptor(JSContext *cx, JNIEnv *jEnv, JavaClassDescriptor *class_descriptor);
extern JSObject *
jsj_define_JavaClass(JSContext *cx, JNIEnv *jEnv, JSObject *obj,
const char *unqualified_class_name,
jclass jclazz);
extern JavaMemberDescriptor *
jsj_GetJavaMemberDescriptor(JSContext *cx,
JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jstring member_name);
extern JavaMemberDescriptor *
jsj_LookupJavaMemberDescriptorById(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jsid id);
extern JavaMemberDescriptor *
jsj_LookupJavaStaticMemberDescriptorById(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jsid id);
extern JavaMemberDescriptor *
jsj_GetJavaStaticMemberDescriptor(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
jstring member_name);
extern JavaMemberDescriptor *
jsj_GetJavaClassConstructors(JSContext *cx,
JavaClassDescriptor *class_descriptor);
extern JavaMemberDescriptor *
jsj_LookupJavaClassConstructors(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor);
extern JavaMemberDescriptor *
jsj_GetClassInstanceMembers(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor);
extern JavaMemberDescriptor *
jsj_GetClassStaticMembers(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor);
extern JSBool
jsj_InitJavaClassReflectionsTable();
/************************* Java field reflection ***************************/
extern JSBool
jsj_GetJavaFieldValue(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field_spec,
jobject java_obj, jsval *vp);
extern JSBool
jsj_SetJavaFieldValue(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field_spec,
jobject java_obj, jsval js_val);
extern JSBool
jsj_ReflectJavaFields(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
JSBool reflect_only_static_fields);
extern void
jsj_DestroyFieldSpec(JSContext *cx, JNIEnv *jEnv, JavaFieldSpec *field);
/************************* Java method reflection ***************************/
extern JSBool
jsj_JavaInstanceMethodWrapper(JSContext *cx, JSObject *obj,
uintN argc, jsval *argv, jsval *vp);
extern JSBool
jsj_JavaStaticMethodWrapper(JSContext *cx, JSObject *obj,
uintN argc, jsval *argv, jsval *vp);
extern JSBool
jsj_JavaConstructorWrapper(JSContext *cx, JSObject *obj,
uintN argc, jsval *argv, jsval *vp);
extern JSBool
jsj_ReflectJavaMethods(JSContext *cx, JNIEnv *jEnv,
JavaClassDescriptor *class_descriptor,
JSBool reflect_only_static_methods);
extern void
jsj_DestroyMethodSpec(JSContext *cx, JNIEnv *jEnv, JavaMethodSpec *method_spec);
/************************* Java member reflection ***************************/
extern JSBool
jsj_init_JavaMember(JSContext *, JSObject *);
extern JSBool
jsj_ReflectJavaMethodsAndFields(JSContext *cx, JavaClassDescriptor *class_descriptor,
JSBool reflect_only_statics);
extern JSObject *
jsj_CreateJavaMember(JSContext *cx, jsval method_val, jsval field_val);
/************************* Java object reflection **************************/
extern JSBool
jsj_init_JavaObject(JSContext *, JSObject *);
extern JSObject *
jsj_WrapJavaObject(JSContext *cx, JNIEnv *jEnv, jobject java_obj, jclass java_class);
extern void
jsj_DiscardJavaObjReflections(JNIEnv *jEnv);
extern JSBool
JavaObject_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp);
extern void
JavaObject_finalize(JSContext *cx, JSObject *obj);
extern JSBool
JavaObject_resolve(JSContext *cx, JSObject *obj, jsval id);
extern JSBool
JavaObject_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
extern JSBool
JavaObject_getPropertyById(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
/************************* Java array reflection ***************************/
extern JSBool
jsj_init_JavaArray(JSContext *cx, JSObject *global_obj);
extern JSBool
jsj_GetJavaArrayElement(JSContext *cx, JNIEnv *jEnv, jarray java_array,
jsize index, JavaSignature *array_component_signature,
jsval *vp);
extern JSBool
jsj_SetJavaArrayElement(JSContext *cx, JNIEnv *jEnv, jarray java_array,
jsize index, JavaSignature *array_component_signature,
jsval js_val);
/********************* JavaScript object reflection ************************/
extern jobject
jsj_WrapJSObject(JSContext *cx, JNIEnv *jEnv, JSObject *js_obj);
extern JSObject *
jsj_UnwrapJSObjectWrapper(JNIEnv *jEnv, jobject java_wrapper_obj);
extern void
jsj_ClearPendingJSErrors(JSJavaThreadState *jsj_env);
extern JSBool
jsj_ReportUncaughtJSException(JSContext *cx, JNIEnv *jEnv, jthrowable java_exception);
/**************************** Utilities ************************************/
extern void
jsj_ReportJavaError(JSContext *cx, JNIEnv *env, const char *format, ...);
extern void
jsj_UnexpectedJavaError(JSContext *cx, JNIEnv *env, const char *format, ...);
extern const char *
jsj_GetJavaErrorMessage(JNIEnv *env);
extern void
jsj_LogError(const char *error_msg);
PR_CALLBACK JSJHashNumber
jsj_HashJavaObject(const void *key, void* env);
PR_CALLBACK intN
jsj_JavaObjectComparator(const void *v1, const void *v2, void *arg);
extern JSJavaThreadState *
jsj_MapJavaThreadToJSJavaThreadState(JNIEnv *jEnv, char **errp);
extern void
jsj_MakeJNIClassname(char *jClassName);
extern const char *
jsj_ClassNameOfJavaObject(JSContext *cx, JNIEnv *jEnv, jobject java_object);
extern jsize
jsj_GetJavaArrayLength(JSContext *cx, JNIEnv *jEnv, jarray java_array);
extern JSBool
JavaStringToId(JSContext *cx, JNIEnv *jEnv, jstring jstr, jsid *idp);
extern const char *
jsj_DupJavaStringUTF(JSContext *cx, JNIEnv *jEnv, jstring jstr);
JSJavaThreadState *
jsj_MapJSContextToJSJThread(JSContext *cx, JNIEnv **envp);
JSJavaThreadState *
jsj_SetJavaJSJEnv(JSJavaThreadState* java_jsj_env);
#ifdef DEBUG
#define DEBUG_LOG(args) printf args
#endif
#define JS_FREE_IF(cx, x) \
PR_BEGIN_MACRO \
if (x) \
JS_free(cx, x); \
PR_END_MACRO
#endif /* _JSJAVA_PVT_H */

View File

@@ -0,0 +1,361 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 Communicator client 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.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains low-level utility code.
*
*/
#include <stdlib.h>
#include <string.h>
#include "jsj_private.h" /* LiveConnect internals */
#include "jsjava.h" /* External LiveConnect API */
/*
* This is a hash-table utility routine that computes the hash code of a Java
* object by calling java.lang.System.identityHashCode()
*/
PR_CALLBACK JSJHashNumber
jsj_HashJavaObject(const void *key, void* env)
{
PRHashNumber hash_code;
jobject java_obj;
JNIEnv *jEnv;
java_obj = (jobject)key;
jEnv = (JNIEnv*) env;
hash_code = (*jEnv)->CallStaticIntMethod(jEnv, jlSystem,
jlSystem_identityHashCode, java_obj);
PR_ASSERT(!(*jEnv)->ExceptionOccurred(jEnv));
return hash_code;
}
/*
* This is a hash-table utility routine for comparing two Java objects.
* It's not possible to use the == operator to directly compare two jobject's,
* since they're opaque references and aren't guaranteed to be simple pointers
* or handles (though they may be in some JVM implementations). Instead,
* use the JNI routine for comparing the two objects.
*/
PR_CALLBACK intN
jsj_JavaObjectComparator(const void *v1, const void *v2, void *arg)
{
jobject java_obj1, java_obj2;
JNIEnv *jEnv;
jEnv = (JNIEnv*)arg;
java_obj1 = (jobject)v1;
java_obj2 = (jobject)v2;
if (java_obj1 == java_obj2)
return 1;
return (*jEnv)->IsSameObject(jEnv, java_obj1, java_obj2);
}
/*
* Return a UTF8, null-terminated encoding of a Java string. The string must
* be free'ed by the caller.
*
* If an error occurs, returns NULL and calls the JS error reporter.
*/
const char *
jsj_DupJavaStringUTF(JSContext *cx, JNIEnv *jEnv, jstring jstr)
{
const char *str, *retval;
str = (*jEnv)->GetStringUTFChars(jEnv, jstr, 0);
if (!str) {
jsj_UnexpectedJavaError(cx, jEnv, "Can't get UTF8 characters from "
"Java string");
return NULL;
}
retval = JS_strdup(cx, str);
(*jEnv)->ReleaseStringUTFChars(jEnv, jstr, str);
return retval;
}
JSBool
JavaStringToId(JSContext *cx, JNIEnv *jEnv, jstring jstr, jsid *idp)
{
const jschar *ucs2;
JSString *jsstr;
jsize ucs2_len;
jsval val;
ucs2 = (*jEnv)->GetStringChars(jEnv, jstr, 0);
if (!ucs2) {
jsj_UnexpectedJavaError(cx, jEnv, "Couldn't obtain Unicode characters"
"from Java string");
return JS_FALSE;
}
ucs2_len = (*jEnv)->GetStringLength(jEnv, jstr);
jsstr = JS_InternUCStringN(cx, ucs2, ucs2_len);
(*jEnv)->ReleaseStringChars(jEnv, jstr, ucs2);
if (!jsstr)
return JS_FALSE;
val = STRING_TO_JSVAL(jsstr);
JS_ValueToId(cx, STRING_TO_JSVAL(jsstr), idp);
return JS_TRUE;
}
/* Not used ?
const char *
jsj_ClassNameOfJavaObject(JSContext *cx, JNIEnv *jEnv, jobject java_object)
{
jobject java_class;
java_class = (*jEnv)->GetObjectClass(jEnv, java_object);
if (!java_class) {
PR_ASSERT(0);
return NULL;
}
return jsj_GetJavaClassName(cx, jEnv, java_class);
}
*/
/*
* Return, as a C string, the error message associated with a Java exception
* that occurred as a result of a JNI call, preceded by the class name of
* the exception. As a special case, if the class of the exception is
* netscape.javascript.JSException, the exception class name is omitted.
*
* NULL is returned if no Java exception is pending. The caller is
* responsible for free'ing the returned string. On exit, the Java exception
* is *not* cleared.
*/
const char *
jsj_GetJavaErrorMessage(JNIEnv *jEnv)
{
const char *java_error_msg;
char *error_msg = NULL;
jthrowable exception;
jstring java_exception_jstring;
exception = (*jEnv)->ExceptionOccurred(jEnv);
if (exception && jlThrowable_toString) {
java_exception_jstring =
(*jEnv)->CallObjectMethod(jEnv, exception, jlThrowable_toString);
java_error_msg = (*jEnv)->GetStringUTFChars(jEnv, java_exception_jstring, NULL);
error_msg = strdup((char*)java_error_msg);
(*jEnv)->ReleaseStringUTFChars(jEnv, java_exception_jstring, java_error_msg);
#ifdef DEBUG
/* (*jEnv)->ExceptionDescribe(jEnv); */
#endif
}
return error_msg;
}
/*
* Return, as a C string, the JVM stack trace associated with a Java
* exception, as would be printed by java.lang.Throwable.printStackTrace().
* The caller is responsible for free'ing the returned string.
*
* Returns NULL if an error occurs.
*/
static const char *
get_java_stack_trace(JSContext *cx, JNIEnv *jEnv, jthrowable java_exception)
{
const char *backtrace;
jstring backtrace_jstr;
backtrace = NULL;
if (java_exception && njJSUtil_getStackTrace) {
backtrace_jstr = (*jEnv)->CallStaticObjectMethod(jEnv, njJSUtil,
njJSUtil_getStackTrace,
java_exception);
if (!backtrace_jstr) {
jsj_UnexpectedJavaError(cx, jEnv, "Unable to get exception stack trace");
return NULL;
}
backtrace = jsj_DupJavaStringUTF(cx, jEnv, backtrace_jstr);
}
return backtrace;
}
/* Full Java backtrace when Java exceptions reported to JavaScript */
#define REPORT_JAVA_EXCEPTION_STACK_TRACE
/*
* This is a wrapper around JS_ReportError(), useful when an error condition
* is the result of a JVM failure or exception condition. It appends the
* message associated with the pending Java exception to the passed in
* printf-style format string and arguments.
*/
static void
vreport_java_error(JSContext *cx, JNIEnv *jEnv, const char *format, va_list ap)
{
char *error_msg, *js_error_msg;
const char *java_stack_trace;
const char *java_error_msg;
jthrowable java_exception;
java_error_msg = NULL;
java_exception = (*jEnv)->ExceptionOccurred(jEnv);
if (java_exception && njJSException &&
(*jEnv)->IsInstanceOf(jEnv, java_exception, njJSException)) {
(*jEnv)->ExceptionClear(jEnv);
jsj_ReportUncaughtJSException(cx, jEnv, java_exception);
return;
}
js_error_msg = PR_vsmprintf(format, ap);
if (!js_error_msg) {
PR_ASSERT(0); /* Out-of-memory */
return;
}
#ifdef REPORT_JAVA_EXCEPTION_STACK_TRACE
java_stack_trace = get_java_stack_trace(cx, jEnv, java_exception);
if (java_stack_trace) {
error_msg = PR_smprintf("%s\n%s", js_error_msg, java_stack_trace);
free((char*)java_stack_trace);
if (!error_msg) {
PR_ASSERT(0); /* Out-of-memory */
return;
}
} else
#endif
{
java_error_msg = jsj_GetJavaErrorMessage(jEnv);
if (java_error_msg) {
error_msg = PR_smprintf("%s (%s)\n", js_error_msg, java_error_msg);
free((char*)java_error_msg);
free(js_error_msg);
} else {
error_msg = js_error_msg;
}
}
JS_ReportError(cx, error_msg);
/* Important: the Java exception must not be cleared until the reporter
has been called, because the capture_js_error_reports_for_java(),
called from JS_ReportError(), needs to read the exception from the JVM */
(*jEnv)->ExceptionClear(jEnv);
free(error_msg);
}
void
jsj_ReportJavaError(JSContext *cx, JNIEnv *env, const char *format, ...)
{
va_list ap;
va_start(ap, format);
vreport_java_error(cx, env, format, ap);
va_end(ap);
}
/*
* Same as jsj_ReportJavaError, except "internal error: " is prepended
* to message.
*/
void
jsj_UnexpectedJavaError(JSContext *cx, JNIEnv *env, const char *format, ...)
{
va_list ap;
const char *format2;
va_start(ap, format);
format2 = PR_smprintf("internal error: %s", format);
if (format2) {
vreport_java_error(cx, env, format2, ap);
free((void*)format2);
}
va_end(ap);
}
/*
* Most LiveConnect errors are signaled by calling JS_ReportError(),
* but in some circumstances, the target JSContext for such errors
* is not determinable, e.g. during initialization. In such cases
* any error messages are routed to this function.
*/
void
jsj_LogError(const char *error_msg)
{
if (JSJ_callbacks && JSJ_callbacks->error_print)
JSJ_callbacks->error_print(error_msg);
else
fputs(error_msg, stderr);
}
jsize
jsj_GetJavaArrayLength(JSContext *cx, JNIEnv *jEnv, jarray java_array)
{
jsize array_length = (*jEnv)->GetArrayLength(jEnv, java_array);
if ((*jEnv)->ExceptionOccurred(jEnv)) {
jsj_UnexpectedJavaError(cx, jEnv, "Couldn't obtain array length");
return -1;
}
return array_length;
}
static JSJavaThreadState *the_java_jsj_env = NULL;
JSJavaThreadState *
jsj_MapJSContextToJSJThread(JSContext *cx, JNIEnv **envp)
{
JSJavaThreadState *jsj_env;
char *err_msg;
*envp = NULL;
err_msg = NULL;
jsj_env = the_java_jsj_env;
if (jsj_env == NULL)
jsj_env = JSJ_callbacks->map_js_context_to_jsj_thread(cx, &err_msg);
if (!jsj_env) {
if (err_msg) {
JS_ReportError(cx, err_msg);
free(err_msg);
}
return NULL;
}
if (envp)
*envp = jsj_env->jEnv;
return jsj_env;
}
/**
* Since only one Java thread is allowed to enter JavaScript, this function is
* used to enforce the use of that thread's state. The static global the_java_jsj_env
* overrides using JSJ_callbacks->map_js_context_to_jsj_thread, which maps
* native threads to JSJavaThreadStates. This isn't appropriate when Java calls
* JavaScript, as there can be a many to one mapping from Java threads to native
* threads.
*/
JSJavaThreadState *
jsj_SetJavaJSJEnv(JSJavaThreadState* java_jsj_env)
{
JSJavaThreadState *old_jsj_env = the_java_jsj_env;
the_java_jsj_env = java_jsj_env;
return old_jsj_env;
}

View File

@@ -0,0 +1,264 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (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 Communicator client 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.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* Publicly exported functions for JavaScript <==> Java communication.
*
*/
#ifndef _JSJAVA_H
#define _JSJAVA_H
#ifndef prtypes_h___
#include "prtypes.h"
#endif
PR_BEGIN_EXTERN_C
#include "jni.h" /* Java Native Interface */
#include "jsapi.h" /* JavaScript engine API */
/*
* A JSJavaVM structure is a wrapper around a JavaVM which incorporates
* additional LiveConnect state.
*/
typedef struct JSJavaVM JSJavaVM;
/* LiveConnect and Java state, one per thread */
typedef struct JSJavaThreadState JSJavaThreadState;
/*
* An opaque type that represents the connection to the Java VM. In stand-alone
* Java environments, this may be a JNI JavaVM object; in the browser environment
* it is a reference to a JVM plugin. A set of callbacks in the JSJCallbacks
* struct allow it to be manipulated.
*/
typedef struct SystemJavaVM SystemJavaVM;
/*
* This callback table provides hooks to external functions that implement
* functionality specific to the embedding. For example, these callbacks are
* necessary in multi-threaded environments or to implement a security
* policy.
*/
typedef struct JSJCallbacks {
/* This callback is invoked when there is no JavaScript execution
environment (JSContext) associated with the current Java thread and
a call is made from Java into JavaScript. (A JSContext is associated
with a Java thread by calling the JSJ_SetJSContextForJavaThread()
function.) This callback is only invoked when Java spontaneously calls
into JavaScript, i.e. it is not called when JS calls into Java which
calls back into JS.
This callback can be used to create a JSContext lazily, or obtain
one from a pool of available JSContexts. The implementation of this
callback can call JSJ_SetJSContextForJavaThread() to avoid any further
callbacks of this type for this Java thread. */
JSContext * (*map_jsj_thread_to_js_context)(JSJavaThreadState *jsj_env,
JNIEnv *jEnv,
char **errp);
/* This callback is invoked whenever a call is made into Java from
JavaScript. It's responsible for mapping from a JavaScript execution
environment (JSContext) to a Java thread. (A JavaContext can only
be associated with one Java thread at a time.) */
JSJavaThreadState * (*map_js_context_to_jsj_thread)(JSContext *cx,
char **errp);
/* This callback implements netscape.javascript.JSObject.getWindow(),
a method named for its behavior in the browser environment, where it
returns the JS "Window" object corresponding to the HTML window that an
applet is embedded within. More generally, it's a way for Java to get
hold of a JS object that has not been explicitly passed to it. */
JSObject * (*map_java_object_to_js_object)(JNIEnv *jEnv, void *pJavaObject,
char **errp);
/* An interim callback function until the LiveConnect security story is
straightened out. This function pointer can be set to NULL. */
JSPrincipals * (*get_JSPrincipals_from_java_caller)(JNIEnv *jEnv, JSContext *pJSContext);
/* The following two callbacks sandwich any JS evaluation performed
from Java. They may be used to implement concurrency constraints, e.g.
by suspending the current thread until some condition is met. In the
browser embedding, these are used to maintain the run-to-completion
semantics of JavaScript. It is acceptable for either function pointer
to be NULL. */
JSBool (*enter_js_from_java)(JNIEnv *jEnv, char **errp);
void (*exit_js)(JNIEnv *jEnv);
/* Most LiveConnect errors are signaled by calling JS_ReportError(), but in
some circumstances, the target JSContext for such errors is not
determinable, e.g. during initialization. In such cases any error
messages are routed to this function. If the function pointer is set to
NULL, error messages are sent to stderr. */
void (*error_print)(const char *error_msg);
/* This enables liveconnect to ask the VM for a java wrapper so that VM gets a chance to
store a mapping between a jsobject and java wrapper. So the unwrapping can be done on the
VM side before calling nsILiveconnect apis. This saves on a round trip request. */
jobject (*get_java_wrapper)(JNIEnv *jEnv, jint jsobject);
/* The following set of methods abstract over the JavaVM object. */
PRBool (*create_java_vm)(SystemJavaVM* *jvm, JNIEnv* *initialEnv, void* initargs);
PRBool (*destroy_java_vm)(SystemJavaVM* jvm, JNIEnv* initialEnv);
JNIEnv* (*attach_current_thread)(SystemJavaVM* jvm);
PRBool (*detach_current_thread)(SystemJavaVM* jvm, JNIEnv* env);
SystemJavaVM* (*get_java_vm)(JNIEnv* env);
/* Reserved for future use */
void * reserved[10];
} JSJCallbacks;
/*===========================================================================*/
/* A flag that denotes that a Java package has no sub-packages other than those
explicitly pre-defined at the time of initialization. An access
to a simple name within such a package, therefore, must either correspond to
one of these explicitly pre-defined sub-packages or to a class within this
package. It is reasonable for LiveConnect to signal an error if a simple
name does not comply with these criteria. */
#define PKG_SYSTEM 1
/* A flag that denotes that a Java package which might contain sub-packages
that are not pre-defined at initialization time, because the sub-packages
may not be the same in all installations. Therefore, an access to a simple
name within such a a package which does not correspond to either a
pre-defined sub-package or to a class, must be assummed to refer to an
unknown sub-package. This behavior may cause bogus JavaPackage objects to be
created if a package name is misspelled, e.g. sun.oi.net. */
#define PKG_USER 2
/* A Java package defined at initialization time. */
typedef struct JavaPackageDef {
const char * name; /* e.g. "java.lang" */
const char * path; /* e.g. "java/lang", or NULL for default */
int flags; /* PKG_USER, PKG_SYSTEM, etc. */
} JavaPackageDef;
/*===========================================================================*/
/* The following two convenience functions present a complete, but simplified
LiveConnect API which is designed to handle the special case of a single
Java-VM, with single-threaded operation, and the use of only one JSContext.
The full API is in the section below. */
/* Initialize the provided JSContext by setting up the JS classes necessary for
reflection and by defining JavaPackage objects for the default Java packages
as properties of global_obj. If java_vm is NULL, a new Java VM is
created, using the provided classpath in addition to any default classpath.
The classpath argument is ignored, however, if java_vm is non-NULL. */
PR_IMPLEMENT(JSBool)
JSJ_SimpleInit(JSContext *cx, JSObject *global_obj,
SystemJavaVM *java_vm, const char *classpath);
/* Free up all resources. Destroy the Java VM if it was created by LiveConnect */
PR_IMPLEMENT(void)
JSJ_SimpleShutdown();
/*===========================================================================*/
/* The "full" LiveConnect API, required when more than one thread, Java VM, or
JSContext is involved. Initialization pseudocode might go roughly like
this:
JSJ_Init() // Setup callbacks
for each JavaVM {
JSJ_ConnectToJavaVM(...)
}
for each JSContext {
JSJ_InitJSContext(...)
}
for each JS evaluation {
run JavaScript code in the JSContext;
}
*/
/* Called once for all instances of LiveConnect to set up callbacks */
PR_IMPLEMENT(void)
JSJ_Init(JSJCallbacks *callbacks);
/* Called once per Java VM, this function initializes the classes, fields, and
methods required for Java reflection. If java_vm is NULL, a new Java VM is
created according to the create_java_vm callback in the JSJCallbacks,
using the provided classpath in addition to any default initargs.
The initargs argument is ignored, however, if java_vm is non-NULL. */
PR_IMPLEMENT(JSJavaVM *)
JSJ_ConnectToJavaVM(SystemJavaVM *java_vm, void* initargs);
/* Initialize the provided JSContext by setting up the JS classes necessary for
reflection and by defining JavaPackage objects for the default Java packages
as properties of global_obj. Additional packages may be pre-defined by
setting the predefined_packages argument. (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.) */
PR_IMPLEMENT(JSBool)
JSJ_InitJSContext(JSContext *cx, JSObject *global_obj,
JavaPackageDef *predefined_packages);
/* This function returns a structure that encapsulates the Java and JavaScript
execution environment for the current native thread. It is intended to
be called from the embedder's implementation of JSJCallback's
map_js_context_to_jsj_thread() function. The thread_name argument is only
used for debugging purposes and can be set to NULL. The Java JNI
environment associated with this thread is returned through the java_envp
argument if java_envp is non-NULL. */
PR_IMPLEMENT(JSJavaThreadState *)
JSJ_AttachCurrentThreadToJava(JSJavaVM *jsjava_vm, const char *thread_name,
JNIEnv **java_envp);
/* Destructor routine for per-thread JSJavaThreadState structure */
PR_IMPLEMENT(JSBool)
JSJ_DetachCurrentThreadFromJava(JSJavaThreadState *jsj_env);
/* This function is used to specify a particular JSContext as *the* JavaScript
execution environment to be used when LiveConnect is accessed from the given
Java thread, i.e. when one of the methods of netscape.javascript.JSObject
has been called. There can only be one such JS context for any given Java
thread at a time. (To multiplex JSContexts among a single thread, this
function could be called before Java is invoked on that thread.) The return
value is the previous JSContext associated with the given Java thread.
If this function has not been called for a thread and a crossing is made
into JavaScript from Java, the map_jsj_thread_to_js_context() callback will
be invoked to determine the JSContext for the thread. The purpose of the
function is to improve performance by avoiding the expense of the callback.
*/
PR_IMPLEMENT(JSContext *)
JSJ_SetDefaultJSContextForJavaThread(JSContext *cx, JSJavaThreadState *jsj_env);
/* This routine severs the connection to a Java VM, freeing all related resources.
It shouldn't be called until the global scope has been cleared in all related
JSContexts (so that all LiveConnect objects are finalized) and a JavaScript
GC is performed. Otherwise, accessed to free'ed memory could result. */
PR_IMPLEMENT(void)
JSJ_DisconnectFromJavaVM(JSJavaVM *);
/*
* Reflect a Java object into a JS value. The source object, java_obj, must
* be of type java.lang.Object or a subclass and may, therefore, be an array.
*/
PR_IMPLEMENT(JSBool)
JSJ_ConvertJavaObjectToJSValue(JSContext *cx, jobject java_obj, jsval *vp);
PR_END_EXTERN_C
#endif /* _JSJAVA_H */

View File

@@ -0,0 +1,182 @@
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
IGNORE_MANIFEST=1
#//------------------------------------------------------------------------
#//
#// Makefile to build the Java reflections of JavaScript objects
#//
#//------------------------------------------------------------------------
#//------------------------------------------------------------------------
#//
#// Specify the depth of the current directory relative to the
#// root of NS
#//
#//------------------------------------------------------------------------
DEPTH= ..\..\..
include <$(DEPTH)/config/config.mak>
!ifdef NSJVM
DIRS = classes
!endif
#//------------------------------------------------------------------------
#//
#// Define any Public Make Variables here: (ie. PDFFILE, MAPFILE, ...)
#//
#//------------------------------------------------------------------------
DLLNAME=jsj$(MOZ_BITS)$(VERSION_NUMBER)
PDBFILE=$(DLLNAME).pdb
MAPFILE = $(DLLNAME).map
RESFILE = jsj1640.res
DLL=.\$(OBJDIR)\$(DLLNAME).dll
MAKE_OBJ_TYPE = DLL
!if "$(MOZ_BITS)" == "16"
DEFFILE = $(DLLNAME).def
!endif
LLIBS=$(LIBNSPR) $(DIST)\lib\js$(MOZ_BITS)$(VERSION_NUMBER).lib \
$(DIST)\lib\xpcom32.lib
!if "$(MOZ_BITS)"=="32" && defined(MOZ_DEBUG) && defined(GLOWCODE)
LLIBS=$(LLIBS) $(GLOWDIR)\glowcode.lib
!endif
#//------------------------------------------------------------------------
#//
#// Define the files necessary to build the target (ie. OBJS)
#//
#//------------------------------------------------------------------------
OBJS= \
.\$(OBJDIR)\jsj.obj \
.\$(OBJDIR)\jsj_JSObject.obj \
.\$(OBJDIR)\jsj_JavaArray.obj \
.\$(OBJDIR)\jsj_JavaClass.obj \
.\$(OBJDIR)\jsj_JavaMember.obj \
.\$(OBJDIR)\jsj_JavaObject.obj \
.\$(OBJDIR)\jsj_JavaPackage.obj \
.\$(OBJDIR)\jsj_array.obj \
.\$(OBJDIR)\jsj_class.obj \
.\$(OBJDIR)\jsj_convert.obj \
.\$(OBJDIR)\jsj_field.obj \
.\$(OBJDIR)\jsj_hash.obj \
.\$(OBJDIR)\jsj_method.obj \
.\$(OBJDIR)\jsj_utils.obj \
.\$(OBJDIR)\nsCLiveconnect.obj \
.\$(OBJDIR)\nsCLiveconnectFactory.obj \
!if "$(MOZ_BITS)" == "16"
.\$(OBJDIR)\jsj_nodl.obj \
!endif
$(NULL)
#//------------------------------------------------------------------------
#//
#// install headers
#//
#//------------------------------------------------------------------------
INSTALL_DIR=$(PUBLIC)\js
INSTALL_FILE_LIST= \
jsjava.h nsILiveconnect.h
#//------------------------------------------------------------------------
#//
#// Define any Public Targets here (ie. PROGRAM, LIBRARY, DLL, ...)
#// (these must be defined before the common makefiles are included)
#//
#//------------------------------------------------------------------------
!ifdef NSJVM
JNI_GEN= \
netscape.javascript.JSObject \
netscape.javascript.JSException \
$(NULL)
!endif
MODULE = java
EXPORTS = \
$(JNI_GEN_DIR)\netscape_javascript_JSObject.h \
$(JNI_GEN_DIR)\netscape_javascript_JSException.h \
$(NULL)
#//------------------------------------------------------------------------
#//
#// Define any local options for the make tools
#// (ie. LCFLAGS, LLFLAGS, LLIBS, LINCS)
#//
#//------------------------------------------------------------------------
LINCS=$(LINCS) -I$(JNI_GEN_DIR) \
-I$(PUBLIC)\js \
-I$(PUBLIC)\java \
-I$(PUBLIC)\xpcom \
$(NULL)
#!ifdef SERVER_BUILD
#LLIBS=$(DIST)/lib/httpdlw.lib $(DIST)/lib/libsjboot.lib
#!endif
#//------------------------------------------------------------------------
#//
#// Include the common makefile rules
#//
#//------------------------------------------------------------------------
include <$(DEPTH)/config/rules.mak>
export:: INSTALL_FILES
libs:: $(DLL)
$(MAKE_INSTALL) .\$(OBJDIR)\$(DLLNAME).dll $(DIST)\bin
$(MAKE_INSTALL) .\$(OBJDIR)\$(DLLNAME).lib $(DIST)\lib
####
# this bit of extreme scariness came from the js/src makefile
# reproduced here since that's where jsjava.c lives now...
!if ("$(MOZ_BITS)" == "16")
#//
#// Win16 Hoovers SO BAD!!!
#//
!if !defined(MOZ_DEBUG)
#//
#// We must turn off codeview debug info so jni.c can build.
#// Otherwise the linker gives errors about data in the $SYMBOLS
#// segment being beyond a segment boundary.
#//
$(OBJDIR)\jsjava.obj: jsjava.c
@$(CC) @<<$(CFGFILE)
-c
-Od
$(CFLAGS)
$(LCFLAGS)
$(LINCS)
$(LINCS_1)
$(INCS)
-Fd$(PDBFILE)
-Fo.\$(OBJDIR)\
$(*B).c
<<KEEP
!endif
!endif

View File

@@ -0,0 +1,111 @@
/* -*- Mode: C; tab-width: 8 -*-
* Copyright (C) 1998 Netscape Communications Corporation, All Rights Reserved.
*/
#include <jni.h>
/* Header for class netscape_javascript_JSObject */
#ifndef _Included_netscape_javascript_JSObject
#define _Included_netscape_javascript_JSObject
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: netscape_javascript_JSObject
* Method: initClass
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_initClass
(JNIEnv *, jclass);
/*
* Class: netscape_javascript_JSObject
* Method: getMember
* Signature: (Ljava/lang/String;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_getMember
(JNIEnv *, jobject, jstring);
/*
* Class: netscape_javascript_JSObject
* Method: getSlot
* Signature: (I)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_getSlot
(JNIEnv *, jobject, jint);
/*
* Class: netscape_javascript_JSObject
* Method: setMember
* Signature: (Ljava/lang/String;Ljava/lang/Object;)V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_setMember
(JNIEnv *, jobject, jstring, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: setSlot
* Signature: (ILjava/lang/Object;)V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_setSlot
(JNIEnv *, jobject, jint, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: removeMember
* Signature: (Ljava/lang/String;)V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_removeMember
(JNIEnv *, jobject, jstring);
/*
* Class: netscape_javascript_JSObject
* Method: call
* Signature: (Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_call
(JNIEnv *, jobject, jstring, jobjectArray);
/*
* Class: netscape_javascript_JSObject
* Method: eval
* Signature: (Ljava/lang/String;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_eval
(JNIEnv *, jobject, jstring);
/*
* Class: netscape_javascript_JSObject
* Method: toString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_netscape_javascript_JSObject_toString
(JNIEnv *, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: getWindow
* Signature: (Ljava/applet/Applet;)Lnetscape/javascript/JSObject;
*/
JNIEXPORT jobject JNICALL Java_netscape_javascript_JSObject_getWindow
(JNIEnv *, jclass, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: finalize
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_netscape_javascript_JSObject_finalize
(JNIEnv *, jobject);
/*
* Class: netscape_javascript_JSObject
* Method: equals
* Signature: (Ljava/lang/Object;)Z
*/
JNIEXPORT jboolean JNICALL Java_netscape_javascript_JSObject_equals
(JNIEnv *, jobject, jobject);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,567 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the implementation providing nsIFactory XP-COM interface.
*
*/
#include <stdlib.h>
#include <string.h>
#include "prtypes.h"
#include "prprf.h"
#include "prlog.h"
PR_BEGIN_EXTERN_C
#ifdef XP_MAC
#include "prosdep.h"
#endif
#include "jsj_private.h"
#include "jsjava.h"
#include "jscntxt.h" /* For js_ReportErrorAgain().
TODO - get rid of private header */
#include "netscape_javascript_JSObject.h" /* javah-generated headers */
/* A captured JavaScript error, created when JS_ReportError() is called while
running JavaScript code that is itself called from Java. */
struct CapturedJSError {
char * message;
JSErrorReport report; /* Line # of error, etc. */
jthrowable java_exception; /* Java exception, error, or null */
CapturedJSError * next; /* Next oldest captured JS error */
};
PR_END_EXTERN_C
#include "nsCLiveconnect.h"
static NS_DEFINE_IID(kILiveconnectIID, NS_ILIVECONNECT_IID);
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
////////////////////////////////////////////////////////////////////////////
// from nsISupports and AggregatedQueryInterface:
// Thes macro expands to the aggregated query interface scheme.
NS_IMPL_AGGREGATED(nsCLiveconnect);
NS_METHOD
nsCLiveconnect::AggregatedQueryInterface(const nsIID& aIID, void** aInstancePtr)
{
if (aIID.Equals(kISupportsIID)) {
*aInstancePtr = GetInner();
AddRef();
return NS_OK;
}
if (aIID.Equals(kILiveconnectIID)) {
*aInstancePtr = this;
AddRef();
return NS_OK;
}
return NS_NOINTERFACE;
}
////////////////////////////////////////////////////////////////////////////
// from nsILiveconnect:
/**
* get member of a Native JSObject for a given name.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
* @param name - Name of a member.
* @param pjobj - return parameter as a java object representing
* the member. If it is a basic data type it is converted to
* a corresponding java type. If it is a NJSObject, then it is
* wrapped up as java wrapper netscape.javascript.JSObject.
*/
NS_METHOD
nsCLiveconnect::GetMember(JNIEnv *jEnv, jsobject obj, const char *name, jobject *pjobj)
{
JSContext *cx = NULL;
JSJavaThreadState *jsj_env = NULL;
JSObject *js_obj = (JSObject *)obj;
nsresult err = NS_OK;
jobject member = NULL;
jsval js_val = NULL;
int dummy_cost = 0;
JSBool dummy_bool = PR_FALSE;
JavaToJSSavedState saved_state = {NULL,NULL};
if(jEnv == NULL)
{
return NS_ERROR_FAILURE;
}
jsj_env = jsj_enter_js(jEnv, NULL, &cx, NULL, &saved_state);
if (!jsj_env)
return NS_ERROR_FAILURE;
if (!name) {
JS_ReportError(cx, "illegal null member name");
member = NULL;
goto done;
}
if (!JS_GetProperty(cx, js_obj, name, &js_val))
goto done;
jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv),
&dummy_cost, &member, &dummy_bool);
done:
if (!jsj_exit_js(cx, jsj_env, &saved_state))
return NS_ERROR_FAILURE;
*pjobj = member;
return NS_OK;
}
/**
* get member of a Native JSObject for a given index.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
* @param index - Index of a member.
* @param pjobj - return parameter as a java object representing
* the member.
*/
NS_METHOD
nsCLiveconnect::GetSlot(JNIEnv *jEnv, jsobject obj, int slot, jobject *pjobj)
{
JSContext *cx = NULL;
JSJavaThreadState *jsj_env = NULL;
JSObject *js_obj = (JSObject *)obj;
nsresult err = NS_OK;
jobject member = NULL;
jsval js_val = NULL;
int dummy_cost = 0;
JSBool dummy_bool = PR_FALSE;
JavaToJSSavedState saved_state = {NULL,NULL};
if(jEnv == NULL)
{
return NS_ERROR_FAILURE;
}
jsj_env = jsj_enter_js(jEnv, NULL, &cx, NULL, &saved_state);
if (!jsj_env)
return NS_ERROR_FAILURE;
// =-= sudu: check to see if slot can be passed in as is.
// Should it be converted to a jsint?
if (!JS_GetElement(cx, js_obj, slot, &js_val))
goto done;
if (!jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv),
&dummy_cost, &member, &dummy_bool))
goto done;
done:
if (!jsj_exit_js(cx, jsj_env, &saved_state))
return NS_ERROR_FAILURE;
*pjobj = member;
return NS_OK;
}
/**
* set member of a Native JSObject for a given name.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
* @param name - Name of a member.
* @param jobj - Value to set. If this is a basic data type, it is converted
* using standard JNI calls but if it is a wrapper to a JSObject
* then a internal mapping is consulted to convert to a NJSObject.
*/
NS_METHOD
nsCLiveconnect::SetMember(JNIEnv *jEnv, jsobject obj, const char *name, jobject java_obj)
{
JSContext *cx = NULL;
JSJavaThreadState *jsj_env = NULL;
JSObject *js_obj = (JSObject *)obj;
nsresult err = NS_OK;
jobject member = NULL;
jsval js_val = NULL;
JavaToJSSavedState saved_state = {NULL,NULL};
if(jEnv == NULL)
{
return NS_ERROR_FAILURE;
}
jsj_env = jsj_enter_js(jEnv, NULL, &cx, NULL, &saved_state);
if (!jsj_env)
return NS_ERROR_FAILURE;
if (!name) {
JS_ReportError(cx, "illegal null member name");
goto done;
}
if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, &js_val))
goto done;
JS_SetProperty(cx, js_obj, name, &js_val);
done:
jsj_exit_js(cx, jsj_env, &saved_state);
return NS_OK;
}
/**
* set member of a Native JSObject for a given index.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
* @param index - Index of a member.
* @param jobj - Value to set. If this is a basic data type, it is converted
* using standard JNI calls but if it is a wrapper to a JSObject
* then a internal mapping is consulted to convert to a NJSObject.
*/
NS_METHOD
nsCLiveconnect::SetSlot(JNIEnv *jEnv, jsobject obj, int slot, jobject java_obj)
{
JSContext *cx = NULL;
JSJavaThreadState *jsj_env = NULL;
JSObject *js_obj = (JSObject *)obj;
nsresult err = NS_OK;
jsval js_val = NULL;
JavaToJSSavedState saved_state = {NULL,NULL};
if(jEnv == NULL)
{
return NS_ERROR_FAILURE;
}
jsj_env = jsj_enter_js(jEnv, NULL, &cx, NULL, &saved_state);
if (!jsj_env)
return NS_ERROR_FAILURE;
if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, java_obj, &js_val))
goto done;
JS_SetElement(cx, js_obj, slot, &js_val);
done:
jsj_exit_js(cx, jsj_env, &saved_state);
return NS_OK;
}
/**
* remove member of a Native JSObject for a given name.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
* @param name - Name of a member.
*/
NS_METHOD
nsCLiveconnect::RemoveMember(JNIEnv *jEnv, jsobject obj, const char *name)
{
JSContext *cx = NULL;
JSJavaThreadState *jsj_env = NULL;
JSObject *js_obj = (JSObject *)obj;
nsresult err = NS_OK;
jsval js_val = NULL;
JavaToJSSavedState saved_state = {NULL,NULL};
if(jEnv == NULL)
{
return NS_ERROR_FAILURE;
}
jsj_env = jsj_enter_js(jEnv, NULL, &cx, NULL, &saved_state);
if (!jsj_env)
return NS_ERROR_FAILURE;
if (!name) {
JS_ReportError(cx, "illegal null member name");
goto done;
}
JS_DeleteProperty2(cx, js_obj, name, &js_val);
done:
jsj_exit_js(cx, jsj_env, &saved_state);
return NS_OK;
}
/**
* call a method of Native JSObject.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
* @param name - Name of a method.
* @param jobjArr - Array of jobjects representing parameters of method being caled.
* @param pjobj - return value.
*/
NS_METHOD
nsCLiveconnect::Call(JNIEnv *jEnv, jsobject obj, const char *name, jobjectArray java_args, jobject *pjobj)
{
int i = 0;
int argc = 0;
int arg_num = 0;
jsval *argv = 0;
JSContext *cx = NULL;
JSJavaThreadState *jsj_env = NULL;
JSObject *js_obj = (JSObject *)obj;
nsresult err = NS_OK;
jobject member = NULL;
jsval js_val = NULL;
jsval function_val = NULL;
int dummy_cost = 0;
JSBool dummy_bool = PR_FALSE;
JavaToJSSavedState saved_state = {NULL,NULL};
jobject result = NULL;
if(jEnv == NULL)
{
return NS_ERROR_FAILURE;
}
jsj_env = jsj_enter_js(jEnv, NULL, &cx, NULL, &saved_state);
if (!jsj_env)
return NS_ERROR_FAILURE;
result = NULL;
if (!name) {
JS_ReportError(cx, "illegal null JavaScript function name");
goto done;
}
/* FIXME: What about security stuff ? Don't principals need to be set here ? */
/* Allocate space for JS arguments */
if (java_args) {
argc = jEnv->GetArrayLength(java_args);
argv = (jsval*)JS_malloc(cx, argc * sizeof(jsval));
} else {
argc = 0;
argv = 0;
}
/* Convert arguments from Java to JS values */
for (arg_num = 0; arg_num < argc; arg_num++) {
jobject arg = jEnv->GetObjectArrayElement(java_args, arg_num);
if (!jsj_ConvertJavaObjectToJSValue(cx, jEnv, arg, &argv[arg_num]))
goto cleanup_argv;
JS_AddRoot(cx, &argv[arg_num]);
}
if (!JS_GetProperty(cx, js_obj, name, &function_val))
goto cleanup_argv;
if (!JS_CallFunctionValue(cx, js_obj, function_val, argc, argv, &js_val))
goto cleanup_argv;
jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv),
&dummy_cost, &result, &dummy_bool);
cleanup_argv:
if (argv) {
for (i = 0; i < arg_num; i++)
JS_RemoveRoot(cx, &argv[i]);
JS_free(cx, argv);
}
done:
if (!jsj_exit_js(cx, jsj_env, &saved_state))
return NS_ERROR_FAILURE;
*pjobj = result;
return NS_OK;
}
/**
* Evaluate a script with a Native JS Object representing scope.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
* @param pNSIPrincipaArray - Array of principals to be used to compare privileges.
* @param numPrincipals - Number of principals being passed.
* @param script - Script to be executed.
* @param pjobj - return value.
*/
NS_METHOD
nsCLiveconnect::Eval(JNIEnv *jEnv, jsobject obj, const char *script, jobject *pjobj)
{
JSContext *cx = NULL;
JSJavaThreadState *jsj_env = NULL;
JSObject *js_obj = (JSObject *)obj;
nsresult err = NS_OK;
jobject member = NULL;
jsval js_val = NULL;
jsval function_val = NULL;
int dummy_cost = 0;
JSBool dummy_bool = PR_FALSE;
JavaToJSSavedState saved_state = {NULL,NULL};
jobject result = NULL;
const char *codebase = NULL;
JSPrincipals *principals = NULL;
JSBool eval_succeeded = PR_FALSE;
if(jEnv == NULL)
{
return NS_ERROR_FAILURE;
}
jsj_env = jsj_enter_js(jEnv, NULL, &cx, NULL, &saved_state);
if (!jsj_env)
return NULL;
result = NULL;
if (!script) {
JS_ReportError(cx, "illegal null string eval argument");
goto done;
}
/* Set up security stuff */
principals = NULL;
if (JSJ_callbacks->get_JSPrincipals_from_java_caller)
principals = JSJ_callbacks->get_JSPrincipals_from_java_caller(jEnv, cx);
codebase = principals ? principals->codebase : NULL;
/* Have the JS engine evaluate the unicode string */
eval_succeeded = JS_EvaluateScriptForPrincipals(cx, js_obj, principals,
script, strlen(script),
codebase, 0, &js_val);
if (!eval_succeeded)
goto done;
/* Convert result to a subclass of java.lang.Object */
jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv),
&dummy_cost, &result, &dummy_bool);
done:
if (!jsj_exit_js(cx, jsj_env, &saved_state))
return NULL;
*pjobj = result;
return NS_OK;
}
/**
* Get the window object for a plugin instance.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param pJavaObject - Either a jobject or a pointer to a plugin instance
* representing the java object.
* @param pjobj - return value. This is a native js object
* representing the window object of a frame
* in which a applet/bean resides.
*/
NS_METHOD
nsCLiveconnect::GetWindow(JNIEnv *jEnv, void *pJavaObject, jsobject *pobj)
{
char *err_msg = NULL;
JSContext *cx = NULL;
JSObject *js_obj = NULL;
jsval js_val = NULL;
int dummy_cost = 0;
JSBool dummy_bool = PR_FALSE;
JavaToJSSavedState saved_state = {NULL,NULL};
jobject java_obj = NULL;
JSJavaThreadState *jsj_env = NULL;
if(jEnv == NULL)
{
return NS_ERROR_FAILURE;
}
jsj_env = jsj_enter_js(jEnv, NULL, &cx, NULL, &saved_state);
if (!jsj_env)
return NULL;
err_msg = NULL;
java_obj = NULL;
js_obj = JSJ_callbacks->map_java_object_to_js_object(jEnv, pJavaObject, &err_msg);
if (!js_obj) {
if (err_msg) {
JS_ReportError(cx, err_msg);
free(err_msg);
}
goto done;
}
#if 0
js_val = OBJECT_TO_JSVAL(js_obj);
jsj_ConvertJSValueToJavaObject(cx, jEnv, js_val, jsj_get_jlObject_descriptor(cx, jEnv),
&dummy_cost, &java_obj, &dummy_bool);
#endif
done:
if (!jsj_exit_js(cx, jsj_env, &saved_state))
return NULL;
//*pjobj = java_obj;
*pobj = (jint)js_obj;
return NS_OK;
}
/**
* Get the window object for a plugin instance.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
*/
NS_METHOD
nsCLiveconnect::FinalizeJSObject(JNIEnv *jEnv, jsobject obj)
{
JSContext *cx = NULL;
JavaToJSSavedState saved_state = {NULL,NULL};
JSJavaThreadState *jsj_env = NULL;
JSObject *js_obj = (JSObject *)obj;
if(jEnv == NULL)
{
return NS_ERROR_FAILURE;
}
jsj_env = jsj_enter_js(jEnv, NULL, &cx, NULL, &saved_state);
if (!jsj_env) /* Note: memory leak if we exit here */
return NS_ERROR_FAILURE;
#ifdef PRESERVE_JSOBJECT_IDENTITY
jsj_remove_js_obj_reflection_from_hashtable(cx, js_obj);
#endif /* PRESERVE_JSOBJECT_IDENTITY */
jsj_exit_js(cx, jsj_env, &saved_state);
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////
// from nsCLiveconnect:
nsCLiveconnect::nsCLiveconnect(nsISupports *aOuter)
{
NS_INIT_AGGREGATED(aOuter);
jsj_init_js_obj_reflections_table();
}
nsCLiveconnect::~nsCLiveconnect()
{
}

View File

@@ -0,0 +1,166 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains class definition implementing the public interface.
*
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the class definition to implement nsILiveconnect XP-COM interface.
*
*/
#ifndef nsCLiveconnect_h___
#define nsCLiveconnect_h___
#include "nsILiveconnect.h"
#include "nsAgg.h"
/**
* nsCLiveconnect implements nsILiveconnect interface for navigator.
* This is used by a JVM to implement netscape.javascript.JSObject functionality.
*/
class nsCLiveconnect :public nsILiveconnect {
public:
////////////////////////////////////////////////////////////////////////////
// from nsISupports and AggregatedQueryInterface:
NS_DECL_AGGREGATED
////////////////////////////////////////////////////////////////////////////
// from nsILiveconnect:
/**
* get member of a Native JSObject for a given name.
*
* @param obj - A Native JS Object.
* @param name - Name of a member.
* @param pjobj - return parameter as a java object representing
* the member. If it is a basic data type it is converted to
* a corresponding java type. If it is a NJSObject, then it is
* wrapped up as java wrapper netscape.javascript.JSObject.
*/
NS_IMETHOD
GetMember(JNIEnv *jEnv, jsobject obj, const char *name, jobject *pjobj);
/**
* get member of a Native JSObject for a given index.
*
* @param obj - A Native JS Object.
* @param index - Index of a member.
* @param pjobj - return parameter as a java object representing
* the member.
*/
NS_IMETHOD
GetSlot(JNIEnv *jEnv, jsobject obj, int index, jobject *pjobj);
/**
* set member of a Native JSObject for a given name.
*
* @param obj - A Native JS Object.
* @param name - Name of a member.
* @param jobj - Value to set. If this is a basic data type, it is converted
* using standard JNI calls but if it is a wrapper to a JSObject
* then a internal mapping is consulted to convert to a NJSObject.
*/
NS_IMETHOD
SetMember(JNIEnv *jEnv, jsobject obj, const char *name, jobject jobj);
/**
* set member of a Native JSObject for a given index.
*
* @param obj - A Native JS Object.
* @param index - Index of a member.
* @param jobj - Value to set. If this is a basic data type, it is converted
* using standard JNI calls but if it is a wrapper to a JSObject
* then a internal mapping is consulted to convert to a NJSObject.
*/
NS_IMETHOD
SetSlot(JNIEnv *jEnv, jsobject obj, int slot, jobject jobj);
/**
* remove member of a Native JSObject for a given name.
*
* @param obj - A Native JS Object.
* @param name - Name of a member.
*/
NS_IMETHOD
RemoveMember(JNIEnv *jEnv, jsobject obj, const char *name);
/**
* call a method of Native JSObject.
*
* @param obj - A Native JS Object.
* @param name - Name of a method.
* @param jobjArr - Array of jobjects representing parameters of method being caled.
* @param pjobj - return value.
*/
NS_IMETHOD
Call(JNIEnv *jEnv, jsobject obj, const char *name, jobjectArray jobjArr, jobject *pjobj);
/**
* Evaluate a script with a Native JS Object representing scope.
*
* @param obj - A Native JS Object.
* @param pNSIPrincipaArray - Array of principals to be used to compare privileges.
* @param numPrincipals - Number of principals being passed.
* @param script - Script to be executed.
* @param pjobj - return value.
*/
NS_IMETHOD
//Eval(JNIEnv *jEnv, jsobject obj, nsIPrincipal **pNSIPrincipaArray, PRInt32 numPrincipals, const char *script, jobject *pjobj);
Eval(JNIEnv *jEnv, jsobject obj, const char *script, jobject *pjobj);
/**
* Get the window object for a plugin instance.
*
* @param pJavaObject - Either a jobject or a pointer to a plugin instance
* representing the java object.
* @param pjobj - return value. This is a native js object
* representing the window object of a frame
* in which a applet/bean resides.
*/
NS_IMETHOD
GetWindow(JNIEnv *jEnv, void *pJavaObject, jsobject *pobj);
/**
* Get the window object for a plugin instance.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
*/
NS_IMETHOD
FinalizeJSObject(JNIEnv *jEnv, jsobject obj);
////////////////////////////////////////////////////////////////////////////
// from nsCLiveconnect:
nsCLiveconnect(nsISupports *aOuter);
virtual ~nsCLiveconnect(void);
protected:
};
#endif // nsCLiveconnect_h___

View File

@@ -0,0 +1,168 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the implementation providing nsILiveconnect XP-COM interface.
*
*/
#include "prtypes.h"
#include "nspr.h"
#include "prmem.h"
#include "prmon.h"
#include "prlog.h"
#include "nsCLiveconnect.h"
#include "nsCLiveconnectFactory.h"
#include "nsRepository.h"
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
static NS_DEFINE_IID(kIFactoryIID, NS_IFACTORY_IID);
static NS_DEFINE_CID(kCLiveconnectCID, NS_CLIVECONNECT_CID);
static NS_DEFINE_IID(kILiveconnectIID, NS_ILIVECONNECT_IID);
nsIFactory *nsCLiveconnectFactory::m_pNSIFactory = NULL;
nsCLiveconnect *nsCLiveconnectFactory::m_pNSCLiveconnect = NULL;
/*+++++++++++++++++++++++++++++++++++++++++++++++++
* NSGetFactory:
* Provides entry point to liveconnect dll.
+++++++++++++++++++++++++++++++++++++++++++++++++*/
extern "C" NS_EXPORT nsresult
NSGetFactory(const nsCID &aClass, nsIFactory **aFactory)
{
if (!aClass.Equals(kCLiveconnectCID)) {
return NS_ERROR_FACTORY_NOT_LOADED; // XXX right error?
}
nsCLiveconnectFactory* pCLiveConnectFactory = new nsCLiveconnectFactory();
if (pCLiveConnectFactory == NULL)
return NS_ERROR_OUT_OF_MEMORY;
pCLiveConnectFactory->AddRef();
*aFactory = pCLiveConnectFactory;
return NS_OK;
}
extern "C" NS_EXPORT PRBool
NSCanUnload(void)
{
return PR_FALSE;
}
////////////////////////////////////////////////////////////////////////////
// from nsISupports
NS_METHOD
nsCLiveconnectFactory::QueryInterface(const nsIID& aIID, void** aInstancePtr)
{
PR_ASSERT(NULL != aInstancePtr);
if (NULL == aInstancePtr) {
return NS_ERROR_NULL_POINTER;
}
if (aIID.Equals(kIFactoryIID) ||
aIID.Equals(kISupportsIID)) {
*aInstancePtr = (void*) this;
AddRef();
return NS_OK;
}
return NS_NOINTERFACE;
}
NS_IMPL_ADDREF(nsCLiveconnectFactory)
NS_IMPL_RELEASE(nsCLiveconnectFactory)
////////////////////////////////////////////////////////////////////////////
// from nsIFactory:
NS_METHOD
nsCLiveconnectFactory::CreateInstance(nsISupports *aOuter, REFNSIID aIID, void **aResult)
{
nsCLiveconnect *pNSCLiveconnect = NULL;
*aResult = NULL;
if (aOuter && !aIID.Equals(kISupportsIID))
return NS_NOINTERFACE; // XXX right error?
if (m_pNSCLiveconnect == NULL)
{
m_pNSCLiveconnect = new nsCLiveconnect(aOuter);
}
if (m_pNSCLiveconnect == NULL)
{
return NS_ERROR_FAILURE;
}
if (m_pNSCLiveconnect->QueryInterface(aIID,
(void**)aResult) != NS_OK) {
// then we're trying get a interface other than nsISupports and
// nsICapsManager
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_METHOD
nsCLiveconnectFactory::LockFactory(PRBool aLock)
{
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////
// from nsCLiveconnectFactory:
nsCLiveconnectFactory::nsCLiveconnectFactory(void)
{
if( m_pNSIFactory != NULL)
{
return;
}
NS_INIT_REFCNT();
nsresult err = NS_OK;
NS_DEFINE_IID(kIFactoryIID, NS_IFACTORY_IID);
err = this->QueryInterface(kIFactoryIID, (void**)&m_pNSIFactory);
if ( (err == NS_OK) && (m_pNSIFactory != NULL) )
{
NS_DEFINE_CID(kCLiveconnectCID, NS_CLIVECONNECT_CID);
nsRepository::RegisterFactory(kCLiveconnectCID, m_pNSIFactory,
PR_FALSE);
}
}
nsCLiveconnectFactory::~nsCLiveconnectFactory()
{
if(mRefCnt == 0)
{
NS_DEFINE_CID(kCLiveconnectCID, NS_CLIVECONNECT_CID);
nsRepository::UnregisterFactory(kCLiveconnectCID, (nsIFactory *)m_pNSIFactory);
}
}

View File

@@ -0,0 +1,59 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the class definition to implement nsIFactory XP-COM interface.
*
*/
#ifndef nsCLiveconnectFactory_h___
#define nsCLiveconnectFactory_h___
#include "nsISupports.h"
#include "nsIFactory.h"
class nsCLiveconnectFactory : public nsIFactory {
public:
////////////////////////////////////////////////////////////////////////////
// from nsISupports and AggregatedQueryInterface:
NS_DECL_ISUPPORTS
////////////////////////////////////////////////////////////////////////////
// from nsIFactory:
NS_IMETHOD
CreateInstance(nsISupports *aOuter, REFNSIID aIID, void **aResult);
NS_IMETHOD
LockFactory(PRBool aLock);
////////////////////////////////////////////////////////////////////////////
// from nsCLiveconnectFactory:
nsCLiveconnectFactory(void);
virtual ~nsCLiveconnectFactory(void);
protected:
static nsIFactory *m_pNSIFactory;
static nsCLiveconnect *m_pNSCLiveconnect;
};
#endif // nsCLiveconnectFactory_h___

View File

@@ -0,0 +1,157 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
/*
* This file is part of the Java-vendor-neutral implementation of LiveConnect
*
* It contains the public XP-COM based interface for java to javascript communication.
*
*/
#ifndef nsILiveconnect_h___
#define nsILiveconnect_h___
#include "nsISupports.h"
#include "nsIFactory.h"
#include "jni.h"
typedef jint jsobject;
class nsILiveconnect : public nsISupports {
public:
/**
* get member of a Native JSObject for a given name.
*
* @param obj - A Native JS Object.
* @param name - Name of a member.
* @param pjobj - return parameter as a java object representing
* the member. If it is a basic data type it is converted to
* a corresponding java type. If it is a NJSObject, then it is
* wrapped up as java wrapper netscape.javascript.JSObject.
*/
NS_IMETHOD
GetMember(JNIEnv *jEnv, jsobject obj, const char *name, jobject *pjobj) = 0;
/**
* get member of a Native JSObject for a given index.
*
* @param obj - A Native JS Object.
* @param index - Index of a member.
* @param pjobj - return parameter as a java object representing
* the member.
*/
NS_IMETHOD
GetSlot(JNIEnv *jEnv, jsobject obj, int index, jobject *pjobj) = 0;
/**
* set member of a Native JSObject for a given name.
*
* @param obj - A Native JS Object.
* @param name - Name of a member.
* @param jobj - Value to set. If this is a basic data type, it is converted
* using standard JNI calls but if it is a wrapper to a JSObject
* then a internal mapping is consulted to convert to a NJSObject.
*/
NS_IMETHOD
SetMember(JNIEnv *jEnv, jsobject obj, const char *name, jobject jobj) = 0;
/**
* set member of a Native JSObject for a given index.
*
* @param obj - A Native JS Object.
* @param index - Index of a member.
* @param jobj - Value to set. If this is a basic data type, it is converted
* using standard JNI calls but if it is a wrapper to a JSObject
* then a internal mapping is consulted to convert to a NJSObject.
*/
NS_IMETHOD
SetSlot(JNIEnv *jEnv, jsobject obj, int slot, jobject jobj) = 0;
/**
* remove member of a Native JSObject for a given name.
*
* @param obj - A Native JS Object.
* @param name - Name of a member.
*/
NS_IMETHOD
RemoveMember(JNIEnv *jEnv, jsobject obj, const char *name) = 0;
/**
* call a method of Native JSObject.
*
* @param obj - A Native JS Object.
* @param name - Name of a method.
* @param jobjArr - Array of jobjects representing parameters of method being caled.
* @param pjobj - return value.
*/
NS_IMETHOD
Call(JNIEnv *jEnv, jsobject obj, const char *name, jobjectArray jobjArr, jobject *pjobj) = 0;
/**
* Evaluate a script with a Native JS Object representing scope.
*
* @param obj - A Native JS Object.
* @param pNSIPrincipaArray - Array of principals to be used to compare privileges.
* @param numPrincipals - Number of principals being passed.
* @param script - Script to be executed.
* @param pjobj - return value.
*/
NS_IMETHOD
//Eval(jsobject obj, nsIPrincipal **pNSIPrincipaArray, PRInt32 numPrincipals, const char *script, jobject *pjobj) = 0;
Eval(JNIEnv *jEnv, jsobject obj, const char *script, jobject *pjobj) = 0;
/**
* Get the window object for a plugin instance.
*
* @param pJavaObject - Either a jobject or a pointer to a plugin instance
* representing the java object.
* @param pjobj - return value. This is a native js object
* representing the window object of a frame
* in which a applet/bean resides.
*/
NS_IMETHOD
GetWindow(JNIEnv *jEnv, void *pJavaObject, jsobject *pobj) = 0;
/**
* Get the window object for a plugin instance.
*
* @param jEnv - JNIEnv on which the call is being made.
* @param obj - A Native JS Object.
*/
NS_IMETHOD
FinalizeJSObject(JNIEnv *jEnv, jsobject obj) = 0;
};
#define NS_ILIVECONNECT_IID \
{ /* 68190910-3318-11d2-97f0-00805f8a28d0 */ \
0x68190910, \
0x3318, \
0x11d2, \
{0x97, 0xf0, 0x00, 0x80, 0x5f, 0x8a, 0x28, 0xd0} \
};
#define NS_CLIVECONNECT_CID \
{ /* b8f0cef0-3931-11d2-97f0-00805f8a28d0 */ \
0xb8f0cef0, \
0x3931, \
0x11d2, \
{0x97, 0xf0, 0x00, 0x80, 0x5f, 0x8a, 0x28, 0xd0} \
};
#endif // nsILiveconnect_h___

File diff suppressed because it is too large Load Diff

View File

@@ -1,38 +0,0 @@
#
# 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):
#
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
DIRS = \
public \
memcache \
filecache \
mgr \
build \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@@ -1,33 +0,0 @@
#!gmake
#
# 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):
DEPTH=..\..
DIRS= \
public \
mgr \
memcache \
filecache \
build \
$(NULL)
include <$(DEPTH)\config\rules.mak>

View File

@@ -1,54 +0,0 @@
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
#
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = nkcacke
LIBRARY_NAME = necko_cache
IS_COMPONENT = 1
CPPSRCS = nsNetDataCacheModule.cpp
SHARED_LIBRARY_LIBS = \
$(DIST)/lib/libnkcachemgr_s.a \
$(DIST)/lib/libnkfilecache_s.a \
$(DIST)/lib/libnkmemcache_s.a \
$(DIST)/lib/libmozdbm_s.a \
$(DIST)/lib/libxpcomio_s.a \
$(NULL)
LOCAL_INCLUDES = \
-I$(DEPTH)/netwerk/cache/memcache \
-I$(DEPTH)/netwerk/cache/filecache \
-I$(DEPTH)/netwerk/cache/mgr \
$(NULL)
EXTRA_DSO_LDOPTS = \
$(MKSHLIB_FORCE_ALL) \
$(SHARED_LIBRARY_LIBS) \
$(MKSHLIB_UNFORCE_ALL) \
$(NULL)
include $(topsrcdir)/config/rules.mk
$(LIBRARY) $(SHARED_LIBRARY): $(SHARED_LIBRARY_LIBS) Makefile

View File

@@ -1,51 +0,0 @@
#!gmake
#
# 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.
#
DEPTH=..\..\..
MODULE=nkcache
MAKE_OBJ_TYPE=DLL
DLLNAME=nkcache
DLL=.\$(OBJDIR)\$(DLLNAME).dll
CPP_OBJS= \
.\$(OBJDIR)\nsNetDataCacheModule.obj \
$(NULL)
LLIBS= \
$(DIST)\lib\nkcachemgr_s.lib \
$(DIST)\lib\nkfilecache_s.lib \
$(DIST)\lib\nkmemcache_s.lib \
$(DIST)\lib\dbm32.lib \
$(DIST)\lib\xpcom.lib \
$(LIBNSPR)
INCS = $(INCS) \
-I$(DEPTH)\netwerk\cache\memcache \
-I$(DEPTH)\netwerk\cache\filecache \
-I$(DEPTH)\netwerk\cache\mgr \
$(NULL)
include <$(DEPTH)\config\rules.mak>
install:: $(DLL)
$(MAKE_INSTALL) .\$(OBJDIR)\$(DLLNAME).dll $(DIST)\bin\components
$(MAKE_INSTALL) .\$(OBJDIR)\$(DLLNAME).lib $(DIST)\lib

View File

@@ -1,49 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
#include "nsCOMPtr.h"
#include "nsIModule.h"
#include "nscore.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsIGenericFactory.h"
#include "nsINetDataCache.h"
#include "nsINetDataCacheManager.h"
#include "nsMemCacheCID.h"
#include "nsMemCache.h"
#include "nsNetDiskCache.h"
#include "nsNetDiskCacheCID.h"
#include "nsCacheManager.h"
// Factory method to create a new nsMemCache instance. Used
// by nsNetDataCacheModule
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsMemCache, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsNetDiskCache, Init)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsCacheManager, Init)
static nsModuleComponentInfo components[] = {
{ "Memory Cache", NS_MEM_CACHE_FACTORY_CID, NS_NETWORK_MEMORY_CACHE_PROGID, nsMemCacheConstructor },
{ "File Cache", NS_NETDISKCACHE_CID, NS_NETWORK_FILE_CACHE_PROGID, nsNetDiskCacheConstructor },
{ "Cache Manager",NS_CACHE_MANAGER_CID, NS_NETWORK_CACHE_MANAGER_PROGID,nsCacheManagerConstructor }
};
NS_IMPL_NSGETMODULE("Network Data Cache", components)

View File

@@ -1,60 +0,0 @@
#
# 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.
#
# The Initial Developer of the Original Code is Intel Corp.
# Portions created by Intel Corp. are
# Copyright (C) 1999, 1999 Intel Corp. All
# Rights Reserved.
#
# Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
# Carl Wong <carl.wong@intel.com>
#
DEPTH = ../../..
topsrcdir = @top_srcdir@
VPATH = @srcdir@
srcdir = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = nkcache
LIBRARY_NAME = nkfilecache_s
REQUIRES = nspr dbm
EXTRA_DSO_LDOPTS += -L$(DIST)/lib -lmozdbm_s
EXPORTS=nsNetDiskCacheCID.h \
nsNetDiskCache.h \
nsIDBAccessor.h \
nsDBAccessor.h \
$(NULL)
CPPSRCS = \
nsDBAccessor.cpp\
nsDBEnumerator.cpp \
nsNetDiskCache.cpp \
nsDiskCacheRecord.cpp \
nsDiskCacheRecordChannel.cpp \
$(NULL)
EXTRA_LIBS = $(NSPR_LIBS)
# we don't want the shared lib, but we want to force the creation of a
# static lib.
override NO_SHARED_LIB=1
override NO_STATIC_LIB=
include $(topsrcdir)/config/rules.mk

View File

@@ -1,44 +0,0 @@
#!gmake
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
DEPTH=..\..\..
include <$(DEPTH)/config/config.mak>
MODULE = nkcache
LIBRARY_NAME = nkfilecache_s
CPP_OBJS= \
.\$(OBJDIR)\nsDBAccessor.obj \
.\$(OBJDIR)\nsDBEnumerator.obj \
.\$(OBJDIR)\nsNetDiskCache.obj \
.\$(OBJDIR)\nsDiskCacheRecord.obj \
.\$(OBJDIR)\nsDiskCacheRecordChannel.obj \
$(NULL)
EXPORTS=nsNetDiskCacheCID.h
include <$(DEPTH)\config\rules.mak>
install:: $(LIBRARY)
$(MAKE_INSTALL) $(LIBRARY) $(DIST)\lib
clobber::
rm -rf $(OBJDIR)
rm -f $(DIST)\lib\$(LIBRARY_NAME).lib

View File

@@ -1,416 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
/*
* This file is part of filecache implementation.
*
* nsIDBAccessor is a interface that shields all the direct database access
* method from nsNetDiskCache.
*
* nsDBAccessor is a implementation of the nsIDBAccessor interface. It
* uses dbm(Berkely) as the database.
*
* a nsDiskCacheRecord is mapped into two entries in the database,
* key->recordID
* recordID->metadata
*/
#include "nsDBAccessor.h"
#include "nscore.h"
#include "prtypes.h"
#include "plhash.h"
#include "nsCRT.h"
nsDBAccessor::nsDBAccessor() :
mDB(0) ,
mDBFile(0) ,
mSessionID(0) ,
mSessionCntr(0) ,
mDBFilesize(0)
{
mLastSyncTime = PR_IntervalNow() ;
NS_INIT_REFCNT();
}
nsDBAccessor::~nsDBAccessor()
{
Shutdown() ;
}
//
// Implement nsISupports methods
//
NS_IMPL_ISUPPORTS(nsDBAccessor, NS_GET_IID(nsIDBAccessor))
///////////////////////////////////////////////////////////
// nsIDBAccessor methods
NS_IMETHODIMP
nsDBAccessor::Init(nsIFileSpec* dbfile)
{
char* dbname ;
// this should cover all platforms.
dbfile->GetNativePath(&dbname) ;
mDBFile = dbfile ;
// FUR - how is page size chosen ? It's worth putting a comment
// in here about the possible usefulness of tuning these parameters
HASHINFO hash_info = {
16*1024 , /* bucket size */
0 , /* fill factor */
0 , /* number of elements */
0 , /* bytes to cache */
0 , /* hash function */
0} ; /* byte order */
mDB = dbopen(dbname,
O_RDWR | O_CREAT ,
0600 ,
DB_HASH ,
& hash_info) ;
nsCRT::free(dbname) ;
if(!mDB)
return NS_ERROR_FAILURE ;
// set mSessionID
DBT db_key, db_data ;
db_key.data = NS_CONST_CAST(char*, SessionKey) ;
db_key.size = PL_strlen(SessionKey) ;
int status = (*mDB->get)(mDB, &db_key, &db_data, 0) ;
if(status == -1) {
NS_ERROR("ERROR: failed get session id in database.") ;
return NS_ERROR_FAILURE ;
}
if(status == 0) {
// get the last session id
PRInt16 *old_ID = NS_STATIC_CAST(PRInt16*, db_data.data) ;
if(*old_ID < ini_sessionID) {
NS_ERROR("ERROR: Bad Session ID in database, corrupted db.") ;
return NS_ERROR_FAILURE ;
}
mSessionID = *old_ID + 1 ;
}
else if(status == 1) {
// must be a new db
mSessionID = ini_sessionID ;
}
db_data.data = NS_REINTERPRET_CAST(void*, &mSessionID) ;
db_data.size = sizeof(PRInt16) ;
// store the new session id
status = (*mDB->put)(mDB, &db_key, &db_data, 0) ;
if(status == 0) {
(*mDB->sync)(mDB, 0) ;
// initialize database filesize
return mDBFile->GetFileSize(&mDBFilesize) ;
}
else {
NS_ERROR("reset session ID failure.") ;
return NS_ERROR_FAILURE ;
}
}
NS_IMETHODIMP
nsDBAccessor::Shutdown(void)
{
if(mDB) {
(*mDB->sync)(mDB, 0) ;
(*mDB->close)(mDB) ;
mDB = nsnull ;
}
return NS_OK ;
}
NS_IMETHODIMP
nsDBAccessor::Get(PRInt32 aID, void** anEntry, PRUint32 *aLength)
{
if(!anEntry)
return NS_ERROR_NULL_POINTER ;
*anEntry = nsnull ;
*aLength = 0 ;
NS_ASSERTION(mDB, "no database") ;
DBT db_key, db_data ;
db_key.data = NS_REINTERPRET_CAST(void*, &aID) ;
db_key.size = sizeof(PRInt32) ;
int status = 0 ;
status = (*mDB->get)(mDB, &db_key, &db_data, 0) ;
if(status == 0) {
*anEntry = db_data.data ;
*aLength = db_data.size ;
return NS_OK ;
}
else if(status == 1)
return NS_OK ;
else
return NS_ERROR_FAILURE ;
}
NS_IMETHODIMP
nsDBAccessor::Put(PRInt32 aID, void* anEntry, PRUint32 aLength)
{
NS_ASSERTION(mDB, "no database") ;
DBT db_key, db_data ;
db_key.data = NS_REINTERPRET_CAST(void*, &aID) ;
db_key.size = sizeof(PRInt32) ;
db_data.data = anEntry ;
db_data.size = aLength ;
if(0 == (*mDB->put)(mDB, &db_key, &db_data, 0)) {
return Sync() ;
}
else {
return NS_ERROR_FAILURE ;
}
}
/*
* It's more important to remove the id->metadata entry first since
* key->id mapping is just a reference
*/
NS_IMETHODIMP
nsDBAccessor::Del(PRInt32 aID, void* anEntry, PRUint32 aLength)
{
NS_ASSERTION(mDB, "no database") ;
DBT db_key ;
// delete recordID->metadata
db_key.data = NS_REINTERPRET_CAST(void*, &aID) ;
db_key.size = sizeof(PRInt32) ;
PRInt32 status = -1 ;
status = (*mDB->del)(mDB, &db_key, 0) ;
if(-1 == status) {
return NS_ERROR_FAILURE ;
}
// delete key->recordID
db_key.data = anEntry ;
db_key.size = aLength ;
status = (*mDB->del)(mDB, &db_key, 0) ;
if(-1 == status) {
return NS_ERROR_FAILURE ;
}
return Sync() ;
}
NS_IMETHODIMP
nsDBAccessor::GetID(const char* key, PRUint32 length, PRInt32* aID)
{
NS_ASSERTION(mDB, "no database") ;
DBT db_key, db_data ;
db_key.data = NS_CONST_CAST(char*, key) ;
db_key.size = length ;
int status = (*mDB->get)(mDB, &db_key, &db_data, 0) ;
if(status == 0) {
// found recordID
*aID = *(NS_REINTERPRET_CAST(PRInt32*, db_data.data)) ;
return NS_OK ;
}
else if(status == 1) {
// create a new one
PRInt32 id = 0 ;
id = mSessionID << 16 | mSessionCntr++ ;
// add new id into mDB
db_data.data = NS_REINTERPRET_CAST(void*, &id) ;
db_data.size = sizeof(PRInt32) ;
status = (*mDB->put)(mDB, &db_key, &db_data, 0) ;
if(status != 0) {
NS_ERROR("updating db failure.") ;
return NS_ERROR_FAILURE ;
}
*aID = id ;
return Sync() ;
}
else {
NS_ERROR("ERROR: keydb failure.") ;
return NS_ERROR_FAILURE ;
}
}
NS_IMETHODIMP
nsDBAccessor::EnumEntry(void** anEntry, PRUint32* aLength, PRBool bReset)
{
if(!anEntry)
return NS_ERROR_NULL_POINTER ;
*anEntry = nsnull ;
*aLength = 0 ;
NS_ASSERTION(mDB, "no database") ;
PRUint32 flag ;
if(bReset)
flag = R_FIRST ;
else
flag = R_NEXT ;
DBT db_key, db_data ;
PRUint32 len = PL_strlen(SessionKey) ;
int status ;
do {
status = (*mDB->seq)(mDB, &db_key, &db_data, flag) ;
flag = R_NEXT ;
if(status == -1)
return NS_ERROR_FAILURE ;
// get next if it's a key->recordID
if(db_key.size > sizeof(PRInt32) && db_data.size == sizeof(PRInt32))
continue ;
// get next if it's a sessionID entry
if(db_key.size == len && db_data.size == sizeof(PRInt16))
continue ;
// recordID is always 32 bits long
if(db_key.size == sizeof(PRInt32))
break ;
} while(!status) ;
if (0 == status) {
*anEntry = db_data.data ;
*aLength = db_data.size ;
}
return NS_OK ;
}
/*
* returns the cached database file size.
* mDBFilesize will be updated during Sync().
*/
NS_IMETHODIMP
nsDBAccessor::GetDBFilesize(PRUint32* aSize)
{
*aSize = mDBFilesize ;
return NS_OK ;
}
NS_IMETHODIMP
nsDBAccessor::GetSpecialEntry(void** anEntry, PRUint32* aLength)
{
if(!anEntry)
return NS_ERROR_NULL_POINTER ;
*anEntry = nsnull ;
*aLength = 0 ;
DBT db_key, db_data ;
db_key.data = NS_CONST_CAST(char*, SpecialEntry) ;
db_key.size = PL_strlen(SpecialEntry) ;
int status = (*mDB->get)(mDB, &db_key, &db_data, 0) ;
if(status == -1) {
NS_ERROR("ERROR: failed get special entry in database.") ;
return NS_ERROR_FAILURE ;
}
if(status == 0) {
*anEntry = db_data.data ;
*aLength = db_data.size ;
}
return NS_OK ;
}
NS_IMETHODIMP
nsDBAccessor::SetSpecialEntry(void* anEntry, PRUint32 aLength)
{
DBT db_key, db_data ;
db_key.data = NS_CONST_CAST(char*, SpecialEntry) ;
db_key.size = PL_strlen(SpecialEntry) ;
db_data.data = anEntry ;
db_data.size = aLength ;
if(0 == (*mDB->put)(mDB, &db_key, &db_data, 0)) {
(*mDB->sync)(mDB, 0) ;
return NS_OK ;
}
else {
return NS_ERROR_FAILURE ;
}
}
/*
* sync routine is only called when the SyncInterval is reached. Otherwise
* it just returns. If db synced, the filesize will be updated at the
* same time.
*/
nsresult
nsDBAccessor::Sync(void)
{
PRIntervalTime time = PR_IntervalNow() ;
PRIntervalTime duration = time - mLastSyncTime ;
if (PR_IntervalToMilliseconds(duration) > SyncInterval) {
int status = (*mDB->sync)(mDB, 0) ;
if(status == 0) {
// printf("\tsynced\n") ;
mLastSyncTime = time ;
// update db filesize here
return mDBFile->GetFileSize(&mDBFilesize) ;
} else
return NS_ERROR_FAILURE ;
} else {
// printf("\tnot synced\n") ;
return NS_OK ;
}
}

View File

@@ -1,93 +0,0 @@
/*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
/*
* This file is part of filecache implementation.
*
* nsIDBAccessor is a interface that shields all the direct database access
* method from nsNetDiskCache.
*
* nsDBAccessor is a implementation of the nsIDBAccessor interface. It
* uses dbm(Berkely) as the database.
*
*/
#ifndef _NSIDBACCESSOR_H_
#define _NSIDBACCESSOR_H_
#include "nsIDBAccessor.h"
#include "mcom_db.h"
#include "prinrval.h"
#include "nsCOMPtr.h"
// bogus string for the key of session id
static const char * const SessionKey = "SK" ;
// bogus string for the size
static const char * const SpecialEntry = "SE" ;
// initial session id number
static const PRInt16 ini_sessionID = 0xff ;
static const PRUint16 SyncInterval = 1000 ;
class nsDBAccessor : public nsIDBAccessor
{
public:
NS_DECL_ISUPPORTS
nsDBAccessor() ;
virtual ~nsDBAccessor() ;
NS_IMETHOD Init(nsIFileSpec* dbfile) ;
NS_IMETHOD Shutdown(void) ;
NS_IMETHOD Put(PRInt32 aID, void* anEntry, PRUint32 aLength) ;
NS_IMETHOD Get(PRInt32 aID, void** anEntry, PRUint32 *aLength) ;
NS_IMETHOD Del(PRInt32 aID, void* anEntry, PRUint32 aLength) ;
NS_IMETHOD GetID(const char* key, PRUint32 length, PRInt32* aID) ;
NS_IMETHOD EnumEntry(void* *anEntry, PRUint32* aLength, PRBool bReset) ;
NS_IMETHOD GetDBFilesize(PRUint32* aSize) ;
NS_IMETHOD GetSpecialEntry(void** anEntry, PRUint32 *aLength) ;
NS_IMETHOD SetSpecialEntry(void* anEntry, PRUint32 aLength) ;
protected:
nsresult Sync(void) ;
private:
DB * mDB ;
nsCOMPtr<nsIFileSpec> mDBFile ;
PRInt16 mSessionID ;
PRInt16 mSessionCntr ;
PRIntervalTime mLastSyncTime ;
PRUint32 mDBFilesize ; // cached DB filesize,
// updated on every sync for now
} ;
#endif // _NSIDBACCESSOR_H_

View File

@@ -1,108 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
/*
* This file is part of filecache implementation.
*
* It implements a simple iterator for the database, see nsDBAccessor.
*/
#include "nsDBEnumerator.h"
#include "nsDiskCacheRecord.h"
nsDBEnumerator::nsDBEnumerator(nsIDBAccessor* aDB, nsNetDiskCache* aCache) :
m_DB(aDB) ,
m_DiskCache(aCache) ,
m_tempEntry(0) ,
m_tempEntry_length(0) ,
m_CacheEntry(0) ,
m_bReset(PR_TRUE)
{
NS_INIT_REFCNT();
}
nsDBEnumerator::~nsDBEnumerator()
{
NS_IF_RELEASE(m_CacheEntry) ;
}
//
// Implement nsISupports methods
//
NS_IMPL_ISUPPORTS(nsDBEnumerator, NS_GET_IID(nsIEnumerator))
/////////////////////////////////////////////////////////////////
// nsISimpleEnumerator methods
NS_IMETHODIMP
nsDBEnumerator::HasMoreElements(PRBool *_retval)
{
*_retval = PR_FALSE ;
nsresult rv = m_DB->EnumEntry(&m_tempEntry, &m_tempEntry_length, m_bReset) ;
if(NS_FAILED(rv)) {
// do some error recovery
m_DiskCache->DBRecovery() ;
return rv ;
}
m_bReset = PR_FALSE ;
if(m_tempEntry && m_tempEntry_length != 0)
*_retval = PR_TRUE ;
return NS_OK ;
}
// this routine does not create a new item by itself
// Rather it reuses the item inside the object. So if you need to use the
// item later, you have to
// create a new item specifically, using copy constructor or some other dup
// function. And don't forget to release it after you're done
//
NS_IMETHODIMP
nsDBEnumerator::GetNext(nsISupports **_retval)
{
if(!m_CacheEntry) {
m_CacheEntry = new nsDiskCacheRecord(m_DB, m_DiskCache) ;
if(m_CacheEntry)
NS_ADDREF(m_CacheEntry) ;
else
return NS_ERROR_OUT_OF_MEMORY ;
}
if(!_retval)
return NS_ERROR_NULL_POINTER ;
*_retval = nsnull ;
nsresult rv = m_CacheEntry->RetrieveInfo(m_tempEntry, m_tempEntry_length) ;
if(NS_FAILED(rv))
return rv ;
*_retval = NS_STATIC_CAST(nsISupports*, m_CacheEntry) ;
NS_ADDREF(*_retval) ; // all good getter addref
return NS_OK ;
}

View File

@@ -1,60 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
/*
* This file is part of filecache implementation.
*
* It implements a simple iterator for the database, see nsDBAccessor.
*/
#ifndef _NS_DBENUMERATOR_H_
#define _NS_DBENUMERATOR_H_
#include "nsISimpleEnumerator.h"
#include "nsINetDataCacheRecord.h"
#include "nsIDBAccessor.h"
#include "nsCOMPtr.h"
#include "nsNetDiskCache.h"
#include "nsDiskCacheRecord.h"
class nsCachedDiskData ; /* forward decl */
class nsDBEnumerator : public nsISimpleEnumerator {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISIMPLEENUMERATOR
nsDBEnumerator(nsIDBAccessor* aDB, nsNetDiskCache* aCache) ;
virtual ~nsDBEnumerator() ;
private:
nsCOMPtr<nsIDBAccessor> m_DB ;
nsCOMPtr<nsNetDiskCache> m_DiskCache ;
void * m_tempEntry ;
PRUint32 m_tempEntry_length ;
nsDiskCacheRecord* m_CacheEntry ;
PRBool m_bReset ;
};
#endif // _NS_DBENUMERATOR_H_

View File

@@ -1,456 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#include "nsDiskCacheRecord.h"
#include "nsINetDataDiskCache.h"
#include "nsNetDiskCacheCID.h"
#include "nsDiskCacheRecordChannel.h"
#include "nsFileStream.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsIProtocolHandler.h"
#include "nsIIOService.h"
#include "nsIAllocator.h"
#include "plstr.h"
#include "prprf.h"
#include "prmem.h"
#include "prlog.h"
#include "prtypes.h"
#include "netCore.h"
#include "nsDBAccessor.h"
#if !defined(IS_LITTLE_ENDIAN) && !defined(IS_BIG_ENDIAN)
ERROR! Must have a byte order
#endif
#ifdef IS_LITTLE_ENDIAN
#define COPY_INT32(_a,_b) memcpy(_a, _b, sizeof(int32))
#else
#define COPY_INT32(_a,_b) /* swap */ \
do { \
((char *)(_a))[0] = ((char *)(_b))[3]; \
((char *)(_a))[1] = ((char *)(_b))[2]; \
((char *)(_a))[2] = ((char *)(_b))[1]; \
((char *)(_a))[3] = ((char *)(_b))[0]; \
} while(0)
#endif
nsDiskCacheRecord::nsDiskCacheRecord(nsIDBAccessor* db, nsNetDiskCache* aCache) :
mKey(0) ,
mKeyLength(0) ,
mRecordID(0) ,
mMetaData(0) ,
mMetaDataLength(0) ,
mDB(db) ,
mInfo(0) ,
mInfoSize(0) ,
mNumChannels(0) ,
mDiskCache(aCache)
{
NS_INIT_REFCNT();
NS_ASSERTION(mDiskCache, "Must have an nsNetDiskCache");
NS_ADDREF(mDiskCache);
}
// mem alloced. so caller should do free() on key.
NS_IMETHODIMP
nsDiskCacheRecord::Init(const char* key, PRUint32 length, PRInt32 ID)
{
NS_NewFileSpec(getter_AddRefs(mFile));
if(!mFile)
return NS_ERROR_OUT_OF_MEMORY ;
// copy key
mKeyLength = length ;
mKey = NS_STATIC_CAST(char*, nsAllocator::Alloc(mKeyLength*sizeof(char))) ;
if(!mKey)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(mKey, key, length) ;
// get RecordID
mRecordID = ID ;
// setup the file name
nsCOMPtr<nsIFileSpec> dbFolder ;
mDiskCache->GetDiskCacheFolder(getter_AddRefs(dbFolder)) ;
nsresult rv = mFile->FromFileSpec(dbFolder) ;
if(NS_FAILED(rv))
return NS_ERROR_FAILURE ;
// dir is a hash result of mRecordID%32, hope it's enough
char filename[9], dirName[3] ;
PR_snprintf(dirName, 3, "%02x", (((PRUint32)mRecordID) % 32)) ;
mFile->AppendRelativeUnixPath(dirName) ;
PR_snprintf(filename, 9, "%08x", mRecordID) ;
mFile->AppendRelativeUnixPath(filename) ;
return NS_OK ;
}
nsDiskCacheRecord::~nsDiskCacheRecord()
{
if(mKey)
nsAllocator::Free(mKey) ;
if(mMetaData)
nsAllocator::Free(mMetaData) ;
if(mInfo)
nsAllocator::Free(mInfo) ;
NS_IF_RELEASE(mDiskCache);
}
//
// Implement nsISupports methods
//
NS_IMPL_ISUPPORTS(nsDiskCacheRecord, NS_GET_IID(nsINetDataCacheRecord))
///////////////////////////////////////////////////////////////////////
// nsINetDataCacheRecord methods
// yes, mem alloced on *_retval.
NS_IMETHODIMP
nsDiskCacheRecord::GetKey(PRUint32 *length, char** _retval)
{
if(!_retval)
return NS_ERROR_NULL_POINTER ;
*length = mKeyLength ;
*_retval = NS_STATIC_CAST(char*, nsAllocator::Alloc(mKeyLength*sizeof(char))) ;
if(!*_retval)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(*_retval, mKey, mKeyLength) ;
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecord::GetRecordID(PRInt32* aRecordID)
{
*aRecordID = mRecordID ;
return NS_OK ;
}
// yes, mem alloced on *_retval.
NS_IMETHODIMP
nsDiskCacheRecord::GetMetaData(PRUint32 *length, char **_retval)
{
if(!_retval)
return NS_ERROR_NULL_POINTER ;
// always null the return value first.
*_retval = nsnull ;
*length = mMetaDataLength ;
if(mMetaDataLength) {
*_retval = NS_STATIC_CAST(char*, nsAllocator::Alloc(mMetaDataLength*sizeof(char))) ;
if(!*_retval)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(*_retval, mMetaData, mMetaDataLength) ;
}
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecord::SetMetaData(PRUint32 length, const char* data)
{
// set the mMetaData
mMetaDataLength = length ;
if(mMetaData)
nsAllocator::Free(mMetaData) ;
mMetaData = NS_STATIC_CAST(char*, nsAllocator::Alloc(mMetaDataLength*sizeof(char))) ;
if(!mMetaData) {
return NS_ERROR_OUT_OF_MEMORY ;
}
memcpy(mMetaData, data, length) ;
// Generate mInfo
nsresult rv = GenInfo() ;
if(NS_FAILED(rv))
return rv ;
// write through into mDB
rv = mDB->Put(mRecordID, mInfo, mInfoSize) ;
return rv ;
}
NS_IMETHODIMP
nsDiskCacheRecord::GetStoredContentLength(PRUint32 *aStoredContentLength)
{
return mFile->GetFileSize(aStoredContentLength) ;
}
// untill nsIFileSpec::Truncate() is in, we have to do all this ugly stuff
NS_IMETHODIMP
nsDiskCacheRecord::SetStoredContentLength(PRUint32 aStoredContentLength)
{
PRUint32 len = 0 ;
nsresult rv = mFile->GetFileSize(&len) ;
if(NS_FAILED(rv))
return rv ;
if(len < aStoredContentLength)
{
NS_ERROR("Error: can not set filesize to something bigger than itself.\n") ;
return NS_ERROR_FAILURE ;
}
else {
rv = mFile->Truncate(aStoredContentLength) ;
if(NS_FAILED(rv))
return rv ;
mDiskCache->m_StorageInUse -= (len - aStoredContentLength) ;
return NS_OK ;
}
}
NS_IMETHODIMP
nsDiskCacheRecord::Delete(void)
{
if(mNumChannels)
return NS_ERROR_NOT_AVAILABLE ;
PRUint32 len ;
mFile->GetFileSize(&len) ;
nsFileSpec cache_file ;
nsresult rv = mFile->GetFileSpec(&cache_file) ;
if(NS_FAILED(rv))
return NS_ERROR_FAILURE ;
cache_file.Delete(PR_TRUE) ;
// updata the storage size
mDiskCache->m_StorageInUse -= len ;
rv = mDB->Del(mRecordID, mKey, mKeyLength) ;
if(NS_FAILED(rv))
return NS_ERROR_FAILURE ;
else
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecord::GetFilename(nsIFileSpec * *aFilename)
{
if(!aFilename)
return NS_ERROR_NULL_POINTER ;
*aFilename = mFile ;
NS_ADDREF(*aFilename) ;
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecord::NewChannel(nsILoadGroup *loadGroup, nsIChannel **_retval)
{
nsDiskCacheRecordChannel* channel = new nsDiskCacheRecordChannel(this, loadGroup) ;
if(!channel)
return NS_ERROR_OUT_OF_MEMORY ;
nsresult rv = channel->Init() ;
if(NS_FAILED(rv))
return rv ;
NS_ADDREF(channel) ;
*_retval = NS_STATIC_CAST(nsIChannel*, channel) ;
return NS_OK ;
}
//////////////////////////////////////////////////////////////////////////
// nsDiskCacheRecord methods
// file name is represented by a url string. I hope this would be more
// generic
nsresult
nsDiskCacheRecord::GenInfo()
{
if(mInfo)
nsAllocator::Free(mInfo) ;
char* file_url=nsnull ;
PRUint32 name_len ;
mFile->GetURLString(&file_url) ;
name_len = PL_strlen(file_url)+1 ;
mInfoSize = sizeof(PRUint32) ; // checksum for mInfoSize
mInfoSize += sizeof(PRInt32) ; // RecordID
mInfoSize += sizeof(PRUint32) ; // key length
mInfoSize += mKeyLength ; // key
mInfoSize += sizeof(PRUint32) ; // metadata length
mInfoSize += mMetaDataLength ; // metadata
mInfoSize += sizeof(PRUint32) ; // filename length
mInfoSize += name_len ; // filename
void* newInfo = nsAllocator::Alloc(mInfoSize*sizeof(char)) ;
if(!newInfo) {
return NS_ERROR_OUT_OF_MEMORY ;
}
// copy the checksum mInfoSize
char* cur_ptr = NS_STATIC_CAST(char*, newInfo) ;
COPY_INT32(cur_ptr, &mInfoSize) ;
cur_ptr += sizeof(PRUint32) ;
// copy RecordID
COPY_INT32(cur_ptr, &mRecordID) ;
cur_ptr += sizeof(PRInt32) ;
// copy key length
COPY_INT32(cur_ptr, &mKeyLength) ;
cur_ptr += sizeof(PRUint32) ;
// copy key
memcpy(cur_ptr, mKey, mKeyLength) ;
cur_ptr += mKeyLength ;
// copy metadata length
COPY_INT32(cur_ptr, &mMetaDataLength) ;
cur_ptr += sizeof(PRUint32) ;
// copy metadata
memcpy(cur_ptr, mMetaData, mMetaDataLength) ;
cur_ptr += mMetaDataLength ;
// copy file name length
COPY_INT32(cur_ptr, &name_len) ;
cur_ptr += sizeof(PRUint32) ;
// copy file name
memcpy(cur_ptr, file_url, name_len) ;
cur_ptr += name_len ;
PR_ASSERT(cur_ptr == NS_STATIC_CAST(char*, newInfo) + mInfoSize);
mInfo = newInfo ;
return NS_OK ;
}
/*
* This Method suppose to get all the info from the db record
* and set them to accroding members. the original values
* will all be overwritten. only minimal error checking is performed.
*/
NS_IMETHODIMP
nsDiskCacheRecord::RetrieveInfo(void* aInfo, PRUint32 aInfoLength)
{
// reset everything
if(mInfo) {
nsAllocator::Free(mInfo) ;
mInfo = nsnull ;
}
if(mKey) {
nsAllocator::Free(mKey) ;
mKey = nsnull ;
}
if(mMetaData) {
nsAllocator::Free(mMetaData) ;
mMetaData = nsnull ;
}
char * cur_ptr = NS_STATIC_CAST(char*, aInfo) ;
char* file_url ;
PRUint32 name_len ;
// set mInfoSize
COPY_INT32(&mInfoSize, cur_ptr) ;
cur_ptr += sizeof(PRUint32) ;
// check this at least
if(mInfoSize != aInfoLength)
return NS_ERROR_FAILURE ;
// set mRecordID
COPY_INT32(&mRecordID, cur_ptr) ;
cur_ptr += sizeof(PRInt32) ;
// set mKeyLength
COPY_INT32(&mKeyLength, cur_ptr) ;
cur_ptr += sizeof(PRUint32) ;
// set mKey
mKey = NS_STATIC_CAST(char*, nsAllocator::Alloc(mKeyLength*sizeof(char))) ;
if(!mKey)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(mKey, cur_ptr, mKeyLength) ;
cur_ptr += mKeyLength ;
PRInt32 id ;
mDB->GetID(mKey, mKeyLength, &id) ;
NS_ASSERTION(id==mRecordID, "\t ++++++ bad record, somethings wrong\n") ;
// set mMetaDataLength
COPY_INT32(&mMetaDataLength, cur_ptr) ;
cur_ptr += sizeof(PRUint32) ;
// set mMetaData
mMetaData = NS_STATIC_CAST(char*, nsAllocator::Alloc(mMetaDataLength*sizeof(char))) ;
if(!mMetaData)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(mMetaData, cur_ptr, mMetaDataLength) ;
cur_ptr += mMetaDataLength ;
// get mFile name length
COPY_INT32(&name_len, cur_ptr) ;
cur_ptr += sizeof(PRUint32) ;
// get mFile native name
file_url = NS_STATIC_CAST(char*, nsAllocator::Alloc(name_len*sizeof(char))) ;
if(!file_url)
return NS_ERROR_OUT_OF_MEMORY ;
memcpy(file_url, cur_ptr, name_len) ;
cur_ptr += name_len ;
PR_ASSERT(cur_ptr == NS_STATIC_CAST(char*, aInfo) + mInfoSize);
// create mFile if Init() isn't called
if(!mFile) {
NS_NewFileSpec(getter_AddRefs(mFile));
if(!mFile)
return NS_ERROR_OUT_OF_MEMORY ;
}
// setup mFile
mFile->SetURLString(file_url) ;
return NS_OK ;
}

View File

@@ -1,71 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#ifndef _NET_CACHEDDISKDATA_H_
#define _NET_CACHEDDISKDATA_H_
#include "nsINetDataCacheRecord.h"
#include "nsCOMPtr.h"
#include "nsIDBAccessor.h"
#include "prtypes.h"
#include "nsILoadGroup.h"
#include "nsIFileChannel.h"
#include "nsNetDiskCache.h"
class nsDiskCacheRecord : public nsINetDataCacheRecord
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSINETDATACACHERECORD
protected:
nsDiskCacheRecord(nsIDBAccessor* db, nsNetDiskCache* aCache) ;
virtual ~nsDiskCacheRecord() ;
NS_IMETHOD RetrieveInfo(void* aInfo, PRUint32 aInfoLength) ;
NS_IMETHOD Init(const char* key, PRUint32 length, PRInt32 ID) ;
nsresult GenInfo(void) ;
private:
char* mKey ;
PRUint32 mKeyLength ;
PRInt32 mRecordID ;
char* mMetaData ;
PRUint32 mMetaDataLength ;
nsCOMPtr<nsIFileSpec> mFile ;
nsCOMPtr<nsIDBAccessor> mDB ;
void* mInfo ;
PRUint32 mInfoSize ;
PRUint32 mNumChannels ;
nsNetDiskCache* mDiskCache ;
friend class nsDiskCacheRecordChannel ;
friend class nsDBEnumerator ;
friend class nsNetDiskCache ;
} ;
#endif // _NET_CACHEDDISKDATA_H_

View File

@@ -1,552 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
/*
* Most of the code are taken from nsFileChannel.
*/
#include "nsDiskCacheRecordChannel.h"
#include "nsIFileTransportService.h"
//#include "nsIIOService.h"
#include "nsIServiceManager.h"
#include "nsIURL.h"
#include "nsIOutputStream.h"
#include "netCore.h"
#include "nsIMIMEService.h"
#include "nsISupportsUtils.h"
//static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
static NS_DEFINE_CID(kFileTransportServiceCID, NS_FILETRANSPORTSERVICE_CID);
static NS_DEFINE_CID(kStandardURLCID, NS_STANDARDURL_CID);
static NS_DEFINE_CID(kMIMEServiceCID, NS_MIMESERVICE_CID);
// This is copied from nsMemCacheChannel, We should consolidate these two.
class WriteStreamWrapper : public nsIOutputStream
{
public:
WriteStreamWrapper(nsDiskCacheRecordChannel* aChannel,
nsIOutputStream *aBaseStream) ;
virtual ~WriteStreamWrapper() ;
static nsresult
Create(nsDiskCacheRecordChannel* aChannel, nsIOutputStream *aBaseStream, nsIOutputStream* *aWrapper) ;
NS_DECL_ISUPPORTS
NS_DECL_NSIBASESTREAM
NS_DECL_NSIOUTPUTSTREAM
private:
nsDiskCacheRecordChannel* mChannel;
nsCOMPtr<nsIOutputStream> mBaseStream;
} ;
// implement nsISupports
NS_IMPL_ISUPPORTS(WriteStreamWrapper, NS_GET_IID(nsIOutputStream))
WriteStreamWrapper::WriteStreamWrapper(nsDiskCacheRecordChannel* aChannel,
nsIOutputStream *aBaseStream)
: mChannel(aChannel), mBaseStream(aBaseStream)
{
NS_INIT_REFCNT();
NS_ADDREF(mChannel);
}
WriteStreamWrapper::~WriteStreamWrapper()
{
NS_RELEASE(mChannel);
}
nsresult
WriteStreamWrapper::Create(nsDiskCacheRecordChannel*aChannel, nsIOutputStream *aBaseStream, nsIOutputStream* * aWrapper)
{
WriteStreamWrapper *wrapper = new WriteStreamWrapper(aChannel, aBaseStream);
if (!wrapper) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(wrapper);
*aWrapper = wrapper;
return NS_OK;
}
NS_IMETHODIMP
WriteStreamWrapper::Write(const char *aBuffer, PRUint32 aCount, PRUint32 *aNumWritten)
{
*aNumWritten = 0;
nsresult rv = mBaseStream->Write(aBuffer, aCount, aNumWritten);
mChannel->NotifyStorageInUse(*aNumWritten);
return rv;
}
NS_IMETHODIMP
WriteStreamWrapper::Flush()
{
return mBaseStream->Flush();
}
NS_IMETHODIMP
WriteStreamWrapper::Close()
{
return mBaseStream->Close();
}
nsDiskCacheRecordChannel::nsDiskCacheRecordChannel(nsDiskCacheRecord *aRecord,
nsILoadGroup *aLoadGroup)
: mRecord(aRecord) ,
mLoadGroup(aLoadGroup)
{
NS_INIT_REFCNT() ;
NS_ADDREF(mRecord);
mRecord->mNumChannels++ ;
}
nsDiskCacheRecordChannel::~nsDiskCacheRecordChannel()
{
mRecord->mNumChannels-- ;
NS_RELEASE(mRecord);
}
// I know that I gave conflicting advice on the issue of file
// transport versus file protocol handler, but I thought that the
// last word was that we would use the raw transport, when I wrote:
//
// > I just thought of an argument for the other side of the coin, i.e. the
// > benefit of *not* reusing the file protocol handler: On the Mac, it's
// > expensive to convert from a string URL to an nsFileSpec, because the Mac
// > is brain-dead and scans every directory on the path to the file. It's
// > cheaper to create a nsFileSpec for a cache file by combining a single,
// > static nsFileSpec that corresponds to the cache directory with the
// > relative path to the cache file (using nsFileSpec's operator +). This
// > operation is optimized on the Mac to avoid the scanning operation.
//
// The Mac guys will eat us alive if we do path string to nsFileSpec
// conversions for every cache file we open.
nsresult
nsDiskCacheRecordChannel::Init(void)
{
nsresult rv = mRecord->mFile->GetFileSpec(&mSpec) ;
#ifdef XP_MAC
// Don't assume we actually created a good file spec
FSSpec theSpec = mSpec.GetFSSpec();
if (!theSpec.name[0]) {
NS_ERROR("failed to create a file spec");
// Since we didn't actually create the file spec
// we return an error
return NS_ERROR_MALFORMED_URI;
}
#endif
return rv ;
}
nsresult
nsDiskCacheRecordChannel::NotifyStorageInUse(PRInt32 aBytesUsed)
{
return mRecord->mDiskCache->m_StorageInUse += aBytesUsed ;
}
// implement nsISupports
NS_IMPL_ISUPPORTS4(nsDiskCacheRecordChannel,
nsIChannel,
nsIRequest,
nsIStreamListener,
nsIStreamObserver)
// implement nsIRequest
NS_IMETHODIMP
nsDiskCacheRecordChannel::IsPending(PRBool *aIsPending)
{
*aIsPending = PR_FALSE ;
if(!mFileTransport)
return NS_OK ;
return mFileTransport->IsPending(aIsPending) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::Cancel(void)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->Cancel() ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::Suspend(void)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->Suspend() ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::Resume(void)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->Resume() ;
}
// implement nsIChannel
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetOriginalURI(nsIURI* *aURI)
{
// FUR - might need to implement this - not sure
return NS_ERROR_NOT_IMPLEMENTED ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetURI(nsIURI * *aURI)
{
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->GetURI(aURI) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::OpenInputStream(PRUint32 aStartPosition,
PRInt32 aReadCount,
nsIInputStream* *aResult)
{
nsresult rv ;
if(mFileTransport)
return NS_ERROR_IN_PROGRESS ;
NS_WITH_SERVICE(nsIFileTransportService, fts, kFileTransportServiceCID, &rv) ;
if(NS_FAILED(rv)) return rv ;
rv = fts->CreateTransport(mSpec, "load", 0, 0, getter_AddRefs(mFileTransport)) ;
if(NS_FAILED(rv))
return rv ;
// we don't need to worry about notification callbacks
rv = mFileTransport->OpenInputStream(aStartPosition, aReadCount, aResult) ;
if(NS_FAILED(rv))
mFileTransport = nsnull ;
return rv ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::OpenOutputStream(PRUint32 startPosition,
nsIOutputStream* *aResult)
{
nsresult rv ;
NS_ENSURE_ARG(aResult) ;
if(mFileTransport)
return NS_ERROR_IN_PROGRESS ;
nsCOMPtr<nsIOutputStream> outputStream ;
NS_WITH_SERVICE(nsIFileTransportService, fts, kFileTransportServiceCID, &rv) ;
if(NS_FAILED(rv)) return rv ;
rv = fts->CreateTransport(mSpec, "load", 0, 0, getter_AddRefs(mFileTransport)) ;
if(NS_FAILED(rv))
return rv ;
// we don't need to worry about notification callbacks
rv = mFileTransport->OpenOutputStream(startPosition, getter_AddRefs(outputStream)) ;
if(NS_FAILED(rv)) {
mFileTransport = nsnull ;
return rv ;
}
return WriteStreamWrapper::Create(this, outputStream, aResult) ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::AsyncOpen(nsIStreamObserver *observer,
nsISupports *ctxt)
{
return NS_ERROR_NOT_IMPLEMENTED ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::AsyncRead(PRUint32 aStartPosition,
PRInt32 aReadCount,
nsISupports *aContext,
nsIStreamListener *aListener)
{
nsresult rv ;
if(mFileTransport)
return NS_ERROR_IN_PROGRESS ;
mRealListener = aListener;
nsCOMPtr<nsIStreamListener> tempListener = this;
if (mLoadGroup) {
nsCOMPtr<nsILoadGroupListenerFactory> factory;
//
// Create a load group "proxy" listener...
//
rv = mLoadGroup->GetGroupListenerFactory(getter_AddRefs(factory));
if (factory) {
nsIStreamListener *newListener;
rv = factory->CreateLoadGroupListener(mRealListener, &newListener);
if (NS_SUCCEEDED(rv)) {
mRealListener = newListener;
NS_RELEASE(newListener);
}
}
rv = mLoadGroup->AddChannel(this, nsnull);
if (NS_FAILED(rv)) return rv;
}
NS_WITH_SERVICE(nsIFileTransportService, fts, kFileTransportServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
rv = fts->CreateTransport(mSpec, "load", 0, 0, getter_AddRefs(mFileTransport));
if (NS_FAILED(rv)) return rv;
// no callbacks
rv = mFileTransport->AsyncRead(aStartPosition,
aReadCount,
aContext,
tempListener);
if (NS_FAILED(rv)) {
// release the transport so that we don't think we're in progress
mFileTransport = nsnull;
}
return rv;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::AsyncWrite(nsIInputStream *fromStream,
PRUint32 startPosition,
PRInt32 writeCount,
nsISupports *ctxt,
nsIStreamObserver *observer)
{
/*
if(!mFileTransport)
return NS_ERROR_FAILURE ;
return mFileTransport->AsyncWrite(fromStream,
startPosition,
writeCount,
ctxt,
observer) ;
*/
// I can't do this since the write is not monitored, and I won't be
// able to updata the storage.
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetLoadAttributes(nsLoadFlags *aLoadAttributes)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsDiskCacheRecordChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::SetLoadAttributes(nsLoadFlags aLoadAttributes)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsDiskCacheRecordChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
#define DUMMY_TYPE "text/html"
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetContentType(char * *aContentType)
{
nsresult rv ;
if (mSpec.IsDirectory()) {
*aContentType = nsCRT::strdup("application/http-index-format");
return *aContentType ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
else {
// I wish I can make this simplier
char* urlStr ;
mRecord->mFile->GetURLString(&urlStr) ;
// file: URLs (currently) have no additional structure beyond that provided by standard
// URLs, so there is no "outer" given to CreateInstance
nsCOMPtr<nsIURI> url;
rv = nsComponentManager::CreateInstance(kStandardURLCID, nsnull,
NS_GET_IID(nsIURI),
//(void**)&url);
getter_AddRefs(url)) ;
if (NS_FAILED(rv)) return rv;
rv = url->SetSpec((char*)urlStr);
if (NS_FAILED(rv))
return rv;
NS_WITH_SERVICE(nsIMIMEService, MIMEService, kMIMEServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
rv = MIMEService->GetTypeFromURI(url, aContentType);
if (NS_SUCCEEDED(rv)) return rv;
}
// if all else fails treat it as text/html?
*aContentType = nsCRT::strdup(DUMMY_TYPE);
if (!*aContentType) {
return NS_ERROR_OUT_OF_MEMORY;
} else {
return NS_OK;
}
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetContentLength(PRInt32 *aContentLength)
{
nsresult rv;
PRUint32 length;
rv = mRecord->mFile->GetFileSize(&length);
if (NS_SUCCEEDED(rv)) {
*aContentLength = (PRInt32)length;
} else {
*aContentLength = -1;
}
return rv;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetOwner(nsISupports* *aOwner)
{
*aOwner = mOwner.get() ;
NS_IF_ADDREF(*aOwner) ;
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::SetOwner(nsISupports* aOwner)
{
mOwner = aOwner ;
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsDiskCacheRecordChannel method unexpectedly called");
return NS_OK ;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsDiskCacheRecordChannel method unexpectedly called");
return NS_OK;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsDiskCacheRecordChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsDiskCacheRecordChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
////////////////////////////////////////////////////////////////////////////////
// nsIStreamListener methods:
////////////////////////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsDiskCacheRecordChannel::OnStartRequest(nsIChannel* transportChannel, nsISupports* context)
{
NS_ASSERTION(mRealListener, "No listener...");
return mRealListener->OnStartRequest(this, context);
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::OnStopRequest(nsIChannel* transportChannel, nsISupports* context,
nsresult aStatus, const PRUnichar* aMsg)
{
nsresult rv;
rv = mRealListener->OnStopRequest(this, context, aStatus, aMsg);
if (mLoadGroup) {
if (NS_SUCCEEDED(rv)) {
mLoadGroup->RemoveChannel(this, context, aStatus, aMsg);
}
}
// Release the reference to the consumer stream listener...
mRealListener = null_nsCOMPtr();
mFileTransport = null_nsCOMPtr();
return rv;
}
NS_IMETHODIMP
nsDiskCacheRecordChannel::OnDataAvailable(nsIChannel* transportChannel, nsISupports* context,
nsIInputStream *aIStream, PRUint32 aSourceOffset,
PRUint32 aLength)
{
nsresult rv;
rv = mRealListener->OnDataAvailable(this, context, aIStream,
aSourceOffset, aLength);
//
// If the connection is being aborted cancel the transport. This will
// insure that the transport will go away even if it is blocked waiting
// for the consumer to empty the pipe...
//
if (NS_FAILED(rv)) {
mFileTransport->Cancel();
}
return rv;
}

View File

@@ -1,76 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#ifndef _ns_DiskCacheRecordChannel_h_
#define _ns_DiskCacheRecordChannel_h_
#include "nsIChannel.h"
#include "nsCOMPtr.h"
#include "nsDiskCacheRecord.h"
#include "nsIStreamListener.h"
/*
* This class is plagiarized from nsMemCacheChannel
*/
class nsDiskCacheRecordChannel : public nsIChannel,
public nsIStreamListener
{
public:
nsDiskCacheRecordChannel(nsDiskCacheRecord *aRecord, nsILoadGroup *aLoadGroup);
virtual ~nsDiskCacheRecordChannel() ;
// Declare nsISupports methods
NS_DECL_ISUPPORTS
// Declare nsIRequest methods
NS_DECL_NSIREQUEST
// Declare nsIChannel methods
NS_DECL_NSICHANNEL
// Declare nsIStreamObserver methods
NS_DECL_NSISTREAMOBSERVER
// Declare nsIStreamListener methods
NS_DECL_NSISTREAMLISTENER
nsresult Init(void) ;
private:
nsresult NotifyStorageInUse(PRInt32 aBytesUsed) ;
nsDiskCacheRecord* mRecord ;
nsCOMPtr<nsILoadGroup> mLoadGroup ;
nsCOMPtr<nsISupports> mOwner ;
nsCOMPtr<nsIChannel> mFileTransport ;
nsFileSpec mSpec ;
nsCOMPtr<nsIStreamListener> mRealListener;
friend class WriteStreamWrapper ;
} ;
#endif // _ns_DiskCacheRecordChannel_h_

View File

@@ -1,66 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#ifndef _NS_IDBACCESSOR_H_
#define _NS_IDBACCESSOR_H_
#include "nsISupports.h"
#include "nsIFileSpec.h"
// nsIDBAccessorIID {6AADD4D0-7785-11d3-87FE-000629D01344}
#define NS_IDBACCESSOR_IID \
{ 0x6aadd4d0, 0x7785, 0x11d3, \
{0x87, 0xfe, 0x0, 0x6, 0x29, 0xd0, 0x13, 0x44}}
// nsDBAccessorCID {6AADD4D1-7785-11d3-87FE-000629D01344}
#define NS_DBACCESSOR_CID \
{ 0x6aadd4d1, 0x7785, 0x11d3, \
{ 0x87, 0xfe, 0x0, 0x6, 0x29, 0xd0, 0x13, 0x44 }}
class nsIDBAccessor : public nsISupports
{
public:
NS_DEFINE_STATIC_IID_ACCESSOR(NS_IDBACCESSOR_IID)
NS_IMETHOD Init(nsIFileSpec* DBFile) = 0 ;
NS_IMETHOD Shutdown(void) = 0 ;
NS_IMETHOD Put(PRInt32 aID, void* anEntry, PRUint32 aLength) = 0 ;
NS_IMETHOD Get(PRInt32 aID, void** anEntry, PRUint32 *aLength) = 0 ;
NS_IMETHOD Del(PRInt32 aID, void* anEntry, PRUint32 aLength) = 0 ;
NS_IMETHOD GetID(const char* key, PRUint32 length, PRInt32* aID) = 0 ;
NS_IMETHOD EnumEntry(void* *anEntry, PRUint32* aLength, PRBool bReset) = 0 ;
NS_IMETHOD GetDBFilesize(PRUint32* aSize) = 0 ;
NS_IMETHOD GetSpecialEntry(void** anEntry, PRUint32 *aLength) = 0 ;
NS_IMETHOD SetSpecialEntry(void* anEntry, PRUint32 aLength) = 0 ;
} ;
#endif // _NS_IDBACCESSOR_H_

View File

@@ -1,704 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#include "nsNetDiskCache.h"
#include "nscore.h"
#include "plstr.h"
#include "prprf.h"
#include "prtypes.h"
#include "prio.h"
#include "prsystem.h" // Directory Seperator
#include "plhash.h"
#include "prclist.h"
#include "prmem.h"
#include "prlog.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsIPref.h"
#include "mcom_db.h"
#include "nsDBEnumerator.h"
#include "nsDiskCacheRecord.h"
#include "netCore.h"
#if !defined(IS_LITTLE_ENDIAN) && !defined(IS_BIG_ENDIAN)
ERROR! Must have a byte order
#endif
#ifdef IS_LITTLE_ENDIAN
#define COPY_INT32(_a,_b) memcpy(_a, _b, sizeof(int32))
#else
#define COPY_INT32(_a,_b) /* swap */ \
do { \
((char *)(_a))[0] = ((char *)(_b))[3]; \
((char *)(_a))[1] = ((char *)(_b))[2]; \
((char *)(_a))[2] = ((char *)(_b))[1]; \
((char *)(_a))[3] = ((char *)(_b))[0]; \
} while(0)
#endif
static NS_DEFINE_CID(kPrefCID, NS_PREF_CID) ;
static NS_DEFINE_CID(kDBAccessorCID, NS_DBACCESSOR_CID) ;
static const PRUint32 DISK_CACHE_SIZE_DEFAULT = 5*1024*1024 ; // 5MB
static const char * const DISK_CACHE_PREF = "browser.cache.disk_cache_size";
static const char * const CACHE_DIR_PREF = "browser.cache.directory";
class nsDiskCacheRecord ;
nsNetDiskCache::nsNetDiskCache() :
m_Enabled(PR_TRUE) ,
m_NumEntries(0) ,
m_pNextCache(0) ,
m_pDiskCacheFolder(0) ,
m_StorageInUse(0) ,
m_DB(0) ,
m_DBCorrupted(PR_FALSE)
{
// set it to INF for now
m_MaxEntries = (PRUint32)-1 ;
NS_INIT_REFCNT();
}
nsNetDiskCache::~nsNetDiskCache()
{
SetSpecialEntry() ;
NS_IF_RELEASE(m_DB) ;
// FUR
// I think that, eventually, we also want a distinguished key in the DB which
// means "clean cache shutdown". You clear this flag when the db is first
// opened and set it just before the db is closed. If the db wasn't shutdown
// cleanly in a prior session, i.e. because the app crashed, on startup you
// scan all the individual files in directories and look for "orphans",
// i.e. cache files which don't have corresponding entries in the db. That's
// also when storage-in-use and number of entries would be recomputed.
//
// We don't necessarily need all this functionality immediately, though.
if(m_DBCorrupted) {
nsFileSpec cacheFolder ;
m_pDiskCacheFolder->GetFileSpec(&cacheFolder) ;
char nameInt[6] ;
for(nsDirectoryIterator di(cacheFolder, PR_FALSE); di.Exists(); di++) {
char* filename = di.Spec().GetLeafName() ;
char* pname = nameInt ;
pname = PL_strncpyz(pname, filename, 6) ;
if(PL_strcmp(pname, "trash") == 0)
RemoveFolder(di.Spec()) ;
nsCRT::free(filename) ;
}
}
}
NS_IMETHODIMP
nsNetDiskCache::Init(void)
{
nsresult rv ;
// don't initialize if no cache folder is set.
if(!m_pDiskCacheFolder) return NS_OK ;
if(!m_DB) {
m_DB = new nsDBAccessor() ;
if(!m_DB)
return NS_ERROR_OUT_OF_MEMORY ;
else
NS_ADDREF(m_DB) ;
}
// create cache sub directories
nsCOMPtr<nsIFileSpec> cacheSubDir;
rv = NS_NewFileSpec(getter_AddRefs(cacheSubDir));
for (int i=0; i < 32; i++) {
rv = cacheSubDir->FromFileSpec(m_pDiskCacheFolder) ;
if(NS_FAILED(rv))
return rv ;
char dirName[3];
PR_snprintf (dirName, 3, "%0.2x", i);
cacheSubDir->AppendRelativeUnixPath (dirName) ;
CreateDir(cacheSubDir);
}
return InitDB() ;
}
NS_IMETHODIMP
nsNetDiskCache::InitDB(void)
{
nsresult rv ;
if(!m_DBFile) {
NS_NewFileSpec(getter_AddRefs(m_DBFile)) ;
if(!m_DBFile)
return NS_ERROR_OUT_OF_MEMORY ;
}
rv = m_DBFile->FromFileSpec(m_pDiskCacheFolder) ;
if(NS_FAILED(rv))
return rv ;
m_DBFile->AppendRelativeUnixPath("cache.db") ;
rv = m_DB->Init(m_DBFile) ;
if(rv == NS_ERROR_FAILURE) {
// try recovery if error
DBRecovery() ;
}
rv = GetSpecialEntry() ;
if(rv == NS_ERROR_FAILURE) {
// try recovery if error
DBRecovery() ;
}
return rv ;
}
//////////////////////////////////////////////////////////////////////////
// nsISupports methods
NS_IMPL_ISUPPORTS3(nsNetDiskCache,
nsINetDataDiskCache,
nsINetDataCache,
nsISupports)
///////////////////////////////////////////////////////////////////////////
// nsINetDataCache Method
NS_IMETHODIMP
nsNetDiskCache::GetDescription(PRUnichar* *aDescription)
{
nsAutoString description("Disk Cache") ;
*aDescription = description.ToNewUnicode() ;
if(!*aDescription)
return NS_ERROR_OUT_OF_MEMORY ;
return NS_OK ;
}
/* don't alloc mem for nsICachedNetData.
* RecordID is generated using the same scheme in nsCacheDiskData,
* see GetCachedNetData() for detail.
*/
NS_IMETHODIMP
nsNetDiskCache::Contains(const char* key, PRUint32 length, PRBool *_retval)
{
*_retval = PR_FALSE ;
NS_ASSERTION(m_DB, "no db.") ;
PRInt32 id = 0 ;
nsresult rv = m_DB->GetID(key, length, &id) ;
if(NS_FAILED(rv)) {
// try recovery if error
DBRecovery() ;
return rv ;
}
void* info = 0 ;
PRUint32 info_size = 0 ;
rv = m_DB->Get(id, &info, &info_size) ;
if(NS_SUCCEEDED(rv) && info)
*_retval = PR_TRUE ;
if(NS_FAILED(rv)) {
// try recovery if error
DBRecovery() ;
}
return rv ;
}
/* regardless if it's cached or not, a copy of nsNetDiskCache would
* always be returned. so release it appropriately.
* if mem alloced, updata m_NumEntries also.
* for now, the new nsCachedNetData is not written into db yet since
* we have nothing to write.
*/
NS_IMETHODIMP
nsNetDiskCache::GetCachedNetData(const char* key, PRUint32 length, nsINetDataCacheRecord **_retval)
{
NS_ASSERTION(m_DB, "no db.") ;
nsresult rv = 0 ;
if (!_retval)
return NS_ERROR_NULL_POINTER ;
*_retval = nsnull ;
PRInt32 id = 0 ;
rv = m_DB->GetID(key, length, &id) ;
if(NS_FAILED(rv)) {
// try recovery if error
DBRecovery() ;
return rv ;
}
// construct an empty record
nsDiskCacheRecord* newRecord = new nsDiskCacheRecord(m_DB, this) ;
if(!newRecord)
return NS_ERROR_OUT_OF_MEMORY ;
rv = newRecord->Init(key, length, id) ;
if(NS_FAILED(rv)) {
delete newRecord ;
return rv ;
}
NS_ADDREF(newRecord) ; // addref for _retval
*_retval = (nsINetDataCacheRecord*) newRecord ;
void* info = 0 ;
PRUint32 info_size = 0 ;
rv = m_DB->Get(id, &info, &info_size) ;
if(NS_SUCCEEDED(rv) && info) {
// this is a previously cached record
nsresult r1 ;
r1 = newRecord->RetrieveInfo(info, info_size) ;
if(NS_SUCCEEDED(rv))
return NS_OK ;
else {
// probably a bad one
NS_RELEASE(newRecord) ;
*_retval = nsnull ;
return r1;
}
} else if (NS_SUCCEEDED(rv) && !info) {
// this is a new record.
m_NumEntries ++ ;
return NS_OK ;
} else {
// database error.
DBRecovery() ;
return rv ;
}
}
/* get an nsICachedNetData, mem needs to be de-alloced if not found. */
NS_IMETHODIMP
nsNetDiskCache::GetCachedNetDataByID(PRInt32 RecordID, nsINetDataCacheRecord **_retval)
{
NS_ASSERTION(m_DB, "no db.") ;
if (!_retval)
return NS_ERROR_NULL_POINTER ;
*_retval = nsnull ;
nsresult rv ;
void* info = 0 ;
PRUint32 info_size = 0 ;
rv = m_DB->Get(RecordID, &info, &info_size) ;
if(NS_SUCCEEDED(rv) && info) {
// construct an empty record if only found in db
nsDiskCacheRecord* newRecord = new nsDiskCacheRecord(m_DB, this) ;
if(!newRecord)
return NS_ERROR_OUT_OF_MEMORY ;
NS_ADDREF(newRecord) ; // addref for _retval
rv = newRecord->RetrieveInfo(info, info_size) ;
if(NS_SUCCEEDED(rv)) {
*_retval = (nsINetDataCacheRecord*) newRecord ;
return NS_OK ;
}
else {
// bad record, I guess
NS_RELEASE(newRecord) ; // release if bad things happen
return rv ;
}
} else {
NS_ERROR("Error: RecordID not in DB\n") ;
DBRecovery() ;
return rv ;
}
}
NS_IMETHODIMP
nsNetDiskCache::GetEnabled(PRBool *aEnabled)
{
*aEnabled = m_Enabled ;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::SetEnabled(PRBool aEnabled)
{
m_Enabled = aEnabled ;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::GetFlags(PRUint32 *aFlags)
{
*aFlags = FILE_PER_URL_CACHE;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::GetNumEntries(PRUint32 *aNumEntries)
{
*aNumEntries = m_NumEntries ;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::GetMaxEntries(PRUint32 *aMaxEntries)
{
*aMaxEntries = m_MaxEntries ;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::NewCacheEntryIterator(nsISimpleEnumerator **_retval)
{
NS_ASSERTION(m_DB, "no db.") ;
if(!_retval)
return NS_ERROR_NULL_POINTER ;
*_retval = nsnull ;
nsISimpleEnumerator* enumerator = new nsDBEnumerator(m_DB, this) ;
if(enumerator) {
NS_ADDREF(enumerator) ;
*_retval = enumerator ;
return NS_OK ;
}
else
return NS_ERROR_OUT_OF_MEMORY ;
}
NS_IMETHODIMP
nsNetDiskCache::GetNextCache(nsINetDataCache * *aNextCache)
{
if(!aNextCache)
return NS_ERROR_NULL_POINTER ;
*aNextCache = m_pNextCache ;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::SetNextCache(nsINetDataCache *aNextCache)
{
m_pNextCache = aNextCache ;
return NS_OK ;
}
// db size can always be measured at the last minute. Since it's hard
// to know before hand.
NS_IMETHODIMP
nsNetDiskCache::GetStorageInUse(PRUint32 *aStorageInUse)
{
NS_ASSERTION(m_DB, "no db.") ;
PRUint32 total_size = m_StorageInUse ;
/*
PRUint32 len = 0 ;
// add the size of the db.
m_DB->GetDBFilesize(&len) ;
total_size += len ;
*/
// we need size in kB
total_size = total_size >> 10 ;
*aStorageInUse = total_size ;
return NS_OK ;
}
/*
* The whole cache dirs can be whiped clean since all the cache
* files are resides in seperate hashed dirs. It's safe to do so.
*/
NS_IMETHODIMP
nsNetDiskCache::RemoveAll(void)
{
NS_ASSERTION(m_DB, "no db.") ;
NS_ASSERTION(m_pDiskCacheFolder, "no cache folder.") ;
// remove all the sub folders
nsFileSpec cacheSubDir;
for (int i=0; i < 32; i++) {
m_pDiskCacheFolder->GetFileSpec(&cacheSubDir) ;
char dirName[3];
PR_snprintf (dirName, 3, "%0.2x", i);
cacheSubDir += dirName ;
RemoveFolder(cacheSubDir) ;
}
// don't forget the db file itself
m_DB->Shutdown() ;
nsFileSpec dbfile ;
m_DBFile->GetFileSpec(&dbfile) ;
dbfile.Delete(PR_TRUE) ;
// reinitilize
return Init() ;
}
//////////////////////////////////////////////////////////////////
// nsINetDataDiskCache methods
NS_IMETHODIMP
nsNetDiskCache::GetDiskCacheFolder(nsIFileSpec * *aDiskCacheFolder)
{
*aDiskCacheFolder = nsnull ;
NS_ASSERTION(m_pDiskCacheFolder, "no cache folder.") ;
*aDiskCacheFolder = m_pDiskCacheFolder ;
NS_ADDREF(*aDiskCacheFolder) ;
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::SetDiskCacheFolder(nsIFileSpec * aDiskCacheFolder)
{
if(!m_pDiskCacheFolder) {
NS_NewFileSpec(getter_AddRefs(m_pDiskCacheFolder));
if(!m_pDiskCacheFolder)
return NS_ERROR_OUT_OF_MEMORY ;
m_pDiskCacheFolder = aDiskCacheFolder ;
return Init() ;
}
else {
char *newfolder, *oldfolder ;
m_pDiskCacheFolder->GetNativePath(&oldfolder) ;
aDiskCacheFolder->GetNativePath(&newfolder) ;
if(PL_strcmp(newfolder, oldfolder) != 0) {
m_pDiskCacheFolder = aDiskCacheFolder ;
// do we need to blow away old cache before building a new one?
// return RemoveAll() ;
m_DB->Shutdown() ;
return Init() ;
} else
return NS_OK ;
}
}
//////////////////////////////////////////////////////////////////
// nsNetDiskCache methods
// create a directory (recursively)
NS_IMETHODIMP
nsNetDiskCache::CreateDir(nsIFileSpec* dir_spec)
{
PRBool does_exist ;
nsCOMPtr<nsIFileSpec> p_spec ;
dir_spec->Exists(&does_exist) ;
if(does_exist)
return NS_OK ;
nsresult rv = dir_spec->GetParent(getter_AddRefs(p_spec)) ;
if(NS_FAILED(rv))
return rv ;
p_spec->Exists(&does_exist) ;
if(!does_exist) {
CreateDir(p_spec) ;
rv = dir_spec->CreateDir() ;
if(NS_FAILED(rv))
return rv ;
}
else {
rv = dir_spec->CreateDir() ;
if(NS_FAILED(rv))
return rv ;
}
return NS_OK ;
}
// We can't afford to make a *separate* pass over the whole db on every
// startup, just to figure out m_NumEntries and m_StorageInUse. (This is a
// several second operation on a large db). We'll likely need to store
// distinguished keys in the db that contain these values and update them
// incrementally, except when failure to shut down the db cleanly is detected.
NS_IMETHODIMP
nsNetDiskCache::GetSpecialEntry(void)
{
void* pInfo ;
PRUint32 InfoSize ;
nsresult rv = m_DB->GetSpecialEntry(&pInfo, &InfoSize) ;
if(NS_FAILED(rv))
return rv ;
if(!pInfo && InfoSize == 0) {
// must be a new DB
m_NumEntries = 0 ;
m_StorageInUse = 0 ;
}
else {
char * cur_ptr = NS_STATIC_CAST(char*, pInfo) ;
// get m_NumEntries
COPY_INT32(&m_NumEntries, cur_ptr) ;
cur_ptr += sizeof(PRUint32) ;
// get m_StorageInUse
COPY_INT32(&m_StorageInUse, cur_ptr) ;
cur_ptr += sizeof(PRUint32) ;
PR_ASSERT(cur_ptr == NS_STATIC_CAST(char*, pInfo) + InfoSize);
}
return NS_OK ;
}
NS_IMETHODIMP
nsNetDiskCache::SetSpecialEntry(void)
{
PRUint32 InfoSize ;
InfoSize = sizeof m_NumEntries ;
InfoSize += sizeof m_StorageInUse ;
void* pInfo = nsAllocator::Alloc(InfoSize*sizeof(char)) ;
if(!pInfo)
return NS_ERROR_OUT_OF_MEMORY ;
char* cur_ptr = NS_STATIC_CAST(char*, pInfo) ;
COPY_INT32(cur_ptr, &m_NumEntries) ;
cur_ptr += sizeof(PRUint32) ;
COPY_INT32(cur_ptr, &m_StorageInUse) ;
cur_ptr += sizeof(PRUint32) ;
PR_ASSERT(cur_ptr == NS_STATIC_CAST(char*, pInfo) + InfoSize);
return m_DB->SetSpecialEntry(pInfo, InfoSize) ;
}
// this routine will be called everytime we have a db corruption.
// m_DB will be re-initialized, m_StorageInUse and m_NumEntries will
// be reset.
NS_IMETHODIMP
nsNetDiskCache::DBRecovery(void)
{
// rename all the sub cache dirs and remove them later during dtor.
nsresult rv = RenameCacheSubDirs() ;
if(NS_FAILED(rv))
return rv ;
// remove corrupted db file, don't care if db->shutdown fails or not.
m_DB->Shutdown() ;
nsFileSpec dbfile ;
m_DBFile->GetFileSpec(&dbfile) ;
dbfile.Delete(PR_TRUE) ;
// make sure it's not there any more
PRBool exists = dbfile.Exists() ;
if(exists) {
NS_ERROR("can't remove old db.") ;
return NS_ERROR_FAILURE ;
}
// reinitilize DB
return InitDB() ;
}
// this routine will add string "trash" to current CacheSubDir names.
// e.g. 00->trash00, 1f->trash1f. and update the m_DBCorrupted.
NS_IMETHODIMP
nsNetDiskCache::RenameCacheSubDirs(void)
{
nsCOMPtr<nsIFileSpec> cacheSubDir;
nsresult rv = NS_NewFileSpec(getter_AddRefs(cacheSubDir)) ;
for (int i=0; i < 32; i++) {
rv = cacheSubDir->FromFileSpec(m_pDiskCacheFolder) ;
if(NS_FAILED(rv))
return rv ;
char oldName[3], newName[8];
PR_snprintf(oldName, 3, "%0.2x", i) ;
cacheSubDir->AppendRelativeUnixPath(oldName) ;
// re-name the directory
PR_snprintf(newName, 8, "trash%0.2x", i) ;
rv = cacheSubDir->Rename(newName) ;
if(NS_FAILED(rv))
// TODO, error checking
return NS_ERROR_FAILURE ;
}
// update m_DBCorrupted
m_DBCorrupted = PR_TRUE ;
return NS_OK ;
}
// this routine is used by dtor and RemoveAll() to clean up dirs.
NS_IMETHODIMP
nsNetDiskCache::RemoveFolder(nsFileSpec aFolder)
{
for(nsDirectoryIterator di(aFolder, PR_FALSE); di.Exists(); di++) {
di.Spec().Delete(PR_TRUE) ;
}
aFolder.Delete(PR_FALSE) ; // recursive delete
return NS_OK ;
}

View File

@@ -1,91 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
/*
* This file is part of filecache implementation.
*
* nsNetDiskCache is the main disk cache module that will create
* the cache database, and then store and retrieve nsDiskCacheRecord
* objects from it. It also contains some basic error recovery procedure.
*/
#ifndef __gen_nsNetDiskCache_h__
#define __gen_nsNetDiskCache_h__
#include "nsINetDataDiskCache.h"
#include "nsNetDiskCacheCID.h"
#include "nsCOMPtr.h"
#include "nsIPref.h"
#include "nsDBAccessor.h"
class nsIURI; /* forward decl */
class nsICachedNetData; /* forward decl */
class nsISimpleEnumerator; /* forward decl */
class nsIFileSpec; /* forward decl */
/* starting interface: nsNetDiskCache */
class nsNetDiskCache : public nsINetDataDiskCache {
public:
NS_DECL_ISUPPORTS
NS_DECL_NSINETDATACACHE
NS_DECL_NSINETDATADISKCACHE
NS_IMETHOD Init(void) ;
nsNetDiskCache() ;
virtual ~nsNetDiskCache() ;
protected:
NS_IMETHOD InitDB(void) ;
NS_IMETHOD CreateDir(nsIFileSpec* dir_spec) ;
NS_IMETHOD GetSpecialEntry(void) ;
NS_IMETHOD SetSpecialEntry(void) ;
NS_IMETHOD RenameCacheSubDirs(void) ;
NS_IMETHOD DBRecovery(void) ;
NS_IMETHOD RemoveFolder(nsFileSpec aFolder) ;
private:
PRBool m_Enabled ;
PRUint32 m_NumEntries ;
nsCOMPtr<nsINetDataCache> m_pNextCache ;
nsCOMPtr<nsIFileSpec> m_pDiskCacheFolder ;
nsCOMPtr<nsIFileSpec> m_DBFile ;
PRUint32 m_MaxEntries ;
PRUint32 m_StorageInUse ;
nsIDBAccessor* m_DB ;
// this is used to indicate a db corruption
PRBool m_DBCorrupted ;
friend class nsDiskCacheRecord ;
friend class nsDiskCacheRecordChannel ;
friend class nsDBEnumerator ;
} ;
#endif /* __gen_nsNetDiskCache_h__ */

View File

@@ -1,32 +0,0 @@
/*
* 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.
*
* The Initial Developer of the Original Code is Intel Corp.
* Portions created by Intel Corp. are
* Copyright (C) 1999, 1999 Intel Corp. All
* Rights Reserved.
*
* Contributor(s): Yixiong Zou <yixiong.zou@intel.com>
* Carl Wong <carl.wong@intel.com>
*/
#ifndef _nsNetDiskCacheCID_h_
#define _nsNetDiskCacheCID_h_
#define NS_NETDISKCACHE_CID_STR "ECFEEA00-7201-11d3-87FE-000629D01344"
#define NS_NETDISKCACHE_CID \
{ 0xecfeea00, 0x7201, 0x11d3, \
{ 0x87, 0xfe, 0x0, 0x6, 0x29, 0xd0, 0x13, 0x44 }}
#endif /* _nsNetDiskCacheCID_h_ */

View File

@@ -1,50 +0,0 @@
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
#
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
REQUIRES = libreg xpcom
CPPSRCS = \
diskcache.cpp \
$(NULL)
SIMPLE_PROGRAMS = $(CPPSRCS:.cpp=)
ifdef NO_LD_ARCHIVE_FLAGS
LOST_SYM_LIBS = -lxpcomds_s -lxptinfo -lmozreg_s
endif
LIBS = \
-lmozjs \
-lxpcom \
-lmozdbm_s \
$(MOZ_NECKO_UTIL_LIBS) \
$(LOST_SYM_LIBS) \
$(NSPR_LIBS) \
$(NULL)
include $(topsrcdir)/config/rules.mk
LOCAL_INCLUDES = -I$(srcdir)/..
DEFINES += -DUSE_NSREG -DCACHE

View File

@@ -1,836 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsIStreamListener.h"
#include "nsIStreamObserver.h"
#include "nsIServiceManager.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
#include "nsIEventQueue.h"
#include "nsIEventQueueService.h"
#include "nsIChannel.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include <stdio.h>
#include <unistd.h>
#include "nsINetDataCache.h"
#include "nsINetDataCacheRecord.h"
//#include "nsMemCacheCID.h"
#include "nsNetDiskCache.h"
#include "nsIPref.h"
#include "prenv.h"
#include "nsIFileStream.h"
// Number of test entries to be placed in the cache
#define NUM_CACHE_ENTRIES 250
// Cache content stream length will have random length between zero and
// MAX_CONTENT_LENGTH bytes
#define MAX_CONTENT_LENGTH 20000
// Length of random-data cache entry key
#define CACHE_KEY_LENGTH 15
// Length of random-data cache entry meta-data
#define CACHE_METADATA_LENGTH 100
//static NS_DEFINE_CID(kMemCacheCID, NS_MEM_CACHE_FACTORY_CID);
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
static NS_DEFINE_CID(kDiskCacheCID, NS_NETDISKCACHE_CID) ;
static NS_DEFINE_CID(kPrefCID, NS_PREF_CID);
static NS_DEFINE_IID(kIPrefIID, NS_IPREF_IID);
// Mapping from test case number to RecordID
static PRInt32 recordID[NUM_CACHE_ENTRIES];
static PRInt32
mapRecordIdToTestNum(PRInt32 aRecordID)
{
int i;
for (i = 0; i < NUM_CACHE_ENTRIES; i++) {
if (recordID[i] == aRecordID)
return i;
}
return -1;
}
// A supply of stream data to either store or compare with
class nsITestDataStream {
public:
virtual ~nsITestDataStream() {};
virtual PRUint32 Next() = 0;
virtual void Read(char* aBuf, PRUint32 aCount) = 0;
virtual PRBool Match(char* aBuf, PRUint32 aCount) = 0;
virtual void Skip(PRUint32 aCount) = 0;
};
// A reproducible stream of random data.
class RandomStream : public nsITestDataStream {
public:
RandomStream(PRUint32 aSeed) {
mStartSeed = mState = aSeed;
}
PRUint32 GetStartSeed() {
return mStartSeed;
}
PRUint32 Next() {
mState = 1103515245 * mState + 12345;
return mState;
}
void Read(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
*aBuf++ = Next();
}
}
PRBool
Match(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
if (*aBuf++ != (char)(Next() & 0xff))
return PR_FALSE;
}
return PR_TRUE;
}
void
Skip(PRUint32 aCount) {
while (aCount--)
Next();
}
protected:
PRUint32 mState;
PRUint32 mStartSeed;
};
// A stream of data that increments on each byte that is read, modulo 256
class CounterStream : public nsITestDataStream {
public:
CounterStream(PRUint32 aSeed) {
mStartSeed = mState = aSeed;
}
PRUint32 GetStartSeed() {
return mStartSeed;
}
PRUint32 Next() {
mState += 1;
mState &= 0xff;
return mState;
}
void Read(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
*aBuf++ = Next();
}
}
PRBool
Match(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
if (*aBuf++ != (char)Next())
return PR_FALSE;
}
return PR_TRUE;
}
void
Skip(PRUint32 aCount) {
mState += aCount;
mState &= 0xff;
}
protected:
PRUint32 mState;
PRUint32 mStartSeed;
};
static int gNumReaders = 0;
static PRUint32 gTotalBytesRead = 0;
static PRUint32 gTotalDuration = 0;
class nsReader : public nsIStreamListener {
public:
NS_DECL_ISUPPORTS
nsReader()
: mStartTime(0), mBytesRead(0)
{
NS_INIT_REFCNT();
gNumReaders++;
}
virtual ~nsReader() {
delete mTestDataStream;
gNumReaders--;
}
nsresult
Init(nsIChannel *aChannel, nsITestDataStream* aRandomStream, PRUint32 aExpectedStreamLength) {
mChannel = aChannel;
mTestDataStream = aRandomStream;
mExpectedStreamLength = aExpectedStreamLength;
mRefCnt = 1;
return NS_OK;
}
NS_IMETHOD OnStartRequest(nsIChannel* channel,
nsISupports* context) {
mStartTime = PR_IntervalNow();
return NS_OK;
}
NS_IMETHOD OnDataAvailable(nsIChannel* channel,
nsISupports* context,
nsIInputStream *aIStream,
PRUint32 aSourceOffset,
PRUint32 aLength) {
char buf[1025];
while (aLength > 0) {
PRUint32 amt;
PRBool match;
aIStream->Read(buf, sizeof buf, &amt);
if (amt == 0) break;
aLength -= amt;
mBytesRead += amt;
match = mTestDataStream->Match(buf, amt);
NS_ASSERTION(match, "Stored data was corrupted on read");
}
return NS_OK;
}
NS_IMETHOD OnStopRequest(nsIChannel* channel,
nsISupports* context,
nsresult aStatus,
const PRUnichar* aMsg) {
PRIntervalTime endTime;
PRIntervalTime duration;
endTime = PR_IntervalNow();
duration = (endTime - mStartTime);
if (NS_FAILED(aStatus)) printf("channel failed.\n");
// printf("read %d bytes\n", mBytesRead);
NS_ASSERTION(mBytesRead == mExpectedStreamLength,
"Stream in cache is wrong length");
gTotalBytesRead += mBytesRead;
gTotalDuration += duration;
// Release channel
mChannel = 0;
return NS_OK;
}
protected:
PRIntervalTime mStartTime;
PRUint32 mBytesRead;
nsITestDataStream* mTestDataStream;
PRUint32 mExpectedStreamLength;
nsCOMPtr<nsIChannel> mChannel;
};
NS_IMPL_ISUPPORTS2(nsReader, nsIStreamListener, nsIStreamObserver)
static nsIEventQueue* eventQueue;
nsresult
InitQueue() {
nsresult rv;
NS_WITH_SERVICE(nsIEventQueueService, eventQService, kEventQueueServiceCID, &rv);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get event queue service");
rv = eventQService->CreateThreadEventQueue();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't create event queue");
rv = eventQService->GetThreadEventQueue(PR_CurrentThread(), &eventQueue);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get event queue for main thread");
return NS_OK;
}
// Process events until all streams are OnStopRequest'ed
nsresult
WaitForEvents() {
while (gNumReaders) {
eventQueue->ProcessPendingEvents();
}
return NS_OK;
}
// Read data for a single cache record and compare against testDataStream
nsresult
TestReadStream(nsINetDataCacheRecord *record, nsITestDataStream *testDataStream,
PRUint32 expectedStreamLength)
{
nsCOMPtr<nsIChannel> channel;
nsresult rv;
PRUint32 actualContentLength;
rv = record->NewChannel(0, getter_AddRefs(channel));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
rv = record->GetStoredContentLength(&actualContentLength);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(actualContentLength == expectedStreamLength,
"nsINetDataCacheRecord::GetContentLength() busted ?");
nsReader *reader = new nsReader;
reader->AddRef();
rv = reader->Init(channel, testDataStream, expectedStreamLength);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
rv = channel->AsyncRead(0, -1, 0, reader);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
reader->Release();
return NS_OK;
}
// Check that records can be retrieved using their record-ID, in addition
// to using the opaque key.
nsresult
TestRecordID(nsINetDataCache *cache)
{
nsresult rv;
nsCOMPtr<nsINetDataCacheRecord> record;
RandomStream *randomStream;
PRUint32 metaDataLength;
char cacheKey[CACHE_KEY_LENGTH];
char *metaData;
PRUint32 testNum;
PRBool match;
for (testNum = 0; testNum < NUM_CACHE_ENTRIES; testNum++) {
randomStream = new RandomStream(testNum);
randomStream->Read(cacheKey, sizeof cacheKey);
rv = cache->GetCachedNetDataByID(recordID[testNum], getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't obtain record using record ID");
// Match against previously stored meta-data
rv = record->GetMetaData(&metaDataLength, &metaData);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get record meta-data");
match = randomStream->Match(metaData, metaDataLength);
NS_ASSERTION(match, "Meta-data corrupted or incorrect");
nsAllocator::Free(metaData);
delete randomStream;
}
return NS_OK;
}
// Check that all cache entries in the database are enumerated and that
// no duplicates appear.
nsresult
TestEnumeration(nsINetDataCache *cache)
{
nsresult rv;
nsCOMPtr<nsINetDataCacheRecord> record;
nsCOMPtr<nsISupports> tempISupports;
nsCOMPtr<nsISimpleEnumerator> iterator;
RandomStream *randomStream;
PRUint32 metaDataLength;
char cacheKey[CACHE_KEY_LENGTH];
char *metaData;
PRUint32 testNum;
PRBool match;
PRInt32 recID;
int numRecords = 0;
// Iterate over all records in the cache
rv = cache->NewCacheEntryIterator(getter_AddRefs(iterator));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't create new cache entry iterator");
PRBool notDone;
while (1) {
// Done iterating ?
rv = iterator->HasMoreElements(&notDone);
if (NS_FAILED(rv)) return rv;
if (!notDone)
break;
// Get next record in iteration
rv = iterator->GetNext(getter_AddRefs(tempISupports));
NS_ASSERTION(NS_SUCCEEDED(rv), "iterator bustage");
record = do_QueryInterface(tempISupports);
numRecords++;
// Get record ID
rv = record->GetRecordID(&recID);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get Record ID");
testNum = mapRecordIdToTestNum(recID);
NS_ASSERTION(testNum != -1, "Corrupted Record ID ?");
// Erase mapping from table, so that duplicate enumerations are detected
recordID[testNum] = -1;
// Make sure stream matches test data
randomStream = new RandomStream(testNum);
randomStream->Read(cacheKey, sizeof cacheKey);
// Match against previously stored meta-data
rv = record->GetMetaData(&metaDataLength, &metaData);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get record meta-data");
match = randomStream->Match(metaData, metaDataLength);
NS_ASSERTION(match, "Meta-data corrupted or incorrect");
nsAllocator::Free(metaData);
delete randomStream;
}
NS_ASSERTION(numRecords == NUM_CACHE_ENTRIES, "Iteration bug");
return NS_OK;
}
// Read the test data that was written in FillCache(), checking for
// corruption, truncation.
nsresult
TestRead(nsINetDataCache *cache)
{
nsresult rv;
PRBool inCache;
nsCOMPtr<nsINetDataCacheRecord> record;
RandomStream *randomStream;
PRUint32 metaDataLength;
char cacheKey[CACHE_KEY_LENGTH];
char *metaData, *storedCacheKey;
PRUint32 testNum, storedCacheKeyLength;
PRBool match;
for (testNum = 0; testNum < NUM_CACHE_ENTRIES; testNum++) {
randomStream = new RandomStream(testNum);
randomStream->Read(cacheKey, sizeof cacheKey);
// Ensure that entry is in the cache
rv = cache->Contains(cacheKey, sizeof cacheKey, &inCache);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(inCache, "nsINetDataCache::Contains error");
rv = cache->GetCachedNetData(cacheKey, sizeof cacheKey, getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
// Match against previously stored meta-data
match = record->GetMetaData(&metaDataLength, &metaData);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
match = randomStream->Match(metaData, metaDataLength);
NS_ASSERTION(match, "Meta-data corrupted or incorrect");
nsAllocator::Free(metaData);
// Test GetKey() method
rv = record->GetKey(&storedCacheKeyLength, &storedCacheKey);
NS_ASSERTION(NS_SUCCEEDED(rv) &&
(storedCacheKeyLength == sizeof cacheKey) &&
!memcmp(storedCacheKey, &cacheKey[0], sizeof cacheKey),
"nsINetDataCacheRecord::GetKey failed");
nsAllocator::Free(storedCacheKey);
PRUint32 expectedStreamLength = randomStream->Next() & 0xffff;
TestReadStream(record, randomStream, expectedStreamLength);
}
WaitForEvents();
// Compute rate in MB/s
double rate = gTotalBytesRead / PR_IntervalToMilliseconds(gTotalDuration);
rate *= NUM_CACHE_ENTRIES;
rate *= 1000;
rate /= (1024 * 1024);
printf("Read %d bytes at a rate of %5.1f MB per second \n",
gTotalBytesRead, rate);
return NS_OK;
}
// Repeatedly call SetStoredContentLength() on a cache entry and make
// read the stream's data to ensure that it's not corrupted by the effect
nsresult
TestTruncation(nsINetDataCache *cache)
{
nsresult rv;
nsCOMPtr<nsINetDataCacheRecord> record;
RandomStream *randomStream;
char cacheKey[CACHE_KEY_LENGTH];
randomStream = new RandomStream(0);
randomStream->Read(cacheKey, sizeof cacheKey);
rv = cache->GetCachedNetData(cacheKey, sizeof cacheKey, getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
randomStream->Skip(CACHE_METADATA_LENGTH);
PRUint32 initialStreamLength = randomStream->Next() & 0xffff;
delete randomStream;
PRUint32 i;
PRUint32 delta = initialStreamLength / 64;
for (i = initialStreamLength; i >= delta; i -= delta) {
PRUint32 expectedStreamLength = i;
// Do the truncation
record->SetStoredContentLength(expectedStreamLength);
randomStream = new RandomStream(0);
randomStream->Skip(CACHE_KEY_LENGTH + CACHE_METADATA_LENGTH + 1);
TestReadStream(record, randomStream, expectedStreamLength);
WaitForEvents();
}
return NS_OK;
}
// Write known data to random offsets in a single cache entry and test
// resulting stream for correctness.
nsresult
TestOffsetWrites(nsINetDataCache *cache)
{
nsresult rv;
nsCOMPtr<nsINetDataCacheRecord> record;
nsCOMPtr<nsIChannel> channel;
nsCOMPtr<nsIOutputStream> outStream;
char buf[512];
char cacheKey[CACHE_KEY_LENGTH];
RandomStream *randomStream;
randomStream = new RandomStream(0);
randomStream->Read(cacheKey, sizeof cacheKey);
rv = cache->GetCachedNetData(cacheKey, sizeof cacheKey, getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't access record via opaque cache key");
nsCOMPtr<nsIFileSpec> file ;
record->GetFilename(getter_AddRefs(file)) ;
char* name ;
file->GetUnixStyleFilePath(&name) ;
printf(" file name is %s \n", name) ;
// Write buffer-fulls of data at random offsets into the cache entry.
// Data written is (offset % 0xff)
PRUint32 startingOffset;
PRUint32 streamLength = 0;
PRUint32 len = 0 ;
CounterStream *counterStream;
int i = 0;
for (i = 0; i < 257; i++) {
rv = record->NewChannel(0, getter_AddRefs(channel));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
startingOffset = streamLength ? streamLength - (randomStream->Next() % sizeof buf): 0;
rv = channel->OpenOutputStream(startingOffset, getter_AddRefs(outStream));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
truncate(name, startingOffset) ;
counterStream = new CounterStream(startingOffset);
counterStream->Read(buf, sizeof buf);
nsresult status ;
nsCOMPtr<nsIRandomAccessStore> ras = do_QueryInterface(outStream, &status);
if (NS_FAILED(status)) {
// mState = END_WRITE;
return NS_ERROR_FAILURE;
}
PRIntn offset ;
ras->Tell(&offset) ;
// printf(" offset is %d \n", offset) ;
PRUint32 numWritten;
rv = outStream->Write(buf, sizeof buf, &numWritten);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(numWritten == sizeof buf, "Write() bug?");
streamLength = startingOffset + sizeof buf;
rv = outStream->Close();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't close channel");
delete counterStream;
record->GetStoredContentLength(&len) ;
if(len != streamLength)
printf(" offset = %d is wrong, filesize = %d\n", startingOffset, len) ;
}
/*
rv = record->NewChannel(0, getter_AddRefs(channel));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
startingOffset = 208;
rv = channel->OpenOutputStream(startingOffset, getter_AddRefs(outStream));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
counterStream = new CounterStream(startingOffset);
counterStream->Read(buf, sizeof buf);
nsresult status ;
nsCOMPtr<nsIRandomAccessStore> ras = do_QueryInterface(outStream, &status);
if (NS_FAILED(status)) {
// mState = END_WRITE;
return NS_ERROR_FAILURE;
}
PRIntn offset = 0 ;
ras->Tell(&offset) ;
printf(" offset is %d \n", offset) ;
PRUint32 numWritten;
rv = outStream->Write(buf, sizeof buf, &numWritten);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(numWritten == sizeof buf, "Write() bug?");
streamLength = startingOffset + sizeof buf;
rv = outStream->Close();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't close channel");
delete counterStream;
record->GetStoredContentLength(&len) ;
if(len != streamLength)
printf(" offset = %d is wrong, filesize = %d\n", startingOffset, len) ;
*/
delete randomStream;
counterStream = new CounterStream(0);
TestReadStream(record, counterStream, streamLength);
WaitForEvents();
return NS_OK;
}
// Create entries in the network data cache, using random data for the
// key, the meta-data and the stored content data.
nsresult
FillCache(nsINetDataCache *cache)
{
nsresult rv;
PRBool inCache;
nsCOMPtr<nsINetDataCacheRecord> record;
nsCOMPtr<nsIChannel> channel;
nsCOMPtr<nsIOutputStream> outStream;
char buf[1000];
PRUint32 metaDataLength;
char cacheKey[CACHE_KEY_LENGTH];
char metaData[CACHE_METADATA_LENGTH];
PRUint32 testNum;
char *data;
RandomStream *randomStream;
PRIntervalTime startTime = PR_IntervalNow();
for (testNum = 0; testNum < NUM_CACHE_ENTRIES; testNum++) {
randomStream = new RandomStream(testNum);
randomStream->Read(cacheKey, sizeof cacheKey);
// No entry should be in cache until we add it
rv = cache->Contains(cacheKey, sizeof cacheKey, &inCache);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(!inCache, "nsINetDataCache::Contains error");
rv = cache->GetCachedNetData(cacheKey, sizeof cacheKey, getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't access record via opaque cache key");
// Test nsINetDataCacheRecord::GetRecordID()
rv = record->GetRecordID(&recordID[testNum]);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get Record ID");
// Test nsINetDataCache::GetNumEntries()
PRUint32 numEntries = (PRUint32)-1;
rv = cache->GetNumEntries(&numEntries);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get number of cache entries");
NS_ASSERTION(numEntries == testNum + 1, "GetNumEntries failure");
// Record meta-data should be initially empty
rv = record->GetMetaData(&metaDataLength, &data);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
if ((metaDataLength != 0) || (data != 0))
return NS_ERROR_FAILURE;
// Store random data as meta-data
randomStream->Read(metaData, sizeof metaData);
record->SetMetaData(sizeof metaData, metaData);
rv = record->NewChannel(0, getter_AddRefs(channel));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
rv = channel->OpenOutputStream(0, getter_AddRefs(outStream));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
PRUint32 beforeOccupancy;
rv = cache->GetStorageInUse(&beforeOccupancy);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache occupancy");
int streamLength = randomStream->Next() & 0xffff;
int remaining = streamLength;
while (remaining) {
PRUint32 numWritten;
int amount = PR_MIN(sizeof buf, remaining);
randomStream->Read(buf, amount);
rv = outStream->Write(buf, amount, &numWritten);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(numWritten == (PRUint32)amount, "Write() bug?");
remaining -= amount;
}
outStream->Close();
PRUint32 afterOccupancy;
rv = cache->GetStorageInUse(&afterOccupancy);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache occupancy");
PRUint32 streamLengthInKB = streamLength >> 10;
NS_ASSERTION((afterOccupancy - beforeOccupancy) >= streamLengthInKB,
"nsINetDataCache::GetStorageInUse() is busted");
// *Now* there should be an entry in the cache
rv = cache->Contains(cacheKey, sizeof cacheKey, &inCache);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(inCache, "nsINetDataCache::Contains error");
delete randomStream;
}
PRIntervalTime endTime = PR_IntervalNow();
return NS_OK;
}
nsresult NS_AutoregisterComponents()
{
nsresult rv = nsComponentManager::AutoRegister(nsIComponentManager::NS_Startup,
NULL /* default */);
return rv;
}
PRBool initPref ()
{
nsresult rv;
NS_WITH_SERVICE(nsIPref, prefPtr, kPrefCID, &rv);
if (NS_FAILED(rv))
return false;
nsCOMPtr<nsIFileSpec> fileSpec;
rv = NS_NewFileSpec (getter_AddRefs(fileSpec));
if (NS_FAILED(rv))
return false;
nsCString defaultPrefFile = PR_GetEnv ("MOZILLA_FIVE_HOME");
if (defaultPrefFile.Length())
defaultPrefFile += "/";
else
defaultPrefFile = "./";
defaultPrefFile += "default_prefs.js";
fileSpec->SetUnixStyleFilePath (defaultPrefFile.GetBuffer());
PRBool exists = false;
fileSpec->Exists(&exists);
if (exists)
prefPtr->ReadUserPrefsFrom(fileSpec);
else
return false;
return true;
}
int
main(int argc, char* argv[])
{
initPref() ;
nsresult rv;
nsCOMPtr<nsINetDataCache> cache;
rv = NS_AutoregisterComponents();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't register XPCOM components");
rv = nsComponentManager::CreateInstance(kDiskCacheCID, nsnull,
NS_GET_IID(nsINetDataCache),
getter_AddRefs(cache));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't create memory cache factory");
InitQueue();
PRUnichar* description;
rv = cache->GetDescription(&description);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache description");
nsCAutoString descStr(description);
printf("Testing: %s\n", descStr.GetBuffer());
rv = cache->RemoveAll();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't clear cache");
PRUint32 startOccupancy;
rv = cache->GetStorageInUse(&startOccupancy);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache occupancy");
PRUint32 numEntries = (PRUint32)-1;
rv = cache->GetNumEntries(&numEntries);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get number of cache entries");
NS_ASSERTION(numEntries == 0, "Couldn't clear cache");
rv = FillCache(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't fill cache with random test data");
rv = TestRead(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't read random test data from cache");
rv = TestRecordID(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't index records using record ID");
rv = TestEnumeration(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't successfully enumerate records");
rv = TestTruncation(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't successfully truncate records");
rv = TestOffsetWrites(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't successfully write to records using non-zero offsets");
rv = cache->RemoveAll();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't clear cache");
rv = cache->GetNumEntries(&numEntries);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get number of cache entries");
NS_ASSERTION(numEntries == 0, "Couldn't clear cache");
PRUint32 endOccupancy;
rv = cache->GetStorageInUse(&endOccupancy);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache occupancy");
NS_ASSERTION(startOccupancy == endOccupancy, "Cache occupancy not correctly computed ?");
return 0;
}

View File

@@ -1,48 +0,0 @@
# Generated automatically from Makefile.in by configure.
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
#
DEPTH = ../../..
topsrcdir = @top_srcdir@
VPATH = @srcdir@
srcdir = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = nkcache
LIBRARY_NAME = nkmemcache_s
REQUIRES = nspr dbm
EXPORTS=nsMemCacheCID.h \
nsMemCache.h \
$(NULL)
CPPSRCS = \
nsMemCache.cpp \
nsMemCacheRecord.cpp \
nsMemCacheChannel.cpp \
$(NULL)
# we don't want the shared lib, but we want to force the creation of a
# static lib.
override NO_SHARED_LIB=1
override NO_STATIC_LIB=
include $(topsrcdir)/config/rules.mk

View File

@@ -1,42 +0,0 @@
#!nmake
#
# The contents of this file are subject to the Netscape Public License
# Version 1.0 (the "NPL"); you may not use this file except in
# compliance with the NPL. You may obtain a copy of the NPL at
# http://www.mozilla.org/NPL/
#
# Software distributed under the NPL is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
# for the specific language governing rights and limitations under the
# NPL.
#
# The Initial Developer of this code under the NPL is Netscape
# Communications Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All Rights
# Reserved.
DEPTH=..\..\..
include <$(DEPTH)/config/config.mak>
MODULE = nkcache
LIBRARY_NAME = nkmemcache_s
CPP_OBJS = \
.\$(OBJDIR)\nsMemCache.obj \
.\$(OBJDIR)\nsMemCacheRecord.obj \
.\$(OBJDIR)\nsMemCacheChannel.obj \
$(NULL)
EXPORTS=nsMemCacheCID.h
include <$(DEPTH)\config\rules.mak>
install:: $(LIBRARY)
$(MAKE_INSTALL) $(LIBRARY) $(DIST)\lib
clobber::
rm -rf $(OBJDIR)
rm -f $(DIST)\lib\$(LIBRARY_NAME).lib

View File

@@ -1,334 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
/**
* nsMemCache is the implementation of an in-memory network-data
* cache, used to cache the responses to network retrieval commands.
* Each cache entry may contain both content, e.g. GIF image data, and
* associated metadata, e.g. HTTP headers. Each entry is indexed by
* two different keys: a record id number and an opaque key, which is
* created by the cache manager by combining the URI with a "secondary
* key", e.g. HTTP post data.
*/
#include "nsMemCache.h"
#include "nsMemCacheRecord.h"
#include "nsIGenericFactory.h"
#include "nsString.h"
#include "nsHashtable.h"
#include "nsHashtableEnumerator.h"
#include "nsEnumeratorUtils.h"
PRInt32 nsMemCache::gRecordSerialNumber = 0;
nsMemCache::nsMemCache()
: mNumEntries(0), mOccupancy(0), mEnabled(PR_TRUE),
mHashTable(0)
{
NS_INIT_REFCNT();
}
nsMemCache::~nsMemCache()
{
nsresult rv;
rv = RemoveAll();
NS_ASSERTION(NS_SUCCEEDED(rv) && (mNumEntries == 0),
"Failure to shut down memory cache. "
"Somewhere, someone is holding references to at least one cache record");
delete mHashTable;
}
nsresult
nsMemCache::Init()
{
mHashTable = new nsHashtable(256);
if (!mHashTable)
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
NS_IMPL_ISUPPORTS(nsMemCache, NS_GET_IID(nsINetDataCache))
NS_IMETHODIMP
nsMemCache::GetDescription(PRUnichar * *aDescription)
{
nsAutoString description("Memory Cache");
*aDescription = description.ToNewUnicode();
if (!*aDescription)
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::Contains(const char *aKey, PRUint32 aKeyLength, PRBool *aFound)
{
nsOpaqueKey *opaqueKey = new nsOpaqueKey(aKey, aKeyLength);
if (!opaqueKey)
return NS_ERROR_OUT_OF_MEMORY;
*aFound = mHashTable->Exists(opaqueKey);
delete opaqueKey;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::GetCachedNetData(const char *aKey, PRUint32 aKeyLength,
nsINetDataCacheRecord* *aRecord)
{
nsresult rv;
nsMemCacheRecord* record = 0;
nsOpaqueKey *opaqueKey2 = 0;
nsOpaqueKey *opaqueKey3 = 0;
nsOpaqueKey *opaqueKey;
opaqueKey = new nsOpaqueKey(aKey, aKeyLength);
if (!opaqueKey)
goto out_of_memory;
record = (nsMemCacheRecord*)mHashTable->Get(opaqueKey);
delete opaqueKey;
// No existing cache database entry was found. Create a new one.
// This requires two mappings in the hash table:
// Record ID ==> record
// Opaque key ==> record
if (!record) {
record = new nsMemCacheRecord;
if (!record)
goto out_of_memory;
rv = record->Init(aKey, aKeyLength, ++gRecordSerialNumber, this);
if (NS_FAILED(rv)) goto out_of_memory;
// Index the record by opaque key
opaqueKey2 = new nsOpaqueKey(record->mKey, record->mKeyLength);
if (!opaqueKey2) goto out_of_memory;
mHashTable->Put(opaqueKey2, record);
// Index the record by it's record ID
char *recordIDbytes = NS_REINTERPRET_CAST(char *, &record->mRecordID);
opaqueKey3 = new nsOpaqueKey(recordIDbytes,
sizeof record->mRecordID);
if (!opaqueKey3) {
// Clean up the first record from the hash table
mHashTable->Remove(opaqueKey);
goto out_of_memory;
}
mHashTable->Put(opaqueKey3, record);
// The hash table holds on to the record
record->AddRef();
delete opaqueKey2;
delete opaqueKey3;
mNumEntries++;
}
record->AddRef();
*aRecord = record;
return NS_OK;
out_of_memory:
delete opaqueKey2;
delete opaqueKey3;
delete record;
return NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP
nsMemCache::GetCachedNetDataByID(PRInt32 RecordID,
nsINetDataCacheRecord* *aRecord)
{
nsOpaqueKey opaqueKey(NS_REINTERPRET_CAST(const char *, &RecordID),
sizeof RecordID);
*aRecord = (nsINetDataCacheRecord*)mHashTable->Get(&opaqueKey);
if (*aRecord) {
NS_ADDREF(*aRecord);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_METHOD
nsMemCache::Delete(nsMemCacheRecord* aRecord)
{
nsMemCacheRecord *removedRecord;
char *recordIDbytes = NS_REINTERPRET_CAST(char *, &aRecord->mRecordID);
nsOpaqueKey opaqueRecordIDKey(recordIDbytes,
sizeof aRecord->mRecordID);
removedRecord = (nsMemCacheRecord*)mHashTable->Remove(&opaqueRecordIDKey);
NS_ASSERTION(removedRecord == aRecord, "memory cache database inconsistent");
nsOpaqueKey opaqueKey(aRecord->mKey, aRecord->mKeyLength);
removedRecord = (nsMemCacheRecord*)mHashTable->Remove(&opaqueKey);
NS_ASSERTION(removedRecord == aRecord, "memory cache database inconsistent");
aRecord->Release();
mNumEntries--;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::GetEnabled(PRBool *aEnabled)
{
NS_ENSURE_ARG(aEnabled);
*aEnabled = mEnabled;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::SetEnabled(PRBool aEnabled)
{
mEnabled = aEnabled;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::GetFlags(PRUint32 *aFlags)
{
NS_ENSURE_ARG(aFlags);
*aFlags = MEMORY_CACHE;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::GetNumEntries(PRUint32 *aNumEntries)
{
NS_ENSURE_ARG(aNumEntries);
*aNumEntries = mNumEntries;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::GetMaxEntries(PRUint32 *aMaxEntries)
{
NS_ENSURE_ARG(aMaxEntries);
*aMaxEntries = MEM_CACHE_MAX_ENTRIES;
return NS_OK;
}
static NS_METHOD
HashEntryConverter(nsHashKey *aKey, void *aValue,
void *unused, nsISupports **retval)
{
nsMemCacheRecord *record;
nsOpaqueKey *opaqueKey;
record = (nsMemCacheRecord*)aValue;
opaqueKey = (nsOpaqueKey*)aKey;
// Hash table keys that index cache entries by their record ID
// shouldn't be enumerated.
if ((opaqueKey->GetKeyLength() == sizeof(PRInt32))) {
#ifdef DEBUG
PRInt32 recordID;
record->GetRecordID(&recordID);
NS_ASSERTION(*((PRInt32*)opaqueKey->GetKey()) == recordID,
"Key has incorrect key length");
#endif
return NS_ERROR_FAILURE;
}
NS_IF_ADDREF(record);
*retval = NS_STATIC_CAST(nsISupports*, record);
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::NewCacheEntryIterator(nsISimpleEnumerator* *aIterator)
{
nsCOMPtr<nsIEnumerator> iterator;
NS_ENSURE_ARG(aIterator);
NS_NewHashtableEnumerator(mHashTable, HashEntryConverter,
mHashTable, getter_AddRefs(iterator));
return NS_NewAdapterEnumerator(aIterator, iterator);
}
NS_IMETHODIMP
nsMemCache::GetNextCache(nsINetDataCache* *aNextCache)
{
NS_ENSURE_ARG(aNextCache);
*aNextCache = mNextCache;
NS_ADDREF(*aNextCache);
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::SetNextCache(nsINetDataCache* aNextCache)
{
mNextCache = aNextCache;
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::GetStorageInUse(PRUint32 *aStorageInUse)
{
NS_ENSURE_ARG(aStorageInUse);
// Convert from bytes to KB
*aStorageInUse = (mOccupancy >> 10);
return NS_OK;
}
NS_IMETHODIMP
nsMemCache::RemoveAll(void)
{
PRBool failed;
nsCOMPtr<nsISimpleEnumerator> iterator;
nsCOMPtr<nsISupports> recordSupports;
nsCOMPtr<nsINetDataCacheRecord> record;
nsresult rv;
failed = PR_FALSE;
rv = NewCacheEntryIterator(getter_AddRefs(iterator));
if (NS_FAILED(rv))
return rv;
PRBool notDone;
while (1) {
rv = iterator->HasMoreElements(&notDone);
if (NS_FAILED(rv)) return rv;
if (!notDone)
break;
iterator->GetNext(getter_AddRefs(recordSupports));
record = do_QueryInterface(recordSupports);
recordSupports = 0;
PRUint32 bytesUsed;
record->GetStoredContentLength(&bytesUsed);
rv = record->Delete();
if (NS_FAILED(rv)) {
failed = PR_TRUE;
continue;
}
mOccupancy -= bytesUsed;
}
if (failed)
return NS_ERROR_FAILURE;
return NS_OK;
}

View File

@@ -1,83 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
/**
* nsMemCache is the implementation of an in-memory network-data
* cache, used to cache the responses to network retrieval commands.
* Each cache entry may contain both content, e.g. GIF image data, and
* associated metadata, e.g. HTTP headers. Each entry is indexed by
* two different keys: a record id number and an opaque key, which is
* created by the cache manager by combining the URI with a "secondary
* key", e.g. HTTP post data.
*/
#ifndef _nsMemCache_h_
#define _nsMemCache_h_
#include "nsINetDataCache.h"
// Maximum number of URIs that may be resident in the cache
#define MEM_CACHE_MAX_ENTRIES 1000
#define MEM_CACHE_SEGMENT_SIZE (1 << 12)
#define MEM_CACHE_MAX_ENTRY_SIZE (1 << 20)
class nsHashtable;
class nsMemCacheRecord;
class nsMemCache : public nsINetDataCache
{
public:
nsMemCache();
virtual ~nsMemCache();
nsresult Init();
// nsISupports methods
NS_DECL_ISUPPORTS
// nsINetDataCache methods
NS_DECL_NSINETDATACACHE
// Factory
static NS_METHOD nsMemCacheConstructor(nsISupports *aOuter, REFNSIID aIID,
void **aResult);
protected:
PRUint32 mNumEntries;
PRUint32 mOccupancy; // Memory used, in bytes
PRBool mEnabled; // If false, bypass mem cache
nsINetDataCache* mNextCache;
// Mapping from either opaque key or record ID to nsMemCacheRecord
nsHashtable* mHashTable;
// Used to assign record ID's
static PRInt32 gRecordSerialNumber;
NS_METHOD Delete(nsMemCacheRecord* aRecord);
friend class nsMemCacheRecord;
friend class nsMemCacheChannel;
};
#endif // _nsMemCache_h_

View File

@@ -1,36 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
// XPCOM Class ID for the network data in-memory cache
#ifndef nsMEMCACHECID_h__
#define nsMEMCACHECID_h__
// {e4710560-7de2-11d3-90cb-0040056a906e}
#define NS_MEM_CACHE_FACTORY_CID \
{ \
0xe4710560, \
0x7de2, \
0x11d3, \
{0x90, 0xcb, 0x00, 0x40, 0x05, 0x6a, 0x90, 0x6e} \
}
#endif // nsMEMCACHECID_h__

View File

@@ -1,462 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
#include "nsMemCache.h"
#include "nsMemCacheChannel.h"
#include "nsIStreamListener.h"
#include "nsIChannel.h"
#include "nsIStorageStream.h"
#include "nsIOutputStream.h"
#include "nsIServiceManager.h"
#include "nsIEventQueueService.h"
#include "nsNetUtil.h"
#include "nsILoadGroup.h"
static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
static NS_DEFINE_CID(kEventQueueService, NS_EVENTQUEUESERVICE_CID);
NS_IMPL_ISUPPORTS(nsMemCacheChannel, NS_GET_IID(nsIChannel))
void
nsMemCacheChannel::NotifyStorageInUse(PRInt32 aBytesUsed)
{
mRecord->mCache->mOccupancy += aBytesUsed;
}
/**
* This class acts as an adaptor around a synchronous input stream to add async
* read capabilities. It adds methods for initiating, suspending, resuming and
* cancelling async reads.
*/
class AsyncReadStreamAdaptor : public nsIInputStream {
public:
AsyncReadStreamAdaptor(nsMemCacheChannel* aChannel, nsIInputStream *aSyncStream):
mSyncStream(aSyncStream), mDataAvailCursor(0),
mRemaining(0), mAvailable(0), mChannel(aChannel), mAborted(PR_FALSE), mSuspended(PR_FALSE)
{
NS_INIT_REFCNT();
NS_ADDREF(mChannel);
}
virtual ~AsyncReadStreamAdaptor() {
mChannel->mAsyncReadStream = 0;
NS_RELEASE(mChannel);
}
NS_DECL_ISUPPORTS
nsresult
IsPending(PRBool* aIsPending) {
*aIsPending = (mRemaining != 0) && !mAborted;
return NS_OK;
}
nsresult
Cancel(void) {
mAborted = PR_TRUE;
return mStreamListener->OnStopRequest(mChannel, mContext, NS_BINDING_ABORTED, nsnull);
}
nsresult
Suspend(void) { mSuspended = PR_TRUE; return NS_OK; }
nsresult
Resume(void) {
if (!mSuspended)
return NS_ERROR_FAILURE;
mSuspended = PR_FALSE;
return NextListenerEvent();
}
NS_IMETHOD
Available(PRUint32 *aNumBytes) { return mAvailable; }
NS_IMETHOD
Read(char* aBuf, PRUint32 aCount, PRUint32 *aBytesRead) {
if (mAborted)
return NS_ERROR_ABORT;
*aBytesRead = 0;
aCount = PR_MIN(aCount, mAvailable);
nsresult rv = mSyncStream->Read(aBuf, aCount, aBytesRead);
mAvailable -= *aBytesRead;
if (NS_FAILED(rv) && (rv != NS_BASE_STREAM_WOULD_BLOCK)) {
Fail();
return rv;
}
if (!mSuspended && !mAvailable) {
rv = NextListenerEvent();
if (NS_FAILED(rv)) {
Fail();
return rv;
}
}
return NS_OK;
}
NS_IMETHOD
Close() {
nsresult rv = mSyncStream->Close();
mSyncStream = 0;
mContext = 0;
mStreamListener = 0;
return rv;
}
nsresult
AsyncRead(PRUint32 aStartPosition, PRInt32 aReadCount,
nsISupports* aContext, nsIStreamListener* aListener) {
nsresult rv;
nsIEventQueue *eventQ;
mContext = aContext;
mStreamListener = aListener;
mRemaining = aReadCount;
NS_WITH_SERVICE(nsIIOService, serv, kIOServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
NS_WITH_SERVICE(nsIEventQueueService, eventQService, kEventQueueService, &rv);
if (NS_FAILED(rv)) return rv;
rv = eventQService->GetThreadEventQueue(PR_CurrentThread(), &eventQ);
if (NS_FAILED(rv)) return rv;
rv = NS_NewAsyncStreamListener(aListener, eventQ,
getter_AddRefs(mStreamListener));
NS_RELEASE(eventQ);
if (NS_FAILED(rv)) return rv;
rv = mStreamListener->OnStartRequest(mChannel, aContext);
if (NS_FAILED(rv)) return rv;
return NextListenerEvent();
}
protected:
nsresult
Fail(void) {
mAborted = PR_TRUE;
return mStreamListener->OnStopRequest(mChannel, mContext, NS_BINDING_FAILED, nsnull);
}
nsresult
NextListenerEvent() {
PRUint32 available;
nsresult rv = mSyncStream->Available(&available);
if (NS_FAILED(rv)) return rv;
available -= mAvailable;
available = PR_MIN(available, mRemaining);
if (available) {
PRUint32 size = PR_MIN(available, MEM_CACHE_SEGMENT_SIZE);
rv = mStreamListener->OnDataAvailable(mChannel, mContext, this,
mDataAvailCursor, size);
mDataAvailCursor += size;
mRemaining -= size;
mAvailable += size;
return rv;
} else {
rv = mStreamListener->OnStopRequest(mChannel, mContext, NS_OK, nsnull);
AsyncReadStreamAdaptor* thisAlias = this;
NS_RELEASE(thisAlias);
return rv;
}
}
private:
nsCOMPtr<nsISupports> mContext; // Opaque context passed to AsyncRead()
nsCOMPtr<nsIStreamListener> mStreamListener; // Stream listener that has been proxied
nsCOMPtr<nsIInputStream> mSyncStream; // Underlying synchronous stream that is
// being converted to an async stream
PRUint32 mDataAvailCursor;
PRUint32 mRemaining; // Size of AsyncRead request less bytes for
// consumer OnDataAvailable's that were fired
PRUint32 mAvailable; // Number of bytes for which OnDataAvailable fired
nsMemCacheChannel* mChannel; // Associated memory cache channel, strong link
// but can not use nsCOMPtr
PRBool mAborted; // Abort() has been called
PRBool mSuspended; // Suspend() has been called
};
NS_IMPL_ISUPPORTS(AsyncReadStreamAdaptor, NS_GET_IID(nsIInputStream))
// The only purpose of this output stream wrapper is to adjust the cache's
// overall occupancy as new data flows into the cache entry.
class MemCacheWriteStreamWrapper : public nsIOutputStream {
public:
MemCacheWriteStreamWrapper(nsMemCacheChannel* aChannel, nsIOutputStream *aBaseStream):
mBaseStream(aBaseStream), mChannel(aChannel)
{
NS_INIT_REFCNT();
NS_ADDREF(mChannel);
}
virtual ~MemCacheWriteStreamWrapper() { NS_RELEASE(mChannel); };
static nsresult
Create(nsMemCacheChannel* aChannel, nsIOutputStream *aBaseStream, nsIOutputStream* *aWrapper) {
MemCacheWriteStreamWrapper *wrapper =
new MemCacheWriteStreamWrapper(aChannel, aBaseStream);
if (!wrapper) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(wrapper);
*aWrapper = wrapper;
return NS_OK;
}
NS_DECL_ISUPPORTS
NS_IMETHOD
Write(const char *aBuffer, PRUint32 aCount, PRUint32 *aNumWritten) {
*aNumWritten = 0;
nsresult rv = mBaseStream->Write(aBuffer, aCount, aNumWritten);
mChannel->NotifyStorageInUse(*aNumWritten);
return rv;
}
NS_IMETHOD
Flush() { return mBaseStream->Flush(); }
NS_IMETHOD
Close() { return mBaseStream->Close(); }
private:
nsCOMPtr<nsIOutputStream> mBaseStream;
nsMemCacheChannel* mChannel;
};
NS_IMPL_ISUPPORTS(MemCacheWriteStreamWrapper, NS_GET_IID(nsIOutputStream))
nsMemCacheChannel::nsMemCacheChannel(nsMemCacheRecord *aRecord, nsILoadGroup *aLoadGroup)
: mRecord(aRecord)
{
NS_INIT_REFCNT();
mRecord->mNumChannels++;
}
nsMemCacheChannel::~nsMemCacheChannel()
{
mRecord->mNumChannels--;
}
NS_IMETHODIMP
nsMemCacheChannel::IsPending(PRBool* aIsPending)
{
*aIsPending = PR_FALSE;
if (!mAsyncReadStream)
return NS_OK;
return mAsyncReadStream->IsPending(aIsPending);
}
NS_IMETHODIMP
nsMemCacheChannel::Cancel(void)
{
if (!mAsyncReadStream)
return NS_ERROR_FAILURE;
return mAsyncReadStream->Cancel();
}
NS_IMETHODIMP
nsMemCacheChannel::Suspend(void)
{
if (!mAsyncReadStream)
return NS_ERROR_FAILURE;
return mAsyncReadStream->Suspend();
}
NS_IMETHODIMP
nsMemCacheChannel::Resume(void)
{
if (!mAsyncReadStream)
return NS_ERROR_FAILURE;
return mAsyncReadStream->Resume();
}
NS_IMETHODIMP
nsMemCacheChannel::GetOriginalURI(nsIURI * *aURI)
{
// Not required
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::GetURI(nsIURI * *aURI)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::OpenInputStream(PRUint32 aStartPosition, PRInt32 aReadCount,
nsIInputStream* *aResult)
{
nsresult rv;
NS_ENSURE_ARG(aResult);
if (mInputStream)
return NS_ERROR_NOT_AVAILABLE;
rv = mRecord->mStorageStream->NewInputStream(aStartPosition, getter_AddRefs(mInputStream));
*aResult = mInputStream;
NS_ADDREF(*aResult);
return rv;
}
NS_IMETHODIMP
nsMemCacheChannel::OpenOutputStream(PRUint32 startPosition, nsIOutputStream* *aResult)
{
nsresult rv;
NS_ENSURE_ARG(aResult);
nsCOMPtr<nsIOutputStream> outputStream;
PRUint32 oldLength;
mRecord->mStorageStream->GetLength(&oldLength);
rv = mRecord->mStorageStream->GetOutputStream(startPosition, getter_AddRefs(outputStream));
if (NS_FAILED(rv)) return rv;
if (startPosition < oldLength)
NotifyStorageInUse(startPosition - oldLength);
return MemCacheWriteStreamWrapper::Create(this, outputStream, aResult);
}
NS_IMETHODIMP
nsMemCacheChannel::AsyncOpen(nsIStreamObserver *observer, nsISupports *ctxt)
{
// Not required
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::AsyncRead(PRUint32 aStartPosition, PRInt32 aReadCount,
nsISupports *aContext, nsIStreamListener *aListener)
{
nsCOMPtr<nsIInputStream> inputStream;
nsresult rv = OpenInputStream(aStartPosition, aReadCount, getter_AddRefs(inputStream));
if (NS_FAILED(rv)) return rv;
AsyncReadStreamAdaptor *asyncReadStreamAdaptor;
asyncReadStreamAdaptor = new AsyncReadStreamAdaptor(this, inputStream);
if (!asyncReadStreamAdaptor)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(asyncReadStreamAdaptor);
mAsyncReadStream = asyncReadStreamAdaptor;
rv = asyncReadStreamAdaptor->AsyncRead(aStartPosition, aReadCount, aContext, aListener);
if (NS_FAILED(rv))
delete asyncReadStreamAdaptor;
return rv;
}
NS_IMETHODIMP
nsMemCacheChannel::AsyncWrite(nsIInputStream *fromStream, PRUint32 startPosition,
PRInt32 writeCount, nsISupports *ctxt,
nsIStreamObserver *observer)
{
// Not required to be implemented
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::GetLoadAttributes(nsLoadFlags *aLoadAttributes)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::SetLoadAttributes(nsLoadFlags aLoadAttributes)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::GetContentType(char* *aContentType)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::GetContentLength(PRInt32 *aContentLength)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::GetOwner(nsISupports* *aOwner)
{
*aOwner = mOwner.get();
NS_IF_ADDREF(*aOwner);
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheChannel::SetOwner(nsISupports* aOwner)
{
// Not required to be implemented, since it is implemented by cache manager
mOwner = aOwner;
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
{
// Not required to be implemented, since it is implemented by cache manager
NS_ASSERTION(0, "nsMemCacheChannel method unexpectedly called");
return NS_ERROR_NOT_IMPLEMENTED;
}

View File

@@ -1,61 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
#ifndef _nsMemCacheChannel_h_
#define _nsMemCacheChannel_h_
#include "nsMemCacheRecord.h"
#include "nsIChannel.h"
#include "nsIInputStream.h"
#include "nsCOMPtr.h"
class AsyncReadStreamAdaptor;
class nsMemCacheChannel : public nsIChannel
{
public:
// Constructors and Destructor
nsMemCacheChannel(nsMemCacheRecord *aRecord, nsILoadGroup *aLoadGroup);
virtual ~nsMemCacheChannel();
// Declare nsISupports methods
NS_DECL_ISUPPORTS
// Declare nsIRequest methods
NS_DECL_NSIREQUEST
// Declare nsIChannel methods
NS_DECL_NSICHANNEL
protected:
void NotifyStorageInUse(PRInt32 aBytesUsed);
nsCOMPtr<nsMemCacheRecord> mRecord;
nsCOMPtr<nsIInputStream> mInputStream;
nsCOMPtr<nsISupports> mOwner;
AsyncReadStreamAdaptor* mAsyncReadStream; // non-owning pointer
friend class MemCacheWriteStreamWrapper;
friend class AsyncReadStreamAdaptor;
};
#endif // _nsMemCacheChannel_h_

View File

@@ -1,164 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
#include "nsMemCache.h"
#include "nsMemCacheRecord.h"
#include "nsMemCacheChannel.h"
#include "nsIAllocator.h"
#include "nsStorageStream.h"
static NS_DEFINE_IID(kINetDataCacheRecord, NS_INETDATACACHERECORD_IID);
nsMemCacheRecord::nsMemCacheRecord()
: mKey(0), mKeyLength(0), mMetaData(0), mMetaDataLength(0), mNumChannels(0)
{
NS_INIT_REFCNT();
}
nsMemCacheRecord::~nsMemCacheRecord()
{
if (mMetaData)
delete[] mMetaData;
if (mKey)
delete[] mKey;
}
NS_IMPL_ISUPPORTS(nsMemCacheRecord, NS_GET_IID(nsINetDataCacheRecord))
NS_IMETHODIMP
nsMemCacheRecord::GetKey(PRUint32 *aLength, char **aResult)
{
NS_ENSURE_ARG(aResult);
*aResult = (char *)nsAllocator::Alloc(mKeyLength);
if (!*aResult)
return NS_ERROR_OUT_OF_MEMORY;
memcpy(*aResult, mKey, mKeyLength);
*aLength = mKeyLength;
return NS_OK;
}
nsresult
nsMemCacheRecord::Init(const char *aKey, PRUint32 aKeyLength,
PRUint32 aRecordID, nsMemCache *aCache)
{
nsresult rv;
NS_ASSERTION(!mKey, "Memory cache record key set multiple times");
rv = NS_NewStorageStream(MEM_CACHE_SEGMENT_SIZE, MEM_CACHE_MAX_ENTRY_SIZE,
getter_AddRefs(mStorageStream));
if (NS_FAILED(rv)) return rv;
mKey = new char[aKeyLength];
if (!mKey)
return NS_ERROR_OUT_OF_MEMORY;
memcpy(mKey, aKey, aKeyLength);
mKeyLength = aKeyLength;
mRecordID = aRecordID;
mCache = aCache;
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheRecord::GetRecordID(PRInt32 *aRecordID)
{
NS_ENSURE_ARG(aRecordID);
*aRecordID = mRecordID;
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheRecord::GetMetaData(PRUint32 *aLength, char **aResult)
{
NS_ENSURE_ARG(aResult);
*aResult = 0;
if (mMetaDataLength) {
*aResult = (char*)nsAllocator::Alloc(mMetaDataLength);
if (!*aResult)
return NS_ERROR_OUT_OF_MEMORY;
memcpy(*aResult, mMetaData, mMetaDataLength);
}
*aLength = mMetaDataLength;
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheRecord::SetMetaData(PRUint32 aLength, const char *aData)
{
if (mMetaData)
delete[] mMetaData;
mMetaData = new char[aLength];
if (!mMetaData)
return NS_ERROR_OUT_OF_MEMORY;
memcpy(mMetaData, aData, aLength);
mMetaDataLength = aLength;
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheRecord::GetStoredContentLength(PRUint32 *aStoredContentLength)
{
NS_ENSURE_ARG(aStoredContentLength);
return mStorageStream->GetLength(aStoredContentLength);
}
NS_IMETHODIMP
nsMemCacheRecord::SetStoredContentLength(PRUint32 aStoredContentLength)
{
PRUint32 before, after;
mStorageStream->GetLength(&before);
nsresult rv = mStorageStream->SetLength(aStoredContentLength);
if (NS_FAILED(rv)) return rv;
mStorageStream->GetLength(&after);
mCache->mOccupancy -= (before - after);
return NS_OK;
}
NS_IMETHODIMP
nsMemCacheRecord::Delete(void)
{
if (mNumChannels)
return NS_ERROR_NOT_AVAILABLE;
return mCache->Delete(this);
}
NS_IMETHODIMP
nsMemCacheRecord::GetFilename(nsIFileSpec* *aFilename)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMemCacheRecord::NewChannel(nsILoadGroup *aLoadGroup, nsIChannel* *aResult)
{
NS_ENSURE_ARG(aResult);
nsMemCacheChannel* channel = new nsMemCacheChannel(this, aLoadGroup);
if (!channel)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(channel);
*aResult = NS_STATIC_CAST(nsIChannel*, channel);
return NS_OK;
}

View File

@@ -1,65 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
*/
#ifndef _nsMemCacheRecord_h_
#define _nsMemCacheRecord_h_
#include "nsINetDataCacheRecord.h"
#include "nsIStorageStream.h"
#include "nsCOMPtr.h"
class nsMemCache;
class nsMemCacheRecord : public nsINetDataCacheRecord
{
public:
// Declare interface methods
NS_DECL_ISUPPORTS
NS_DECL_NSINETDATACACHERECORD
protected:
// Constructors and Destructor
nsMemCacheRecord();
virtual ~nsMemCacheRecord();
nsresult Init(const char *aKey, PRUint32 aKeyLength,
PRUint32 aRecordID, nsMemCache *aCache);
char* mKey; // opaque database key for this record
PRUint32 mKeyLength; // length, in bytes, of mKey
PRInt32 mRecordID; // An alternate key for this record
char* mMetaData; // opaque URI metadata
PRUint32 mMetaDataLength; // length, in bytes, of mMetaData
nsMemCache* mCache; // weak pointer to the cache database
// that this record inhabits
nsCOMPtr<nsIStorageStream> mStorageStream;
PRUint32 mNumChannels; // Count un-Release'ed nsIChannels
friend class nsMemCache;
friend class nsMemCacheChannel;
};
#endif // _nsMemCacheRecord_h_

View File

@@ -1,55 +0,0 @@
#
# 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):
#
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = nkcache
LIBRARY_NAME = nkcachemgr_s
REQUIRES = nspr
EXPORTS = \
nsCacheManager.h \
$(NULL)
CPPSRCS = \
nsCacheManager.cpp \
nsCachedNetData.cpp \
nsReplacementPolicy.cpp \
nsCacheEntryChannel.cpp \
$(NULL)
LOCAL_INCLUDES = -I$(srcdir)/../public -I$(srcdir)/../include
EXTRA_LIBS = $(NSPR_LIBS)
# we don't want the shared lib, but we want to force the creation of a
# static lib.
override NO_SHARED_LIB=1
override NO_STATIC_LIB=
include $(topsrcdir)/config/rules.mk

View File

@@ -1,45 +0,0 @@
#!gmake
#
# 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):
DEPTH=..\..\..
include <$(DEPTH)/config/config.mak>
MODULE = nkcache
LIBRARY_NAME = nkcachemgr_s
CPP_OBJS = \
.\$(OBJDIR)\nsCacheManager.obj \
.\$(OBJDIR)\nsCachedNetData.obj \
.\$(OBJDIR)\nsReplacementPolicy.obj \
.\$(OBJDIR)\nsCacheEntryChannel.obj \
$(NULL)
include <$(DEPTH)\config\rules.mak>
install:: $(LIBRARY)
$(MAKE_INSTALL) $(LIBRARY) $(DIST)\lib
clobber::
rm -rf $(OBJDIR)
rm -f $(DIST)\lib\$(LIBRARY_NAME).lib

View File

@@ -1,261 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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 Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#include "nsCacheManager.h"
#include "nsCacheEntryChannel.h"
#include "nsIOutputStream.h"
#include "nsIIOService.h"
#include "nsIServiceManager.h"
#include "nsIStreamListener.h"
nsCacheEntryChannel::nsCacheEntryChannel(nsCachedNetData* aCacheEntry, nsIChannel* aChannel,
nsILoadGroup* aLoadGroup):
nsChannelProxy(aChannel), mCacheEntry(aCacheEntry), mLoadGroup(aLoadGroup), mLoadAttributes(0)
{
NS_ASSERTION(aCacheEntry->mChannelCount < 0xFF, "Overflowed channel counter");
mCacheEntry->mChannelCount++;
NS_INIT_REFCNT();
}
nsCacheEntryChannel::~nsCacheEntryChannel()
{
mCacheEntry->mChannelCount--;
}
NS_IMPL_ISUPPORTS3(nsCacheEntryChannel, nsISupports, nsIChannel, nsIRequest)
// A proxy for nsIOutputStream
class CacheOutputStream : public nsIOutputStream {
public:
CacheOutputStream(nsIOutputStream *aOutputStream, nsCachedNetData *aCacheEntry):
mOutputStream(aOutputStream), mCacheEntry(aCacheEntry), mStartTime(PR_Now())
{ NS_INIT_REFCNT(); }
virtual ~CacheOutputStream() {
mCacheEntry->NoteDownloadTime(mStartTime, PR_Now());
mCacheEntry->ClearFlag(nsCachedNetData::UPDATE_IN_PROGRESS);
}
NS_DECL_ISUPPORTS
NS_IMETHOD Close() {
return mOutputStream->Close();
}
NS_IMETHOD Flush() { return mOutputStream->Flush(); }
NS_IMETHOD
Write(const char *aBuf, PRUint32 aCount, PRUint32 *aActualBytes) {
nsresult rv;
*aActualBytes = 0;
rv = mOutputStream->Write(aBuf, aCount, aActualBytes);
mCacheEntry->mLogicalLength += *aActualBytes;
if (NS_FAILED(rv)) return rv;
nsCacheManager::LimitCacheSize();
return rv;
}
protected:
nsCOMPtr<nsIOutputStream> mOutputStream;
nsCOMPtr<nsCachedNetData> mCacheEntry;
// Time at which stream was opened
PRTime mStartTime;
};
NS_IMPL_ISUPPORTS(CacheOutputStream, NS_GET_IID(nsIOutputStream))
NS_IMETHODIMP
nsCacheEntryChannel::OpenOutputStream(PRUint32 aStartPosition, nsIOutputStream* *aOutputStream)
{
nsresult rv;
nsCOMPtr<nsIOutputStream> baseOutputStream;
rv = mChannel->OpenOutputStream(aStartPosition, getter_AddRefs(baseOutputStream));
if (NS_FAILED(rv)) return rv;
mCacheEntry->NoteUpdate();
mCacheEntry->NoteAccess();
mCacheEntry->mLogicalLength = aStartPosition;
*aOutputStream = new CacheOutputStream(baseOutputStream, mCacheEntry);
if (!*aOutputStream)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*aOutputStream);
return NS_OK;
}
NS_IMETHODIMP
nsCacheEntryChannel::OpenInputStream(PRUint32 aStartPosition, PRInt32 aReadCount,
nsIInputStream* *aInputStream)
{
mCacheEntry->NoteAccess();
return mChannel->OpenInputStream(aStartPosition, aReadCount, aInputStream);
}
class CacheManagerStreamListener: public nsIStreamListener {
public:
CacheManagerStreamListener(nsIStreamListener *aListener,
nsILoadGroup *aLoadGroup, nsIChannel *aChannel):
mListener(aListener), mLoadGroup(aLoadGroup), mChannel(aChannel)
{ NS_INIT_REFCNT(); }
virtual ~CacheManagerStreamListener() {}
private:
NS_DECL_ISUPPORTS
NS_IMETHOD
OnDataAvailable(nsIChannel *channel, nsISupports *aContext,
nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count) {
return mListener->OnDataAvailable(mChannel, aContext, inStr, sourceOffset, count);
}
NS_IMETHOD
OnStartRequest(nsIChannel *channel, nsISupports *aContext) {
if (mLoadGroup)
mLoadGroup->AddChannel(mChannel, aContext);
return mListener->OnStartRequest(mChannel, aContext);
}
NS_IMETHOD
OnStopRequest(nsIChannel *channel, nsISupports *aContext,
nsresult status, const PRUnichar *errorMsg) {
nsresult rv;
rv = mListener->OnStopRequest(mChannel, aContext, status, errorMsg);
if (mLoadGroup)
mLoadGroup->RemoveChannel(mChannel, aContext, status, errorMsg);
return rv;
}
private:
nsCOMPtr<nsIStreamListener> mListener;
nsCOMPtr<nsILoadGroup> mLoadGroup;
nsCOMPtr<nsIChannel> mChannel;
};
NS_IMPL_ISUPPORTS2(CacheManagerStreamListener, nsIStreamListener, nsIStreamObserver)
NS_IMETHODIMP
nsCacheEntryChannel::AsyncRead(PRUint32 aStartPosition, PRInt32 aReadCount,
nsISupports *aContext, nsIStreamListener *aListener)
{
nsresult rv;
mCacheEntry->NoteAccess();
nsCOMPtr<nsIStreamListener> headListener;
if (mLoadGroup) {
mLoadGroup->GetDefaultLoadAttributes(&mLoadAttributes);
// Create a load group "proxy" listener...
nsCOMPtr<nsILoadGroupListenerFactory> factory;
rv = mLoadGroup->GetGroupListenerFactory(getter_AddRefs(factory));
if (NS_SUCCEEDED(rv) && factory) {
rv = factory->CreateLoadGroupListener(aListener,
getter_AddRefs(headListener));
if (NS_FAILED(rv)) return rv;
}
} else {
headListener = aListener;
}
CacheManagerStreamListener* cacheManagerStreamListener;
nsIChannel *channelForListener;
channelForListener = mProxyChannel ? mProxyChannel.get() : NS_STATIC_CAST(nsIChannel*, this);
cacheManagerStreamListener =
new CacheManagerStreamListener(headListener, mLoadGroup, channelForListener);
if (!cacheManagerStreamListener) return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(cacheManagerStreamListener);
rv = mChannel->AsyncRead(aStartPosition, aReadCount, aContext,
cacheManagerStreamListener);
NS_RELEASE(cacheManagerStreamListener);
return rv;
}
// No async writes allowed to the cache yet
NS_IMETHODIMP
nsCacheEntryChannel::AsyncWrite(nsIInputStream *aFromStream, PRUint32 aStartPosition,
PRInt32 aWriteCount, nsISupports *aContext,
nsIStreamObserver *aObserver)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsCacheEntryChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
{
*aLoadGroup = mLoadGroup;
return NS_OK;
}
NS_IMETHODIMP
nsCacheEntryChannel::GetLoadAttributes(nsLoadFlags *aLoadAttributes)
{
*aLoadAttributes = mLoadAttributes;
return NS_OK;
}
NS_IMETHODIMP
nsCacheEntryChannel::SetLoadAttributes(nsLoadFlags aLoadAttributes)
{
mLoadAttributes = aLoadAttributes;
return NS_OK;
}
static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
NS_IMETHODIMP
nsCacheEntryChannel::GetURI(nsIURI * *aURI)
{
char* spec;
nsresult rv;
rv = mCacheEntry->GetUriSpec(&spec);
if (NS_FAILED(rv)) return rv;
NS_WITH_SERVICE(nsIIOService, serv, kIOServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
rv = serv->NewURI(spec, 0, aURI);
nsAllocator::Free(spec);
return rv;
}
NS_IMETHODIMP
nsCacheEntryChannel::GetOriginalURI(nsIURI * *aURI)
{
// FIXME - should return original URI passed into NewChannel() ?
return NS_ERROR_NOT_IMPLEMENTED;
}

View File

@@ -1,82 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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 Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#ifndef _nsCacheEntryChannel_h_
#define _nsCacheEntryChannel_h_
#include "nsCOMPtr.h"
#include "nsIChannel.h"
#include "nsCachedNetData.h"
#include "nsILoadGroup.h"
class nsIStreamListener;
// A proxy for an nsIChannel, useful when only a few nsIChannel
// methods must be overridden
class nsChannelProxy : public nsIChannel {
public:
NS_FORWARD_NSICHANNEL(mChannel->)
NS_FORWARD_NSIREQUEST(mChannel->)
protected:
nsChannelProxy(nsIChannel* aChannel):mChannel(aChannel) {};
virtual ~nsChannelProxy() {};
nsCOMPtr<nsIChannel> mChannel;
};
// Override several nsIChannel methods so that they interact with the cache manager
class nsCacheEntryChannel : public nsChannelProxy {
public:
NS_DECL_ISUPPORTS
NS_IMETHOD OpenOutputStream(PRUint32 aStartPosition, nsIOutputStream* *aOutputStream);
NS_IMETHOD OpenInputStream(PRUint32 aStartPosition, PRInt32 aReadCount,
nsIInputStream* *aInputStream);
NS_IMETHOD AsyncRead(PRUint32 aStartPosition, PRInt32 aReadCount,
nsISupports *aContext, nsIStreamListener *aListener);
NS_IMETHOD AsyncWrite(nsIInputStream *aFromStream, PRUint32 aStartPosition,
PRInt32 aWriteCount, nsISupports *aContext,
nsIStreamObserver *aObserver);
NS_IMETHOD GetLoadAttributes(nsLoadFlags *aLoadAttributes);
NS_IMETHOD SetLoadAttributes(nsLoadFlags aLoadAttributes);
NS_IMETHOD GetLoadGroup(nsILoadGroup* *aLoadGroup);
NS_IMETHOD GetURI(nsIURI * *aURI);
NS_IMETHOD GetOriginalURI(nsIURI * *aURI);
protected:
nsCacheEntryChannel(nsCachedNetData* aCacheEntry, nsIChannel* aChannel, nsILoadGroup* aLoadGroup);
virtual ~nsCacheEntryChannel();
friend class nsCachedNetData;
private:
nsCOMPtr<nsCachedNetData> mCacheEntry;
nsCOMPtr<nsILoadGroup> mLoadGroup;
nsCOMPtr<nsIChannel> mProxyChannel;
nsLoadFlags mLoadAttributes;
};
#endif // _nsCacheEntryChannel_h_

View File

@@ -1,497 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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 Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#include "nsINetDataCache.h"
#include "nsCacheManager.h"
#include "nsCachedNetData.h"
#include "nsReplacementPolicy.h"
#include "nsString.h"
#include "nsIURI.h"
#include "nsHashtable.h"
#include "nsIComponentManager.h"
#include "nsINetDataDiskCache.h"
// Limit the number of entries in the cache to conserve memory space
// in the nsReplacementPolicy code
#define MAX_MEM_CACHE_ENTRIES 800
#define MAX_DISK_CACHE_ENTRIES 3200
// Cache capacities in MB, overridable via APIs
#define DEFAULT_MEMORY_CACHE_CAPACITY 1024
#define DEFAULT_DISK_CACHE_CAPACITY 10000
#define CACHE_HIGH_WATER_MARK(capacity) ((PRUint32)(0.98 * (capacity)))
#define CACHE_LOW_WATER_MARK(capacity) ((PRUint32)(0.97 * (capacity)))
nsCacheManager* gCacheManager = 0;
NS_IMPL_ISUPPORTS(nsCacheManager, NS_GET_IID(nsINetDataCacheManager))
nsCacheManager::nsCacheManager()
: mActiveCacheRecords(0),
mDiskCacheCapacity(DEFAULT_DISK_CACHE_CAPACITY),
mMemCacheCapacity(DEFAULT_MEMORY_CACHE_CAPACITY)
{
NS_ASSERTION(!gCacheManager, "Multiple cache managers created");
gCacheManager = this;
NS_INIT_REFCNT();
}
nsCacheManager::~nsCacheManager()
{
gCacheManager = 0;
delete mActiveCacheRecords;
delete mMemSpaceManager;
delete mDiskSpaceManager;
}
nsresult
nsCacheManager::Init()
{
nsresult rv;
mActiveCacheRecords = new nsHashtable(64);
if (!mActiveCacheRecords)
return NS_ERROR_OUT_OF_MEMORY;
// Instantiate the memory cache component
rv = nsComponentManager::CreateInstance(NS_NETWORK_MEMORY_CACHE_PROGID,
nsnull,
NS_GET_IID(nsINetDataCache),
getter_AddRefs(mMemCache));
if (NS_FAILED(rv))
return rv;
rv = nsComponentManager::CreateInstance(NS_NETWORK_FLAT_CACHE_PROGID,
nsnull,
NS_GET_IID(nsINetDataCache),
getter_AddRefs(mFlatCache));
if (NS_FAILED(rv)) {
// For now, we don't require a flat cache module to be present
if (rv != NS_ERROR_FACTORY_NOT_REGISTERED)
return rv;
}
#ifdef FILE_CACHE_IS_READY
// Instantiate the file cache component
rv = nsComponentManager::CreateInstance(NS_NETWORK_FILE_CACHE_PROGID,
nsnull,
NS_GET_IID(nsINetDataCache),
getter_AddRefs(mFileCache));
if (NS_FAILED(rv)) {
NS_WARNING("No disk cache present");
}
#endif
// Set up linked list of caches in search order
mCacheSearchChain = mMemCache;
if (mFlatCache) {
mMemCache->SetNextCache(mFlatCache);
mFlatCache->SetNextCache(mFileCache);
} else {
mMemCache->SetNextCache(mFileCache);
}
// TODO - Load any extension caches here
// Initialize replacement policy for memory cache module
mMemSpaceManager = new nsReplacementPolicy;
if (!mMemSpaceManager)
return NS_ERROR_OUT_OF_MEMORY;
rv = mMemSpaceManager->Init(MAX_MEM_CACHE_ENTRIES);
if (NS_FAILED(rv)) return rv;
rv = mMemSpaceManager->AddCache(mMemCache);
// Initialize replacement policy for disk cache modules (file
// cache and flat cache)
mDiskSpaceManager = new nsReplacementPolicy;
if (!mDiskSpaceManager)
return NS_ERROR_OUT_OF_MEMORY;
rv = mDiskSpaceManager->Init(MAX_DISK_CACHE_ENTRIES);
if (NS_FAILED(rv)) return rv;
if (mFileCache) {
rv = mDiskSpaceManager->AddCache(mFileCache);
if (NS_FAILED(rv)) return rv;
}
if (mFlatCache) {
rv = mDiskSpaceManager->AddCache(mFlatCache);
if (NS_FAILED(rv)) return rv;
}
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::GetCachedNetData(const char *aUriSpec, const char *aSecondaryKey,
PRUint32 aSecondaryKeyLength,
PRUint32 aFlags, nsICachedNetData* *aResult)
{
nsCachedNetData *cachedData;
nsresult rv;
nsINetDataCache *cache;
nsReplacementPolicy *spaceManager;
if (aFlags & CACHE_AS_FILE) {
cache = mFileCache;
spaceManager = mDiskSpaceManager;
// Ensure that cache is initialized
if (mDiskCacheCapacity == (PRUint32)-1)
return NS_ERROR_NOT_AVAILABLE;
} else if ((aFlags & BYPASS_PERSISTENT_CACHE) ||
(!mFileCache && !mFlatCache) || !mDiskCacheCapacity) {
cache = mMemCache;
spaceManager = mMemSpaceManager;
} else {
cache = mFlatCache ? mFlatCache : mFileCache;
spaceManager = mDiskSpaceManager;
}
// Construct the cache key by appending the secondary key to the URI spec
nsCAutoString cacheKey(aUriSpec);
// Insert NUL at end of URI spec
cacheKey += '\0';
if (aSecondaryKey)
cacheKey.Append(aSecondaryKey, aSecondaryKeyLength);
nsStringKey key(cacheKey);
cachedData = (nsCachedNetData*)mActiveCacheRecords->Get(&key);
// There is no existing instance of nsCachedNetData for this URL.
// Make one from the corresponding record in the cache module.
if (cachedData) {
NS_ASSERTION(cache == cachedData->mCache,
"Cannot yet handle simultaneously active requests for the "
"same URL using different caches");
NS_ADDREF(cachedData);
} else {
rv = spaceManager->GetCachedNetData(cacheKey.GetBuffer(), cacheKey.Length(),
cache, &cachedData);
if (NS_FAILED(rv)) return rv;
mActiveCacheRecords->Put(&key, cachedData);
}
*aResult = cachedData;
return NS_OK;
}
// Remove this cache entry from the list of active ones
nsresult
nsCacheManager::NoteDormant(nsCachedNetData* aEntry)
{
nsresult rv;
PRUint32 keyLength;
char* key;
nsCOMPtr<nsINetDataCacheRecord> record;
nsCachedNetData* deletedEntry;
rv = aEntry->GetRecord(getter_AddRefs(record));
if (NS_FAILED(rv)) return rv;
rv = record->GetKey(&keyLength, &key);
if (NS_FAILED(rv)) return rv;
nsStringKey hashTableKey(nsCString(key, keyLength));
deletedEntry = (nsCachedNetData*)gCacheManager->mActiveCacheRecords->Remove(&hashTableKey);
// NS_ASSERTION(deletedEntry == aEntry, "Hash table inconsistency");
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::Contains(const char *aUriSpec, const char *aSecondaryKey,
PRUint32 aSecondaryKeyLength,
PRUint32 aFlags, PRBool *aResult)
{
nsINetDataCache *cache;
nsReplacementPolicy *spaceManager;
nsCachedNetData *cachedData;
if (aFlags & CACHE_AS_FILE) {
cache = mFileCache;
spaceManager = mDiskSpaceManager;
} else if ((aFlags & BYPASS_PERSISTENT_CACHE) ||
(!mFileCache && !mFlatCache) || !mDiskCacheCapacity) {
cache = mMemCache;
spaceManager = mMemSpaceManager;
} else {
cache = mFlatCache ? mFlatCache : mFileCache;
spaceManager = mDiskSpaceManager;
}
// Construct the cache key by appending the secondary key to the URI spec
nsCAutoString cacheKey(aUriSpec);
// Insert NUL between URI spec and secondary key
cacheKey += '\0';
cacheKey.Append(aSecondaryKey, aSecondaryKeyLength);
// Locate the record using (URI + secondary key)
nsStringKey key(cacheKey);
cachedData = (nsCachedNetData*)mActiveCacheRecords->Get(&key);
if (cachedData && (cache == cachedData->mCache)) {
*aResult = PR_TRUE;
return NS_OK;
} else {
// No active cache entry, see if there is a dormant one
return cache->Contains(cacheKey.GetBuffer(), cacheKey.Length(), aResult);
}
}
NS_IMETHODIMP
nsCacheManager::GetNumEntries(PRUint32 *aNumEntries)
{
nsresult rv;
nsCOMPtr<nsISimpleEnumerator> iterator;
nsCOMPtr<nsISupports> cacheSupports;
nsCOMPtr<nsINetDataCache> cache;
PRUint32 totalEntries = 0;
rv = NewCacheModuleIterator(getter_AddRefs(iterator));
if (NS_FAILED(rv)) return rv;
while (1) {
PRBool notDone;
rv = iterator->HasMoreElements(&notDone);
if (NS_FAILED(rv)) return rv;
if (!notDone)
break;
iterator->GetNext(getter_AddRefs(cacheSupports));
cache = do_QueryInterface(cacheSupports);
PRUint32 numEntries;
rv = cache->GetNumEntries(&numEntries);
if (NS_FAILED(rv)) return rv;
totalEntries += numEntries;
}
*aNumEntries = totalEntries;
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::NewCacheEntryIterator(nsISimpleEnumerator* *aResult)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
class CacheEnumerator : public nsISimpleEnumerator
{
public:
CacheEnumerator(nsINetDataCache* aFirstCache):mCache(aFirstCache)
{ NS_INIT_REFCNT(); }
virtual ~CacheEnumerator() {};
NS_DECL_ISUPPORTS
NS_IMETHODIMP
HasMoreElements(PRBool* aMoreElements) {
*aMoreElements = (mCache != 0);
return NS_OK;
}
NS_IMETHODIMP
GetNext(nsISupports* *aSupports) {
*aSupports = mCache;
if (!mCache)
return NS_ERROR_FAILURE;
NS_ADDREF(*aSupports);
nsCOMPtr<nsINetDataCache> nextCache;
nsresult rv = mCache->GetNextCache(getter_AddRefs(nextCache));
mCache = nextCache;
return rv;
}
private:
nsCOMPtr<nsINetDataCache> mCache;
};
NS_IMPL_ISUPPORTS(CacheEnumerator, NS_GET_IID(nsISimpleEnumerator))
NS_IMETHODIMP
nsCacheManager::NewCacheModuleIterator(nsISimpleEnumerator* *aResult)
{
*aResult = new CacheEnumerator(mCacheSearchChain);
if (!*aResult)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*aResult);
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::RemoveAll(void)
{
nsresult rv, result;
nsCOMPtr<nsISimpleEnumerator> iterator;
nsCOMPtr<nsINetDataCache> cache;
nsCOMPtr<nsISupports> iSupports;
result = NS_OK;
rv = NewCacheModuleIterator(getter_AddRefs(iterator));
if (NS_FAILED(rv)) return rv;
while (1) {
PRBool notDone;
rv = iterator->HasMoreElements(&notDone);
if (NS_FAILED(rv)) return rv;
if (!notDone)
break;
iterator->GetNext(getter_AddRefs(iSupports));
cache = do_QueryInterface(iSupports);
PRUint32 cacheFlags;
rv = cache->GetFlags(&cacheFlags);
if (NS_FAILED(rv)) return rv;
if ((cacheFlags & nsINetDataCache::READ_ONLY) == 0) {
rv = cache->RemoveAll();
if (NS_FAILED(rv))
result = rv;
}
}
return result;
}
nsresult
nsCacheManager::LimitMemCacheSize()
{
nsresult rv;
nsReplacementPolicy* spaceManager;
NS_ASSERTION(gCacheManager, "No cache manager");
spaceManager = gCacheManager->mMemSpaceManager;
PRUint32 occupancy;
rv = spaceManager->GetStorageInUse(&occupancy);
if (NS_FAILED(rv)) return rv;
PRUint32 memCacheCapacity = gCacheManager->mMemCacheCapacity;
if (occupancy > CACHE_HIGH_WATER_MARK(memCacheCapacity))
return spaceManager->Evict(CACHE_LOW_WATER_MARK(memCacheCapacity));
return NS_OK;
}
nsresult
nsCacheManager::LimitDiskCacheSize()
{
nsresult rv;
nsReplacementPolicy* spaceManager;
NS_ASSERTION(gCacheManager, "No cache manager");
spaceManager = gCacheManager->mDiskSpaceManager;
PRUint32 occupancy;
rv = spaceManager->GetStorageInUse(&occupancy);
if (NS_FAILED(rv)) return rv;
PRUint32 diskCacheCapacity = gCacheManager->mDiskCacheCapacity;
if (occupancy > CACHE_HIGH_WATER_MARK(diskCacheCapacity))
return spaceManager->Evict(CACHE_LOW_WATER_MARK(diskCacheCapacity));
return NS_OK;
}
nsresult
nsCacheManager::LimitCacheSize()
{
nsresult rv;
rv = LimitDiskCacheSize();
if (NS_FAILED(rv)) return rv;
rv = LimitMemCacheSize();
if (NS_FAILED(rv)) return rv;
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::SetMemCacheCapacity(PRUint32 aCapacity)
{
mMemCacheCapacity = aCapacity;
LimitCacheSize();
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::GetMemCacheCapacity(PRUint32* aCapacity)
{
NS_ENSURE_ARG_POINTER(aCapacity);
*aCapacity = mMemCacheCapacity;
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::SetDiskCacheCapacity(PRUint32 aCapacity)
{
mDiskCacheCapacity = aCapacity;
LimitCacheSize();
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::GetDiskCacheCapacity(PRUint32* aCapacity)
{
NS_ENSURE_ARG_POINTER(aCapacity);
*aCapacity = mDiskCacheCapacity;
return NS_OK;
}
NS_IMETHODIMP
nsCacheManager::SetDiskCacheFolder(nsIFileSpec* aFolder)
{
NS_ENSURE_ARG(aFolder);
if (!mFileCache)
return NS_ERROR_NOT_AVAILABLE;
nsCOMPtr<nsINetDataDiskCache> fileCache;
fileCache = do_QueryInterface(mFileCache);
return fileCache->SetDiskCacheFolder(aFolder);
}
NS_IMETHODIMP
nsCacheManager::GetDiskCacheFolder(nsIFileSpec* *aFolder)
{
NS_ENSURE_ARG(aFolder);
if (!mFileCache)
return NS_ERROR_NOT_AVAILABLE;
nsCOMPtr<nsINetDataDiskCache> fileCache;
fileCache = do_QueryInterface(mFileCache);
return fileCache->GetDiskCacheFolder(aFolder);
}

View File

@@ -1,100 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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 Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#ifndef _nsCacheManager_h_
#define _nsCacheManager_h_
// 2030f0b0-9567-11d3-90d3-0040056a906e
#define NS_CACHE_MANAGER_CID \
{ \
0x2030f0b0, \
0x9567, \
0x11d3, \
{0x90, 0xd3, 0x00, 0x40, 0x05, 0x6a, 0x90, 0x6e} \
}
#include "nsINetDataCacheManager.h"
#include "nsINetDataCache.h"
#include "nsCOMPtr.h"
class nsHashtable;
class nsReplacementPolicy;
class nsCachedNetData;
class nsCacheManager : public nsINetDataCacheManager {
public:
nsCacheManager();
virtual ~nsCacheManager();
NS_METHOD Init();
// nsISupports methods
NS_DECL_ISUPPORTS
// nsINetDataCacheManager methods
NS_DECL_NSINETDATACACHEMANAGER
private:
// Mapping from cache key to nsCachedNetData, but only for those cache
// entries with external references, i.e. those referred to outside the
// cache manager
nsHashtable* mActiveCacheRecords;
// Memory cache
nsCOMPtr<nsINetDataCache> mMemCache;
// Flat-file database cache; All content aggregated into single disk file
nsCOMPtr<nsINetDataCache> mFlatCache;
// stream-as-file cache
nsCOMPtr<nsINetDataCache> mFileCache;
// Unified replacement policy for flat-cache and file-cache
nsReplacementPolicy* mDiskSpaceManager;
// Replacement policy for memory cache
nsReplacementPolicy* mMemSpaceManager;
// List of caches in search order
nsINetDataCache* mCacheSearchChain;
// Combined file/flat cache capacity, in KB
PRUint32 mDiskCacheCapacity;
// Memory cache capacity, in KB
PRUint32 mMemCacheCapacity;
protected:
static nsresult NoteDormant(nsCachedNetData* aEntry);
static nsresult LimitCacheSize();
static nsresult LimitMemCacheSize();
static nsresult LimitDiskCacheSize();
friend class nsCachedNetData;
friend class CacheOutputStream;
};
#endif // _nsCacheManager_h_

File diff suppressed because it is too large Load Diff

View File

@@ -1,242 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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 Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#ifndef _nsCachedNetData_h_
#define _nsCachedNetData_h_
#include "nsICachedNetData.h"
#include "nsCOMPtr.h"
#include "nsINetDataCacheRecord.h"
class nsINetDataCache;
class nsIStreamAsFileObserver;
class nsIStreamAsFile;
class nsIArena;
class StreamAsFileObserverClosure;
class CacheMetaData;
// Number of recent access times recorded
#define MAX_K 3
/**
* FIXME - add comment. There are a lot of these data structures resident in
* memory, so be careful about adding members unnecessarily.
*/
class nsCachedNetData : public nsICachedNetData {
public:
NS_DECL_ISUPPORTS
// nsICachedNetData methods
NS_DECL_NSICACHEDNETDATA
NS_METHOD Init(nsINetDataCacheRecord *aRecord, nsINetDataCache *aCache);
protected:
// Bits for mFlags, below
typedef enum {
DIRTY = 1 << 0, // Cache entry data needs to be flushed to database
// ==== Flags that can be set by the protocol handler ====
ALLOW_PARTIAL = 1 << 1, // Protocol handler supports partial fetching
UPDATE_IN_PROGRESS = 1 << 2, // Protocol handler now modifying cache data
// ==== Cache-entry state flags. At most one of these flags can be set ====
TRUNCATED_CONTENT = 1 << 4, // Entry contains valid content, but it has
// been truncated by cache manager
// A previously-used cache entry, which has been purged of all cached
// content and protocol-private data. This cache entry can be refilled
// with new content or it may be retained in this vestigial state
// because the usage statistics it contains will be used by the
// replacement policy if the same URI is ever cached again.
VESTIGIAL = 1 << 5,
// ==== Memory usage status bits. At most one of these flags can be set ====
RECYCLED = 1 << 8, // Previously associated database record has
// been deleted; This cache entry is available
// for recycling.
DORMANT = 1 << 9, // No references to this cache entry, except by
// the cache manager itself
// ==== Setter bits ====
LAST_MODIFIED_KNOWN = 1 <<12, // Protocol handler called SetLastModifiedTime()
EXPIRATION_KNOWN = 1 <<13, // Protocol handler called SetExpirationTime()
STALE_TIME_KNOWN = 1 <<14, // Protocol handler called SetStaleTime()
// ==== Useful flag combinations ====
// Cache entry not eligible for eviction
UNEVICTABLE = VESTIGIAL | RECYCLED | UPDATE_IN_PROGRESS,
// State flags that are in-memory only, i.e. not persistent
TRANSIENT_FLAGS = DIRTY | RECYCLED | DORMANT
} Flag;
PRBool GetFlag(Flag aFlag) { return (mFlags & aFlag) != 0; }
nsresult GetFlag(PRBool *aResult, Flag aFlag) { *aResult = GetFlag(aFlag); return NS_OK; }
// Set a boolean flag for the cache entry
nsresult SetFlag(PRBool aValue, Flag aFlag);
nsresult SetFlag(Flag aFlag) { return SetFlag(PR_TRUE, aFlag); }
nsresult ClearFlag(Flag aFlag) { return SetFlag(PR_FALSE, aFlag); }
void ComputeProfit(PRUint32 aCurrentTime);
static int Compare(const void *a, const void *b, void *unused);
void NoteAccess();
void NoteUpdate();
// Get underlying raw cache database record.
nsresult GetRecord(nsINetDataCacheRecord* *aRecord);
nsresult GetRecordID(PRInt32 *aRecordID);
nsresult Evict(PRUint32 aTruncatedContentLength);
nsresult GetFileSpec(nsIFileSpec* *aFileSpec);
void NoteDownloadTime(PRTime start, PRTime end);
// placement new for arena-allocation
void *operator new (size_t aSize, nsIArena *aArena);
friend class nsReplacementPolicy;
friend class nsCacheManager;
friend class StreamAsFile;
friend class nsCacheEntryChannel;
friend class CacheOutputStream;
friend class InterceptStreamListener;
private:
nsCachedNetData() {};
virtual ~nsCachedNetData() {};
// Initialize internal fields of this nsCachedNetData instance from the
// underlying raw cache database record.
nsresult Deserialize(PRBool aDeserializeFlags);
// Notify stream-as-file observers about change in cache entry status
nsresult Notify(PRUint32 aMessage, nsresult aError);
// Add/Remove stream-as-file observers
nsresult AddObserver(nsIStreamAsFile *aStreamAsFile, nsIStreamAsFileObserver* aObserver);
nsresult RemoveObserver(nsIStreamAsFileObserver* aObserver);
// Mark cache entry to indicate a write out to the cache database is required
void SetDirty() { mFlags |= DIRTY; }
nsresult Resurrect(nsINetDataCacheRecord *aRecord);
nsresult CommitFlags();
CacheMetaData* FindTaggedMetaData(const char* aTag, PRBool aCreate);
private:
// List of nsIStreamAsFileObserver's that will receive notification events
// when the cache manager or a client desires to delete/truncate a cache
// entry file.
StreamAsFileObserverClosure* mObservers;
// Protocol-specific meta-data, opaque to the cache manager
CacheMetaData *mMetaData;
// Next in chain for a single bucket in the replacement policy hash table
// that maps from record ID to nsCachedNetData
nsCachedNetData* mNext;
// See flag bits, above
// NOTE: 16 bit member is combined with members below for
// struct packing efficiency. Do not change order of members!
PRUint16 mFlags;
protected:
// Number of nsCacheEntryChannels referring to this record
PRUint8 mChannelCount;
// Below members are statistics kept per cache-entry, used to decide how
// profitable it will be to evict a record from the cache relative to other
// existing records. Note: times are measured in *seconds* since the
// 1/1/70 epoch, same as a unix time_t.
// Number of accesses for this cache record
// NOTE: 8 bit member is combined with members above for
// struct packing efficiency. Do not change order of members!
PRUint8 mNumAccesses;
// A reference to the underlying, raw cache database record, either as a
// pointer to an in-memory object or as a database record identifier
union {
nsINetDataCacheRecord* mRecord;
// Database record ID of associated cache record. See
// nsINetDataCache::GetRecordByID().
PRInt32 mRecordID;
};
// Weak link to parent cache
nsINetDataCache* mCache;
// Length of stored content, which may be less than storage consumed if
// compression is used
PRUint32 mLogicalLength;
// Most recent cache entry access times, used to compute access frequency
PRUint32 mAccessTime[MAX_K];
// We use modification time of the original document for replacement policy
// computations, i.e. to compute a document's age, but if we don't know it,
// we use the time that the document was last written to the cache.
union {
// Document modification time, if known.
PRUint32 mLastModifiedTime;
// Time of last cache update for this doc
PRUint32 mLastUpdateTime;
};
union {
// Time until which document is fresh, i.e. does not have to be validated
// with server and, therefore, data in cache is guaranteed usable
PRUint32 mExpirationTime;
// Heuristic time at which cached document is likely to be out-of-date
// with respect to canonical copy on server. Used for cache replacement
// policy, not for validation.
PRUint32 mStaleTime;
};
// Download time per byte, measure roughly in units of KB/s
float mDownloadRate;
// Heuristic estimate of cache entry future benefits, based on above values
float mProfit;
};
#endif // _nsCachedNetData_h_

View File

@@ -1,666 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* 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 Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#include "nsReplacementPolicy.h"
#include "nsCachedNetData.h"
#include "nsQuickSort.h"
#include "nsIAllocator.h"
#include "nsIEnumerator.h"
#include "prtime.h"
#include "prbit.h"
#include "nsCOMPtr.h"
#include <math.h>
// Constant used to estimate frequency of access to a document based on size
#define CACHE_CONST_B 1.35
// A cache whose space is managed by this replacement policy
class nsReplacementPolicy::CacheInfo {
public:
CacheInfo(nsINetDataCache* aCache):mCache(aCache),mNext(0) {}
nsINetDataCache* mCache;
CacheInfo* mNext;
};
nsReplacementPolicy::nsReplacementPolicy()
: mRankedEntries(0), mCaches(0), mRecordsRemovedSinceLastRanking(0),
mNumEntries(0), mCapacityRankedEntriesArray(0), mLastRankTime(0) {}
nsReplacementPolicy::~nsReplacementPolicy()
{
if (mRankedEntries)
nsAllocator::Free(mRankedEntries);
if (mMapRecordIdToEntry)
nsAllocator::Free(mMapRecordIdToEntry);
}
nsresult
nsReplacementPolicy::Init(PRUint32 aMaxCacheEntries)
{
nsresult rv;
rv = NS_NewHeapArena(getter_AddRefs(mArena), sizeof(nsCachedNetData) * 32);
if (NS_FAILED(rv)) return rv;
mMaxEntries = aMaxCacheEntries;
mHashArrayLength = PR_CeilingLog2(aMaxCacheEntries) >> 3;
size_t numBytes = mHashArrayLength * sizeof(*mMapRecordIdToEntry);
mMapRecordIdToEntry = (nsCachedNetData**)nsAllocator::Alloc(numBytes);
if (!mMapRecordIdToEntry)
return NS_ERROR_OUT_OF_MEMORY;
nsCRT::zero(mMapRecordIdToEntry, numBytes);
return NS_OK;
}
nsresult
nsReplacementPolicy::AddCache(nsINetDataCache *aCache)
{
CacheInfo *cacheInfo = new CacheInfo(aCache);
if (!cacheInfo)
return NS_ERROR_OUT_OF_MEMORY;
cacheInfo->mNext = mCaches;
mCaches = cacheInfo;
return NS_OK;
}
PRUint32
nsReplacementPolicy::HashRecordID(PRInt32 aRecordID)
{
return ((aRecordID >> 16) ^ aRecordID) & (mHashArrayLength - 1);
}
nsCachedNetData*
nsReplacementPolicy::FindCacheEntryByRecordID(PRInt32 aRecordID, nsINetDataCache *aCache)
{
nsresult rv;
nsCachedNetData* cacheEntry;
PRUint32 bucket = HashRecordID(aRecordID);
cacheEntry = mMapRecordIdToEntry[bucket];
for (;cacheEntry; cacheEntry = cacheEntry->mNext) {
PRInt32 recordID;
rv = cacheEntry->GetRecordID(&recordID);
if (NS_FAILED(rv))
continue;
if ((recordID == aRecordID) && (cacheEntry->mCache == aCache))
return cacheEntry;
}
return 0;
}
// Add a cache entry to the hash table that maps record ID to entries
void
nsReplacementPolicy::AddCacheEntry(nsCachedNetData* aCacheEntry, PRInt32 aRecordID)
{
nsCachedNetData** cacheEntryp;
PRUint32 bucket = HashRecordID(aRecordID);
cacheEntryp = &mMapRecordIdToEntry[bucket];
while (*cacheEntryp)
cacheEntryp = &(*cacheEntryp)->mNext;
*cacheEntryp = aCacheEntry;
aCacheEntry->mNext = 0;
}
// Delete a cache entry from the hash table that maps record ID to entries
nsresult
nsReplacementPolicy::DeleteCacheEntry(nsCachedNetData* aCacheEntry)
{
nsresult rv;
PRInt32 recordID;
rv = aCacheEntry->GetRecordID(&recordID);
if (NS_FAILED(rv)) return rv;
PRUint32 bucket = HashRecordID(recordID);
nsCachedNetData** cacheEntryp;
cacheEntryp = &mMapRecordIdToEntry[bucket];
while (*cacheEntryp) {
if (*cacheEntryp == aCacheEntry) {
*cacheEntryp = aCacheEntry->mNext;
return NS_OK;
}
cacheEntryp = &(*cacheEntryp)->mNext;
}
NS_ASSERTION(0, "hash table inconsistency");
return NS_ERROR_FAILURE;
}
nsresult
nsReplacementPolicy::AddAllRecordsInCache(nsINetDataCache *aCache)
{
nsresult rv;
nsCOMPtr<nsISimpleEnumerator> iterator;
nsCOMPtr<nsISupports> iSupports;
nsCOMPtr<nsINetDataCacheRecord> record;
rv = aCache->NewCacheEntryIterator(getter_AddRefs(iterator));
if (!NS_SUCCEEDED(rv)) return rv;
while (1) {
PRBool notDone;
rv = iterator->HasMoreElements(&notDone);
if (NS_FAILED(rv)) return rv;
if (!notDone)
break;
rv = iterator->GetNext(getter_AddRefs(iSupports));
if (!NS_SUCCEEDED(rv)) return rv;
record = do_QueryInterface(iSupports);
rv = AssociateCacheEntryWithRecord(record, aCache, 0);
if (!NS_SUCCEEDED(rv)) return rv;
}
return NS_OK;
}
// Get current time and convert to seconds since the epoch
static PRUint32
now32()
{
double nowFP;
PRInt64 now64 = PR_Now();
LL_L2D(nowFP, now64);
PRUint32 now = (PRUint32)(nowFP * 1e-6);
return now;
}
void
nsCachedNetData::NoteDownloadTime(PRTime start, PRTime end)
{
double startFP, endFP, rate, duration;
LL_L2D(startFP, start);
LL_L2D(endFP, end);
duration = endFP - startFP;
// If the data arrives so fast that it can not be timed due to the clock
// granularity, assume a data arrival duration of 10 ms
if (!duration)
duration = 10000;
// Compute download rate in kB/s
rate = mLogicalLength / (duration * (1e-6 * 1024.0));
if (mDownloadRate) {
// Exponentially smooth download rate
const double alpha = 0.5;
mDownloadRate = (float)(mDownloadRate * alpha + rate * (1.0 - alpha));
} else {
mDownloadRate = (float)rate;
}
}
// 1 hour
#define MIN_HALFLIFE (60 * 60)
// 1 week
#define TYPICAL_HALFLIFE (7 * 24 * 60 * 60)
/**
* Estimate the profit that would be lost if the given cache entry was evicted
* from the cache. Profit is defined as the future expected download delay per
* byte of cached content. The profit computation is made based on projected
* frequency of access, prior download performance and a heuristic staleness
* criteria. The technique used is a variation of that described in the
* following paper:
*
* "A Case for Delay-Conscious Caching of Web Documents"
* http://www.bell-labs.com/user/rvingral/www97.html
*
* Briefly, expected profit is:
*
* (projected frequency of access) * (download time per byte) * (probability freshness)
*/
void
nsCachedNetData::ComputeProfit(PRUint32 aNow)
{
PRUint32 K, now;
if (aNow)
now = aNow;
else
now = now32();
K = PR_MIN(MAX_K, mNumAccesses);
if (!K) {
mProfit = 0;
return;
}
// Compute time, in seconds, since k'th most recent access
double timeSinceKthAccess = now - mAccessTime[K - 1];
if (timeSinceKthAccess <= 0.0) // Sanity check
timeSinceKthAccess = 1.0;
// Estimate frequency of future document access based on past
// access frequency
double frequencyAccess = K / timeSinceKthAccess;
// If we don't have much historical data on access frequency
// use a heuristic based on document size as an estimate
if (mLogicalLength) {
if (K == 1) {
frequencyAccess /= pow(mLogicalLength, CACHE_CONST_B);
} else if (K == 2) {
frequencyAccess /= pow(mLogicalLength, CACHE_CONST_B / 2);
}
}
// Estimate likelihood that data in cache is fresh, i.e.
// that it corresponds to the document on the server
double probabilityFreshness;
PRInt32 halfLife, age, docTime;
PRBool potentiallyStale;
docTime = GetFlag(LAST_MODIFIED_KNOWN) ? mLastModifiedTime : mLastUpdateTime;
age = now - docTime;
probabilityFreshness = 1.0; // Optimistic
if (GetFlag(EXPIRATION_KNOWN)) {
potentiallyStale = now > mExpirationTime;
halfLife = mExpirationTime - mLastModifiedTime;
} else if (GetFlag(STALE_TIME_KNOWN)) {
potentiallyStale = PR_TRUE;
halfLife = mStaleTime - docTime;
} else {
potentiallyStale = PR_TRUE;
halfLife = TYPICAL_HALFLIFE;
}
if (potentiallyStale) {
if (halfLife < MIN_HALFLIFE)
halfLife = MIN_HALFLIFE;
probabilityFreshness = pow(0.5, (double)age / (double)halfLife);
}
mProfit = (float)(frequencyAccess * probabilityFreshness);
if (mDownloadRate)
mProfit /= mDownloadRate;
}
// Number of entries to grow mRankedEntries array when it's full
#define STATS_GROWTH_INCREMENT 256
// Sorting predicate for NS_Quicksort
int
nsCachedNetData::Compare(const void *a, const void *b, void *unused)
{
nsCachedNetData* entryA = *(nsCachedNetData**)a;
nsCachedNetData* entryB = *(nsCachedNetData**)b;
// Percolate deleted or empty entries to the end of the mRankedEntries
// array, so that they can be recycled.
if (!entryA || entryA->GetFlag(RECYCLED)) {
if (!entryB || entryB->GetFlag(RECYCLED))
return 0;
else
return +1;
}
if (!entryB || entryB->GetFlag(RECYCLED))
return -1;
// Evicted entries (those with no content data) and active entries (those
// currently being updated) are collected towards the end of the sorted
// array just prior to the deleted cache entries, since evicted entries
// can't be re-evicted.
if (entryA->GetFlag(UPDATE_IN_PROGRESS)) {
if (entryB->GetFlag(UPDATE_IN_PROGRESS))
return 0;
else
return +1;
}
if (entryB->GetFlag(UPDATE_IN_PROGRESS))
return -1;
PRUint16 Ka = PR_MIN(MAX_K, entryA->mNumAccesses);
PRUint16 Kb = PR_MIN(MAX_K, entryB->mNumAccesses);
// Order cache entries by the number of times they've been accessed
if (Ka < Kb)
return -1;
if (Ka > Kb)
return +1;
/*
* Among records that have been accessed an equal number of times, order
* them by profit.
*/
if (entryA->mProfit > entryB->mProfit)
return +1;
if (entryA->mProfit < entryB->mProfit)
return -1;
return 0;
}
/**
* Rank cache entries in terms of their elegibility for eviction.
*/
nsresult
nsReplacementPolicy::RankRecords()
{
PRUint32 i, now;
// Add all cache records if this is the first ranking
if (!mLastRankTime) {
nsresult rv;
CacheInfo *cacheInfo;
cacheInfo = mCaches;
while (cacheInfo) {
rv = AddAllRecordsInCache(cacheInfo->mCache);
if (NS_FAILED(rv)) return rv;
cacheInfo = cacheInfo->mNext;
}
}
// Get current time and convert to seconds since the epoch
now = now32();
// Recompute profit for every known cache record, except deleted ones
for (i = 0; i < mNumEntries; i++) {
nsCachedNetData* entry = mRankedEntries[i];
if (entry && !entry->GetFlag(nsCachedNetData::RECYCLED))
entry->ComputeProfit(now);
}
NS_QuickSort(mRankedEntries, mNumEntries, sizeof *mRankedEntries,
nsCachedNetData::Compare, 0);
mNumEntries -= mRecordsRemovedSinceLastRanking;
mRecordsRemovedSinceLastRanking = 0;
mLastRankTime = now;
return NS_OK;
}
// A heuristic policy to avoid the cost of re-ranking cache records by
// profitability every single time space must be made available in the cache.
void
nsReplacementPolicy::MaybeRerankRecords()
{
// Rank at most once per minute
PRUint32 now = now32();
if ((now - mLastRankTime) >= 60)
RankRecords();
}
void
nsReplacementPolicy::CompactRankedEntriesArray()
{
if (mRecordsRemovedSinceLastRanking || !mLastRankTime)
RankRecords();
}
nsresult
nsReplacementPolicy::CheckForTooManyCacheEntries()
{
if (mCapacityRankedEntriesArray == mMaxEntries) {
return DeleteOneEntry(0);
} else {
nsresult rv;
CacheInfo *cacheInfo;
cacheInfo = mCaches;
while (cacheInfo) {
PRUint32 numEntries, maxEntries;
rv = cacheInfo->mCache->GetNumEntries(&numEntries);
if (NS_FAILED(rv)) return rv;
rv = cacheInfo->mCache->GetMaxEntries(&maxEntries);
if (NS_FAILED(rv)) return rv;
if (numEntries == maxEntries)
return DeleteOneEntry(cacheInfo->mCache);
cacheInfo = cacheInfo->mNext;
}
}
return NS_OK;
}
/**
* Create a new association between a low-level cache database record and a
* cache entry. Add the entry to the set of entries eligible for eviction from
* the cache. This would typically be done when the cache entry is created.
*/
nsresult
nsReplacementPolicy::AssociateCacheEntryWithRecord(nsINetDataCacheRecord *aRecord,
nsINetDataCache* aCache,
nsCachedNetData** aResult)
{
nsCachedNetData* cacheEntry;
nsresult rv;
// First, see if the record is already known to the replacement policy
PRInt32 recordID;
rv = aRecord->GetRecordID(&recordID);
if (NS_FAILED(rv)) return rv;
cacheEntry = FindCacheEntryByRecordID(recordID, aCache);
if (cacheEntry) {
if (aResult) {
if (cacheEntry->GetFlag(nsCachedNetData::DORMANT))
cacheEntry->Resurrect(aRecord);
NS_ADDREF(cacheEntry);
*aResult = cacheEntry;
}
return NS_OK;
}
// Compact the array of cache entry statistics, so that free entries appear
// at the end, for possible reuse.
if (mNumEntries && (mNumEntries == mCapacityRankedEntriesArray))
CompactRankedEntriesArray();
// If compaction doesn't yield available entries in the
// mRankedEntries array, then extend the array.
if (mNumEntries == mCapacityRankedEntriesArray) {
PRUint32 newCapacity;
rv = CheckForTooManyCacheEntries();
if (NS_FAILED(rv)) return rv;
newCapacity = mCapacityRankedEntriesArray + STATS_GROWTH_INCREMENT;
if (newCapacity > mMaxEntries)
newCapacity = mMaxEntries;
nsCachedNetData** newRankedEntriesArray;
PRUint32 numBytes = sizeof(nsCachedNetData*) * newCapacity;
newRankedEntriesArray =
(nsCachedNetData**)nsAllocator::Realloc(mRankedEntries, numBytes);
if (!newRankedEntriesArray)
return NS_ERROR_OUT_OF_MEMORY;
mRankedEntries = newRankedEntriesArray;
mCapacityRankedEntriesArray = newCapacity;
PRUint32 i;
for (i = mNumEntries; i < newCapacity; i++)
mRankedEntries[i] = 0;
}
// Recycle the record after the last in-use record in the array
nsCachedNetData *entry = mRankedEntries[mNumEntries];
NS_ASSERTION(!entry || !entry->GetFlag(nsCachedNetData::RECYCLED),
"Only deleted cache entries should appear at end of array");
if (!entry) {
entry = new(mArena) nsCachedNetData;
if (!entry)
return NS_ERROR_OUT_OF_MEMORY;
mRankedEntries[mNumEntries] = entry;
} else {
// Clear out recycled data structure
nsCRT::zero(entry, sizeof(*entry));
}
entry->Init(aRecord, aCache);
AddCacheEntry(entry, recordID);
// Add one reference to the cache entry from the cache manager
NS_ADDREF(entry);
if (aResult) {
// And one reference from our caller
NS_ADDREF(entry);
*aResult = entry;
}
mNumEntries++;
return NS_OK;
}
nsresult
nsReplacementPolicy::GetCachedNetData(const char* cacheKey, PRUint32 cacheKeyLength,
nsINetDataCache* aCache,
nsCachedNetData** aResult)
{
nsresult rv;
nsCOMPtr<nsINetDataCacheRecord> record;
rv = aCache->GetCachedNetData(cacheKey, cacheKeyLength,
getter_AddRefs(record));
if (NS_FAILED(rv)) return rv;
return AssociateCacheEntryWithRecord(record, aCache, aResult);
}
/**
* Delete the least desirable record from the cache database. This is used
* when the addition of another record would exceed either the cache manager or
* the cache's maximum permitted number of records.
*/
nsresult
nsReplacementPolicy::DeleteOneEntry(nsINetDataCache *aCache)
{
PRUint32 i;
nsresult rv;
nsCachedNetData *entry;
i = 0;
while (1) {
MaybeRerankRecords();
for (; i < mNumEntries; i++) {
entry = mRankedEntries[i];
if (!entry || entry->GetFlag(nsCachedNetData::RECYCLED))
continue;
if (!aCache || (entry->mCache == aCache))
break;
}
// Report error if no record found to delete
if (i == mNumEntries)
return NS_ERROR_FAILURE;
rv = entry->Delete();
if (NS_SUCCEEDED(rv)) {
rv = DeleteCacheEntry(entry);
return rv;
}
}
}
nsresult
nsReplacementPolicy::GetStorageInUse(PRUint32* aStorageInUse)
{
nsresult rv;
CacheInfo *cacheInfo;
*aStorageInUse = 0;
cacheInfo = mCaches;
while (cacheInfo) {
PRUint32 cacheStorage;
rv = cacheInfo->mCache->GetStorageInUse(&cacheStorage);
if (NS_FAILED(rv)) return rv;
*aStorageInUse += cacheStorage;
cacheInfo = cacheInfo->mNext;
}
return NS_OK;
}
/**
* Delete the least desirable records from the cache until the occupancy of the
* cache has been reduced by the given number of KB. This is used when the
* addition of more cache data would exceed the cache's capacity.
*/
nsresult
nsReplacementPolicy::Evict(PRUint32 aTargetOccupancy)
{
PRUint32 i;
nsCachedNetData *entry;
nsresult rv;
PRUint32 occupancy;
PRInt32 truncatedLength;
nsCOMPtr<nsINetDataCacheRecord> record;
MaybeRerankRecords();
for (i = 0; i < mNumEntries; i++) {
rv = GetStorageInUse(&occupancy);
if (!NS_SUCCEEDED(rv)) return rv;
if (occupancy <= aTargetOccupancy)
return NS_OK;
entry = mRankedEntries[i];
// Skip deleted/empty cache entries and ones that have already been evicted
if (!entry || entry->GetFlag(nsCachedNetData::UNEVICTABLE))
continue;
if (entry->GetFlag(nsCachedNetData::ALLOW_PARTIAL)) {
rv = entry->GetRecord(getter_AddRefs(record));
if (NS_FAILED(rv))
continue;
PRUint32 contentLength;
rv = record->GetStoredContentLength(&contentLength);
if (NS_FAILED(rv))
continue;
// Additional cache storage required, in KB
PRUint32 storageToReclaim = (occupancy - aTargetOccupancy) << 10;
truncatedLength = (PRInt32)(contentLength - storageToReclaim);
if (truncatedLength < 0)
truncatedLength = 0;
} else {
truncatedLength = 0;
}
rv = entry->Evict(truncatedLength);
}
return NS_ERROR_FAILURE;
}

View File

@@ -1,136 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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 Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
/**
* This class manages one or more caches that share a storage resource, e.g. a
* file cache and a flat-database cache might each occupy space on the disk and
* they would share a single instance of nsReplacementPolicy. The replacement
* policy heuristically chooses which cache entries to evict when storage is
* required to accommodate incoming cache data.
*/
#ifndef _nsReplacementPolicy_h_
#define _nsReplacementPolicy_h_
#include "nscore.h"
#include "nsISupportsUtils.h"
#include "nsINetDataCache.h"
#include "nsICachedNetData.h"
#include "nsIArena.h"
#include "nsCOMPtr.h"
#include "nsHashtable.h"
class nsCachedNetData;
struct PL_HashTable;
/**
* This private class is responsible for implementing the network data cache's
* replacement policy, i.e. it decides which cache data should be evicted to
* make room for new incoming data.
*/
class nsReplacementPolicy {
public:
nsReplacementPolicy();
~nsReplacementPolicy();
protected:
nsresult Init(PRUint32 aMaxCacheEntries);
nsresult AddCache(nsINetDataCache *aCache);
nsresult GetCachedNetData(const char* cacheKey, PRUint32 cacheKeyLength,
nsINetDataCache* aCache,
nsCachedNetData** aResult);
nsresult GetStorageInUse(PRUint32* aNumKBytes);
friend class nsCacheManager;
private:
nsresult RankRecords();
void MaybeRerankRecords();
void CompactRankedEntriesArray();
nsresult DeleteOneEntry(nsINetDataCache* aCache);
nsresult Evict(PRUint32 aTargetOccupancy);
nsCachedNetData* FindCacheEntryByRecordID(PRInt32 aRecordID, nsINetDataCache *aCache);
void AddCacheEntry(nsCachedNetData* aCacheEntry, PRInt32 aRecordID);
nsresult DeleteCacheEntry(nsCachedNetData* aCacheEntry);
PRUint32 HashRecordID(PRInt32 aRecordID);
nsresult AssociateCacheEntryWithRecord(nsINetDataCacheRecord *aRecord,
nsINetDataCache* aCache,
nsCachedNetData** aResult);
nsresult AddAllRecordsInCache(nsINetDataCache *aCache);
nsresult CheckForTooManyCacheEntries();
class CacheInfo;
private:
// Growable array of pointers to individual cache entries; It is sorted by
// profitability, such that low-numbered array indices refer to cache
// entries that are the least profitable to retain. New cache entries are
// added to the end of the array. Deleted cache entries are specially
// marked and percolate to the end of the array for recycling whenever
// mRankedEntries is sorted. Evicted cache entries (those with no
// associated content data) are retained for the purpose of improving the
// replacement policy efficacy, and are percolated towards the end of the
// array, just prior to the deleted cache entries.
//
// The array is not in sorted order 100% of the time; For efficiency
// reasons, sorting is only done when heuristically deemed necessary.
nsCachedNetData** mRankedEntries;
// Hash table buckets to map Record ID to cache entry. We use this instead
// of a PL_HashTable to reduce storage requirements
nsCachedNetData** mMapRecordIdToEntry;
// Length of mMapRecordIdToEntry array
PRUint32 mHashArrayLength;
// Linked list of caches that share this replacement policy
CacheInfo* mCaches;
// Allocation area for cache entry (nsCachedNetData) instances
nsCOMPtr<nsIArena> mArena;
// Bookkeeping
PRUint32 mRecordsRemovedSinceLastRanking;
// Maximum permitted length of mRankedEntries array
PRUint32 mMaxEntries;
// Number of occupied slots in mRankedEntries array
PRUint32 mNumEntries;
// Capacity of mRankedEntries array
PRUint32 mCapacityRankedEntriesArray;
// Time at which cache entries were last ranked by profitability
PRUint32 mLastRankTime;
};
#endif // _nsReplacementPolicy_h_

View File

@@ -1,43 +0,0 @@
#
# 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):
#
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
MODULE = nkcache
include $(DEPTH)/config/autoconf.mk
XPIDLSRCS = \
nsICachedNetData.idl \
nsINetDataCacheManager.idl \
nsINetDataCache.idl \
nsINetDataCacheRecord.idl \
nsINetDataDiskCache.idl \
nsIStreamAsFile.idl \
$(NULL)
EXPORTS := $(addprefix $(srcdir)/, $(EXPORTS))
include $(topsrcdir)/config/rules.mk

View File

@@ -1,41 +0,0 @@
#!gmake
#
# 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):
MODULE = nkcache
DEPTH = ..\..\..
include <$(DEPTH)/config/config.mak>
EXPORTS = \
$(NULL)
XPIDLSRCS = \
.\nsICachedNetData.idl \
.\nsINetDataCacheManager.idl \
.\nsINetDataCache.idl \
.\nsINetDataCacheRecord.idl \
.\nsINetDataDiskCache.idl \
.\nsIStreamAsFile.idl \
$(NULL)
include <$(DEPTH)/config/rules.mak>

View File

@@ -1,229 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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):
*/
#include "nsrootidl.idl"
#include "nsISupports.idl"
interface nsIFileSpec;
interface nsIURI;
interface nsIObserver;
interface nsIChannel;
interface nsINetDataCache;
interface nsINetDataCacheRecord;
interface nsILoadGroup;
interface nsIStreamListener;
/**
* The nsICachedNetData interface represents a single entry in a database that
* caches data retrieved from the network. This interface is implemented by the
* cache manager on top of the low-level nsINetDataCacheRecord and
* nsINetDataCache interfaces that are implemented by the database.
*
* Each cache record may contain both content and metadata. The content may
* be, for example, GIF image data or HTML, and it is accessed through
* nsIChannel's streaming API. The opaque metadata, which may contain HTTP
* headers among other things, is stored as a byte array. Each entry in the
* cache is indexed by two different keys: a record id number and a key created
* by combining the URI with a "secondary key", e.g. HTTP post data.
*
* @See nsINetDataCacheRecord
* @See nsINetDataCache
* @See nsINetDataDiskCache
* @See nsINetDataCacheManager
*/
[scriptable, uuid(6aeb2a40-6d43-11d3-90c8-000064657374)]
interface nsICachedNetData : nsISupports
{
/**
* String form of the URI provided as an argument to the call to
* nsINetDataCacheManager::GetCachedNetData() that created this record.
*/
readonly attribute string uriSpec;
/**
* Getter for the opaque secondary database key provided as an argument to
* the call to nsINetDataCacheManager::GetCachedNetData() that created this
* record.
*/
void getSecondaryKey(out unsigned long length,
[retval, size_is(length)] out string secondaryKey);
/**
* This flag may be set by a protocol handler to indicate that it supports
* partial fetching of data. In that case, the cache manager is permitted
* to truncate the entry's content to accommodate incoming data for other
* cache entries rather than deleting it wholesale.
*/
attribute boolean allowPartial;
/**
* This flag indicates that the write stream supplying content data for the
* cache did not complete normally and, therefore, the content may be
* truncated.
*/
readonly attribute boolean partialFlag;
/**
* This flag can be set and cleared by a protocol handler as a form of
* self-notification, so as to avoid race conditions in which a protocol
* handler issues two identical network requests to fill the same cache
* entry. The cache manager itself largely ignores this flag.
*/
attribute boolean updateInProgress;
/**
* inUse is set if any existing channels are associated with this cache
* entry or if the updateInProgess flag is set. This can be used to
* prevent writing to a cache entry by a protocol handler if it's being
* read or written elsewhere.
*/
readonly attribute boolean inUse;
/**
* Date/time that the document was last stored on the origin server, as
* supplied by the protocol handler. This value is used as input to the
* cache replacement policy, i.e. it is not used for validation. If the
* protocol can't supply a last-modified time, this attribute should remain
* unset. When unset, the value of this attribute is zero.
*
* FIXME: Should use nsIDateTime interface, once it's created
* instead of PRTime, for improved scriptability ?
*/
attribute PRTime lastModifiedTime;
/**
* Supplied by the protocol handler, the expirationTime attribute specifies
* the time until which the document is guaranteed fresh, i.e. the document
* does not have to be validated with the server and, therefore, any data
* in cache is definitely usable. The value of this attribute serves as a
* hint to the cache replacement policy. Only one of either staleTime or
* expirationTime may be set for a single cache record. When unset, the
* value of this attribute is zero.
*/
attribute PRTime expirationTime;
/**
* Date/time supplied by the protocol handler, at which point the content
* is *likely* to be stale, i.e. the data in the cache may be out-of-date
* with respect to the data on the server. This heuristic date does not
* necessarily correspond to the HTTP Expires header, as it does not
* determine when cached network data must be validated with the origin
* server, but only serves as a hint to the cache replacement policy. Only
* one of either staleTime or expirationTime may be set for a single cache
* record. When unset, the value of this attribute is zero.
*/
attribute PRTime staleTime;
/**
* Date/time of last access of the data in this cache record, as determined
* by the cache manager.
*/
readonly attribute PRTime lastAccessTime;
/**
* Number of times this record has been accessed since it was first stored.
*/
readonly attribute PRUint16 numberAccesses;
/**
* Accessor methods for opaque meta-data which can be read and updated
* independently of the content data.
*
* The aTag argument can be used to accommodate multiple clients of the
* cache API, each of which wants to store its own private meta-data into
* the cache. For example, there could be a "headers" tag that the HTTP
* protocol handler uses to store http response headers and a "image size"
* tag used to store the image dimensions of a GIF file. The aData
* argument refers to an opaque blob of arbitrary bytes.
*
* IMPORTANT: If aData does not contain byte-oriented data, i.e. it's not a
* string, the contents of aData must be byte-swapped by the,
* caller, so as to make the cache files endian-independent.
*/
void getAnnotation(in string aTag,
out PRUint32 aLength, [size_is(aLength), retval] out string aData);
void setAnnotation(in string aTag,
in PRUint32 aLength, [size_is(aLength)] in string aData);
/**
* As a getter, return the number of content bytes stored in the cache,
* i.e. via the nsIChannel streaming APIs. This may be less than the
* complete content length if a partial cache fill occurred. The cached
* content can be truncated by setting the value of this attribute. The
* value of the attribute represents a logical, not a physical, length. If
* compression has been used, the content may consume less storage than
* indicated by this attribute.
*
* When this attribute is set to zero the associated cache disk file, if
* any, should be deleted.
*/
attribute PRUint32 storedContentLength;
/**
* Notify any observers associated with this cache entry of the deletion
* request. If all observers drop their reference to the cache entry,
* proceed to delete the underlying cache database record and associated
* content storage.
*/
void delete();
/**
* Flush any changes in this entry's data to the cache database. This
* method will automatically be called when the last reference to the cache
* is dropped, but it can also be called explicitly for a synchronous
* effect.
*/
void commit();
/**
* Parent container cache for this entry.
*/
readonly attribute nsINetDataCache cache;
/**
* Create a channel for reading or writing a stream of content into the
* entry. It is expected that many of the nsIChannel methods return
* NS_NOT_IMPLEMENTED, including:
*
* + GetURI()
* + GetContentType()
* + GetContentLength()
*
* Though nsIChannel provides for both async and synchronous I/O APIs, both
* may not be implemented. Only AsyncRead() and OpenOutputStream() is
* required. The aProxyChannel argument allows another channel to be
* specified as the proffered argument to nsIStreamListener methods rather
* than the cache's own channel.
*/
nsIChannel newChannel(in nsILoadGroup aLoadGroup,
in nsIChannel aProxyChannel);
/**
* This method can be used by a caching protocol handler to store data in
* the cache by forking an asynchronous read stream so that it is
* simultaneously sent to a requester and written into the cache. This
* method implicitly sets the updateInProgress flag, if it has not already
* been set.
*/
nsIStreamListener interceptAsyncRead(in nsIStreamListener aOriginalListener,
in PRUint32 aStartOffset);
};

View File

@@ -1,143 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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):
*/
#include "nsISupports.idl"
interface nsIURI;
interface nsINetDataCacheRecord;
interface nsISimpleEnumerator;
interface nsIFileSpec;
/**
* The nsINetDataCache defines the low-level API for a network-data
* cache, used to cache the responses to network retrieval commands.
* This interface, along with nsINetDataCacheRecord, is implemented by
* the memory cache, the file cache and, optionally, by some extension
* caches. This interface is essentially a pseudo-private API for the
* cache manager. Other clients should never use this interface.
*
* Each cache entry may contain both content, e.g. GIF image data, and
* associated metadata, e.g. HTTP headers. Each entry is indexed by two
* different keys: a record id number and a key created by combining the URI
* with a "secondary key", e.g. HTTP post data.
*
* The nsINetDataCache interface is agnostic as to where the data is
* stored and whether the storage is volatile or persistent. The
* memory cache, any disk caches and any extension caches must all
* implement this interface.
*
*/
[scriptable, uuid(ccfc58c0-6dde-11d3-90c8-000064657374)]
interface nsINetDataCache : nsISupports
{
/**
* Human-readable description of the cache module, e.g. "Disk Cache"
*/
readonly attribute wstring description;
/**
* Returns true if cached data is available for the given opaque key,
* even if only partial data is stored.
*/
boolean contains([size_is(length)] in string key, in PRUint32 length);
/**
* Fetch the cache entry record for the given opaque key. If one does not
* exist, create a new, empty record.
*/
nsINetDataCacheRecord getCachedNetData([size_is(length)] in string key,
in PRUint32 length);
/**
* Fetch the cache entry record for the given URI using the record ID as a key.
*/
nsINetDataCacheRecord getCachedNetDataByID(in PRInt32 RecordID);
/**
* False indicates that this cache is entirely bypassed.
*/
attribute boolean enabled;
/**
* Constants for flags attribute, below
*/
// Used for extension caches, e.g. a CD-ROM cache
const long READ_ONLY = 1 << 0;
// One of these bits must be set
const long MEMORY_CACHE = 1 << 1;
const long FLAT_FILE_CACHE = 1 << 2;
const long FILE_PER_URL_CACHE = 1 << 3;
/**
* See constants defined above.
*/
readonly attribute PRUint32 flags;
/**
* Total number of URI entries stored in the cache.
*/
readonly attribute PRUint32 numEntries;
/**
* Maximum number of URI entries that may be stored in the cache.
*/
readonly attribute PRUint32 maxEntries;
/**
* Enumerate the URI entries stored in the cache.
*/
nsISimpleEnumerator newCacheEntryIterator();
/**
* Contains a reference to the next cache in search order. For the memory
* cache, this attribute always references the disk cache. For the disk
* cache, it contains a reference to the first extension cache.
*/
attribute nsINetDataCache nextCache;
/**
* An estimate of the amount of storage occupied by the cache, in kB.
* Actual use may be slightly higher than reported due to cache overhead
* and heap fragmentation (in the memory cache) or block quantization (in
* the disk cache).
*/
readonly attribute PRUint32 storageInUse;
/**
* Remove all entries from a writable cache. This could be used, for
* example, after a guest ends a browser session. This is equivalent to
* setting the cache's Capacity to zero, except that all cache entries,
* even those in active use, will be deleted. Also, any global cache
* database files will be deleted.
*/
void removeAll();
};
%{ C++
// ProgID prefix for Components that implement this interface
#define NS_NETWORK_CACHE_PROGID "component://netscape/network/cache"
#define NS_NETWORK_MEMORY_CACHE_PROGID NS_NETWORK_CACHE_PROGID "?name=memory-cache"
#define NS_NETWORK_FLAT_CACHE_PROGID NS_NETWORK_CACHE_PROGID "?name=flat-cache"
#define NS_NETWORK_FILE_CACHE_PROGID NS_NETWORK_CACHE_PROGID "?name=file-cache"
%}

View File

@@ -1,163 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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):
*/
#include "nsISupports.idl"
interface nsISimpleEnumerator;
interface nsICachedNetData;
interface nsINetDataCache;
interface nsINetDataDiskCache;
interface nsIURI;
interface nsIFileSpec;
/**
* The network-response cache manager is partly responsible for the caching of
* content and associated metadata that has been retrieved via the network.
* (The remaining responsibility for caching lies with individual network
* protocol handlers.)
*
* The cache manager supervises the actions of individual cache components,
* such as the memory cache, the disk cache and any extension caches, e.g. a
* read-only CD-ROM cache.
*
* @See nsINetDataCache
* @See nsICachedNetData
*/
[scriptable, uuid(71c8ab00-6d5c-11d3-90c8-000064657374)]
interface nsINetDataCacheManager : nsISupports
{
/**
* Flag for the GetCachedNetData() method: If set, the memory cache is
* neither searched nor will any data be stored into it. This might be
* appropriate, for example, with images, because they have their own
* cache for storing *decoded* images.
*/
const unsigned long BYPASS_MEMORY_CACHE = 1 << 0;
/**
* Flag for the GetCachedNetData() method: If set, the disk cache
* is neither searched nor will any be data stored into it.
* However, read-only extension caches may be searched. This
* might be used to avoid leaving persistent records of secure
* data.
*/
const unsigned long BYPASS_PERSISTENT_CACHE = 1 << 1;
/**
* Flag for the GetCachedNetData() method: If set, any stream
* content is stored in the cache as a single disk file. Content
* will not be cached in the memory cache nor is it cached in a
* flat-file cache database. This is used to implement the jar
* protocol handler and to provide the stream-as-file semantics
* required by the classic bowser plugin API.
*/
const unsigned long CACHE_AS_FILE = 1 << 2;
/**
* Fetch the cache entry record for the given URI. If one does not exist,
* create a new, empty record. The normal search order for caches is:
* + Memory cache
* + Disk cache
* + File cache (stream-as-file cache)
* + All extension caches
*
* When writing, data is typically stored in both the memory cache and the
* disk cache. Both the search order and this write policy can be modified by
* setting one or more of the flag argument bits, as defined above.
*
* The optionally-NULL secondaryKey argument can be used, e.g. for form
* post data or for HTTP headers in the case of HTTP.
*/
nsICachedNetData getCachedNetData(in string uri,
[size_is(secondaryKeyLength)] in string secondaryKey,
in PRUint32 secondaryKeyLength,
in PRUint32 flags);
/**
* Returns true if cached content is available for the given URI, even if
* only partial data is stored. The flags argument behaves the same as for
* the GetCachedNetData() method, above.
*/
boolean contains(in string uri,
[size_is(secondaryKeyLength)] in string secondaryKey,
in PRUint32 secondaryKeyLength,
in PRUint32 flags);
/**
* Total number of unexpired URI entries stored in all caches. This number
* does not take into account duplicate URIs, e.g. because the memory cache
* and the disk cache might each contain an entry for the same URI.
*/
readonly attribute PRUint32 numEntries;
/**
* Enumerate the unexpired URI entries stored in all caches. Some URIs may
* be enumerated more than once, e.g. because the the memory cache and the
* disk cache might each contain an entry for the same URI.
*/
nsISimpleEnumerator newCacheEntryIterator();
/*
* Enumerate all the loaded nsINetDataCache-implementing cache modules.
* The first module enumerated will be the memory cache, the second will be
* the disk cache, then the file cache, followed by all the extension
* caches, in search order.
*/
nsISimpleEnumerator newCacheModuleIterator();
/**
* Remove all entries from all writable caches. This could be used, for
* example, after a guest ends a browser session. This is equivalent to
* setting the DiskCacheCapacity to zero, except that all cache entries,
* even those in active use, will be deleted. Also, any global cache
* database files will be deleted.
*/
void RemoveAll();
/**
* The disk cache is made up of the file cache (for stream-as-file
* requests) and a (possibly independent) persistent cache that handles all
* other cache requests. This attribute sets/gets the combined capacity of
* these caches, measured in KBytes. Setting the capacity lower than the
* current amount of space currently in use may cause cache entries to be
* evicted from the cache to accomodate the requested capacity.
*/
attribute PRUint32 diskCacheCapacity;
/**
* This attribute sets/gets the capacity of the memory cache, measured in
* KBytes. Setting the capacity lower than the current amount of space
* currently in use may cause cache entries to be evicted from the cache to
* accomodate the requested capacity.
*/
attribute PRUint32 memCacheCapacity;
/**
* This attribute must be set before attempting to store into the disk cache.
*/
attribute nsIFileSpec diskCacheFolder;
};
%{ C++
// ProgID prefix for Components that implement this interface
#define NS_NETWORK_CACHE_MANAGER_PROGID NS_NETWORK_CACHE_PROGID "?name=manager"
%}

View File

@@ -1,125 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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):
*/
#include "nsISupports.idl"
#include "nsrootidl.idl"
interface nsIFileSpec;
interface nsIChannel;
interface nsINetDataCache;
/**
* The nsINetDataCacheRecord represents a single entry in a database that
* caches data retrieved from the network. On top of this low-level interface
* to the raw record data, the cache manager implements a higher-level record
* interface, nsICachedNetData. Each instance of nsINetDataCacheRecord is
* (internally) associated with a parent database, an instance of the
* nsINetDataCache interface. This interface is essentially a pseudo-private
* API for the cache manager. Other clients should never use this interface.
*
* Each cache record may contain both content and metadata. The content may
* be, for example, GIF image data or HTML, and it is accessed through
* nsIChannel's streaming API. The opaque metadata, which may contain HTTP
* headers among other things, is accessed as a contiguous byte array. Each
* entry in the cache is indexed by two different keys: a unique record id
* number, generated by the cache, and an opaque string. The latter contains
* the URI and other secondary key information, e.g. HTTP form post key/value
* pairs.
*
* The nsINetDataCacheRecord interface is agnostic as to where the data is
* stored and whether the storage is volatile or persistent. The memory cache,
* the disk cache, a flat-file cache and any read-only extension caches must
* all implement this interface.
*
* @See nsICachedNetData
* @See nsINetDataCache
* @See nsINetDataDiskCache
* @See nsINetDataCacheManager
*/
interface nsILoadGroup;
[scriptable, uuid(fdcdd6a0-7461-11d3-90ca-0040056a906e)]
interface nsINetDataCacheRecord : nsISupports
{
/**
* As far as the nsINetDataCacheRecord implementation is concerned, the
* cache entry database key is an opaque blob, but it's intended to contain
* both the URI and any secondary keys, such as HTTP post data.
*/
void getKey(out unsigned long length, [size_is(length), retval] out string key);
/**
* A persistent record number assigned by the cache which must be unique
* among all entries stored within the same cache. The record ID serves as
* an alternate key to the cache record. Providing that they satisfy the
* afforementioned uniqueness requirement, record IDs can be assigned any
* value by the database except that they may never be zero.
*/
readonly attribute PRInt32 recordID;
/**
* Opaque data which can be updated for each cache entry independently of
* the content data. This data is a combination of protocol-independent
* data provided by the cache manager and protocol-specific meta-data,
* e.g. HTTP headers.
*/
void getMetaData(out PRUint32 length, [size_is(length), retval] out string metaData);
void setMetaData(in PRUint32 length, [size_is(length)] in string data);
/**
* Number of content bytes stored in the cache, i.e. via the nsIChannel
* streaming APIs. This may be less than the complete content length if a
* partial cache fill occurred. Additionally, the cached content can be
* truncated by reducing the value of this attribute. When this attribute
* is set to zero the associated cache disk file, if any, should be
* deleted.
*/
attribute PRUint32 storedContentLength;
/**
* Delete this cache entry and its associated content.
*/
void delete();
/**
* Create a channel for reading or writing a stream of content into the
* entry. However, many of the nsIChannel methods may return
* NS_NOT_IMPLEMENTED, including:
*
* + GetURI()
* + GetContentType()
* + GetContentLength()
*/
nsIChannel newChannel(in nsILoadGroup loadGroup);
/**
* If a cache is implemented such that it stores each URI's content in an
* individual disk file, this method will identify the file corresponding
* to this record. This may be used to implement the "stream-as-file"
* semantics required by some plugins and by the 'jar:' protocol handler.
* However, not all cache implementations are *required* to store the data
* from each URI in an individual file, so it is acceptable for an
* implementation of this method to signal NS_NOT_IMPLEMENTED.
*/
readonly attribute nsIFileSpec filename;
};

View File

@@ -1,42 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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):
*/
#include "nsINetDataCache.idl"
interface nsIFileSpec;
/**
/**
* A network-data disk cache is used to persistently cache the responses to
* network retrieval commands. Each cache entry may contain both content,
* e.g. GIF image data, and associated metadata, e.g. HTTP headers.
*/
[scriptable, uuid(6408e390-6f13-11d3-90c8-000064657374)]
interface nsINetDataDiskCache : nsINetDataCache
{
/**
* This attribute must be set before calling any other methods of this
* interface.
*/
attribute nsIFileSpec diskCacheFolder;
};

View File

@@ -1,106 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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 Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Scott Furman, fur@netscape.com
*/
#include "nsrootidl.idl"
#include "nsISupports.idl"
interface nsIFileSpec;
interface nsIStreamAsFileObserver;
/**
* In addition to enhancing effective network response time via caching, the
* cache manager serves a second purpose by providing the stream-as-file
* service required by traditional browser plugins and the jar: protocol
* handler. The interface below provides a means for a client to determine the
* filename associated with a stream and to detect modification/deletion of
* that file.
*/
[scriptable, uuid(0eedbbf0-92d9-11d3-90d3-0040056a906e)]
interface nsIStreamAsFile : nsISupports
{
/**
* Filename containing stream-as-file
*/
readonly attribute nsIFileSpec fileSpec;
/**
* Add an observer for this cache record. When the cache wants to delete
* or truncate a record, so as to make space for another cache entry's
* content data, it will call <code>aObserver</code>'s Observe() method,
* passing the nsIStreamAsFile instance as the <code>aSubject</code>
* argument and an appropriate message. If the observer does not wish to
* inhibit deletion/truncation, it should Release() any references it has to the
* cache record.
*
* @See nsIStreamAsFileObserver
*/
void addObserver(in nsIStreamAsFileObserver aObserver);
/**
* Delete an observer that was added by the AddObserver() method.
*/
void removeObserver(in nsIStreamAsFileObserver aObserver);
};
/**
* This interface can be implemented by a client to receive notifications of
* either modification or deletion of a file created by the cache manager using
* the stream-as-file semantics.
*/
[scriptable, uuid(a26e27c0-92da-11d3-90d3-0040056a906e)]
interface nsIStreamAsFileObserver : nsISupports
{
/**
* Flag bits for argument to Observe() method.
*/
const long NOTIFY_AVAILABLE = 1 << 0; // Stream-as-file now available for reading
const long NOTIFY_ERROR = 1 << 1; // Error while loading stream / creating file
const long REQUEST_DELETION = 1 << 2; // Cache manager wishes to delete/truncate file
const long INVALIDATE = 1 << 3; // File is out-of-date
// Convenience value
const long MAKE_UNAVAILABLE = REQUEST_DELETION | INVALIDATE;
/**
* Receive either a notification or a request concerning a file that has
* been opened using stream-as-file. The aMessage and aError arguments
* have varying values depending on the nature of the notification.
* aMessage is set to NOTIFY_AVAILABLE when a complete stream has been read
* and stored on disk in a file. At that point, and no sooner, may the
* filename attribute of the associated nsIStreamAsFile be accessed via the
* associated nsIStreamAsFile interface. If the aMessage argument is
* NOTIFY_ERROR, the aError argument contains the relevant error code. If
* the aMessage argument is either REQUEST_DELETION or REQUEST_TRUNCATION,
* the callee should immediately Release() all references to the
* nsIStreamAsFile (and any references to its associated nsICachedNetData
* instances), unless it wishes to inhibit the requested file modification.
* If the aMessage argument is INVALIDATE, the cache manager is replacing
* the file with a more recent version. If a client wants to continue
* using the (now out-of-date) file, it must delete it when it has finished,
* as the cache manager will effectively relinquished ownership of the
* file.
*/
void ObserveStreamAsFile(in nsIStreamAsFile aStreamAsFile,
in PRUint32 aMessage,
in nsresult aError);
};

View File

@@ -1,39 +0,0 @@
#!gmake
#
# 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):
DEPTH = ..
MODULE = necko
DIRS= \
base \
dns \
build \
protocol \
socket \
util \
mime \
streamconv \
cache \
test \
$(NULL)
include <$(DEPTH)\config\rules.mak>

View File

@@ -1,32 +0,0 @@
/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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):
*/
#include "nsISupports.idl"
interface nsIAtom;
[scriptable, uuid(a3ec67f0-465a-11d3-9a89-0080c7cb1080)]
interface nsIHTTPHeader : nsISupports
{
nsIAtom GetField();
string GetValue();
};

View File

@@ -1,603 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsIStreamListener.h"
#include "nsIStreamObserver.h"
#include "nsIServiceManager.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
#include "nsIEventQueue.h"
#include "nsIEventQueueService.h"
#include "nsIChannel.h"
#include "nsCOMPtr.h"
#include "nsINetDataCache.h"
#include "nsINetDataCacheManager.h"
#include "nsICachedNetData.h"
// Number of test entries to be placed in the cache
// FIXME - temporary
#define NUM_CACHE_ENTRIES 25
// Cache content stream length will have random length between zero and
// MAX_CONTENT_LENGTH bytes
#define MAX_CONTENT_LENGTH 20000
// Limits, converted to KB
#define DISK_CACHE_CAPACITY ((MAX_CONTENT_LENGTH * 4) >> 10)
#define MEM_CACHE_CAPACITY ((MAX_CONTENT_LENGTH * 3) >> 10)
// Length of random-data cache entry URI key
#define CACHE_KEY_LENGTH 13
// Length of random-data cache entry secondary key
#define CACHE_SECONDARY_KEY_LENGTH 10
// Length of random-data cache entry meta-data
#define CACHE_PROTOCOL_PRIVATE_LENGTH 10
// Mapping from test case number to RecordID
static PRInt32 recordID[NUM_CACHE_ENTRIES];
static PRInt32
mapRecordIdToTestNum(PRInt32 aRecordID)
{
int i;
for (i = 0; i < NUM_CACHE_ENTRIES; i++) {
if (recordID[i] == aRecordID)
return i;
}
return -1;
}
// A supply of stream data to either store or compare with
class nsITestDataStream {
public:
virtual ~nsITestDataStream() {};
virtual PRUint32 Next() = 0;
virtual void Read(char* aBuf, PRUint32 aCount) = 0;
virtual void ReadString(char* aBuf, PRUint32 aCount) = 0;
virtual PRBool Match(char* aBuf, PRUint32 aCount) = 0;
virtual PRBool MatchString(char* aBuf, PRUint32 aCount) = 0;
virtual void Skip(PRUint32 aCount) = 0;
virtual void SkipString(PRUint32 aCount) = 0;
};
// A reproducible stream of random data.
class RandomStream : public nsITestDataStream {
public:
RandomStream(PRUint32 aSeed) {
mStartSeed = mState = aSeed;
}
PRUint32 GetStartSeed() {
return mStartSeed;
}
PRUint32 Next() {
mState = 1103515245 * mState + 12345 ^ (mState >> 16);
return mState;
}
PRUint8 NextChar() {
PRUint8 c;
do {
c = Next();
} while (!isalnum(c));
return c;
}
void Read(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
*aBuf++ = Next();
}
}
// Same as Read(), but using only printable chars and
// with a terminating NUL
void ReadString(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
*aBuf++ = NextChar();
}
*aBuf = 0;
}
PRBool
Match(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
if (*aBuf++ != (char)(Next() & 0xff))
return PR_FALSE;
}
return PR_TRUE;
}
PRBool
MatchString(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
if (*aBuf++ != (char)(NextChar() & 0xff))
return PR_FALSE;
}
// Check for terminating NUL character
if (*aBuf)
return PR_FALSE;
return PR_TRUE;
}
void
Skip(PRUint32 aCount) {
while (aCount--)
Next();
}
void
SkipString(PRUint32 aCount) {
while (aCount--)
NextChar();
}
protected:
PRUint32 mState;
PRUint32 mStartSeed;
};
static int gNumReaders = 0;
static PRUint32 gTotalBytesRead = 0;
static PRUint32 gTotalBytesWritten = 0;
static PRUint32 gTotalDuration = 0;
class nsReader : public nsIStreamListener {
public:
NS_DECL_ISUPPORTS
nsReader()
: mStartTime(0), mBytesRead(0)
{
NS_INIT_REFCNT();
gNumReaders++;
}
virtual ~nsReader() {
delete mTestDataStream;
gNumReaders--;
}
nsresult
Init(nsITestDataStream* aRandomStream, PRUint32 aExpectedStreamLength) {
mTestDataStream = aRandomStream;
mExpectedStreamLength = aExpectedStreamLength;
mRefCnt = 1;
return NS_OK;
}
NS_IMETHOD OnStartRequest(nsIChannel* channel,
nsISupports* context) {
mStartTime = PR_IntervalNow();
return NS_OK;
}
NS_IMETHOD OnDataAvailable(nsIChannel* channel,
nsISupports* context,
nsIInputStream *aIStream,
PRUint32 aSourceOffset,
PRUint32 aLength) {
char buf[1025];
while (aLength > 0) {
PRUint32 amt;
PRBool match;
aIStream->Read(buf, sizeof buf, &amt);
if (amt == 0) break;
aLength -= amt;
mBytesRead += amt;
match = mTestDataStream->Match(buf, amt);
NS_ASSERTION(match, "Stored data was corrupted on read");
}
return NS_OK;
}
NS_IMETHOD OnStopRequest(nsIChannel* channel,
nsISupports* context,
nsresult aStatus,
const PRUnichar* aMsg) {
PRIntervalTime endTime;
PRIntervalTime duration;
endTime = PR_IntervalNow();
duration = (endTime - mStartTime);
if (NS_FAILED(aStatus)) printf("channel failed.\n");
// printf("read %d bytes\n", mBytesRead);
//FIXME NS_ASSERTION(mBytesRead == mExpectedStreamLength,
// "Stream in cache is wrong length");
gTotalBytesRead += mBytesRead;
gTotalDuration += duration;
return NS_OK;
}
protected:
PRIntervalTime mStartTime;
PRUint32 mBytesRead;
nsITestDataStream* mTestDataStream;
PRUint32 mExpectedStreamLength;
};
NS_IMPL_ISUPPORTS2(nsReader, nsIStreamListener, nsIStreamObserver)
static nsIEventQueue* eventQueue;
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
nsresult
InitQueue() {
nsresult rv;
NS_WITH_SERVICE(nsIEventQueueService, eventQService, kEventQueueServiceCID, &rv);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get event queue service");
rv = eventQService->CreateThreadEventQueue();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't create event queue");
rv = eventQService->GetThreadEventQueue(PR_CurrentThread(), &eventQueue);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get event queue for main thread");
return NS_OK;
}
// Process events until all streams are OnStopRequest'ed
nsresult
WaitForEvents() {
while (gNumReaders) {
eventQueue->ProcessPendingEvents();
}
return NS_OK;
}
// Read data for a single cache record and compare against testDataStream
nsresult
TestReadStream(nsICachedNetData *cacheEntry, nsITestDataStream *testDataStream,
PRUint32 expectedStreamLength)
{
nsCOMPtr<nsIChannel> channel;
nsresult rv;
PRUint32 actualContentLength;
rv = cacheEntry->NewChannel(0, 0, getter_AddRefs(channel));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
rv = cacheEntry->GetStoredContentLength(&actualContentLength);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
// FIXME NS_ASSERTION(actualContentLength == expectedStreamLength,
// "nsICachedNetData::GetContentLength() busted ?");
nsReader *reader = new nsReader;
reader->AddRef();
rv = reader->Init(testDataStream, expectedStreamLength);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
rv = channel->AsyncRead(0, -1, 0, reader);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
reader->Release();
return NS_OK;
}
// Convert PRTime to unix-style time_t, i.e. seconds since the epoch
static PRUint32
convertPRTimeToSeconds(PRTime aTime64)
{
double fpTime;
LL_L2D(fpTime, aTime64);
return (PRUint32)(fpTime * 1e-6 + 0.5);
}
// Convert unix-style time_t, i.e. seconds since the epoch, to PRTime
static PRTime
convertSecondsToPRTime(PRUint32 aSeconds)
{
PRInt64 t64;
LL_L2I(t64, aSeconds);
LL_MUL(t64, t64, 1000000);
return t64;
}
// Read the test data that was written in FillCache(), checking for
// corruption, truncation.
nsresult
TestRead(nsINetDataCacheManager *aCache, PRUint32 aFlags)
{
nsresult rv;
PRBool inCache;
nsCOMPtr<nsICachedNetData> cacheEntry;
RandomStream *randomStream;
char uriCacheKey[CACHE_KEY_LENGTH];
char secondaryCacheKey[CACHE_SECONDARY_KEY_LENGTH];
char *storedUriKey;
PRUint32 testNum;
gTotalBytesRead = 0;
gTotalDuration = 0;
for (testNum = 0; testNum < NUM_CACHE_ENTRIES; testNum++) {
randomStream = new RandomStream(testNum);
randomStream->ReadString(uriCacheKey, sizeof uriCacheKey - 1);
randomStream->Read(secondaryCacheKey, sizeof secondaryCacheKey);
// Ensure that entry is in the cache
rv = aCache->Contains(uriCacheKey,
secondaryCacheKey, sizeof secondaryCacheKey,
aFlags, &inCache);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(inCache, "nsINetDataCacheManager::Contains error");
rv = aCache->GetCachedNetData(uriCacheKey,
secondaryCacheKey, sizeof secondaryCacheKey,
aFlags,
getter_AddRefs(cacheEntry));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
// Test GetUriSpec() method
rv = cacheEntry->GetUriSpec(&storedUriKey);
NS_ASSERTION(NS_SUCCEEDED(rv) &&
!memcmp(storedUriKey, &uriCacheKey[0], sizeof uriCacheKey),
"nsICachedNetData::GetKey failed");
nsAllocator::Free(storedUriKey);
// Test GetSecondaryKey() method
PRUint32 storedSecondaryKeyLength;
char* storedSecondaryKey;
rv = cacheEntry->GetSecondaryKey(&storedSecondaryKeyLength, &storedSecondaryKey);
NS_ASSERTION(NS_SUCCEEDED(rv) &&
!memcmp(storedSecondaryKey, &secondaryCacheKey[0],
sizeof secondaryCacheKey),
"nsICachedNetData::GetSecondaryKey failed");
// Compare against stored protocol data
char *storedProtocolData;
PRUint32 storedProtocolDataLength;
rv = cacheEntry->GetAnnotation("test data", &storedProtocolDataLength, &storedProtocolData);
NS_ASSERTION(NS_SUCCEEDED(rv) &&
storedProtocolDataLength == CACHE_PROTOCOL_PRIVATE_LENGTH,
"nsICachedNetData::GetAnnotation() failed");
randomStream->Match(storedProtocolData, storedProtocolDataLength);
// Test GetAllowPartial()
PRBool allowPartial;
rv = cacheEntry->GetAllowPartial(&allowPartial);
NS_ASSERTION(NS_SUCCEEDED(rv) &&
(allowPartial == (PRBool)(randomStream->Next() & 1)),
"nsICachedNetData::GetAllowPartial() failed");
// Test GetExpirationTime()
PRTime expirationTime;
PRTime expectedExpirationTime = convertSecondsToPRTime(randomStream->Next() & 0xffffff);
rv = cacheEntry->GetExpirationTime(&expirationTime);
NS_ASSERTION(NS_SUCCEEDED(rv) && LL_EQ(expirationTime, expectedExpirationTime),
"nsICachedNetData::GetExpirationTime() failed");
PRUint32 expectedStreamLength = randomStream->Next() % MAX_CONTENT_LENGTH;
TestReadStream(cacheEntry, randomStream, expectedStreamLength);
}
WaitForEvents();
// Compute rate in MB/s
double rate = gTotalBytesRead / PR_IntervalToMilliseconds(gTotalDuration);
rate *= NUM_CACHE_ENTRIES;
rate *= 1000;
rate /= (1024 * 1024);
printf("Read %7d bytes at a rate of %5.1f MB per second \n",
gTotalBytesRead, rate);
return NS_OK;
}
// Create entries in the network data cache, using random data for the
// key, the meta-data and the stored content data.
nsresult
FillCache(nsINetDataCacheManager *aCache, PRUint32 aFlags, PRUint32 aCacheCapacity)
{
nsresult rv;
PRBool inCache;
nsCOMPtr<nsICachedNetData> cacheEntry;
nsCOMPtr<nsIChannel> channel;
nsCOMPtr<nsIOutputStream> outStream;
nsCOMPtr<nsINetDataCache> containingCache;
char buf[1000];
PRUint32 protocolDataLength;
char cacheKey[CACHE_KEY_LENGTH];
char secondaryCacheKey[CACHE_SECONDARY_KEY_LENGTH];
char protocolData[CACHE_PROTOCOL_PRIVATE_LENGTH];
PRUint32 testNum;
RandomStream *randomStream;
gTotalBytesWritten = 0;
PRIntervalTime startTime = PR_IntervalNow();
for (testNum = 0; testNum < NUM_CACHE_ENTRIES; testNum++) {
randomStream = new RandomStream(testNum);
randomStream->ReadString(cacheKey, sizeof cacheKey - 1);
randomStream->Read(secondaryCacheKey, sizeof secondaryCacheKey);
// No entry should be in cache until we add it
rv = aCache->Contains(cacheKey,
secondaryCacheKey, sizeof secondaryCacheKey,
aFlags, &inCache);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(!inCache, "nsINetDataCacheManager::Contains error");
rv = aCache->GetCachedNetData(cacheKey,
secondaryCacheKey, sizeof secondaryCacheKey,
aFlags,
getter_AddRefs(cacheEntry));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't access cacheEntry via cache key");
// Test nsINetDataCacheManager::GetNumEntries()
PRUint32 numEntries = (PRUint32)-1;
rv = aCache->GetNumEntries(&numEntries);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get number of cache entries");
NS_ASSERTION(numEntries == testNum + 1, "GetNumEntries failure");
// Record meta-data should be initially empty
char *protocolDatap;
rv = cacheEntry->GetAnnotation("test data", &protocolDataLength, &protocolDatap);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
if ((protocolDataLength != 0) || (protocolDatap != 0))
return NS_ERROR_FAILURE;
// Store random data as meta-data
randomStream->Read(protocolData, sizeof protocolData);
cacheEntry->SetAnnotation("test data", sizeof protocolData, protocolData);
// Store random data as allow-partial flag
PRBool allowPartial = randomStream->Next() & 1;
rv = cacheEntry->SetAllowPartial(allowPartial);
NS_ASSERTION(NS_SUCCEEDED(rv),
"nsICachedNetData::SetAllowPartial() failed");
// Store random data as expiration time
PRTime expirationTime = convertSecondsToPRTime(randomStream->Next() & 0xffffff);
rv = cacheEntry->SetExpirationTime(expirationTime);
NS_ASSERTION(NS_SUCCEEDED(rv),
"nsICachedNetData::SetExpirationTime() failed");
// Cache manager complains if expiration set without setting last-modified time
rv = cacheEntry->SetLastModifiedTime(expirationTime);
rv = cacheEntry->NewChannel(0, 0, getter_AddRefs(channel));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
rv = cacheEntry->GetCache(getter_AddRefs(containingCache));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
rv = channel->OpenOutputStream(0, getter_AddRefs(outStream));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
int streamLength = randomStream->Next() % MAX_CONTENT_LENGTH;
int remaining = streamLength;
while (remaining) {
PRUint32 numWritten;
int amount = PR_MIN(sizeof buf, remaining);
randomStream->Read(buf, amount);
rv = outStream->Write(buf, amount, &numWritten);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(numWritten == (PRUint32)amount, "Write() bug?");
remaining -= amount;
PRUint32 storageInUse;
rv = containingCache->GetStorageInUse(&storageInUse);
NS_ASSERTION(NS_SUCCEEDED(rv) && (storageInUse <= aCacheCapacity),
"Cache manager failed to limit cache growth");
}
outStream->Close();
gTotalBytesWritten += streamLength;
// *Now* there should be an entry in the cache
rv = aCache->Contains(cacheKey,
secondaryCacheKey, sizeof secondaryCacheKey,
aFlags, &inCache);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(inCache, "nsINetDataCacheManager::Contains error");
delete randomStream;
}
PRIntervalTime endTime = PR_IntervalNow();
// Compute rate in MB/s
double rate = gTotalBytesWritten / PR_IntervalToMilliseconds(endTime - startTime);
rate *= 1000;
rate /= (1024 * 1024);
printf("Wrote %7d bytes at a rate of %5.1f MB per second \n",
gTotalBytesWritten, rate);
return NS_OK;
}
nsresult NS_AutoregisterComponents()
{
nsresult rv = nsComponentManager::AutoRegister(nsIComponentManager::NS_Startup,
NULL /* default */);
return rv;
}
nsresult
Test(nsINetDataCacheManager *aCache, PRUint32 aFlags, PRUint32 aCacheCapacity)
{
nsresult rv;
rv = aCache->RemoveAll();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't clear cache");
PRUint32 numEntries = (PRUint32)-1;
rv = aCache->GetNumEntries(&numEntries);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get number of cache entries");
NS_ASSERTION(numEntries == 0, "Couldn't clear cache");
rv = FillCache(aCache, aFlags, aCacheCapacity);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't fill cache with random test data");
rv = TestRead(aCache, aFlags);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't read random test data from cache");
rv = aCache->RemoveAll();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't clear cache");
rv = aCache->GetNumEntries(&numEntries);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get number of cache entries");
NS_ASSERTION(numEntries == 0, "Couldn't clear cache");
return 0;
}
int
main(int argc, char* argv[])
{
nsresult rv;
nsCOMPtr<nsINetDataCacheManager> cache;
rv = NS_AutoregisterComponents();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't register XPCOM components");
rv = nsComponentManager::CreateInstance(NS_NETWORK_CACHE_MANAGER_PROGID,
nsnull,
NS_GET_IID(nsINetDataCacheManager),
getter_AddRefs(cache));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't create cache manager factory") ;
cache->SetDiskCacheCapacity(DISK_CACHE_CAPACITY);
cache->SetMemCacheCapacity(MEM_CACHE_CAPACITY);
InitQueue();
Test(cache, nsINetDataCacheManager::BYPASS_PERSISTENT_CACHE, MEM_CACHE_CAPACITY);
Test(cache, nsINetDataCacheManager::BYPASS_MEMORY_CACHE, DISK_CACHE_CAPACITY);
return 0;
}

View File

@@ -1,820 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsIStreamListener.h"
#include "nsIStreamObserver.h"
#include "nsIServiceManager.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
#include "nsIEventQueue.h"
#include "nsIEventQueueService.h"
#include "nsIChannel.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include <stdio.h>
#include "nsINetDataCache.h"
#include "nsINetDataDiskCache.h"
#include "nsINetDataCacheRecord.h"
#include "nsMemCacheCID.h"
// file cache include
#include "nsNetDiskCacheCID.h"
#include "nsIPref.h"
#include "prenv.h"
#include "nsIFileStream.h"
// Number of test entries to be placed in the cache
#define NUM_CACHE_ENTRIES 250
// Cache content stream length will have random length between zero and
// MAX_CONTENT_LENGTH bytes
#define MAX_CONTENT_LENGTH 20000
// Length of random-data cache entry key
#define CACHE_KEY_LENGTH 15
// Length of random-data cache entry meta-data
#define CACHE_METADATA_LENGTH 100
static NS_DEFINE_CID(kMemCacheCID, NS_MEM_CACHE_FACTORY_CID);
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
// file cache cid
static NS_DEFINE_CID(kDiskCacheCID, NS_NETDISKCACHE_CID) ;
static NS_DEFINE_CID(kPrefCID, NS_PREF_CID);
static NS_DEFINE_IID(kIPrefIID, NS_IPREF_IID);
// Mapping from test case number to RecordID
static PRInt32 recordID[NUM_CACHE_ENTRIES];
static PRInt32
mapRecordIdToTestNum(PRInt32 aRecordID)
{
int i;
for (i = 0; i < NUM_CACHE_ENTRIES; i++) {
if (recordID[i] == aRecordID)
return i;
}
return -1;
}
// A supply of stream data to either store or compare with
class nsITestDataStream {
public:
virtual ~nsITestDataStream() {};
virtual PRUint32 Next() = 0;
virtual void Read(char* aBuf, PRUint32 aCount) = 0;
virtual PRBool Match(char* aBuf, PRUint32 aCount) = 0;
virtual void Skip(PRUint32 aCount) = 0;
};
// A reproducible stream of random data.
class RandomStream : public nsITestDataStream {
public:
RandomStream(PRUint32 aSeed) {
mStartSeed = mState = aSeed;
}
PRUint32 GetStartSeed() {
return mStartSeed;
}
PRUint32 Next() {
mState = 1103515245 * mState + 12345;
return mState;
}
void Read(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
*aBuf++ = Next();
}
}
PRBool
Match(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
if (*aBuf++ != (char)(Next() & 0xff))
return PR_FALSE;
}
return PR_TRUE;
}
void
Skip(PRUint32 aCount) {
while (aCount--)
Next();
}
protected:
PRUint32 mState;
PRUint32 mStartSeed;
};
// A stream of data that increments on each byte that is read, modulo 256
class CounterStream : public nsITestDataStream {
public:
CounterStream(PRUint32 aSeed) {
mStartSeed = mState = aSeed;
}
PRUint32 GetStartSeed() {
return mStartSeed;
}
PRUint32 Next() {
mState += 1;
mState &= 0xff;
return mState;
}
void Read(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
*aBuf++ = Next();
}
}
PRBool
Match(char* aBuf, PRUint32 aCount) {
PRUint32 i;
for (i = 0; i < aCount; i++) {
if (*aBuf++ != (char)Next())
return PR_FALSE;
}
return PR_TRUE;
}
void
Skip(PRUint32 aCount) {
mState += aCount;
mState &= 0xff;
}
protected:
PRUint32 mState;
PRUint32 mStartSeed;
};
static int gNumReaders = 0;
static PRUint32 gTotalBytesRead = 0;
static PRUint32 gTotalDuration = 0;
class nsReader : public nsIStreamListener {
public:
NS_DECL_ISUPPORTS
nsReader()
: mStartTime(0), mBytesRead(0)
{
NS_INIT_REFCNT();
gNumReaders++;
}
virtual ~nsReader() {
delete mTestDataStream;
gNumReaders--;
}
nsresult
Init(nsITestDataStream* aRandomStream, PRUint32 aExpectedStreamLength) {
mTestDataStream = aRandomStream;
mExpectedStreamLength = aExpectedStreamLength;
mRefCnt = 1;
return NS_OK;
}
NS_IMETHOD OnStartRequest(nsIChannel* channel,
nsISupports* context) {
mStartTime = PR_IntervalNow();
return NS_OK;
}
NS_IMETHOD OnDataAvailable(nsIChannel* channel,
nsISupports* context,
nsIInputStream *aIStream,
PRUint32 aSourceOffset,
PRUint32 aLength) {
char buf[1025];
while (aLength > 0) {
PRUint32 amt;
PRBool match;
aIStream->Read(buf, sizeof buf, &amt);
if (amt == 0) break;
aLength -= amt;
mBytesRead += amt;
match = mTestDataStream->Match(buf, amt);
NS_ASSERTION(match, "Stored data was corrupted on read");
}
return NS_OK;
}
NS_IMETHOD OnStopRequest(nsIChannel* channel,
nsISupports* context,
nsresult aStatus,
const PRUnichar* aMsg) {
PRIntervalTime endTime;
PRIntervalTime duration;
endTime = PR_IntervalNow();
duration = (endTime - mStartTime);
if (NS_FAILED(aStatus)) printf("channel failed.\n");
// printf("read %d bytes\n", mBytesRead);
NS_ASSERTION(mBytesRead == mExpectedStreamLength,
"Stream in cache is wrong length");
gTotalBytesRead += mBytesRead;
gTotalDuration += duration;
return NS_OK;
}
protected:
PRIntervalTime mStartTime;
PRUint32 mBytesRead;
nsITestDataStream* mTestDataStream;
PRUint32 mExpectedStreamLength;
};
NS_IMPL_ISUPPORTS2(nsReader, nsIStreamListener, nsIStreamObserver)
static nsIEventQueue* eventQueue;
nsresult
InitQueue() {
nsresult rv;
NS_WITH_SERVICE(nsIEventQueueService, eventQService, kEventQueueServiceCID, &rv);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get event queue service");
rv = eventQService->CreateThreadEventQueue();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't create event queue");
rv = eventQService->GetThreadEventQueue(PR_CurrentThread(), &eventQueue);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get event queue for main thread");
return NS_OK;
}
// Process events until all streams are OnStopRequest'ed
nsresult
WaitForEvents() {
while (gNumReaders) {
eventQueue->ProcessPendingEvents();
}
return NS_OK;
}
// Read data for a single cache record and compare against testDataStream
nsresult
TestReadStream(nsINetDataCacheRecord *record, nsITestDataStream *testDataStream,
PRUint32 expectedStreamLength)
{
nsCOMPtr<nsIChannel> channel;
nsresult rv;
PRUint32 actualContentLength;
rv = record->NewChannel(0, getter_AddRefs(channel));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
rv = record->GetStoredContentLength(&actualContentLength);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(actualContentLength == expectedStreamLength,
"nsINetDataCacheRecord::GetContentLength() busted ?");
nsReader *reader = new nsReader;
rv = reader->Init(testDataStream, expectedStreamLength);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
rv = channel->AsyncRead(0, -1, 0, reader);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
reader->Release();
return NS_OK;
}
// Check that records can be retrieved using their record-ID, in addition
// to using the opaque key.
nsresult
TestRecordID(nsINetDataCache *cache)
{
nsresult rv;
nsCOMPtr<nsINetDataCacheRecord> record;
RandomStream *randomStream;
PRUint32 metaDataLength;
char cacheKey[CACHE_KEY_LENGTH];
char *metaData;
PRUint32 testNum;
PRBool match;
for (testNum = 0; testNum < NUM_CACHE_ENTRIES; testNum++) {
randomStream = new RandomStream(testNum);
randomStream->Read(cacheKey, sizeof cacheKey);
rv = cache->GetCachedNetDataByID(recordID[testNum], getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't obtain record using record ID");
// Match against previously stored meta-data
rv = record->GetMetaData(&metaDataLength, &metaData);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get record meta-data");
match = randomStream->Match(metaData, metaDataLength);
NS_ASSERTION(match, "Meta-data corrupted or incorrect");
nsAllocator::Free(metaData);
delete randomStream;
}
return NS_OK;
}
// Check that all cache entries in the database are enumerated and that
// no duplicates appear.
nsresult
TestEnumeration(nsINetDataCache *cache)
{
nsresult rv;
nsCOMPtr<nsINetDataCacheRecord> record;
nsCOMPtr<nsISupports> tempISupports;
nsCOMPtr<nsISimpleEnumerator> iterator;
RandomStream *randomStream;
PRUint32 metaDataLength;
char cacheKey[CACHE_KEY_LENGTH];
char *metaData;
PRUint32 testNum;
PRBool match;
PRInt32 recID;
int numRecords = 0;
// Iterate over all records in the cache
rv = cache->NewCacheEntryIterator(getter_AddRefs(iterator));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't create new cache entry iterator");
PRBool notDone;
while (1) {
// Done iterating ?
rv = iterator->HasMoreElements(&notDone);
if (NS_FAILED(rv)) return rv;
if (!notDone)
break;
// Get next record in iteration
rv = iterator->GetNext(getter_AddRefs(tempISupports));
NS_ASSERTION(NS_SUCCEEDED(rv), "iterator bustage");
record = do_QueryInterface(tempISupports);
numRecords++;
// Get record ID
rv = record->GetRecordID(&recID);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get Record ID");
testNum = mapRecordIdToTestNum(recID);
NS_ASSERTION(testNum != -1, "Corrupted Record ID ?");
// Erase mapping from table, so that duplicate enumerations are detected
recordID[testNum] = -1;
// Make sure stream matches test data
randomStream = new RandomStream(testNum);
randomStream->Read(cacheKey, sizeof cacheKey);
// Match against previously stored meta-data
rv = record->GetMetaData(&metaDataLength, &metaData);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get record meta-data");
match = randomStream->Match(metaData, metaDataLength);
NS_ASSERTION(match, "Meta-data corrupted or incorrect");
nsAllocator::Free(metaData);
delete randomStream;
}
NS_ASSERTION(numRecords == NUM_CACHE_ENTRIES, "Iteration bug");
return NS_OK;
}
// Read the test data that was written in FillCache(), checking for
// corruption, truncation.
nsresult
TestRead(nsINetDataCache *cache)
{
nsresult rv;
PRBool inCache;
nsCOMPtr<nsINetDataCacheRecord> record;
RandomStream *randomStream;
PRUint32 metaDataLength;
char cacheKey[CACHE_KEY_LENGTH];
char *metaData, *storedCacheKey;
PRUint32 testNum, storedCacheKeyLength;
PRBool match;
for (testNum = 0; testNum < NUM_CACHE_ENTRIES; testNum++) {
randomStream = new RandomStream(testNum);
randomStream->Read(cacheKey, sizeof cacheKey);
// Ensure that entry is in the cache
rv = cache->Contains(cacheKey, sizeof cacheKey, &inCache);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(inCache, "nsINetDataCache::Contains error");
rv = cache->GetCachedNetData(cacheKey, sizeof cacheKey, getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
// Match against previously stored meta-data
match = record->GetMetaData(&metaDataLength, &metaData);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
match = randomStream->Match(metaData, metaDataLength);
NS_ASSERTION(match, "Meta-data corrupted or incorrect");
nsAllocator::Free(metaData);
// Test GetKey() method
rv = record->GetKey(&storedCacheKeyLength, &storedCacheKey);
NS_ASSERTION(NS_SUCCEEDED(rv) &&
(storedCacheKeyLength == sizeof cacheKey) &&
!memcmp(storedCacheKey, &cacheKey[0], sizeof cacheKey),
"nsINetDataCacheRecord::GetKey failed");
nsAllocator::Free(storedCacheKey);
PRUint32 expectedStreamLength = randomStream->Next() % MAX_CONTENT_LENGTH;
TestReadStream(record, randomStream, expectedStreamLength);
}
WaitForEvents();
// Compute rate in MB/s
double rate = gTotalBytesRead / PR_IntervalToMilliseconds(gTotalDuration);
rate *= NUM_CACHE_ENTRIES;
rate *= 1000;
rate /= (1024 * 1024);
printf("Read %d bytes at a rate of %5.1f MB per second \n",
gTotalBytesRead, rate);
return NS_OK;
}
// Repeatedly call SetStoredContentLength() on a cache entry and make
// read the stream's data to ensure that it's not corrupted by the effect
nsresult
TestTruncation(nsINetDataCache *cache)
{
nsresult rv;
nsCOMPtr<nsINetDataCacheRecord> record;
RandomStream *randomStream;
char cacheKey[CACHE_KEY_LENGTH];
randomStream = new RandomStream(0);
randomStream->Read(cacheKey, sizeof cacheKey);
rv = cache->GetCachedNetData(cacheKey, sizeof cacheKey, getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
randomStream->Skip(CACHE_METADATA_LENGTH);
PRUint32 initialStreamLength = randomStream->Next() % MAX_CONTENT_LENGTH;
delete randomStream;
PRUint32 i;
PRUint32 delta = initialStreamLength / 64;
for (i = initialStreamLength; i >= delta; i -= delta) {
PRUint32 expectedStreamLength = i;
// Do the truncation
record->SetStoredContentLength(expectedStreamLength);
randomStream = new RandomStream(0);
randomStream->Skip(CACHE_KEY_LENGTH + CACHE_METADATA_LENGTH + 1);
PRUint32 afterContentLength;
rv = record->GetStoredContentLength(&afterContentLength);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(afterContentLength == expectedStreamLength,
"nsINetDataCacheRecord::SetContentLength() failed to truncate record");
TestReadStream(record, randomStream, expectedStreamLength);
WaitForEvents();
}
return NS_OK;
}
// Write known data to random offsets in a single cache entry and test
// resulting stream for correctness.
nsresult
TestOffsetWrites(nsINetDataCache *cache)
{
nsresult rv;
nsCOMPtr<nsINetDataCacheRecord> record;
nsCOMPtr<nsIChannel> channel;
nsCOMPtr<nsIOutputStream> outStream;
char buf[512];
char cacheKey[CACHE_KEY_LENGTH];
RandomStream *randomStream;
randomStream = new RandomStream(0);
randomStream->Read(cacheKey, sizeof cacheKey);
rv = cache->GetCachedNetData(cacheKey, sizeof cacheKey, getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't access record via opaque cache key");
// Write buffer-fulls of data at random offsets into the cache entry.
// Data written is (offset % 0xff)
PRUint32 startingOffset;
PRUint32 streamLength = 0;
CounterStream *counterStream;
int i;
for (i = 0; i < 100; i++) {
rv = record->NewChannel(0, getter_AddRefs(channel));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
startingOffset = streamLength ? streamLength - (randomStream->Next() % sizeof buf): 0;
rv = channel->OpenOutputStream(startingOffset, getter_AddRefs(outStream));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
counterStream = new CounterStream(startingOffset);
counterStream->Read(buf, sizeof buf);
PRUint32 numWritten;
rv = outStream->Write(buf, sizeof buf, &numWritten);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(numWritten == sizeof buf, "Write() bug?");
streamLength = startingOffset + sizeof buf;
rv = outStream->Close();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't close channel");
delete counterStream;
}
delete randomStream;
counterStream = new CounterStream(0);
TestReadStream(record, counterStream, streamLength);
WaitForEvents();
return NS_OK;
}
// Create entries in the network data cache, using random data for the
// key, the meta-data and the stored content data.
nsresult
FillCache(nsINetDataCache *cache)
{
nsresult rv;
PRBool inCache;
nsCOMPtr<nsINetDataCacheRecord> record;
nsCOMPtr<nsIChannel> channel;
nsCOMPtr<nsIOutputStream> outStream;
char buf[1000];
PRUint32 metaDataLength;
char cacheKey[CACHE_KEY_LENGTH];
char metaData[CACHE_METADATA_LENGTH];
PRUint32 testNum;
char *data;
RandomStream *randomStream;
PRUint32 totalBytesWritten = 0;
PRIntervalTime startTime = PR_IntervalNow();
for (testNum = 0; testNum < NUM_CACHE_ENTRIES; testNum++) {
randomStream = new RandomStream(testNum);
randomStream->Read(cacheKey, sizeof cacheKey);
// No entry should be in cache until we add it
rv = cache->Contains(cacheKey, sizeof cacheKey, &inCache);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(!inCache, "nsINetDataCache::Contains error");
rv = cache->GetCachedNetData(cacheKey, sizeof cacheKey, getter_AddRefs(record));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't access record via opaque cache key");
// Test nsINetDataCacheRecord::GetRecordID()
rv = record->GetRecordID(&recordID[testNum]);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get Record ID");
// Test nsINetDataCache::GetNumEntries()
PRUint32 numEntries = (PRUint32)-1;
rv = cache->GetNumEntries(&numEntries);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get number of cache entries");
NS_ASSERTION(numEntries == testNum + 1, "GetNumEntries failure");
// Record meta-data should be initially empty
rv = record->GetMetaData(&metaDataLength, &data);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
if ((metaDataLength != 0) || (data != 0))
return NS_ERROR_FAILURE;
// Store random data as meta-data
randomStream->Read(metaData, sizeof metaData);
record->SetMetaData(sizeof metaData, metaData);
rv = record->NewChannel(0, getter_AddRefs(channel));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
rv = channel->OpenOutputStream(0, getter_AddRefs(outStream));
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
PRUint32 beforeOccupancy;
rv = cache->GetStorageInUse(&beforeOccupancy);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache occupancy");
int streamLength = randomStream->Next() % MAX_CONTENT_LENGTH;
int remaining = streamLength;
while (remaining) {
PRUint32 numWritten;
int amount = PR_MIN(sizeof buf, remaining);
randomStream->Read(buf, amount);
rv = outStream->Write(buf, amount, &numWritten);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(numWritten == (PRUint32)amount, "Write() bug?");
remaining -= amount;
}
outStream->Close();
totalBytesWritten += streamLength;
PRUint32 afterOccupancy;
rv = cache->GetStorageInUse(&afterOccupancy);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache occupancy");
PRUint32 streamLengthInKB = streamLength >> 10;
NS_ASSERTION((afterOccupancy - beforeOccupancy) >= streamLengthInKB,
"nsINetDataCache::GetStorageInUse() is busted");
// *Now* there should be an entry in the cache
rv = cache->Contains(cacheKey, sizeof cacheKey, &inCache);
NS_ASSERTION(NS_SUCCEEDED(rv), " ");
NS_ASSERTION(inCache, "nsINetDataCache::Contains error");
delete randomStream;
}
PRIntervalTime endTime = PR_IntervalNow();
// Compute rate in MB/s
double rate = totalBytesWritten / PR_IntervalToMilliseconds(endTime - startTime);
rate *= 1000;
rate /= (1024 * 1024);
printf("Wrote %7d bytes at a rate of %5.1f MB per second \n",
totalBytesWritten, rate);
return NS_OK;
}
nsresult NS_AutoregisterComponents()
{
nsresult rv = nsComponentManager::AutoRegister(nsIComponentManager::NS_Startup,
NULL /* default */);
return rv;
}
PRBool initPref ()
{
nsresult rv;
NS_WITH_SERVICE(nsIPref, prefPtr, kPrefCID, &rv);
if (NS_FAILED(rv))
return false;
nsCOMPtr<nsIFileSpec> fileSpec;
rv = NS_NewFileSpec (getter_AddRefs(fileSpec));
if (NS_FAILED(rv))
return false;
nsCString defaultPrefFile = PR_GetEnv ("MOZILLA_FIVE_HOME");
if (defaultPrefFile.Length())
defaultPrefFile += "/";
else
defaultPrefFile = "./";
defaultPrefFile += "default_prefs.js";
fileSpec->SetUnixStyleFilePath (defaultPrefFile.GetBuffer());
PRBool exists = false;
fileSpec->Exists(&exists);
if (exists)
prefPtr->ReadUserPrefsFrom(fileSpec);
else
return false;
return true;
}
int
main(int argc, char* argv[])
{
nsresult rv;
if(argc <2) {
printf(" %s -f to test filecache\n", argv[0]) ;
printf(" %s -m to test memcache\n", argv[0]) ;
return -1 ;
}
rv = NS_AutoregisterComponents();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't register XPCOM components");
nsCOMPtr<nsINetDataCache> cache;
if (PL_strcasecmp(argv[1], "-m") == 0) {
rv = nsComponentManager::CreateInstance(kMemCacheCID, nsnull,
NS_GET_IID(nsINetDataCache),
getter_AddRefs(cache));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't create memory cache factory");
} else if (PL_strcasecmp(argv[1], "-f") == 0) {
nsCOMPtr<nsINetDataDiskCache> diskcache ;
rv = nsComponentManager::CreateInstance(kDiskCacheCID, nsnull,
NS_GET_IID(nsINetDataDiskCache),
getter_AddRefs(diskcache));
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't create disk cache factory") ;
nsCOMPtr<nsIFileSpec> folder ;
NS_NewFileSpec(getter_AddRefs(folder)) ;
folder->SetUnixStyleFilePath("/tmp") ;
diskcache->SetDiskCacheFolder(folder) ;
cache = diskcache ;
} else {
printf(" %s -f to test filecache\n", argv[0]) ;
printf(" %s -m to test memcache\n", argv[0]) ;
return -1 ;
}
InitQueue();
PRUnichar* description;
rv = cache->GetDescription(&description);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache description");
nsCAutoString descStr(description);
printf("Testing: %s\n", descStr.GetBuffer());
rv = cache->RemoveAll();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't clear cache");
PRUint32 startOccupancy;
rv = cache->GetStorageInUse(&startOccupancy);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache occupancy");
PRUint32 numEntries = (PRUint32)-1;
rv = cache->GetNumEntries(&numEntries);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get number of cache entries");
NS_ASSERTION(numEntries == 0, "Couldn't clear cache");
rv = FillCache(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't fill cache with random test data");
rv = TestRead(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't read random test data from cache");
rv = TestRecordID(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't index records using record ID");
rv = TestEnumeration(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't successfully enumerate records");
rv = TestTruncation(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't successfully truncate records");
rv = TestOffsetWrites(cache);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't successfully write to records using non-zero offsets");
rv = cache->RemoveAll();
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't clear cache");
rv = cache->GetNumEntries(&numEntries);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get number of cache entries");
NS_ASSERTION(numEntries == 0, "Couldn't clear cache");
PRUint32 endOccupancy;
rv = cache->GetStorageInUse(&endOccupancy);
NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't get cache occupancy");
NS_ASSERTION(startOccupancy == endOccupancy, "Cache occupancy not correctly computed ?");
return 0;
}

View File

@@ -1,82 +0,0 @@
#!nmake
#
# 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):
DEPTH=..\..
MAKE_OBJ_TYPE = EXE
PROG1 = .\$(OBJDIR)\TestFileInput.exe
PROG2 = .\$(OBJDIR)\TestSocketInput.exe
PROG3 = .\$(OBJDIR)\TestSocketIO.exe
PROG4 = .\$(OBJDIR)\TestProtocols.exe
PROG5 = .\$(OBJDIR)\TestSocketTransport.exe
PROG6 = .\$(OBJDIR)\urltest.exe
PROG7 = .\$(OBJDIR)\TestFileInput2.exe
PROG8 = .\$(OBJDIR)\TestFileTransport.exe
PROG9 = .\$(OBJDIR)\TestRes.exe
PROGA = .\$(OBJDIR)\TestRawCache.exe
PROGB = .\$(OBJDIR)\TestCacheMgr.exe
PROGRAMS = \
#$(PROG1) $(PROG2) $(PROG3) $(PROG4) $(PROG5) $(PROG6) $(PROG7) $(PROG8) $(PROG9)\
$(PROGA) $(PROGB)
LCFLAGS=-DUSE_NSREG -GX
REQUIRES=libreg
INCS = $(INCS) \
-I$(DEPTH)\dist\include \
$(NULL)
LLIBS= \
$(DIST)\lib\xpcom.lib \
$(LIBNSPR) \
$(NULL)
include <$(DEPTH)\config\rules.mak>
install:: $(PROGRAMS)
-for %p in ($(PROGRAMS)) do $(MAKE_INSTALL) %p $(DIST)\bin
clobber::
-for %p in ($(PROGRAMS)) do $(RM) %p $(DIST)\bin\%p
$(PROG1): $(OBJDIR) TestFileInput.cpp
$(PROG2): $(OBJDIR) TestSocketInput.cpp
$(PROG3): $(OBJDIR) TestSocketIO.cpp
$(PROG4): $(OBJDIR) TestProtocols.cpp
$(PROG5): $(OBJDIR) TestSocketTransport.cpp
$(PROG6): $(OBJDIR) urltest.cpp
$(PROG7): $(OBJDIR) TestFileInput2.cpp
$(PROG8): $(OBJDIR) TestFileTransport.cpp
$(PROG9): $(OBJDIR) TestRes.cpp
$(PROGA): $(OBJDIR) TestRawCache.cpp
$(PROGB): $(OBJDIR) TestCacheMgr.cpp

File diff suppressed because it is too large Load Diff

View File

@@ -1,226 +0,0 @@
/* -*- 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):
*/
#ifndef nsError_h
#define nsError_h
#ifndef prtypes_h___
#include "prtypes.h"
#endif
/**
* Generic result data type
*/
typedef PRUint32 nsresult;
/*
* To add error code to your module, you need to do the following:
*
* 1) Add a module offset code. Add yours to the bottom of the list
* right below this comment, adding 1.
*
* 2) In your module, define a header file which uses one of the
* NE_ERROR_GENERATExxxxxx macros. Some examples below:
*
* #define NS_ERROR_MYMODULE_MYERROR1 NS_ERROR_GENERATE(NS_ERROR_SEVERITY_ERROR,NS_ERROR_MODULE_MYMODULE,1)
* #define NS_ERROR_MYMODULE_MYERROR2 NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_MYMODULE,2)
* #define NS_ERROR_MYMODULE_MYERROR3 NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_MYMODULE,3)
*
*/
/**
* @name Standard Module Offset Code. Each Module should identify a unique number
* and then all errors associated with that module become offsets from the
* base associated with that module id. There are 16 bits of code bits for
* each module.
*/
#define NS_ERROR_MODULE_XPCOM 1
#define NS_ERROR_MODULE_BASE 2
#define NS_ERROR_MODULE_GFX 3
#define NS_ERROR_MODULE_WIDGET 4
#define NS_ERROR_MODULE_CALENDAR 5
#define NS_ERROR_MODULE_NETWORK 6
#define NS_ERROR_MODULE_PLUGINS 7
#define NS_ERROR_MODULE_LAYOUT 8
#define NS_ERROR_MODULE_HTMLPARSER 9
#define NS_ERROR_MODULE_RDF 10
#define NS_ERROR_MODULE_UCONV 11
#define NS_ERROR_MODULE_REG 12
#define NS_ERROR_MODULE_FILES 13
#define NS_ERROR_MODULE_DOM 14
#define NS_ERROR_MODULE_IMGLIB 15
#define NS_ERROR_MODULE_MAILNEWS 16
#define NS_ERROR_MODULE_EDITOR 17
#define NS_ERROR_MODULE_XPCONNECT 18
/**
* @name Standard Error Handling Macros
*/
#define NS_FAILED(_nsresult) ((_nsresult) & 0x80000000)
#define NS_SUCCEEDED(_nsresult) (!((_nsresult) & 0x80000000))
/**
* @name Severity Code. This flag identifies the level of warning
*/
#define NS_ERROR_SEVERITY_SUCCESS 0
#define NS_ERROR_SEVERITY_ERROR 1
/**
* @name Mozilla Code. This flag separates consumers of mozilla code
* from the native platform
*/
#define NS_ERROR_MODULE_BASE_OFFSET 0x45
/**
* @name Standard Error Generating Macros
*/
#define NS_ERROR_GENERATE(sev,module,code) \
((nsresult) (((PRUint32)(sev)<<31) | ((PRUint32)(module+NS_ERROR_MODULE_BASE_OFFSET)<<16) | ((PRUint32)(code))) )
#define NS_ERROR_GENERATE_SUCCESS(module,code) \
((nsresult) (((PRUint32)(NS_ERROR_SEVERITY_SUCCESS)<<31) | ((PRUint32)(module+NS_ERROR_MODULE_BASE_OFFSET)<<16) | ((PRUint32)(code))) )
#define NS_ERROR_GENERATE_FAILURE(module,code) \
((nsresult) (((PRUint32)(NS_ERROR_SEVERITY_ERROR)<<31) | ((PRUint32)(module+NS_ERROR_MODULE_BASE_OFFSET)<<16) | ((PRUint32)(code))) )
/**
* @name Standard Macros for retrieving error bits
*/
#define NS_ERROR_GET_CODE(err) ((err) & 0xffff)
#define NS_ERROR_GET_MODULE(err) (((((err) >> 16) - NS_ERROR_MODULE_BASE_OFFSET) & 0x1fff))
#define NS_ERROR_GET_SEVERITY(err) (((err) >> 31) & 0x1)
/**
* @name Standard return values
*/
/*@{*/
/* Standard "it worked" return value */
#define NS_OK 0
/* The backwards COM false */
#define NS_COMFALSE 1
#define NS_ERROR_BASE ((nsresult) 0xC1F30000)
/* Returned when an instance is not initialized */
#define NS_ERROR_NOT_INITIALIZED (NS_ERROR_BASE + 1)
/* Returned when an instance is already initialized */
#define NS_ERROR_ALREADY_INITIALIZED (NS_ERROR_BASE + 2)
/* Returned by a not implemented function */
#define NS_ERROR_NOT_IMPLEMENTED ((nsresult) 0x80004001L)
/* Returned when a given interface is not supported. */
#define NS_NOINTERFACE ((nsresult) 0x80004002L)
#define NS_ERROR_NO_INTERFACE NS_NOINTERFACE
#define NS_ERROR_INVALID_POINTER ((nsresult) 0x80004003L)
#define NS_ERROR_NULL_POINTER NS_ERROR_INVALID_POINTER
/* Returned when a function aborts */
#define NS_ERROR_ABORT ((nsresult) 0x80004004L)
/* Returned when a function fails */
#define NS_ERROR_FAILURE ((nsresult) 0x80004005L)
/* Returned when an unexpected error occurs */
#define NS_ERROR_UNEXPECTED ((nsresult) 0x8000ffffL)
/* Returned when a memory allocation failes */
#define NS_ERROR_OUT_OF_MEMORY ((nsresult) 0x8007000eL)
/* Returned when an illegal value is passed */
#define NS_ERROR_ILLEGAL_VALUE ((nsresult) 0x80070057L)
#define NS_ERROR_INVALID_ARG NS_ERROR_ILLEGAL_VALUE
/* Returned when a class doesn't allow aggregation */
#define NS_ERROR_NO_AGGREGATION ((nsresult) 0x80040110L)
/* Returned when an operation can't complete due to an unavailable resource */
#define NS_ERROR_NOT_AVAILABLE ((nsresult) 0x80040111L)
/* Returned when a class is not registered */
#define NS_ERROR_FACTORY_NOT_REGISTERED ((nsresult) 0x80040154L)
/* Returned when a class cannot be registered, but may be tried again later */
#define NS_ERROR_FACTORY_REGISTER_AGAIN ((nsresult) 0x80040155L)
/* Returned when a dynamically loaded factory couldn't be found */
#define NS_ERROR_FACTORY_NOT_LOADED ((nsresult) 0x800401f8L)
/* Returned when a factory doesn't support signatures */
#define NS_ERROR_FACTORY_NO_SIGNATURE_SUPPORT \
(NS_ERROR_BASE + 0x101)
/* Returned when a factory already is registered */
#define NS_ERROR_FACTORY_EXISTS (NS_ERROR_BASE + 0x100)
/* For COM compatibility reasons, we want to use exact error code numbers
for NS_ERROR_PROXY_INVALID_IN_PARAMETER and NS_ERROR_PROXY_INVALID_OUT_PARAMETER.
The first matches:
#define RPC_E_INVALID_PARAMETER _HRESULT_TYPEDEF_(0x80010010L)
Errors returning this mean that the xpcom proxy code could not create a proxy for
one of the in paramaters.
Because of this, we are ignoring the convention if using a base and offset for
error numbers.
*/
/* Returned when a proxy could not be create a proxy for one of the IN parameters
This is returned only when the "real" meathod has NOT been invoked.
*/
#define NS_ERROR_PROXY_INVALID_IN_PARAMETER ((nsresult) 0x80010010L)
/* Returned when a proxy could not be create a proxy for one of the OUT parameters
This is returned only when the "real" meathod has ALREADY been invoked.
*/
#define NS_ERROR_PROXY_INVALID_OUT_PARAMETER ((nsresult) 0x80010011L)
/*@}*/
////////////////////////////////////////////////////////////////////////////////
#ifdef XP_PC
#pragma warning(disable: 4251) // 'nsCOMPtr<class nsIInputStream>' needs to have dll-interface to be used by clients of class 'nsInputStream'
#pragma warning(disable: 4275) // non dll-interface class 'nsISupports' used as base for dll-interface class 'nsIRDFNode'
#endif
#endif

View File

@@ -1,126 +0,0 @@
/* -*- Mode: C++; tab-width: 2; 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):
*/
// Force references to all of the symbols that we want exported from
// the dll that are located in the .lib files we link with
#include "nsVoidArray.h"
#include "nsIAtom.h"
#include "nsFileSpec.h"
//#include "nsIBuffer.h"
//#include "nsIByteBufferInputStream.h"
#include "nsFileStream.h"
#include "nsFileSpecStreaming.h"
#include "nsSpecialSystemDirectory.h"
#include "nsIThread.h"
#include "nsDeque.h"
#include "nsObserver.h"
#include "nsTraceRefcnt.h"
#include "nsXPIDLString.h"
#include "nsIEnumerator.h"
#include "nsEnumeratorUtils.h"
#include "nsQuickSort.h"
#include "nsString2.h"
#include "nsProxyEventPrivate.h"
#include "xpt_xdr.h"
#include "nsInterfaceInfo.h"
#include "xptcall.h"
#include "nsIFileSpec.h"
#include "nsIGenericFactory.h"
#include "nsAVLTree.h"
#include "nsHashtableEnumerator.h"
#include "nsPipe2.h"
#include "nsCWeakReference.h"
#include "nsWeakReference.h"
#include "nsISizeOfHandler.h"
#include "nsTextFormater.h"
#include "nsStorageStream.h"
#include "nsIBinaryInputStream.h"
#ifdef DEBUG
#include "pure.h"
#endif
class dummyComparitor: public nsAVLNodeComparitor {
public:
virtual PRInt32 operator()(void* anItem1,void* anItem2)
{
return 0;
}
};
#ifdef DEBUG
extern NS_COM void
TestSegmentedBuffer();
#endif
void XXXNeverCalled()
{
nsTextFormater::snprintf(nsnull,0,nsnull);
dummyComparitor dummy;
nsVoidArray();
nsAVLTree(dummy, nsnull);
NS_GetNumberOfAtoms();
nsFileURL(NULL);
// NS_NewPipe(NULL, NULL, 0, 0, 0, NULL);
NS_NewPipe(NULL, NULL, NULL, 0, 0);
nsFileSpec s;
NS_NewIOFileStream(NULL, s, 0, 0);
nsInputFileStream(s, 0, 0);
nsPersistentFileDescriptor d;
ReadDescriptor(NULL, d);
new nsSpecialSystemDirectory(nsSpecialSystemDirectory::OS_DriveDirectory);
nsIThread::GetCurrent(NULL);
nsDeque(NULL);
NS_NewObserver(NULL, NULL);
nsTraceRefcnt::DumpStatistics();
nsXPIDLCString::Copy(NULL);
NS_NewEmptyEnumerator(NULL);
nsArrayEnumerator(NULL);
NS_NewIntersectionEnumerator(NULL, NULL, NULL);
NS_QuickSort(NULL, 0, 0, NULL, NULL);
nsString2();
nsProxyObject(NULL, 0, NULL);
XPT_DoString(NULL, NULL);
XPT_DoHeader(NULL, NULL);
nsInterfaceInfo* info = NULL;
info->GetName(NULL);
#ifdef DEBUG
info->print(NULL);
PurePrintf(0);
#endif
XPTC_InvokeByIndex(NULL, 0, 0, NULL);
NS_NewFileSpec(NULL);
xptc_dummy();
xptc_dummy2();
XPTI_GetInterfaceInfoManager();
NS_NewGenericFactory(NULL, NULL, NULL);
NS_NewHashtableEnumerator(NULL, NULL, NULL, NULL);
nsCWeakProxy(0, 0);
nsCWeakReferent(0);
NS_GetWeakReference(NULL);
#ifdef DEBUG
TestSegmentedBuffer();
#endif
NS_NewSizeOfHandler(0);
nsStorageStream();
NS_NewBinaryInputStream(0, 0);
}

View File

@@ -1,170 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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):
*/
#ifndef nsIGenericFactory_h___
#define nsIGenericFactory_h___
#include "nsIFactory.h"
// {3bc97f01-ccdf-11d2-bab8-b548654461fc}
#define NS_GENERICFACTORY_CID \
{ 0x3bc97f01, 0xccdf, 0x11d2, { 0xba, 0xb8, 0xb5, 0x48, 0x65, 0x44, 0x61, 0xfc } }
// {3bc97f00-ccdf-11d2-bab8-b548654461fc}
#define NS_IGENERICFACTORY_IID \
{ 0x3bc97f00, 0xccdf, 0x11d2, { 0xba, 0xb8, 0xb5, 0x48, 0x65, 0x44, 0x61, 0xfc } }
#define NS_GENERICFACTORY_PROGID "component:/netscape/generic-factory"
#define NS_GENERICFACTORY_CLASSNAME "Generic Factory"
/**
* Provides a Generic nsIFactory implementation that can be used by
* DLLs with very simple factory needs.
*/
class nsIGenericFactory : public nsIFactory {
public:
static const nsIID& GetIID() { static nsIID iid = NS_IGENERICFACTORY_IID; return iid; }
typedef NS_CALLBACK(ConstructorProcPtr) (nsISupports *aOuter, REFNSIID aIID, void **aResult);
typedef NS_CALLBACK(DestructorProcPtr) (void);
/**
* Establishes the generic factory's constructor function, which will be called
* by CreateInstance.
*/
NS_IMETHOD SetConstructor(ConstructorProcPtr constructor) = 0;
/**
* Establishes the generic factory's destructor function, which will be called
* whe the generic factory is deleted. This is used to notify the DLL that
* an instance of one of its generic factories is going away.
*/
NS_IMETHOD SetDestructor(DestructorProcPtr destructor) = 0;
};
extern NS_COM nsresult
NS_NewGenericFactory(nsIGenericFactory* *result,
nsIGenericFactory::ConstructorProcPtr constructor,
nsIGenericFactory::DestructorProcPtr destructor = NULL);
////////////////////////////////////////////////////////////////////////////////
// Generic Modules
//
// (See xpcom/sample/nsSampleModule.cpp to see how to use this.)
#include "nsIModule.h"
/**
* Use this type to define a list of module component info to pass to
* NS_NewGenericModule. E.g.:
* static nsModuleComponentInfo components[] = { ... };
* See xpcom/sample/nsSampleModule.cpp for more info.
*/
struct nsModuleComponentInfo {
const char* mDescription;
nsCID mCID;
const char* mProgID;
nsIGenericFactory::ConstructorProcPtr mConstructor;
};
extern NS_COM nsresult
NS_NewGenericModule(const char* moduleName,
PRUint32 componentCount,
nsModuleComponentInfo* components,
nsIModule* *result);
#define NS_IMPL_NSGETMODULE(_name, _components) \
extern "C" NS_EXPORT nsresult NSGetModule(nsIComponentManager *servMgr, \
nsIFileSpec* location, \
nsIModule** result) \
{ \
return NS_NewGenericModule((_name), \
sizeof(_components) / sizeof(_components[0]), \
(_components), result); \
}
////////////////////////////////////////////////////////////////////////////////
#define NS_GENERIC_FACTORY_CONSTRUCTOR(_InstanceClass) \
static NS_IMETHODIMP \
_InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, void **aResult) \
{ \
nsresult rv; \
\
_InstanceClass * inst; \
\
if (NULL == aResult) { \
rv = NS_ERROR_NULL_POINTER; \
return rv; \
} \
*aResult = NULL; \
if (NULL != aOuter) { \
rv = NS_ERROR_NO_AGGREGATION; \
return rv; \
} \
\
NS_NEWXPCOM(inst, _InstanceClass); \
if (NULL == inst) { \
rv = NS_ERROR_OUT_OF_MEMORY; \
return rv; \
} \
NS_ADDREF(inst); \
rv = inst->QueryInterface(aIID, aResult); \
NS_RELEASE(inst); \
\
return rv; \
} \
#define NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(_InstanceClass, _InitMethod) \
static NS_IMETHODIMP \
_InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, void **aResult) \
{ \
nsresult rv; \
\
_InstanceClass * inst; \
\
if (NULL == aResult) { \
rv = NS_ERROR_NULL_POINTER; \
return rv; \
} \
*aResult = NULL; \
if (NULL != aOuter) { \
rv = NS_ERROR_NO_AGGREGATION; \
return rv; \
} \
\
NS_NEWXPCOM(inst, _InstanceClass); \
if (NULL == inst) { \
rv = NS_ERROR_OUT_OF_MEMORY; \
return rv; \
} \
NS_ADDREF(inst); \
rv = inst->_InitMethod(); \
if(NS_SUCCEEDED(rv)) { \
rv = inst->QueryInterface(aIID, aResult); \
} \
NS_RELEASE(inst); \
\
return rv; \
} \
#endif /* nsIGenericFactory_h___ */

View File

@@ -1,521 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* 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):
*/
#include "prmem.h"
#include "prlog.h"
#include "nsHashtable.h"
//
// Key operations
//
static PR_CALLBACK PLHashNumber _hashValue(const void *key)
{
return ((const nsHashKey *) key)->HashValue();
}
static PR_CALLBACK PRIntn _hashKeyCompare(const void *key1, const void *key2) {
return ((const nsHashKey *) key1)->Equals((const nsHashKey *) key2);
}
static PR_CALLBACK PRIntn _hashValueCompare(const void *value1,
const void *value2) {
// We're not going to make any assumptions about value equality
return 0;
}
//
// Memory callbacks
//
static PR_CALLBACK void *_hashAllocTable(void *pool, PRSize size) {
return PR_MALLOC(size);
}
static PR_CALLBACK void _hashFreeTable(void *pool, void *item) {
PR_DELETE(item);
}
static PR_CALLBACK PLHashEntry *_hashAllocEntry(void *pool, const void *key) {
return PR_NEW(PLHashEntry);
}
static PR_CALLBACK void _hashFreeEntry(void *pool, PLHashEntry *entry,
PRUintn flag) {
if (flag == HT_FREE_ENTRY) {
delete (nsHashKey *) (entry->key);
PR_DELETE(entry);
}
}
static PLHashAllocOps _hashAllocOps = {
_hashAllocTable, _hashFreeTable,
_hashAllocEntry, _hashFreeEntry
};
//
// Enumerator callback
//
struct _HashEnumerateArgs {
nsHashtableEnumFunc fn;
void* arg;
};
static PR_CALLBACK PRIntn _hashEnumerate(PLHashEntry *he, PRIntn i, void *arg)
{
_HashEnumerateArgs* thunk = (_HashEnumerateArgs*)arg;
return thunk->fn((nsHashKey *) he->key, he->value, thunk->arg)
? HT_ENUMERATE_NEXT
: HT_ENUMERATE_STOP;
}
//
// HashKey
//
nsHashKey::nsHashKey(void)
{
}
nsHashKey::~nsHashKey(void)
{
}
nsHashtable::nsHashtable(PRUint32 aInitSize, PRBool threadSafe)
: mLock(NULL)
{
hashtable = PL_NewHashTable(aInitSize,
_hashValue,
_hashKeyCompare,
_hashValueCompare,
&_hashAllocOps,
NULL);
if (threadSafe == PR_TRUE)
{
mLock = PR_NewLock();
if (mLock == NULL)
{
// Cannot create a lock. If running on a multiprocessing system
// we are sure to die.
PR_ASSERT(mLock != NULL);
}
}
}
nsHashtable::~nsHashtable() {
PL_HashTableDestroy(hashtable);
if (mLock) PR_DestroyLock(mLock);
}
PRBool nsHashtable::Exists(nsHashKey *aKey)
{
PLHashNumber hash = aKey->HashValue();
if (mLock) PR_Lock(mLock);
PLHashEntry **hep = PL_HashTableRawLookup(hashtable, hash, (void *) aKey);
if (mLock) PR_Unlock(mLock);
return *hep != NULL;
}
void *nsHashtable::Put(nsHashKey *aKey, void *aData) {
void *res = NULL;
PLHashNumber hash = aKey->HashValue();
PLHashEntry *he;
if (mLock) PR_Lock(mLock);
PLHashEntry **hep = PL_HashTableRawLookup(hashtable, hash, (void *) aKey);
if ((he = *hep) != NULL) {
res = he->value;
he->value = aData;
} else {
PL_HashTableRawAdd(hashtable, hep, hash,
(void *) aKey->Clone(), aData);
}
if (mLock) PR_Unlock(mLock);
return res;
}
void *nsHashtable::Get(nsHashKey *aKey) {
if (mLock) PR_Lock(mLock);
void *ret = PL_HashTableLookup(hashtable, (void *) aKey);
if (mLock) PR_Unlock(mLock);
return ret;
}
void *nsHashtable::Remove(nsHashKey *aKey) {
PLHashNumber hash = aKey->HashValue();
PLHashEntry *he;
if (mLock) PR_Lock(mLock);
PLHashEntry **hep = PL_HashTableRawLookup(hashtable, hash, (void *) aKey);
void *res = NULL;
if ((he = *hep) != NULL) {
res = he->value;
PL_HashTableRawRemove(hashtable, hep, he);
}
if (mLock) PR_Unlock(mLock);
return res;
}
// XXX This method was called _hashEnumerateCopy, but it didn't copy the element!
// I don't know how this was supposed to work since the elements are neither copied
// nor refcounted.
static PR_CALLBACK PRIntn _hashEnumerateShare(PLHashEntry *he, PRIntn i, void *arg)
{
nsHashtable *newHashtable = (nsHashtable *)arg;
newHashtable->Put((nsHashKey *) he->key, he->value);
return HT_ENUMERATE_NEXT;
}
nsHashtable * nsHashtable::Clone() {
PRBool threadSafe = PR_FALSE;
if (mLock)
threadSafe = PR_TRUE;
nsHashtable *newHashTable = new nsHashtable(hashtable->nentries, threadSafe);
PL_HashTableEnumerateEntries(hashtable, _hashEnumerateShare, newHashTable);
return newHashTable;
}
void nsHashtable::Enumerate(nsHashtableEnumFunc aEnumFunc, void* closure) {
_HashEnumerateArgs thunk;
thunk.fn = aEnumFunc;
thunk.arg = closure;
PL_HashTableEnumerateEntries(hashtable, _hashEnumerate, &thunk);
}
static PR_CALLBACK PRIntn _hashEnumerateRemove(PLHashEntry *he, PRIntn i, void *arg)
{
_HashEnumerateArgs* thunk = (_HashEnumerateArgs*)arg;
if (thunk)
return thunk->fn((nsHashKey *) he->key, he->value, thunk->arg)
? HT_ENUMERATE_REMOVE
: HT_ENUMERATE_STOP;
else
return HT_ENUMERATE_REMOVE;
}
void nsHashtable::Reset() {
Reset(NULL);
}
void nsHashtable::Reset(nsHashtableEnumFunc destroyFunc, void* closure)
{
if (destroyFunc != NULL)
{
_HashEnumerateArgs thunk;
thunk.fn = destroyFunc;
thunk.arg = closure;
PL_HashTableEnumerateEntries(hashtable, _hashEnumerateRemove, &thunk);
}
else
PL_HashTableEnumerateEntries(hashtable, _hashEnumerateRemove, NULL);
}
////////////////////////////////////////////////////////////////////////////////
nsStringKey::nsStringKey(const char* str)
{
mStr.Assign(str);
}
nsStringKey::nsStringKey(const PRUnichar* str)
{
mStr.Assign(str);
}
nsStringKey::nsStringKey(const nsStr& str)
{
mStr.Assign(str);
}
nsStringKey::~nsStringKey(void)
{
}
PRUint32 nsStringKey::HashValue(void) const
{
if(mStr.IsUnicode())
{
PRUint32 h;
PRUint32 n;
PRUint32 m;
const PRUnichar* c;
h = 0;
n = mStr.Length();
c = mStr.GetUnicode();
if(n < 16)
{ /* Hash every char in a short string. */
for(; n; c++, n--)
h = (h >> 28) ^ (h << 4) ^ *c;
}
else
{ /* Sample a la jave.lang.String.hash(). */
for(m = n / 8; n >= m; c += m, n -= m)
h = (h >> 28) ^ (h << 4) ^ *c;
}
return h;
}
return (PRUint32)PL_HashString((const void*) mStr.GetBuffer());
}
PRBool nsStringKey::Equals(const nsHashKey* aKey) const
{
return ((nsStringKey*)aKey)->mStr == mStr;
}
nsHashKey* nsStringKey::Clone() const
{
return new nsStringKey(mStr);
}
const nsString& nsStringKey::GetString() const
{
return mStr;
}
////////////////////////////////////////////////////////////////////////////////
// nsObjectHashtable: an nsHashtable where the elements are C++ objects to be
// deleted
nsObjectHashtable::nsObjectHashtable(nsHashtableCloneElementFunc cloneElementFun,
void* cloneElementClosure,
nsHashtableEnumFunc destroyElementFun,
void* destroyElementClosure,
PRUint32 aSize, PRBool threadSafe)
: nsHashtable(aSize, threadSafe),
mCloneElementFun(cloneElementFun),
mCloneElementClosure(cloneElementClosure),
mDestroyElementFun(destroyElementFun),
mDestroyElementClosure(destroyElementClosure)
{
}
nsObjectHashtable::~nsObjectHashtable()
{
Reset();
}
PR_CALLBACK PRIntn
nsObjectHashtable::CopyElement(PLHashEntry *he, PRIntn i, void *arg)
{
nsObjectHashtable *newHashtable = (nsObjectHashtable *)arg;
void* newElement =
newHashtable->mCloneElementFun((nsHashKey*)he->key, he->value,
newHashtable->mCloneElementClosure);
if (newElement == nsnull)
return HT_ENUMERATE_STOP;
newHashtable->Put((nsHashKey*)he->key, newElement);
return HT_ENUMERATE_NEXT;
}
nsHashtable*
nsObjectHashtable::Clone()
{
PRBool threadSafe = PR_FALSE;
if (mLock)
threadSafe = PR_TRUE;
nsObjectHashtable* newHashTable =
new nsObjectHashtable(mCloneElementFun, mCloneElementClosure,
mDestroyElementFun, mDestroyElementClosure,
hashtable->nentries, threadSafe);
PL_HashTableEnumerateEntries(hashtable, CopyElement, newHashTable);
return newHashTable;
}
void
nsObjectHashtable::Reset()
{
nsHashtable::Reset(mDestroyElementFun, mDestroyElementClosure);
}
PRBool
nsObjectHashtable::RemoveAndDelete(nsHashKey *aKey)
{
void *value = Remove(aKey);
if (value && mDestroyElementFun)
{
return (*mDestroyElementFun)(aKey, value, mDestroyElementClosure);
}
else
return PR_FALSE;
}
////////////////////////////////////////////////////////////////////////////////
// nsSupportsHashtable: an nsHashtable where the elements are nsISupports*
static PR_CALLBACK PRBool
_ReleaseElement(nsHashKey *aKey, void *aData, void* closure)
{
nsISupports* element = NS_STATIC_CAST(nsISupports*, aData);
NS_IF_RELEASE(element);
return PR_TRUE;
}
nsSupportsHashtable::~nsSupportsHashtable()
{
Enumerate(_ReleaseElement, nsnull);
}
void*
nsSupportsHashtable::Put(nsHashKey *aKey, void *aData)
{
nsISupports* element = NS_STATIC_CAST(nsISupports*, aData);
NS_IF_ADDREF(element);
return nsHashtable::Put(aKey, aData);
}
void*
nsSupportsHashtable::Get(nsHashKey *aKey)
{
void* data = nsHashtable::Get(aKey);
if (!data)
return nsnull;
nsISupports* element = NS_STATIC_CAST(nsISupports*, data);
NS_IF_ADDREF(element);
return data;
}
void*
nsSupportsHashtable::Remove(nsHashKey *aKey)
{
void* data = nsHashtable::Remove(aKey);
if (!data)
return nsnull;
nsISupports* element = NS_STATIC_CAST(nsISupports*, data);
NS_IF_RELEASE(element);
return data;
}
static PR_CALLBACK PRIntn
_hashEnumerateCopy(PLHashEntry *he, PRIntn i, void *arg)
{
nsHashtable *newHashtable = (nsHashtable *)arg;
nsISupports* element = NS_STATIC_CAST(nsISupports*, he->value);
NS_IF_ADDREF(element);
newHashtable->Put((nsHashKey*)he->key, he->value);
return HT_ENUMERATE_NEXT;
}
nsHashtable*
nsSupportsHashtable::Clone()
{
PRBool threadSafe = PR_FALSE;
if (mLock)
threadSafe = PR_TRUE;
nsSupportsHashtable* newHashTable =
new nsSupportsHashtable(hashtable->nentries, threadSafe);
PL_HashTableEnumerateEntries(hashtable, _hashEnumerateCopy, newHashTable);
return newHashTable;
}
void
nsSupportsHashtable::Reset()
{
Enumerate(_ReleaseElement, nsnull);
nsHashtable::Reset();
}
////////////////////////////////////////////////////////////////////////////////
// nsOpaqueKey: Where keys are opaque byte array blobs
//
// Note opaque keys are not copied by this constructor. If you want a private
// copy in each hash key, you must create one and pass it in to this function.
nsOpaqueKey::nsOpaqueKey(const char *aOpaqueKey, PRUint32 aKeyLength)
{
mOpaqueKey = aOpaqueKey;
mKeyLength = aKeyLength;
}
nsOpaqueKey::~nsOpaqueKey(void)
{
}
PRUint32
nsOpaqueKey::HashValue(void) const
{
PRUint32 h, i, k;
h = 0;
// Same hashing technique as for java.lang.String.hashCode()
if (mKeyLength <= 15) {
// A short key; Use a dense sampling to compute the hash code
for (i = 0; i < mKeyLength; i++)
h += 37 * mOpaqueKey[i];
} else {
// A long key; Use a sparse sampling to compute the hash code
k = mKeyLength >> 3;
for (i = 0; i < mKeyLength; i += k)
h += 39 * mOpaqueKey[i];
}
return h;
}
PRBool
nsOpaqueKey::Equals(const nsHashKey* aKey) const
{
nsOpaqueKey *otherKey = (nsOpaqueKey*)aKey;
if (mKeyLength != otherKey->mKeyLength)
return PR_FALSE;
return !(PRBool)memcmp(otherKey->mOpaqueKey, mOpaqueKey, mKeyLength);
}
nsHashKey*
nsOpaqueKey::Clone() const
{
return new nsOpaqueKey(mOpaqueKey, mKeyLength);
}
PRUint32
nsOpaqueKey::GetKeyLength() const
{
return mKeyLength;
}
const char*
nsOpaqueKey::GetKey() const
{
return mOpaqueKey;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,248 +0,0 @@
/* -*- 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):
*/
#ifndef nsHashtable_h__
#define nsHashtable_h__
#include "plhash.h"
#include "prlock.h"
#include "nsCom.h"
class NS_COM nsHashKey {
protected:
nsHashKey(void);
public:
virtual ~nsHashKey(void);
virtual PRUint32 HashValue(void) const = 0;
virtual PRBool Equals(const nsHashKey *aKey) const = 0;
virtual nsHashKey *Clone(void) const = 0;
};
// Enumerator callback function. Use
typedef PRBool (*nsHashtableEnumFunc)(nsHashKey *aKey, void *aData, void* closure);
class NS_COM nsHashtable {
protected:
// members
PLHashTable *hashtable;
PRLock *mLock;
public:
nsHashtable(PRUint32 aSize = 256, PRBool threadSafe = PR_FALSE);
~nsHashtable();
PRInt32 Count(void) { return hashtable->nentries; }
PRBool Exists(nsHashKey *aKey);
void *Put(nsHashKey *aKey, void *aData);
void *Get(nsHashKey *aKey);
void *Remove(nsHashKey *aKey);
nsHashtable *Clone();
void Enumerate(nsHashtableEnumFunc aEnumFunc, void* closure = NULL);
void Reset();
void Reset(nsHashtableEnumFunc destroyFunc, void* closure = NULL);
};
////////////////////////////////////////////////////////////////////////////////
// nsObjectHashtable: an nsHashtable where the elements are C++ objects to be
// deleted
typedef void* (*nsHashtableCloneElementFunc)(nsHashKey *aKey, void *aData, void* closure);
class NS_COM nsObjectHashtable : public nsHashtable {
public:
nsObjectHashtable(nsHashtableCloneElementFunc cloneElementFun,
void* cloneElementClosure,
nsHashtableEnumFunc destroyElementFun,
void* destroyElementClosure,
PRUint32 aSize = 256, PRBool threadSafe = PR_FALSE);
~nsObjectHashtable();
nsHashtable *Clone();
void Reset();
PRBool RemoveAndDelete(nsHashKey *aKey);
protected:
static PR_CALLBACK PRIntn CopyElement(PLHashEntry *he, PRIntn i, void *arg);
nsHashtableCloneElementFunc mCloneElementFun;
void* mCloneElementClosure;
nsHashtableEnumFunc mDestroyElementFun;
void* mDestroyElementClosure;
};
////////////////////////////////////////////////////////////////////////////////
// nsSupportsHashtable: an nsHashtable where the elements are nsISupports*
class NS_COM nsSupportsHashtable : public nsHashtable {
public:
nsSupportsHashtable(PRUint32 aSize = 256, PRBool threadSafe = PR_FALSE)
: nsHashtable(aSize, threadSafe) {}
~nsSupportsHashtable();
void *Put(nsHashKey *aKey, void *aData);
void *Get(nsHashKey *aKey);
void *Remove(nsHashKey *aKey);
nsHashtable *Clone();
void Reset();
};
////////////////////////////////////////////////////////////////////////////////
// nsISupportsKey: Where keys are nsISupports objects that get refcounted.
#include "nsISupports.h"
class nsISupportsKey : public nsHashKey {
protected:
nsISupports* mKey;
public:
nsISupportsKey(nsISupports* key) {
mKey = key;
NS_IF_ADDREF(mKey);
}
~nsISupportsKey(void) {
NS_IF_RELEASE(mKey);
}
PRUint32 HashValue(void) const {
return (PRUint32)mKey;
}
PRBool Equals(const nsHashKey *aKey) const {
return (mKey == ((nsISupportsKey *) aKey)->mKey);
}
nsHashKey *Clone(void) const {
return new nsISupportsKey(mKey);
}
};
////////////////////////////////////////////////////////////////////////////////
// nsVoidKey: Where keys are void* objects that don't get refcounted.
class nsVoidKey : public nsHashKey {
protected:
const void* mKey;
public:
nsVoidKey(const void* key) {
mKey = key;
}
PRUint32 HashValue(void) const {
return (PRUint32)mKey;
}
PRBool Equals(const nsHashKey *aKey) const {
return (mKey == ((const nsVoidKey *) aKey)->mKey);
}
nsHashKey *Clone(void) const {
return new nsVoidKey(mKey);
}
};
////////////////////////////////////////////////////////////////////////////////
// nsIDKey: Where keys are nsIDs (e.g. nsIID, nsCID).
#include "nsID.h"
class nsIDKey : public nsHashKey {
protected:
nsID mID;
public:
nsIDKey(const nsID &aID) {
mID = aID;
}
PRUint32 HashValue(void) const {
return mID.m0;
}
PRBool Equals(const nsHashKey *aKey) const {
return (mID.Equals(((const nsIDKey *) aKey)->mID));
}
nsHashKey *Clone(void) const {
return new nsIDKey(mID);
}
};
////////////////////////////////////////////////////////////////////////////////
// nsStringKey: Where keys are PRUnichar* or char*
// Some uses: hashing ProgIDs, filenames, URIs
#include "nsString.h"
class NS_COM nsStringKey : public nsHashKey {
protected:
nsAutoString mStr;
public:
nsStringKey(const char* str);
nsStringKey(const PRUnichar* str);
nsStringKey(const nsStr& str);
~nsStringKey(void);
PRUint32 HashValue(void) const;
PRBool Equals(const nsHashKey* aKey) const;
nsHashKey* Clone() const;
// For when the owner of the hashtable wants to peek at the actual
// string in the key. No copy is made, so be careful.
const nsString& GetString() const;
};
////////////////////////////////////////////////////////////////////////////////
// nsOpaqueKey: Where keys are opaque byte-array blobs
#include "nsString.h"
class NS_COM nsOpaqueKey : public nsHashKey {
protected:
const char* mOpaqueKey; // Byte array of opaque data
PRUint32 mKeyLength; // Length, in bytes, of mOpaqueKey
public:
// Note opaque keys are not copied by this constructor. If you want a private
// copy in each hash key, you must create one and pass it in to this function.
nsOpaqueKey(const char* aOpaqueKey, PRUint32 aKeyLength);
~nsOpaqueKey(void);
PRUint32 HashValue(void) const;
PRBool Equals(const nsHashKey* aKey) const;
nsHashKey* Clone() const;
// For when the owner of the hashtable wants to peek at the actual
// opaque array in the key. No copy is made, so be careful.
const char* GetKey() const;
PRUint32 GetKeyLength() const;
};
#endif

View File

@@ -1,247 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* 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) 1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
/*
* Implementation of nsHashEnumerator.
* Use it to expose nsIEnumerator interfaces around nsHashtable objects.
* Contributed by Rob Ginda, rginda@ix.netcom.com
*/
#include "nscore.h"
#include "nsHashtableEnumerator.h"
class nsHashtableEnumerator : public nsIBidirectionalEnumerator
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIENUMERATOR
NS_DECL_NSIBIDIRECTIONALENUMERATOR
public:
virtual ~nsHashtableEnumerator ();
nsHashtableEnumerator (nsHashtable *aHash,
NS_HASH_ENUMERATOR_CONVERTER aConverter,
void *aData);
nsHashtableEnumerator (); /* no implementation */
private:
NS_IMETHOD Reset(nsHashtable *aHash,
NS_HASH_ENUMERATOR_CONVERTER aConverter,
void *aData);
NS_IMETHOD ReleaseElements();
nsISupports **mElements;
PRInt16 mCount, mCurrent;
PRBool mDoneFlag;
};
struct nsHashEnumClosure
{
NS_HASH_ENUMERATOR_CONVERTER Converter;
nsISupports **Elements;
PRInt16 Current;
void *Data;
};
extern "C" NS_COM nsresult
NS_NewHashtableEnumerator (nsHashtable *aHash,
NS_HASH_ENUMERATOR_CONVERTER aConverter,
void *aData, nsIEnumerator **retval)
{
NS_PRECONDITION (retval, "null ptr");
*retval = nsnull;
nsHashtableEnumerator *hte = new nsHashtableEnumerator (aHash, aConverter,
aData);
if (!hte)
return NS_ERROR_OUT_OF_MEMORY;
return hte->QueryInterface (nsCOMTypeInfo<nsIEnumerator>::GetIID(),
(void **)retval);
}
nsHashtableEnumerator::nsHashtableEnumerator (nsHashtable *aHash,
NS_HASH_ENUMERATOR_CONVERTER aConverter,
void *aData)
: mElements(nsnull), mCount(0), mDoneFlag(PR_TRUE)
{
NS_INIT_REFCNT();
Reset (aHash, aConverter, aData);
}
PRBool
hash_enumerator (nsHashKey *aKey, void *aObject, void *closure)
{
nsresult rv;
nsHashEnumClosure *c = (nsHashEnumClosure *)closure;
rv = c->Converter (aKey, (void *)aObject, (void *)c->Data,
&c->Elements[c->Current]);
if (!NS_FAILED(rv))
c->Current++;
return PR_TRUE;
}
NS_IMETHODIMP
nsHashtableEnumerator::Reset (nsHashtable *aHash,
NS_HASH_ENUMERATOR_CONVERTER aConverter,
void *aData)
{
nsHashEnumClosure c;
ReleaseElements();
mCurrent = c.Current = 0;
mCount = aHash->Count();
if (mCount == 0)
return NS_ERROR_FAILURE;
mElements = c.Elements = new nsISupports*[mCount];
c.Data = aData;
c.Converter = aConverter;
aHash->Enumerate (&hash_enumerator, &c);
mCount = c.Current; /* some items may not have converted correctly */
mDoneFlag = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsHashtableEnumerator::ReleaseElements()
{
for (; mCount > 0; mCount--)
if (mElements[mCount - 1])
NS_RELEASE(mElements[mCount - 1]);
delete[] mElements;
mElements = nsnull;
return NS_OK;
}
NS_IMPL_ISUPPORTS2(nsHashtableEnumerator, nsIBidirectionalEnumerator, nsIEnumerator)
nsHashtableEnumerator::~nsHashtableEnumerator()
{
ReleaseElements();
}
NS_IMETHODIMP
nsHashtableEnumerator::First ()
{
if (!mElements || (mCount == 0))
return NS_ERROR_FAILURE;
mCurrent = 0;
mDoneFlag = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsHashtableEnumerator::Last ()
{
if (!mElements || (mCount == 0))
return NS_ERROR_FAILURE;
mCurrent = mCount - 1;
mDoneFlag = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsHashtableEnumerator::Prev ()
{
if (!mElements || (mCount == 0) || (mCurrent == 0)) {
mDoneFlag = PR_TRUE;
return NS_ERROR_FAILURE;
}
mCurrent--;
mDoneFlag = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsHashtableEnumerator::Next ()
{
if (!mElements || (mCount == 0) || (mCurrent == mCount - 1)) {
mDoneFlag = PR_TRUE;
return NS_ERROR_FAILURE;
}
mCurrent++;
mDoneFlag = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP
nsHashtableEnumerator::CurrentItem (nsISupports **retval)
{
NS_PRECONDITION (retval, "null ptr");
NS_PRECONDITION (mElements, "invalid state");
NS_ASSERTION (mCurrent >= 0, "mCurrent less than zero");
if (mCount == 0)
{
*retval = nsnull;
return NS_ERROR_FAILURE;
}
NS_ASSERTION (mCurrent <= mCount - 1, "mCurrent too high");
*retval = mElements[mCurrent];
/* who says the item can't be null? */
if (*retval)
NS_ADDREF(*retval);
return NS_OK;
}
NS_IMETHODIMP
nsHashtableEnumerator::IsDone ()
{
if ((!mElements) || (mCount == 0) || (mDoneFlag))
return NS_OK;
return NS_COMFALSE;
}

Some files were not shown because too many files have changed in this diff Show More