diff options
Diffstat (limited to 'plugin/icedteanp/java')
32 files changed, 8153 insertions, 0 deletions
diff --git a/plugin/icedteanp/java/netscape/javascript/JSException.java b/plugin/icedteanp/java/netscape/javascript/JSException.java new file mode 100644 index 0000000..96ba310 --- /dev/null +++ b/plugin/icedteanp/java/netscape/javascript/JSException.java @@ -0,0 +1,140 @@ +/* -*- Mode: Java; tab-width: 8; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package netscape.javascript; + +/** + * JSException is an exception which is thrown when JavaScript code + * returns an error. + */ + +public +class JSException extends RuntimeException { + public static final int EXCEPTION_TYPE_EMPTY = -1; + public static final int EXCEPTION_TYPE_VOID = 0; + public static final int EXCEPTION_TYPE_OBJECT = 1; + public static final int EXCEPTION_TYPE_FUNCTION = 2; + public static final int EXCEPTION_TYPE_STRING = 3; + public static final int EXCEPTION_TYPE_NUMBER = 4; + public static final int EXCEPTION_TYPE_BOOLEAN = 5; + public static final int EXCEPTION_TYPE_ERROR = 6; + + public String filename; + public int lineno; + public String source; + public int tokenIndex; + public int wrappedExceptionType; + public Object wrappedException; + + /** + * Constructs a JSException without a detail message. + * A detail message is a String that describes this particular exception. + * + * @deprecated Not for public use in future versions. + */ + public JSException() { + super(); + filename = "unknown"; + lineno = 0; + source = ""; + tokenIndex = 0; + wrappedExceptionType = EXCEPTION_TYPE_EMPTY; + } + + /** + * Constructs a JSException with a detail message. + * A detail message is a String that describes this particular exception. + * @param s the detail message + * + * @deprecated Not for public use in future versions. + */ + public JSException(String s) { + super(s); + filename = "unknown"; + lineno = 0; + source = ""; + tokenIndex = 0; + wrappedExceptionType = EXCEPTION_TYPE_EMPTY; + } + + /** + * Constructs a JSException with a wrapped JavaScript exception object. + * This constructor needs to be public so that Java users can throw + * exceptions to JS cleanly. + */ + public JSException(int wrappedExceptionType, Object wrappedException) { + super(); + this.wrappedExceptionType = wrappedExceptionType; + this.wrappedException = wrappedException; + } + + /** + * Constructs a JSException with a detail message and all the + * other info that usually comes with a JavaScript error. + * @param s the detail message + * + * @deprecated Not for public use in future versions. + */ + 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; + wrappedExceptionType = EXCEPTION_TYPE_EMPTY; + } + + /** + * Instance method getWrappedExceptionType returns the int mapping of the + * type of the wrappedException Object. + */ + public int getWrappedExceptionType() { + return wrappedExceptionType; + } + + /** + * Instance method getWrappedException. + */ + public Object getWrappedException() { + return wrappedException; + } + +} + diff --git a/plugin/icedteanp/java/netscape/javascript/JSObject.java b/plugin/icedteanp/java/netscape/javascript/JSObject.java new file mode 100644 index 0000000..74158c2 --- /dev/null +++ b/plugin/icedteanp/java/netscape/javascript/JSObject.java @@ -0,0 +1,297 @@ +/* -*- Mode: Java; tab-width: 8; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* more doc todo: + * threads + * gc + * + * + */ + +package netscape.javascript; + +import java.applet.Applet; +import java.security.AccessControlContext; +import java.security.AccessControlException; +import java.security.AccessController; +import java.security.BasicPermission; + +import sun.applet.PluginAppletViewer; +import sun.applet.PluginDebug; + + + +/** + * 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 + * doubleValue 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, Double, + * 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 long internal; + + /** + * initialize + */ + private static void initClass() { + PluginDebug.debug ("JSObject.initClass"); + } + + static { + PluginDebug.debug ("JSObject INITIALIZER"); + } + + /** + * it is illegal to construct a JSObject manually + */ + public JSObject(int jsobj_addr) { + this((long) jsobj_addr); + } + + /** + * it is illegal to construct a JSObject manually + */ + public JSObject(String jsobj_addr) { + this((long) Long.parseLong(jsobj_addr)); + } + + public JSObject(long jsobj_addr) { + + // See if the caller has permission + + try { + AccessController.getContext().checkPermission(new JSObjectCreatePermission()); + } catch (AccessControlException ace) { + + // If not, only caller with JSObject.getWindow on the stack may + // make this call unprivileged. + + // Although this check is inefficient, it should happen only once + // during applet init, so we look the other way + + StackTraceElement[] stack = Thread.currentThread().getStackTrace(); + boolean mayProceed = false; + + for (int i=0; i < stack.length; i++) { + if (stack[i].getClassName().equals("netscape.javascript.JSObject") && + stack[i].getMethodName().equals("getWindow")) { + mayProceed = true; + } + } + + if (!mayProceed) throw ace; + } + + PluginDebug.debug ("JSObject long CONSTRUCTOR"); + internal = jsobj_addr; + } + + /** + * Retrieves a named member of a JavaScript object. + * Equivalent to "this.<i>name</i>" in JavaScript. + */ + public Object getMember(String name) + { + PluginDebug.debug ("JSObject.getMember " + name); + + Object o = PluginAppletViewer.getMember(internal, name); + PluginDebug.debug ("JSObject.getMember GOT " + o); + return o; + } + + + /** + * 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 Object getSlot(int index) + { + PluginDebug.debug ("JSObject.getSlot " + index); + + return PluginAppletViewer.getSlot(internal, index); + } + + + /** + * Sets a named member of a JavaScript object. + * Equivalent to "this.<i>name</i> = <i>value</i>" in JavaScript. + */ + public void setMember(String name, Object value) + { + PluginDebug.debug ("JSObject.setMember " + name + " " + value); + + PluginAppletViewer.setMember(internal, name, 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 void setSlot(int index, Object value) + { + PluginDebug.debug ("JSObject.setSlot " + index + " " + value); + + PluginAppletViewer.setSlot(internal, index, value); + } + + + // TODO: toString, finalize. + + /** + * Removes a named member of a JavaScript object. + */ + public void removeMember(String name) + { + PluginDebug.debug ("JSObject.removeMember " + name); + + PluginAppletViewer.removeMember(internal, name); + } + + + /** + * Calls a JavaScript method. + * Equivalent to "this.<i>methodName</i>(<i>args</i>[0], <i>args</i>[1], ...)" in JavaScript. + */ + public Object call(String methodName, Object args[]) + { + if (args == null) + args = new Object[0]; + + PluginDebug.debug ("JSObject.call " + methodName); + for (int i = 0; i < args.length; i++) + PluginDebug.debug (" " + args[i]); + PluginDebug.debug(""); + return PluginAppletViewer.call(internal, methodName, 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 Object eval(String s) + { + PluginDebug.debug("JSObject.eval " + s); + return PluginAppletViewer.eval(internal, s); + } + + + /** + * Converts a JSObject to a String. + */ + public String toString() + { + PluginDebug.debug("JSObject.toString"); + return PluginAppletViewer.javascriptToString(internal); + } + + + // 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 JSObject getWindow(Applet applet) + { + PluginDebug.debug("JSObject.getWindow"); + // FIXME: handle long case as well. + long internal = 0; + internal = ((PluginAppletViewer) + applet.getAppletContext()).getWindow(); + PluginDebug.debug ("GOT IT: " + internal); + return new JSObject((long) internal); + } + + + /** + * Finalization decrements the reference count on the corresponding + * JavaScript object. + */ + protected void finalize() + { + PluginDebug.debug("JSObject.finalize "); + PluginAppletViewer.JavaScriptFinalize(internal); + } + + + /** + * Override java.lang.Object.equals() because identity is not preserved + * with instances of JSObject. + */ + public boolean equals(Object obj) + { + PluginDebug.debug("JSObject.equals " + obj); + + return false; + } +} diff --git a/plugin/icedteanp/java/netscape/javascript/JSObjectCreatePermission.java b/plugin/icedteanp/java/netscape/javascript/JSObjectCreatePermission.java new file mode 100644 index 0000000..f8e510f --- /dev/null +++ b/plugin/icedteanp/java/netscape/javascript/JSObjectCreatePermission.java @@ -0,0 +1,47 @@ +/* JSObjectCreatePermission.java + Copyright (C) 2009 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package netscape.javascript; + +import java.security.BasicPermission; + + +public class JSObjectCreatePermission extends BasicPermission { + public JSObjectCreatePermission() { + super("JSObjectCreate"); + } +} diff --git a/plugin/icedteanp/java/netscape/javascript/JSProxy.java b/plugin/icedteanp/java/netscape/javascript/JSProxy.java new file mode 100644 index 0000000..e4f1f6e --- /dev/null +++ b/plugin/icedteanp/java/netscape/javascript/JSProxy.java @@ -0,0 +1,58 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/** + * 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); +} diff --git a/plugin/icedteanp/java/netscape/javascript/JSRunnable.java b/plugin/icedteanp/java/netscape/javascript/JSRunnable.java new file mode 100644 index 0000000..8f1cf72 --- /dev/null +++ b/plugin/icedteanp/java/netscape/javascript/JSRunnable.java @@ -0,0 +1,72 @@ +/* -*- Mode: Java; tab-width: 8; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +package netscape.javascript; + +import sun.applet.PluginDebug; + +/** + * 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) { + PluginDebug.debug(t.toString()); + t.printStackTrace(System.err); + } + } +} diff --git a/plugin/icedteanp/java/netscape/javascript/JSUtil.java b/plugin/icedteanp/java/netscape/javascript/JSUtil.java new file mode 100644 index 0000000..47bc6e6 --- /dev/null +++ b/plugin/icedteanp/java/netscape/javascript/JSUtil.java @@ -0,0 +1,59 @@ +/* -*- Mode: Java; tab-width: 8; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* ** */ + +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; + PrintWriter p; + + captureStream = new ByteArrayOutputStream(); + p = new PrintWriter(captureStream); + + t.printStackTrace(p); + p.flush(); + + return captureStream.toString(); + } +} diff --git a/plugin/icedteanp/java/netscape/security/ForbiddenTargetException.java b/plugin/icedteanp/java/netscape/security/ForbiddenTargetException.java new file mode 100644 index 0000000..c7ce827 --- /dev/null +++ b/plugin/icedteanp/java/netscape/security/ForbiddenTargetException.java @@ -0,0 +1,52 @@ +/* ForbiddenTargetException.java + Copyright (C) 2010 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package netscape.security; + +public class ForbiddenTargetException extends RuntimeException{ + + private static final long serialVersionUID = 1271219852541058396L; + + public ForbiddenTargetException() { + super(); + } + + public ForbiddenTargetException(String s) { + super(s); + } + +} diff --git a/plugin/icedteanp/java/sun/applet/AppletSecurityContextManager.java b/plugin/icedteanp/java/sun/applet/AppletSecurityContextManager.java new file mode 100644 index 0000000..3820aa4 --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/AppletSecurityContextManager.java @@ -0,0 +1,71 @@ +/* VoidPluginCallRequest -- represent Java-to-JavaScript requests + Copyright (C) 2008 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package sun.applet; + +import java.security.AccessControlContext; +import java.util.HashMap; + +public class AppletSecurityContextManager { + + // Context identifier -> PluginAppletSecurityContext object. + // FIXME: make private + private static HashMap<Integer, PluginAppletSecurityContext> contexts = new HashMap(); + + public static void addContext(int identifier, PluginAppletSecurityContext context) { + contexts.put(identifier, context); + } + + public static PluginAppletSecurityContext getSecurityContext(int identifier) { + return contexts.get(identifier); + } + + public static void dumpStore(int identifier) { + contexts.get(identifier).dumpStore(); + } + + public static void handleMessage(int identifier, int reference, String src, String[] privileges, String message) { + PluginDebug.debug(identifier + " -- " + src + " -- " + reference + " -- " + message + " CONTEXT= " + contexts.get(identifier)); + AccessControlContext callContext = null; + + privileges = privileges != null ? privileges : new String[0]; + callContext = contexts.get(identifier).getAccessControlContext(privileges, src); + + contexts.get(identifier).handleMessage(reference, src, callContext, message); + } +} diff --git a/plugin/icedteanp/java/sun/applet/GetMemberPluginCallRequest.java b/plugin/icedteanp/java/sun/applet/GetMemberPluginCallRequest.java new file mode 100644 index 0000000..7a0a623 --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/GetMemberPluginCallRequest.java @@ -0,0 +1,63 @@ +/* GetMemberPluginCallRequest -- represent Java-to-JavaScript requests + Copyright (C) 2008 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package sun.applet; + + +public class GetMemberPluginCallRequest extends PluginCallRequest { + Object object = null; + + public GetMemberPluginCallRequest(String message, Long reference) { + super(message, reference); + PluginDebug.debug ("GetMemberPluginCall " + message); + } + + public void parseReturn(String message) { + PluginDebug.debug ("GetMemberParseReturn GOT: " + message); + String[] args = message.split(" "); + // FIXME: Is it even possible to distinguish between null and void + // here? + if (args[3] != "null" && args[3] != "void") + object = AppletSecurityContextManager.getSecurityContext(0).getObject(Integer.parseInt(args[3])); + setDone(true); + } + + public Object getObject() { + return this.object; + } +} + diff --git a/plugin/icedteanp/java/sun/applet/GetWindowPluginCallRequest.java b/plugin/icedteanp/java/sun/applet/GetWindowPluginCallRequest.java new file mode 100644 index 0000000..9c13726 --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/GetWindowPluginCallRequest.java @@ -0,0 +1,65 @@ +/* GetWindowPluginCallRequest -- represent Java-to-JavaScript requests + Copyright (C) 2008 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package sun.applet; + +import java.security.AccessControlContext; +import java.security.ProtectionDomain; + + + +public class GetWindowPluginCallRequest extends PluginCallRequest { + // FIXME: look into int vs long JavaScript internal values. + long internal; + + public GetWindowPluginCallRequest(String message, Long reference) { + super(message, reference); + } + + public void parseReturn(String message) { + PluginDebug.debug ("GetWindowParseReturn GOT: " + message); + String[] args = message.split(" "); + // FIXME: add thread ID to messages to support multiple + // threads using the netscape.javascript package. + internal = Long.parseLong(args[3]); + setDone(true); + } + + public Long getObject() { + return this.internal; + } +} diff --git a/plugin/icedteanp/java/sun/applet/JavaConsole.java b/plugin/icedteanp/java/sun/applet/JavaConsole.java new file mode 100644 index 0000000..768be7b --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/JavaConsole.java @@ -0,0 +1,365 @@ +/* JavaConsole -- A java console for the plugin + Copyright (C) 2009 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package sun.applet; + +import java.awt.Dimension; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTextArea; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +/** + * A simple Java console for IcedTeaPlugin + * + */ +public class JavaConsole { + + private boolean initialized = false; + + JFrame consoleWindow; + JTextArea stdErrText; + JTextArea stdOutText; + + /** + * Initialize the console + */ + public void initialize() { + + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) { + e.printStackTrace(); + } + + consoleWindow = new JFrame("Java Console"); + + JPanel contentPanel = new JPanel(); + contentPanel.setLayout(new GridBagLayout()); + + GridBagConstraints c; + + Font monoSpace = new Font("Monospaced", Font.PLAIN, 12); + + /* std out */ + + stdOutText = new JTextArea(); + JScrollPane stdOutScrollPane = new JScrollPane(stdOutText); + stdOutScrollPane.setBorder(new TitledBorder( + new EmptyBorder(5, 5, 5, 5), "System.out")); + stdOutText.setEditable(false); + stdOutText.setFont(monoSpace); + + TextAreaUpdater stdOutUpdater = new TextAreaUpdater(new File( + PluginMain.PLUGIN_STDOUT_FILE), stdOutText); + stdOutUpdater.setName("IcedteaPlugin Console Thread(System.out)"); + + /* std err */ + + stdErrText = new JTextArea(); + JScrollPane stdErrScrollPane = new JScrollPane(stdErrText); + stdErrScrollPane.setBorder(new TitledBorder( + new EmptyBorder(5, 5, 5, 5), "System.err")); + stdErrText.setEditable(false); + stdErrText.setFont(monoSpace); + + TextAreaUpdater stdErrUpdater = new TextAreaUpdater(new File( + PluginMain.PLUGIN_STDERR_FILE), stdErrText); + stdErrUpdater.setName("IcedteaPlugin Console Thread(System.err)"); + + JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, + stdOutScrollPane, stdErrScrollPane); + + c = new GridBagConstraints(); + c.fill = GridBagConstraints.BOTH; + c.gridheight = 10; + c.weighty = 1; + + contentPanel.add(splitPane, c); + + /* buttons */ + + c = new GridBagConstraints(); + c.gridy = 10; + c.gridheight = 1; + c.weightx = 0.5; + c.weighty = 0; + + JPanel buttonPanel = new JPanel(); + contentPanel.add(buttonPanel, c); + + JButton gcButton = new JButton("Run GC"); + buttonPanel.add(gcButton); + gcButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + printMemoryInfo(); + System.out.print("Performing Garbage Collection...."); + System.gc(); + System.out.println("Done"); + printMemoryInfo(); + } + + }); + + JButton finalizersButton = new JButton("Run Finalizers"); + buttonPanel.add(finalizersButton); + finalizersButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + printMemoryInfo(); + System.out.print("Running finalization...."); + Runtime.getRuntime().runFinalization(); + System.out.println("Done"); + printMemoryInfo(); + } + }); + + JButton memoryButton = new JButton("Memory Info"); + buttonPanel.add(memoryButton); + memoryButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + printMemoryInfo(); + } + + }); + + JButton systemPropertiesButton = new JButton("System Properties"); + buttonPanel.add(systemPropertiesButton); + systemPropertiesButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + printSystemProperties(); + } + + }); + + JButton classloadersButton = new JButton("Classloaders"); + buttonPanel.add(classloadersButton); + classloadersButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + printClassLoaders(); + } + + }); + + JButton threadListButton = new JButton("Thread List"); + buttonPanel.add(threadListButton); + threadListButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + printThreadInfo(); + } + + }); + + JButton closeButton = new JButton("Close"); + buttonPanel.add(closeButton); + closeButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + hideConsole(); + } + }); + } + }); + + stdOutUpdater.start(); + stdErrUpdater.start(); + + consoleWindow.add(contentPanel); + consoleWindow.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); + consoleWindow.pack(); + consoleWindow.setSize(new Dimension(900, 600)); + consoleWindow.setMinimumSize(new Dimension(900, 300)); + + initialized = true; + + splitPane.setDividerLocation(0.5); + splitPane.setResizeWeight(0.5); + } + + public void showConsole() { + + if (!initialized) { + initialize(); + } + + consoleWindow.setVisible(true); + } + + public void hideConsole() { + consoleWindow.setVisible(false); + } + + protected void printSystemProperties() { + + System.out.println(" ----"); + System.out.println("System Properties:"); + System.out.println(); + Properties p = System.getProperties(); + Set<Object> keys = p.keySet(); + for (Object key : keys) { + System.out.println(key.toString() + ": " + p.get(key)); + } + + System.out.println(" ----"); + } + + private void printClassLoaders() { + System.out.println(" ----"); + System.out.println("Available Classloaders: "); + Set<String> loaders = PluginAppletSecurityContext.getLoaderInfo().keySet(); + for (String loader: loaders) { + System.out.println(loader + "\n" + + " codebase = " + + PluginAppletSecurityContext.getLoaderInfo().get(loader)); + } + System.out.println(" ----"); + } + + private void printMemoryInfo() { + System.out.println(" ----- "); + System.out.println(" Memory Info:"); + System.out.println(" Max Memory: " + + String.format("%1$10d", Runtime.getRuntime().maxMemory())); + System.out.println(" Total Memory: " + + String.format("%1$10d", Runtime.getRuntime().totalMemory())); + System.out.println(" Free Memory: " + + String.format("%1$10d", Runtime.getRuntime().freeMemory())); + System.out.println(" ----"); + + } + + private void printThreadInfo() { + Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces(); + Set<Thread> keys = map.keySet(); + for (Thread key : keys) { + System.out.println("Thread " + key.getId() + ": " + key.getName()); + for (StackTraceElement element : map.get(key)) { + System.out.println(" " + element); + } + + } + } + + public static void main(String[] args) { + + final JavaConsole console = new JavaConsole(); + + boolean toShowConsole = false; + + for (int i = 0; i < args.length; i++) { + if (args[i] == "--show-console") { + toShowConsole = true; + } + } + + if (toShowConsole) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + console.showConsole(); + } + }); + } + + } + + /** + * This thread updates the text on a JTextArea based on the text in a file + */ + class TextAreaUpdater extends Thread { + + File fileToRead; + JTextArea outputTextArea; + + public TextAreaUpdater(File file, JTextArea textArea) { + fileToRead = file; + outputTextArea = textArea; + setDaemon(true); + } + + public void run() { + + try { + BufferedReader reader = new BufferedReader(new FileReader( + fileToRead)); + String line; + while (true) { + while ((line = reader.readLine()) != null) { + outputTextArea.insert(line + "\n", outputTextArea + .getDocument().getLength()); + outputTextArea.setCaretPosition(outputTextArea + .getText().length()); + } + Thread.sleep(1000); + } + + } catch (FileNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + + } + + } + +} diff --git a/plugin/icedteanp/java/sun/applet/MethodOverloadResolver.java b/plugin/icedteanp/java/sun/applet/MethodOverloadResolver.java new file mode 100644 index 0000000..bb41e27 --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/MethodOverloadResolver.java @@ -0,0 +1,696 @@ +/* MethodOverloadResolver -- Resolves overloaded methods + Copyright (C) 2009 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package sun.applet; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; + +/* + * This class resolved overloaded methods in Java objects using a cost + * based-approach as described here: + * + * http://java.sun.com/javase/6/webnotes/6u10/plugin2/liveconnect/#OVERLOADED_METHODS + */ + +public class MethodOverloadResolver { + + private static boolean debugging = false; + + public static void main(String[] args) { + testMethodResolver(); + } + + public static void testMethodResolver() { + debugging = true; + + ArrayList<Object[]> list = new ArrayList<Object[]>(20); + FooClass fc = new FooClass(); + + // Numeric to java primitive + // foo_i has Integer and int params + String s1 = "foo_string_int(S,I)"; + String s1a = "foo_string_int(S,S)"; + Object[] o1 = { fc.getClass(), "foo_string_int", "blah", 42 }; + list.add(o1); + Object[] o1a = { fc.getClass(), "foo_string_int", "blah", "42.42" }; + list.add(o1a); + + // Null to non-primitive type + // foo_i is overloaded with Integer and int + String s2 = "foo_string_int(N)"; + Object[] o2 = { fc.getClass(), "foo_string_int", "blah", null }; + list.add(o2); + + // foo_jsobj is overloaded with JSObject and String params + String s3 = "foo_jsobj(LLowCostSignatureComputer/JSObject;)"; + Object[] o3 = { fc.getClass(), "foo_jsobj", new JSObject() }; + list.add(o3); + + // foo_classtype is overloaded with Number and Integer + String s4 = "foo_classtype(Ljava/lang/Integer;)"; + Object[] o4 = { fc.getClass(), "foo_classtype", 42 }; + list.add(o4); + + // foo_multiprim is overloaded with int, long and float types + String s5 = "foo_multiprim(I)"; + String s6 = "foo_multiprim(F)"; + String s6a = "foo_multiprim(D)"; + + Object[] o5 = { fc.getClass(), "foo_multiprim", new Integer(42) }; + Object[] o6 = { fc.getClass(), "foo_multiprim", new Float(42.42) }; + Object[] o6a = { fc.getClass(), "foo_multiprim", new Double(42.42) }; + list.add(o5); + list.add(o6); + list.add(o6a); + + // foo_float has float, String and JSObject type + String s7 = "foo_float(I)"; + Object[] o7 = { fc.getClass(), "foo_float", new Integer(42) }; + list.add(o7); + + // foo_multiprim(float) is what this should convert + String s8 = "foo_float(S)"; + Object[] o8 = { fc.getClass(), "foo_float", "42" }; + list.add(o8); + + // foo_class is overloaded with BarClass 2 and 3 + String s9 = "foo_class(LLowCostSignatureComputer/BarClass3;)"; + Object[] o9 = { fc.getClass(), "foo_class", new BarClass3() }; + list.add(o9); + + // foo_strandbyteonly takes string and byte + String s10 = "foo_strandbyteonly(I)"; + Object[] o10 = { fc.getClass(), "foo_strandbyteonly", 42 }; + list.add(o10); + + // JSOBject to string + String s11 = "foo_strandbyteonly(LLowCostSignatureComputer/JSObject;)"; + Object[] o11 = { fc.getClass(), "foo_strandbyteonly", new JSObject() }; + list.add(o11); + + // jsobject to string and int to float + String s12 = "foo_str_and_float(S,I)"; + Object[] o12 = { fc.getClass(), "foo_str_and_float", new JSObject(), new Integer(42) }; + list.add(o12); + + // call for which no match will be found + String s13 = "foo_int_only(JSObject)"; + Object[] o13 = { fc.getClass(), "foo_int_only", new JSObject() }; + list.add(o13); + + // method with no args + String s14 = "foo_noargs()"; + Object[] o14 = { fc.getClass(), "foo_noargs" }; + list.add(o14); + + // method which takes a primitive bool, given a Boolean + String s15 = "foo_boolonly()"; + Object[] o15 = { fc.getClass(), "foo_boolonly", new Boolean(true) }; + list.add(o15); + + for (Object[] o : list) { + Object[] methodAndArgs = getMatchingMethod(o); + if (debugging) + if (methodAndArgs != null) + System.out.println("Best match: " + methodAndArgs[0] + "\n"); + else + System.out.println("No match found.\n"); + + } + + } + + /* + * Cost based overload resolution algorithm based on cost rules specified here: + * + * http://java.sun.com/javase/6/webnotes/6u10/plugin2/liveconnect/#OVERLOADED_METHODS + */ + + public static Object[] getMatchingMethod(Object[] callList) { + Object[] ret = null; + Class c = (Class) callList[0]; + String methodName = (String) callList[1]; + + Method[] matchingMethods = getMatchingMethods(c, methodName, callList.length - 2); + + if (debugging) + System.out.println("getMatchingMethod called with: " + printList(callList)); + + int lowestCost = Integer.MAX_VALUE; + + ArrayList<Object> paramList = new ArrayList<Object>(); + + for (Method matchingMethod : matchingMethods) { + + int methodCost = 0; + Class[] paramTypes = matchingMethod.getParameterTypes(); + Object[] methodAndArgs = new Object[paramTypes.length + 1]; + methodAndArgs[0] = matchingMethod; + + // Figure out which of the matched methods best represents what we + // want + for (int i = 0; i < paramTypes.length; i++) { + Class paramTypeClass = paramTypes[i]; + Object suppliedParam = callList[i + 2]; + Class suppliedParamClass = suppliedParam != null ? suppliedParam + .getClass() + : null; + + Object[] costAndCastedObj = getCostAndCastedObject( + suppliedParam, paramTypeClass); + methodCost += (Integer) costAndCastedObj[0]; + + if ((Integer) costAndCastedObj[0] < 0) break; + + Object castedObj = paramTypeClass.isPrimitive() ? costAndCastedObj[1] + : paramTypeClass.cast(costAndCastedObj[1]); + methodAndArgs[i + 1] = castedObj; + + Class castedObjClass = castedObj == null ? null : castedObj + .getClass(); + Boolean castedObjIsPrim = castedObj == null ? null : castedObj + .getClass().isPrimitive(); + + if (debugging) + System.out.println("Param " + i + " of method " + + matchingMethod + " has cost " + + (Integer) costAndCastedObj[0] + + " original param type " + suppliedParamClass + + " casted to " + castedObjClass + " isPrimitive=" + + castedObjIsPrim + " value " + castedObj); + } + + if ((methodCost > 0 && methodCost < lowestCost) || + paramTypes.length == 0) { + ret = methodAndArgs; + lowestCost = methodCost; + } + + } + + return ret; + } + + public static Object[] getMatchingConstructor(Object[] callList) { + Object[] ret = null; + Class c = (Class) callList[0]; + + Constructor[] matchingConstructors = getMatchingConstructors(c, callList.length - 1); + + if (debugging) + System.out.println("getMatchingConstructor called with: " + printList(callList)); + + int lowestCost = Integer.MAX_VALUE; + + ArrayList<Object> paramList = new ArrayList<Object>(); + + for (Constructor matchingConstructor : matchingConstructors) { + + int constructorCost = 0; + Class[] paramTypes = matchingConstructor.getParameterTypes(); + Object[] constructorAndArgs = new Object[paramTypes.length + 1]; + constructorAndArgs[0] = matchingConstructor; + + // Figure out which of the matched methods best represents what we + // want + for (int i = 0; i < paramTypes.length; i++) { + Class paramTypeClass = paramTypes[i]; + Object suppliedParam = callList[i + 1]; + Class suppliedParamClass = suppliedParam != null ? suppliedParam + .getClass() + : null; + + Object[] costAndCastedObj = getCostAndCastedObject( + suppliedParam, paramTypeClass); + constructorCost += (Integer) costAndCastedObj[0]; + + if ((Integer) costAndCastedObj[0] < 0) break; + + Object castedObj = paramTypeClass.isPrimitive() ? costAndCastedObj[1] + : paramTypeClass.cast(costAndCastedObj[1]); + constructorAndArgs[i + 1] = castedObj; + + Class castedObjClass = castedObj == null ? null : castedObj + .getClass(); + Boolean castedObjIsPrim = castedObj == null ? null : castedObj + .getClass().isPrimitive(); + + if (debugging) + System.out.println("Param " + i + " of constructor " + + matchingConstructor + " has cost " + + (Integer) costAndCastedObj[0] + + " original param type " + suppliedParamClass + + " casted to " + castedObjClass + " isPrimitive=" + + castedObjIsPrim + " value " + castedObj); + } + + if ((constructorCost > 0 && constructorCost < lowestCost) || + paramTypes.length == 0) { + ret = constructorAndArgs; + lowestCost = constructorCost; + } + } + + return ret; + } + + public static Object[] getCostAndCastedObject(Object suppliedParam, Class paramTypeClass) { + + Object[] ret = new Object[2]; + Integer cost = new Integer(0); + Object castedObj; + + Class suppliedParamClass = suppliedParam != null ? suppliedParam.getClass() : null ; + + // Either both are an array, or neither are + boolean suppliedParamIsArray = suppliedParamClass != null && suppliedParamClass.isArray(); + if (paramTypeClass.isArray() != suppliedParamIsArray && + !paramTypeClass.equals(Object.class) && + !paramTypeClass.equals(String.class)) { + ret[0] = Integer.MIN_VALUE; // Not allowed + ret[1] = suppliedParam; + return ret; + } + + // If param type is an array, supplied obj must be an array, Object or String (guaranteed by checks above) + // If it is an array, we need to copy/cast as we scan the array + // If it an object, we return "as is" [Everything can be narrowed to an object, cost=6] + // If it is a string, we need to convert according to the JS engine rules + + if (paramTypeClass.isArray()) { + + Object newArray = Array.newInstance(paramTypeClass.getComponentType(), Array.getLength(suppliedParam)); + for (int i=0; i < Array.getLength(suppliedParam); i++) { + Object original = Array.get(suppliedParam, i); + + // When dealing with arrays, we represent empty slots with + // null. We need to convert this to 0 before recursive + // calling, since normal transformation does not allow + // null -> primitive + + if (original == null && paramTypeClass.getComponentType().isPrimitive()) + original = 0; + + Object[] costAndCastedObject = getCostAndCastedObject(original, paramTypeClass.getComponentType()); + + if ((Integer) costAndCastedObject[0] < 0) { + ret[0] = Integer.MIN_VALUE; // Not allowed + ret[1] = suppliedParam; + return ret; + } + + Array.set(newArray, i, costAndCastedObject[1]); + } + + ret[0] = 9; + ret[1] = newArray; + return ret; + } + + if (suppliedParamIsArray && paramTypeClass.equals(String.class)) { + + ret[0] = 9; + ret[1] = getArrayAsString(suppliedParam); + return ret; + } + + // If this is null, there are only 2 possible cases + if (suppliedParamClass == null) { + castedObj = null; // if value is null.. well, it is null + + if (!paramTypeClass.isPrimitive()) { + cost += 2; // Null to any non-primitive type + } else { + cost = Integer.MIN_VALUE; // Null to primitive not allowed + } + } else if (paramTypeClass.isPrimitive() && paramTypeClass.equals(getPrimitive(suppliedParam))) { + cost += 1; // Numeric type to the analogous Java primitive type + castedObj = suppliedParam; // Let auto-boxing handle it + } else if (suppliedParamClass.equals(paramTypeClass)) { + cost += 3; // Class type to Class type where the types are equal + castedObj = suppliedParam; + } else if (isNum(suppliedParam) && + (paramTypeClass.isPrimitive() || + java.lang.Number.class.isAssignableFrom(paramTypeClass) || + java.lang.Character.class.isAssignableFrom(paramTypeClass) || + java.lang.Byte.class.isAssignableFrom(paramTypeClass) + ) + ) { + cost += 4; // Numeric type to a different primitive type + + if (suppliedParam.toString().equals("true")) + suppliedParam = "1"; + else if (suppliedParam.toString().equals("false")) + suppliedParam = "0"; + + if (paramTypeClass.equals(Boolean.TYPE)) + castedObj = getNum(suppliedParam.toString(), paramTypeClass).doubleValue() != 0D; + else if (paramTypeClass.equals(Character.TYPE)) + castedObj = (char) Short.decode(suppliedParam.toString()).shortValue(); + else + castedObj = getNum(suppliedParam.toString(), paramTypeClass); + } else if (suppliedParam instanceof java.lang.String && + isNum(suppliedParam) && + (paramTypeClass.isInstance(java.lang.Number.class) || + paramTypeClass.isInstance(java.lang.Character.class) || + paramTypeClass.isInstance(java.lang.Byte.class) || + paramTypeClass.isPrimitive()) + ) { + cost += 5; // String to numeric type + + if (suppliedParam.toString().equals("true")) + suppliedParam = "1"; + else if (suppliedParam.toString().equals("false")) + suppliedParam = "0"; + + if (paramTypeClass.equals(Character.TYPE)) + castedObj = (char) Short.decode(suppliedParam.toString()).shortValue(); + else + castedObj = getNum(suppliedParam.toString(), paramTypeClass); + } else if (suppliedParam instanceof java.lang.String && + (paramTypeClass.equals(java.lang.Boolean.class) || + paramTypeClass.equals(java.lang.Boolean.TYPE)) + ){ + + cost += 5; // Same cost as above + castedObj = new Boolean(suppliedParam.toString().length() > 0); + } else if (paramTypeClass.isAssignableFrom(suppliedParamClass)) { + cost += 6; // Class type to superclass type; + castedObj = paramTypeClass.cast(suppliedParam); + } else if (paramTypeClass.equals(String.class)) { + cost += 7; // Any Java value to String + castedObj = suppliedParam.toString(); + } else if (suppliedParam instanceof JSObject && + paramTypeClass.isArray()) { + cost += 8; // JSObject to Java array + castedObj = (JSObject) suppliedParam; + } else { + cost = Integer.MIN_VALUE; // Not allowed + castedObj = suppliedParam; + } + + ret[0] = cost; + ret[1] = castedObj; + + return ret; + + } + + private static Method[] getMatchingMethods(Class c, String name, int paramCount) { + Method[] allMethods = c.getMethods(); + ArrayList<Method> matchingMethods = new ArrayList(5); + + for (Method m: allMethods) { + if (m.getName().equals(name) && m.getParameterTypes().length == paramCount) + matchingMethods.add(m); + } + + return matchingMethods.toArray(new Method[0]); + } + + private static Constructor[] getMatchingConstructors(Class c, int paramCount) { + Constructor[] allConstructors = c.getConstructors(); + ArrayList<Constructor> matchingConstructors = new ArrayList<Constructor>(5); + + for (Constructor cs: allConstructors) { + if (cs.getParameterTypes().length == paramCount) + matchingConstructors.add(cs); + } + + return matchingConstructors.toArray(new Constructor[0]); + } + + private static Class getPrimitive(Object o) { + + if (o instanceof java.lang.Byte) { + return java.lang.Byte.TYPE; + } else if (o instanceof java.lang.Character) { + return java.lang.Character.TYPE; + } else if (o instanceof java.lang.Short) { + return java.lang.Short.TYPE; + } else if (o instanceof java.lang.Integer) { + return java.lang.Integer.TYPE; + } else if (o instanceof java.lang.Long) { + return java.lang.Long.TYPE; + } else if (o instanceof java.lang.Float) { + return java.lang.Float.TYPE; + } else if (o instanceof java.lang.Double) { + return java.lang.Double.TYPE; + } else if (o instanceof java.lang.Boolean) { + return java.lang.Boolean.TYPE; + } + + return o.getClass(); + } + + private static boolean isNum (Object o) { + + if (o instanceof java.lang.Number) + return true; + + // Boolean is changeable to number as well + if (o instanceof java.lang.Boolean) + return true; + + // At this point, it _has_ to be a string else automatically + // return false + if (!(o instanceof java.lang.String)) + return false; + + try { + Long.parseLong((String) o); // whole number test + return true; + } catch (NumberFormatException nfe) {} + + try { + Float.parseFloat((String) o); // decimal + return true; + } catch (NumberFormatException nfe) {} + + + return false; + } + + private static Number getNum (String s, Class c) throws NumberFormatException { + + Number n; + if (s.contains(".")) + n = new Double(s); + else + n = new Long(s); + + // See if we need to collapse first + if (c.equals(java.lang.Integer.class) || + c.equals(java.lang.Integer.TYPE)) { + return n.intValue(); + } + + if (c.equals(java.lang.Long.class) || + c.equals(java.lang.Long.TYPE)) { + return n.longValue(); + } + + if (c.equals(java.lang.Short.class) || + c.equals(java.lang.Short.TYPE)) { + return n.shortValue(); + } + + if (c.equals(java.lang.Float.class) || + c.equals(java.lang.Float.TYPE)) { + return n.floatValue(); + } + + if (c.equals(java.lang.Double.class) || + c.equals(java.lang.Double.TYPE)) { + return n.doubleValue(); + } + + if (c.equals(java.lang.Byte.class) || + c.equals(java.lang.Byte.TYPE)) { + return n.byteValue(); + } + + return n; + } + + private static String printList (Object[] oList) { + + String ret = ""; + + ret += "{ "; + for (Object o : oList) { + + String oStr = o != null ? o.toString() + " [" + o.getClass() + "]" : "null"; + + ret += oStr; + ret += ", "; + } + ret = ret.substring(0, ret.length()-2); // remove last ", " + ret += " }"; + + return ret; + } + + private static String getArrayAsString(Object array) { + // We are guaranteed that supplied object is a String + + String ret = new String(); + + for (int i=0; i < Array.getLength(array); i++) { + Object element = Array.get(array, i); + + if (element != null) { + if (element.getClass().isArray()) { + ret += getArrayAsString(element); + } else { + ret += element; + } + } + + ret += ","; + } + + // Trim the final "," + if (ret.length() > 0) { + ret = ret.substring(0, ret.length() - 1); + } + + return ret; + } +} + +/** Begin test classes **/ + +class FooClass { + + public FooClass() {} + + public FooClass(Boolean b, int i) {} + + public FooClass(Boolean b, Integer i) {} + + public FooClass(Boolean b, short s) {} + + public FooClass(String s, int i) {} + + public FooClass(String s, Integer i) {} + + public FooClass(java.lang.Number num) {} + + public FooClass(java.lang.Integer integer) {} + + public FooClass(long l) {} + + public FooClass(double d) {} + + public FooClass(float f) {} + + public FooClass(JSObject j) {} + + public FooClass(BarClass1 b) {} + + public FooClass(BarClass2 b) {} + + public FooClass(String s) {} + + public FooClass(byte b) {} + + public FooClass(String s, Float f) {} + + public FooClass (int i) {} + + public void FooClass() {} + + public void FooClass(boolean b) {} + + + public void foo(Boolean b, int i) {} + + public void foo(Boolean b, Integer i) {} + + public void foo(Boolean b, short s) {} + + public void foo_string_int(String s, int i) {} + + public void foo_string_int(String s, Integer i) {} + + public void foo_jsobj(JSObject j) {} + + public void foo_jsobj(String s) {} + + public void foo_classtype(java.lang.Number num) {} + + public void foo_classtype(java.lang.Integer integer) {} + + public void foo_multiprim(int i) {} + + public void foo_multiprim(long l) {} + + public void foo_multiprim(float f) {} + + public void foo_multiprim(double d) {} + + public void foo_float(float f) {} + + public void foo_float(String s) {} + + public void foo_float(JSObject j) {} + + public void foo_class(BarClass1 b) {} + + public void foo_class(BarClass2 b) {} + + public void foo_strandbyteonly(String s) {} + + public void foo_strandbyteonly(byte b) {} + + public void foo_str_and_float(String s, Float f) {} + + public void foo_int_only (int i) {} + + public void foo_noargs() {} + + public void foo_boolonly(boolean b) {} +} + +class BarClass1 {} +class BarClass2 extends BarClass1 {} +class BarClass3 extends BarClass2 {} +class JSObject {} diff --git a/plugin/icedteanp/java/sun/applet/PasswordAuthenticationDialog.java b/plugin/icedteanp/java/sun/applet/PasswordAuthenticationDialog.java new file mode 100644 index 0000000..843603e --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/PasswordAuthenticationDialog.java @@ -0,0 +1,241 @@ +/* PasswordAuthenticationDialog -- requests authentication information from users + Copyright (C) 2009 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package sun.applet; + +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.net.PasswordAuthentication; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPasswordField; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; + +/** + * Modal non-minimizable dialog to request http authentication credentials + */ + +public class PasswordAuthenticationDialog extends JDialog { + + private JLabel jlInfo = new JLabel(""); + private JTextField jtfUserName = new JTextField(); + private JPasswordField jpfPassword = new JPasswordField(); + private boolean userCancelled; + + public PasswordAuthenticationDialog() { + initialize(); + } + + /** + * Initialized the dialog components + */ + + public void initialize() { + + setTitle("IcedTea Java Plugin - Authorization needed to proceed"); + + setLayout(new GridBagLayout()); + + JLabel jlUserName = new JLabel("Username: "); + JLabel jlPassword = new JLabel("Password: "); + JButton jbOK = new JButton("OK"); + JButton jbCancel = new JButton("Cancel"); + + jtfUserName.setSize(20, 10); + jpfPassword.setSize(20, 10); + + GridBagConstraints c; + + c = new GridBagConstraints(); + c.fill = c.HORIZONTAL; + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 2; + c.insets = new Insets(10, 5, 3, 3); + add(jlInfo, c); + + c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 1; + c.insets = new Insets(10, 5, 3, 3); + add(jlUserName, c); + + c = new GridBagConstraints(); + c.fill = c.HORIZONTAL; + c.gridx = 1; + c.gridy = 1; + c.insets = new Insets(10, 5, 3, 3); + c.weightx = 1.0; + add(jtfUserName, c); + + + c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 2; + c.insets = new Insets(5, 5, 3, 3); + add(jlPassword, c); + + c = new GridBagConstraints(); + c.fill = c.HORIZONTAL; + c.gridx = 1; + c.gridy = 2; + c.insets = new Insets(5, 5, 3, 3); + c.weightx = 1.0; + add(jpfPassword, c); + + c = new GridBagConstraints(); + c.anchor = c.SOUTHEAST; + c.gridx = 1; + c.gridy = 3; + c.insets = new Insets(5, 5, 3, 70); + c.weightx = 0.0; + add(jbCancel, c); + + c = new GridBagConstraints(); + c.anchor = c.SOUTHEAST; + c.gridx = 1; + c.gridy = 3; + c.insets = new Insets(5, 5, 3, 3); + c.weightx = 0.0; + add(jbOK, c); + + setMinimumSize(new Dimension(400,150)); + setMaximumSize(new Dimension(1024,150)); + setAlwaysOnTop(true); + + setSize(400,150); + setLocationRelativeTo(null); + + // OK => read supplied info and pass it on + jbOK.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + userCancelled = false; + dispose(); + } + }); + + // Cancel => discard supplied info and pass on an empty auth + jbCancel.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + userCancelled = true; + dispose(); + } + }); + + // "return" key in either user or password field => OK + + jtfUserName.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + userCancelled = false; + dispose(); + } + }); + + jpfPassword.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + userCancelled = false; + dispose(); + } + }); + } + + /** + * Present a dialog to the user asking them for authentication information + * + * @param hostThe host for with authentication is needed + * @param port The port being accessed + * @param prompt The prompt (realm) as presented by the server + * @param type The type of server (proxy/web) + * @return PasswordAuthentication containing the credentials (empty credentials if user cancelled) + */ + protected PasswordAuthentication askUser(String host, int port, String prompt, String type) { + PasswordAuthentication auth = null; + + host += port != -1 ? ":" + port : ""; + + // This frame is reusable. So reset everything first. + userCancelled = true; + jlInfo.setText("<html>The " + type + " server at " + host + " is requesting authentication. It says \"" + prompt + "\"</html>"); + + try { + SwingUtilities.invokeAndWait( new Runnable() { + public void run() { + // show dialog to user + setVisible(true); + } + }); + + PluginDebug.debug("password dialog shown"); + + // wait until dialog is gone + while (this.isShowing()) { + try { + Thread.sleep(200); + } catch (InterruptedException ie) { + } + } + + PluginDebug.debug("password dialog closed"); + + if (!userCancelled) { + auth = new PasswordAuthentication(jtfUserName.getText(), jpfPassword.getText().toCharArray()); + } + } catch (Exception e) { + e.printStackTrace(); + + // Nothing else we can do. Empty auth will be returned + } + + return auth; + } + + public static void main(String[] args) { + PasswordAuthenticationDialog frame = new PasswordAuthenticationDialog(); + + PasswordAuthentication auth = frame.askUser("127.0.0.1", 3128, "Password for local proxy", "proxy"); + + System.err.println("Auth info: " + auth.getUserName() + ":" + new String(auth.getPassword())); + System.exit(0); + } +} diff --git a/plugin/icedteanp/java/sun/applet/PluginAppletSecurityContext.java b/plugin/icedteanp/java/sun/applet/PluginAppletSecurityContext.java new file mode 100644 index 0000000..bef2bd8 --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/PluginAppletSecurityContext.java @@ -0,0 +1,1505 @@ +/* PluginAppletSecurityContext -- execute plugin JNI messages + Copyright (C) 2008 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package sun.applet; + +import java.io.File; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.AccessControlContext; +import java.security.AccessControlException; +import java.security.AccessController; +import java.security.AllPermission; +import java.security.BasicPermission; +import java.security.CodeSource; +import java.security.Permissions; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +import javax.swing.text.html.HTMLDocument.HTMLReader.IsindexAction; + +import net.sourceforge.jnlp.runtime.JNLPRuntime; +import netscape.javascript.JSObjectCreatePermission; + + + +class Signature { + private String signature; + private int currentIndex; + private List<Class> typeList; + private static final char ARRAY = '['; + private static final char OBJECT = 'L'; + private static final char SIGNATURE_ENDCLASS = ';'; + private static final char SIGNATURE_FUNC = '('; + private static final char SIGNATURE_ENDFUNC = ')'; + private static final char VOID = 'V'; + private static final char BOOLEAN = 'Z'; + private static final char BYTE = 'B'; + private static final char CHARACTER = 'C'; + private static final char SHORT = 'S'; + private static final char INTEGER = 'I'; + private static final char LONG = 'J'; + private static final char FLOAT = 'F'; + private static final char DOUBLE = 'D'; + + private String nextTypeName() { + char key = signature.charAt(currentIndex++); + + switch (key) { + case ARRAY: + return nextTypeName() + "[]"; + + case OBJECT: + int endClass = signature.indexOf(SIGNATURE_ENDCLASS, currentIndex); + String retVal = signature.substring(currentIndex, endClass); + retVal = retVal.replace('/', '.'); + currentIndex = endClass + 1; + return retVal; + + // FIXME: generated bytecode with classes named after + // primitives will not work in this scheme -- those + // classes will be incorrectly treated as primitive + // types. + case VOID: + return "void"; + case BOOLEAN: + return "boolean"; + case BYTE: + return "byte"; + case CHARACTER: + return "char"; + case SHORT: + return "short"; + case INTEGER: + return "int"; + case LONG: + return "long"; + case FLOAT: + return "float"; + case DOUBLE: + return "double"; + + case SIGNATURE_ENDFUNC: + return null; + + case SIGNATURE_FUNC: + return nextTypeName(); + + default: + throw new IllegalArgumentException( + "Invalid JNI signature character '" + key + "'"); + } + } + + public Signature(String signature, ClassLoader cl) { + this.signature = signature; + currentIndex = 0; + typeList = new ArrayList<Class>(10); + + String elem; + while (currentIndex < signature.length()) { + elem = nextTypeName(); + + if (elem == null) // end of signature + continue; + + // System.out.println ("NEXT TYPE: " + elem); + Class primitive = primitiveNameToType(elem); + if (primitive != null) + typeList.add(primitive); + else { + // System.out.println ("HERE1"); + int dimsize = 0; + int n = elem.indexOf('['); + if (n != -1) { + // System.out.println ("HERE2"); + String arrayType = elem.substring(0, n); + dimsize++; + n = elem.indexOf('[', n + 1); + // System.out.println ("HERE2.5"); + while (n != -1) { + dimsize++; + n = elem.indexOf('[', n + 1); + // System.out.println ("HERE2.8"); + } + int[] dims = new int[dimsize]; + primitive = primitiveNameToType(arrayType); + // System.out.println ("HERE3"); + if (primitive != null) { + typeList.add(Array.newInstance(primitive, dims) + .getClass()); + // System.out.println ("HERE4"); + } else + typeList.add(Array.newInstance( + getClass(arrayType, cl), dims).getClass()); + } else { + typeList.add(getClass(elem, cl)); + } + } + } + if (signature.length() < 2) { + throw new IllegalArgumentException("Invalid JNI signature '" + + signature + "'"); + } + } + + public static Class getClass(String name, ClassLoader cl) { + + Class c = null; + + try { + c = Class.forName(name); + } catch (ClassNotFoundException cnfe) { + + PluginDebug.debug("Class " + name + " not found in primordial loader. Looking in " + cl); + try { + c = cl.loadClass(name); + } catch (ClassNotFoundException e) { + throw (new RuntimeException(new ClassNotFoundException("Unable to find class " + name))); + } + } + + return c; + } + + public static Class primitiveNameToType(String name) { + if (name.equals("void")) + return Void.TYPE; + else if (name.equals("boolean")) + return Boolean.TYPE; + else if (name.equals("byte")) + return Byte.TYPE; + else if (name.equals("char")) + return Character.TYPE; + else if (name.equals("short")) + return Short.TYPE; + else if (name.equals("int")) + return Integer.TYPE; + else if (name.equals("long")) + return Long.TYPE; + else if (name.equals("float")) + return Float.TYPE; + else if (name.equals("double")) + return Double.TYPE; + else + return null; + } + + public Class[] getClassArray() { + return typeList.subList(0, typeList.size()).toArray(new Class[] {}); + } +} + +public class PluginAppletSecurityContext { + + private static Hashtable<ClassLoader, URL> classLoaders = new Hashtable<ClassLoader, URL>(); + private static Hashtable<Integer, ClassLoader> instanceClassLoaders = new Hashtable<Integer, ClassLoader>(); + + // FIXME: make private + public PluginObjectStore store = new PluginObjectStore(); + private Throwable throwable = null; + private ClassLoader liveconnectLoader = ClassLoader.getSystemClassLoader(); + int identifier = 0; + + public static PluginStreamHandler streamhandler; + + long startTime = 0; + + public PluginAppletSecurityContext(int identifier) { + this.identifier = identifier; + + // also, override the basedir, use a different one for the plugin + File f = new File(System.getProperty("user.home") + "/.icedteaplugin/"); + f.mkdir(); + JNLPRuntime.setBaseDir(f); + + // We need a security manager.. and since there is a good chance that + // an applet will be loaded at some point, we should make it the SM + // that JNLPRuntime will try to install + if (System.getSecurityManager() == null) { + JNLPRuntime.initialize(/* isApplication */ false); + } + + JNLPRuntime.disableExit(); + + URL u = null; + try { + u = new URL("file://"); + } catch (Exception e) { + e.printStackTrace(); + } + + + this.classLoaders.put(liveconnectLoader, u); + } + + private static <V> V parseCall(String s, ClassLoader cl, Class<V> c) { + if (c == Integer.class) + return (V) new Integer(s); + else if (c == String.class) + return (V) new String(s); + else if (c == Signature.class) + return (V) new Signature(s, cl); + else + throw new RuntimeException("Unexpected call value."); + } + + private Object parseArgs(String s, Class c) { + if (c == Boolean.TYPE || c == Boolean.class) + return new Boolean(s); + else if (c == Byte.TYPE || c == Byte.class) + return new Byte(s); + else if (c == Character.TYPE || c == Character.class) { + String[] bytes = s.split("_"); + int low = Integer.parseInt(bytes[0]); + int high = Integer.parseInt(bytes[1]); + int full = ((high << 8) & 0x0ff00) | (low & 0x0ff); + return new Character((char) full); + } else if (c == Short.TYPE || c == Short.class) + return new Short(s); + else if (c == Integer.TYPE || c == Integer.class) + return new Integer(s); + else if (c == Long.TYPE || c == Long.class) + return new Long(s); + else if (c == Float.TYPE || c == Float.class) + return new Float(s); + else if (c == Double.TYPE || c == Double.class) + return new Double(s); + else + return store.getObject(new Integer(s)); + } + + public void associateSrc(ClassLoader cl, URL src) { + PluginDebug.debug("Associating " + cl + " with " + src); + this.classLoaders.put(cl, src); + } + + public void associateInstance(Integer i, ClassLoader cl) { + PluginDebug.debug("Associating " + cl + " with instance " + i); + this.instanceClassLoaders.put(i, cl); + } + + public static void setStreamhandler(PluginStreamHandler sh) { + streamhandler = sh; + } + + public static Map<String, String> getLoaderInfo() { + Hashtable<String, String> map = new Hashtable<String, String>(); + + for (ClassLoader loader : PluginAppletSecurityContext.classLoaders.keySet()) { + map.put(loader.getClass().getName(), classLoaders.get(loader).toString()); + } + + return map; + } + + public void handleMessage(int reference, String src, AccessControlContext callContext, String message) { + + startTime = new java.util.Date().getTime(); + + try { + if (message.startsWith("FindClass")) { + ClassLoader cl = null; + Class c = null; + cl = liveconnectLoader; + String[] args = message.split(" "); + Integer instance = new Integer(args[1]); + String className = args[2].replace('/', '.'); + PluginDebug.debug("Searching for class " + className + " in " + cl); + + try { + c = cl.loadClass(className); + store.reference(c); + write(reference, "FindClass " + store.getIdentifier(c)); + } catch (ClassNotFoundException cnfe) { + + cl = this.instanceClassLoaders.get(instance); + PluginDebug.debug("Not found. Looking in " + cl); + + if (instance != 0 && cl != null) { + try { + c = cl.loadClass(className); + store.reference(c); + write(reference, "FindClass " + store.getIdentifier(c)); + } catch (ClassNotFoundException cnfe2) { + write(reference, "FindClass 0"); + } + } else { + write(reference, "FindClass 0"); + } + } + + } else if (message.startsWith("GetStaticMethodID") + || message.startsWith("GetMethodID")) { + String[] args = message.split(" "); + Integer classID = parseCall(args[1], null, Integer.class); + String methodName = parseCall(args[2], null, String.class); + Signature signature = parseCall(args[3], ((Class) store.getObject(classID)).getClassLoader(), Signature.class); + Object[] a = signature.getClassArray(); + + Class c; + + if (message.startsWith("GetStaticMethodID") || + methodName.equals("<init>") || + methodName.equals("<clinit>")) + c = (Class) store.getObject(classID); + else + c = store.getObject(classID).getClass(); + + Method m = null; + Constructor cs = null; + Object o = null; + if (methodName.equals("<init>") + || methodName.equals("<clinit>")) { + o = cs = c.getConstructor(signature.getClassArray()); + store.reference(cs); + } else { + o = m = c.getMethod(methodName, signature.getClassArray()); + store.reference(m); + } + PluginDebug.debug(o + " has id " + store.getIdentifier(o)); + write(reference, args[0] + " " + store.getIdentifier(o)); + } else if (message.startsWith("GetStaticFieldID") + || message.startsWith("GetFieldID")) { + String[] args = message.split(" "); + Integer classID = parseCall(args[1], null, Integer.class); + Integer fieldID = parseCall(args[2], null, Integer.class); + String fieldName = (String) store.getObject(fieldID); + + Class c = (Class) store.getObject(classID); + + PluginDebug.debug("GetStaticFieldID/GetFieldID got class=" + c.getName()); + + Field f = null; + f = c.getField(fieldName); + + store.reference(f); + + write(reference, "GetStaticFieldID " + store.getIdentifier(f)); + } else if (message.startsWith("GetStaticField")) { + String[] args = message.split(" "); + String type = parseCall(args[1], null, String.class); + Integer classID = parseCall(args[1], null, Integer.class); + Integer fieldID = parseCall(args[2], null, Integer.class); + + final Class c = (Class) store.getObject(classID); + final Field f = (Field) store.getObject(fieldID); + + AccessControlContext acc = callContext != null ? callContext : getClosedAccessControlContext(); + checkPermission(src, c, acc); + + Object ret = AccessController.doPrivileged(new PrivilegedAction<Object> () { + public Object run() { + try { + return f.get(c); + } catch (Throwable t) { + return t; + } + } + }, acc); + + if (ret instanceof Throwable) + throw (Throwable) ret; + + if (ret == null) { + write(reference, "GetStaticField literalreturn null"); + } else if (f.getType() == Boolean.TYPE + || f.getType() == Byte.TYPE + || f.getType() == Short.TYPE + || f.getType() == Integer.TYPE + || f.getType() == Long.TYPE) { + write(reference, "GetStaticField literalreturn " + ret); + } else if (f.getType() == Float.TYPE + || f.getType() == Double.TYPE) { + write(reference, "GetStaticField literalreturn " + String.format("%308.308e", ret)); + } else if (f.getType() == Character.TYPE) { + write(reference, "GetStaticField literalreturn " + (int) (Character) ret); + } else { + // Track returned object. + store.reference(ret); + write(reference, "GetStaticField " + store.getIdentifier(ret)); + } + } else if (message.startsWith("GetValue")) { + String[] args = message.split(" "); + Integer index = parseCall(args[1], null, Integer.class); + + Object ret = store.getObject(index); + + if (ret == null) { + write(reference, "GetValue literalreturn null"); + } else if (ret.getClass() == Boolean.TYPE + || ret.getClass() == Boolean.class + || ret.getClass() == Byte.TYPE + || ret.getClass() == Byte.class + || ret.getClass() == Short.TYPE + || ret.getClass() == Short.class + || ret.getClass() == Integer.TYPE + || ret.getClass() == Integer.class + || ret.getClass() == Long.TYPE + || ret.getClass() == Long.class) { + write(reference, "GetValue literalreturn " + ret); + } else if (ret.getClass() == Float.TYPE + || ret.getClass() == Float.class + || ret.getClass() == Double.TYPE + || ret.getClass() == Double.class) { + write(reference, "GetValue literalreturn " + String.format("%308.308e", ret)); + } else if (ret.getClass() == Character.TYPE + || ret.getClass() == Character.class) { + write(reference, "GetValue literalreturn " + (int) (Character) ret); + } else { + // Track returned object. + store.reference(ret); + write(reference, "GetValue " + store.getIdentifier(ret)); + } + } else if (message.startsWith("SetStaticField") || + message.startsWith("SetField")) { + String[] args = message.split(" "); + Integer classOrObjectID = parseCall(args[1], null, Integer.class); + Integer fieldID = parseCall(args[2], null, Integer.class); + Object value = store.getObject(parseCall(args[3], null, Integer.class)); + + final Object o = store.getObject(classOrObjectID); + final Field f = (Field) store.getObject(fieldID); + + final Object fValue = MethodOverloadResolver.getCostAndCastedObject(value, f.getType())[1]; + + AccessControlContext acc = callContext != null ? callContext : getClosedAccessControlContext(); + checkPermission(src, + message.startsWith("SetStaticField") ? (Class) o : o.getClass(), + acc); + + Object ret = AccessController.doPrivileged(new PrivilegedAction<Object> () { + public Object run() { + try { + f.set(o, fValue); + } catch (Throwable t) { + return t; + } + + return null; + } + }, acc); + + if (ret instanceof Throwable) + throw (Throwable) ret; + + write(reference, "SetField"); + } else if (message.startsWith("GetObjectArrayElement")) { + String[] args = message.split(" "); + Integer arrayID = parseCall(args[1], null, Integer.class); + Integer index = parseCall(args[2], null, Integer.class); + + Object ret = Array.get(store.getObject(arrayID), index); + Class retClass = store.getObject(arrayID).getClass().getComponentType(); // prevent auto-boxing influence + + if (ret == null) { + write(reference, "GetObjectArrayElement literalreturn null"); + } else if (retClass == Boolean.TYPE + || retClass == Byte.TYPE + || retClass == Short.TYPE + || retClass== Integer.TYPE + || retClass== Long.TYPE) { + write(reference, "GetObjectArrayElement literalreturn " + ret); + } else if (retClass == Float.TYPE + || retClass == Double.TYPE) { + write(reference, "GetObjectArrayElement literalreturn " + String.format("%308.308e", ret)); + } else if (retClass == Character.TYPE) { + write(reference, "GetObjectArrayElement literalreturn " + (int) (Character) ret); + } else { + // Track returned object. + store.reference(ret); + write(reference, "GetObjectArrayElement " + store.getIdentifier(ret)); + } + + } else if (message.startsWith("SetObjectArrayElement")) { + String[] args = message.split(" "); + Integer arrayID = parseCall(args[1], null, Integer.class); + Integer index = parseCall(args[2], null, Integer.class); + Integer objectID = parseCall(args[3], null, Integer.class); + + Object value = store.getObject(objectID); + + // Cast the object to appropriate type before insertion + value = MethodOverloadResolver.getCostAndCastedObject(value, store.getObject(arrayID).getClass().getComponentType())[1]; + + //if (value == null && + // store.getObject(arrayID).getClass().getComponentType().isPrimitive()) { + // value = 0; + //} + + Array.set(store.getObject(arrayID), index, value); + + write(reference, "SetObjectArrayElement"); + } else if (message.startsWith("GetArrayLength")) { + String[] args = message.split(" "); + Integer arrayID = parseCall(args[1], null, Integer.class); + + //System.out.println("ARRAYID: " + arrayID); + Object o = (Object) store.getObject(arrayID); + int len = 0; + len = Array.getLength(o); + // System.out.println ("Returning array length: " + len); + + // System.out.println ("array length: " + o + " " + len); + write(reference, "GetArrayLength " + Array.getLength(o)); + } else if (message.startsWith("GetField")) { + String[] args = message.split(" "); + String type = parseCall(args[1], null, String.class); + Integer objectID = parseCall(args[1], null, Integer.class); + Integer fieldID = parseCall(args[2], null, Integer.class); + + final Object o = (Object) store.getObject(objectID); + final Field f = (Field) store.getObject(fieldID); + + AccessControlContext acc = callContext != null ? callContext : getClosedAccessControlContext(); + checkPermission(src, o.getClass(), acc); + + Object ret = AccessController.doPrivileged(new PrivilegedAction<Object> () { + public Object run() { + try { + return f.get(o); + } catch (Throwable t) { + return t; + } + } + }, acc); + + if (ret instanceof Throwable) + throw (Throwable) ret; + + if (ret == null) { + write(reference, "GetField literalreturn null"); + } else if (f.getType() == Boolean.TYPE + || f.getType() == Byte.TYPE + || f.getType() == Short.TYPE + || f.getType() == Integer.TYPE + || f.getType() == Long.TYPE) { + write(reference, "GetField literalreturn " + ret); + } else if (f.getType() == Float.TYPE + || f.getType() == Double.TYPE) { + write(reference, "GetField literalreturn " + String.format("%308.308e", ret)); + } else if (f.getType() == Character.TYPE) { + write(reference, "GetField literalreturn " + (int) (Character) ret); + } else { + // Track returned object. + store.reference(ret); + write(reference, "GetField " + store.getIdentifier(ret)); + } + + } else if (message.startsWith("GetObjectClass")) { + int oid = Integer.parseInt(message.substring("GetObjectClass" + .length() + 1)); + // System.out.println ("GETTING CLASS FOR: " + oid); + Class c = store.getObject(oid).getClass(); + // System.out.println (" OBJ: " + store.getObject(oid)); + // System.out.println (" CLS: " + c); + store.reference(c); + + write(reference, "GetObjectClass " + store.getIdentifier(c)); + } else if (message.startsWith("CallMethod") || + message.startsWith("CallStaticMethod")) { + String[] args = message.split(" "); + Integer objectID = parseCall(args[1], null, Integer.class); + String methodName = parseCall(args[2], null, String.class); + Object o = null; + Class c; + + if (message.startsWith("CallMethod")) { + o = (Object) store.getObject(objectID); + c = o.getClass(); + } else { + c = (Class) store.getObject(objectID); + } + + // length -3 to discard first 3, + 2 for holding object + // and method name + Object[] arguments = new Object[args.length - 1]; + arguments[0] = c; + arguments[1] = methodName; + for (int i = 0; i < args.length - 3; i++) { + arguments[i+2] = store.getObject(parseCall(args[3 + i], null, Integer.class)); + PluginDebug.debug("GOT ARG: " + arguments[i+2]); + } + + Object[] matchingMethodAndArgs = MethodOverloadResolver.getMatchingMethod(arguments); + + if (matchingMethodAndArgs == null) { + write(reference, "Error: No suitable method named " + methodName + " with matching args found"); + return; + } + + final Method m = (Method) matchingMethodAndArgs[0]; + Object[] castedArgs = new Object[matchingMethodAndArgs.length - 1]; + for (int i=0; i < castedArgs.length; i++) { + castedArgs[i] = matchingMethodAndArgs[i+1]; + } + + String collapsedArgs = ""; + for (Object arg : castedArgs) { + collapsedArgs += " " + arg; + } + + PluginDebug.debug("Calling method " + m + " on object " + o + + " (" + c + ") with " + collapsedArgs); + + AccessControlContext acc = callContext != null ? callContext : getClosedAccessControlContext(); + checkPermission(src, c, acc); + + final Object[] fArguments = castedArgs; + final Object callableObject = o; + // Set the method accessible prior to calling. See: + // http://forums.sun.com/thread.jspa?threadID=332001&start=15&tstart=0 + m.setAccessible(true); + Object ret = AccessController.doPrivileged(new PrivilegedAction<Object> () { + public Object run() { + try { + return m.invoke(callableObject, fArguments); + } catch (Throwable t) { + return t; + } + } + }, acc); + + if (ret instanceof Throwable) + throw (Throwable) ret; + + String retO; + if (ret == null) { + retO = "null"; + } else { + retO = m.getReturnType().toString(); + } + + PluginDebug.debug("Calling " + m + " on " + o + " with " + + collapsedArgs + " and that returned: " + ret + + " of type " + retO); + + if (m.getReturnType().equals(java.lang.Void.class) || + m.getReturnType().equals(java.lang.Void.TYPE)) { + write(reference, "CallMethod literalreturn void"); + } else if (ret == null) { + write(reference, "CallMethod literalreturn null"); + } else if (m.getReturnType() == Boolean.TYPE + || m.getReturnType() == Byte.TYPE + || m.getReturnType() == Short.TYPE + || m.getReturnType() == Integer.TYPE + || m.getReturnType() == Long.TYPE) { + write(reference, "CallMethod literalreturn " + ret); + } else if (m.getReturnType() == Float.TYPE + || m.getReturnType() == Double.TYPE) { + write(reference, "CallMethod literalreturn " + String.format("%308.308e", ret)); + } else if (m.getReturnType() == Character.TYPE) { + write(reference, "CallMethod literalreturn " + (int) (Character) ret); + } else { + // Track returned object. + store.reference(ret); + write(reference, "CallMethod " + store.getIdentifier(ret)); + } + } else if (message.startsWith("GetSuperclass")) { + String[] args = message.split(" "); + Integer classID = parseCall(args[1], null, Integer.class); + Class c = null; + Class ret = null; + + c = (Class) store.getObject(classID); + ret = c.getSuperclass(); + store.reference(ret); + + write(reference, "GetSuperclass " + store.getIdentifier(ret)); + } else if (message.startsWith("IsAssignableFrom")) { + String[] args = message.split(" "); + Integer classID = parseCall(args[1], null, Integer.class); + Integer superclassID = parseCall(args[2], null, Integer.class); + + boolean result = false; + Class clz = (Class) store.getObject(classID); + Class sup = (Class) store.getObject(superclassID); + + result = sup.isAssignableFrom(clz); + + write(reference, "IsAssignableFrom " + (result ? "1" : "0")); + } else if (message.startsWith("IsInstanceOf")) { + String[] args = message.split(" "); + Integer objectID = parseCall(args[1], null, Integer.class); + Integer classID = parseCall(args[2], null, Integer.class); + + boolean result = false; + Object o = (Object) store.getObject(objectID); + Class c = (Class) store.getObject(classID); + + result = c.isInstance(o); + + write(reference, "IsInstanceOf " + (result ? "1" : "0")); + } else if (message.startsWith("GetStringUTFLength")) { + String[] args = message.split(" "); + Integer stringID = parseCall(args[1], null, Integer.class); + + String o = null; + byte[] b = null; + o = (String) store.getObject(stringID); + b = o.getBytes("UTF-8"); + // System.out.println ("STRING UTF-8 LENGTH: " + o + " " + + // b.length); + + write(reference, "GetStringUTFLength " + o.length()); + } else if (message.startsWith("GetStringLength")) { + String[] args = message.split(" "); + Integer stringID = parseCall(args[1], null, Integer.class); + + String o = null; + byte[] b = null; + o = (String) store.getObject(stringID); + b = o.getBytes("UTF-16LE"); + // System.out.println ("STRING UTF-16 LENGTH: " + o + " " + + // b.length); + + // System.out.println ("Java: GetStringLength " + b.length); + write(reference, "GetStringLength " + o.length()); + } else if (message.startsWith("GetStringUTFChars")) { + String[] args = message.split(" "); + Integer stringID = parseCall(args[1], null, Integer.class); + + String o = null; + byte[] b = null; + StringBuffer buf = null; + o = (String) store.getObject(stringID); + b = o.getBytes("UTF-8"); + buf = new StringBuffer(b.length * 2); + buf.append(b.length); + for (int i = 0; i < b.length; i++) + buf + .append(" " + + Integer + .toString(((int) b[i]) & 0x0ff, 16)); + + // System.out.println ("Java: GetStringUTFChars: " + o); + // //System.out.println ("String UTF BYTES: " + buf); + write(reference, "GetStringUTFChars " + buf); + } else if (message.startsWith("GetStringChars")) { + String[] args = message.split(" "); + Integer stringID = parseCall(args[1], null, Integer.class); + + String o = null; + byte[] b = null; + StringBuffer buf = null; + o = (String) store.getObject(stringID); + // FIXME: LiveConnect uses UCS-2. + b = o.getBytes("UTF-16LE"); + buf = new StringBuffer(b.length * 2); + buf.append(b.length); + for (int i = 0; i < b.length; i++) + buf + .append(" " + + Integer + .toString(((int) b[i]) & 0x0ff, 16)); + + PluginDebug.debug("Java: GetStringChars: " + o); + PluginDebug.debug(" String BYTES: " + buf); + write(reference, "GetStringChars " + buf); + } else if (message.startsWith("GetToStringValue")) { + String[] args = message.split(" "); + Integer objectID = parseCall(args[1], null, Integer.class); + + String o = null; + byte[] b = null; + StringBuffer buf = null; + o = store.getObject(objectID).toString(); + b = o.getBytes("UTF-8"); + buf = new StringBuffer(b.length * 2); + buf.append(b.length); + for (int i = 0; i < b.length; i++) + buf + .append(" " + + Integer + .toString(((int) b[i]) & 0x0ff, 16)); + + write(reference, "GetToStringValue " + buf); + } else if (message.startsWith("NewArray")) { + String[] args = message.split(" "); + String type = parseCall(args[1], null, String.class); + Integer length = parseCall(args[2], null, Integer.class); + + // System.out.println ("CALLING: NewArray: " + type + " " + + // length + " " + // + Signature.primitiveNameToType(type)); + + Object newArray = null; + + Class c; + if (type.equals("bool")) { + c = Boolean.class; + } else if (type.equals("double")) { + c = Double.class; + } else if (type.equals("int")) { + c = Integer.class; + } else if (type.equals("string")) { + c = String.class; + } else if (isInt(type)) { + c = (Class) store.getObject(Integer.parseInt(type)); + } else { + c = JSObject.class; + } + + if (args.length > 3) + newArray = Array.newInstance(c, new int[] { length, parseCall(args[3], null, Integer.class)}); + else + newArray = Array.newInstance(c, length); + + store.reference(newArray); + write(reference, "NewArray " + store.getIdentifier(newArray)); + } else if (message.startsWith("HasMethod")) { + String[] args = message.split(" "); + Integer classNameID = parseCall(args[1], null, Integer.class); + Integer methodNameID = parseCall(args[2], null, Integer.class); + + Class c = (Class) store.getObject(classNameID); + String methodName = (String) store.getObject(methodNameID); + + Method method = null; + Method[] classMethods = c.getMethods(); + for (Method m: classMethods) { + if (m.getName().equals(methodName)) { + method = m; + break; + } + } + + int hasMethod = (method != null) ? 1 : 0; + + write(reference, "HasMethod " + hasMethod); + } else if (message.startsWith("HasPackage")) { + String[] args = message.split(" "); + Integer instance = parseCall(args[1], null, Integer.class); + Integer nameID = parseCall(args[2], null, Integer.class); + String pkgName = (String) store.getObject(nameID); + + Package pkg = Package.getPackage(pkgName); + int hasPkg = (pkg != null) ? 1 : 0; + + write(reference, "HasPackage " + hasPkg); + + } else if (message.startsWith("HasField")) { + String[] args = message.split(" "); + Integer classNameID = parseCall(args[1], null, Integer.class); + Integer fieldNameID = parseCall(args[2], null, Integer.class); + + Class c = (Class) store.getObject(classNameID); + String fieldName = (String) store.getObject(fieldNameID); + + Field field = null; + Field[] classFields = c.getFields(); + for (Field f: classFields) { + if (f.getName().equals(fieldName)) { + field = f; + break; + } + } + + int hasField = (field != null) ? 1 : 0; + + write(reference, "HasField " + hasField); + } else if (message.startsWith("NewObjectArray")) { + String[] args = message.split(" "); + Integer length = parseCall(args[1], null, Integer.class); + Integer classID = parseCall(args[2], null, Integer.class); + Integer objectID = parseCall(args[3], null, Integer.class); + + // System.out.println ("CALLING: NewObjectArray: " + + // classID + " " + length + " " + // + objectID); + + Object newArray = null; + newArray = Array.newInstance((Class) store.getObject(classID), + length); + + Object[] array = (Object[]) newArray; + for (int i = 0; i < array.length; i++) + array[i] = store.getObject(objectID); + store.reference(newArray); + write(reference, "NewObjectArray " + + store.getIdentifier(newArray)); + } else if (message.startsWith("NewObjectWithConstructor")) { + + String[] args = message.split(" "); + Integer classID = parseCall(args[1], null, Integer.class); + Integer methodID = parseCall(args[2], null, Integer.class); + + final Constructor m = (Constructor) store.getObject(methodID); + Class[] argTypes = m.getParameterTypes(); + + // System.out.println ("NEWOBJ: HERE1"); + Object[] arguments = new Object[argTypes.length]; + // System.out.println ("NEWOBJ: HERE2"); + for (int i = 0; i < argTypes.length; i++) { + arguments[i] = parseArgs(args[3 + i], argTypes[i]); + // System.out.println ("NEWOBJ: GOT ARG: " + arguments[i]); + } + + final Object[] fArguments = arguments; + AccessControlContext acc = callContext != null ? callContext : getClosedAccessControlContext(); + + Class c = (Class) store.getObject(classID); + checkPermission(src, c, acc); + + Object ret = AccessController.doPrivileged(new PrivilegedAction<Object> () { + public Object run() { + try { + return m.newInstance(fArguments); + } catch (Throwable t) { + return t; + } + } + }, acc); + + if (ret instanceof Throwable) + throw (Throwable) ret; + + store.reference(ret); + + write(reference, "NewObject " + store.getIdentifier(ret)); + + } else if (message.startsWith("NewObject")) { + String[] args = message.split(" "); + Integer classID = parseCall(args[1], null, Integer.class); + Class c = (Class) store.getObject(classID); + final Constructor cons; + final Object[] fArguments; + + Object[] arguments = new Object[args.length - 1]; + arguments[0] = c; + for (int i = 0; i < args.length - 2; i++) { + arguments[i + 1] = store.getObject(parseCall(args[2 + i], + null, Integer.class)); + PluginDebug.debug("GOT ARG: " + arguments[i + 1]); + } + + Object[] matchingConstructorAndArgs = MethodOverloadResolver + .getMatchingConstructor(arguments); + + if (matchingConstructorAndArgs == null) { + write(reference, + "Error: No suitable constructor with matching args found"); + return; + } + + Object[] castedArgs = new Object[matchingConstructorAndArgs.length - 1]; + for (int i = 0; i < castedArgs.length; i++) { + castedArgs[i] = matchingConstructorAndArgs[i + 1]; + } + + cons = (Constructor) matchingConstructorAndArgs[0]; + fArguments = castedArgs; + + String collapsedArgs = ""; + for (Object arg : fArguments) { + collapsedArgs += " " + arg.toString(); + } + + PluginDebug.debug("Calling constructor on class " + c + + " with " + collapsedArgs); + + AccessControlContext acc = callContext != null ? callContext : getClosedAccessControlContext(); + checkPermission(src, c, acc); + + Object ret = AccessController.doPrivileged(new PrivilegedAction<Object> () { + public Object run() { + try { + return cons.newInstance(fArguments); + } catch (Throwable t) { + return t; + } + } + }, acc); + + if (ret instanceof Throwable) + throw (Throwable) ret; + + store.reference(ret); + + write(reference, "NewObject " + store.getIdentifier(ret)); + + } else if (message.startsWith("NewStringUTF")) { + PluginDebug.debug("MESSAGE: " + message); + String[] args = message.split(" "); + int length = new Integer(args[1]); + byte[] byteArray = new byte[length]; + String ret = null; + int i = 0; + int j = 2; + int c; + while (i < length) { + c = Integer.parseInt(args[j++], 16); + byteArray[i++] = (byte) c; + } + + ret = new String(byteArray, "UTF-8"); + PluginDebug.debug("NEWSTRINGUTF: " + ret); + + store.reference(ret); + write(reference, "NewStringUTF " + store.getIdentifier(ret)); + } else if (message.startsWith("NewString")) { + PluginDebug.debug("MESSAGE: " + message); + String[] args = message.split(" "); + Integer strlength = parseCall(args[1], null, Integer.class); + int bytelength = 2 * strlength; + byte[] byteArray = new byte[bytelength]; + String ret = null; + for (int i = 0; i < strlength; i++) { + int c = parseCall(args[2 + i], null, Integer.class); + PluginDebug.debug("char " + i + " " + c); + // Low. + byteArray[2 * i] = (byte) (c & 0x0ff); + // High. + byteArray[2 * i + 1] = (byte) ((c >> 8) & 0x0ff); + } + ret = new String(byteArray, 0, bytelength, "UTF-16LE"); + PluginDebug.debug("NEWSTRING: " + ret); + + // System.out.println ("NEWOBJ: CALLED: " + ret); + // System.out.println ("NEWOBJ: CALLED: " + + // store.getObject(ret)); + store.reference(ret); + write(reference, "NewString " + store.getIdentifier(ret)); + + } else if (message.startsWith("ExceptionOccurred")) { + PluginDebug.debug("EXCEPTION: " + throwable); + if (throwable != null) + store.reference(throwable); + write(reference, "ExceptionOccurred " + + store.getIdentifier(throwable)); + } else if (message.startsWith("ExceptionClear")) { + if (throwable != null && store.contains(throwable)) + store.unreference(store.getIdentifier(throwable)); + throwable = null; + write(reference, "ExceptionClear"); + } else if (message.startsWith("DeleteGlobalRef")) { + String[] args = message.split(" "); + Integer id = parseCall(args[1], null, Integer.class); + store.unreference(id); + write(reference, "DeleteGlobalRef"); + } else if (message.startsWith("DeleteLocalRef")) { + String[] args = message.split(" "); + Integer id = parseCall(args[1], null, Integer.class); + store.unreference(id); + write(reference, "DeleteLocalRef"); + } else if (message.startsWith("NewGlobalRef")) { + String[] args = message.split(" "); + Integer id = parseCall(args[1], null, Integer.class); + store.reference(store.getObject(id)); + write(reference, "NewGlobalRef " + id); + } else if (message.startsWith("GetClassName")) { + String[] args = message.split(" "); + Integer objectID = parseCall(args[1], null, Integer.class); + Object o = (Object) store.getObject(objectID); + write(reference, "GetClassName " + o.getClass().getName()); + } else if (message.startsWith("GetClassID")) { + String[] args = message.split(" "); + Integer objectID = parseCall(args[1], null, Integer.class); + store.reference(store.getObject(objectID).getClass()); + write(reference, "GetClassID " + store.getIdentifier(store.getObject(objectID).getClass())); + } + } catch (Throwable t) { + t.printStackTrace(); + String msg = t.getCause() != null ? t.getCause().getMessage() : t.getMessage(); + + // add an identifier string to let javaside know of the type of error + // check for cause as well, since the top level exception will be InvocationTargetException in most cases + if (t instanceof AccessControlException || t.getCause() instanceof AccessControlException) { + msg = "LiveConnectPermissionNeeded " + msg; + } + + write(reference, " Error " + msg); + + // ExceptionOccured is only called after Callmethod() by mozilla. So + // for exceptions that are not related to CallMethod, we need a way + // to log them. This is how we do it.. send an error message to the + // c++ side to let it know that something went wrong, and it will do + // the right thing to let mozilla know + + // Store the cause as the actual exception. This is needed because + // the exception we get here will always be an + // "InvocationTargetException" due to the use of reflection above + if (message.startsWith("CallMethod") || message.startsWith("CallStaticMethod")) + throwable = t.getCause(); + } + + } + + /** + * Checks if the calling script is allowed to access the specified class + * + * @param jsSrc The source of the script + * @param target The target class that the script is trying to access + * @param acc AccessControlContext for this execution + * @throws AccessControlException If the script has insufficient permissions + */ + public void checkPermission(String jsSrc, Class target, AccessControlContext acc) throws AccessControlException { + // NPRuntime does not allow cross-site calling. We therefore always + // allow this, for the time being + return; + } + + private void write(int reference, String message) { + PluginDebug.debug("appletviewer writing " + message); + streamhandler.write("context " + identifier + " reference " + reference + + " " + message); + } + + public void prePopulateLCClasses() { + + int classID; + + prepopulateClass("netscape/javascript/JSObject"); + classID = prepopulateClass("netscape/javascript/JSException"); + prepopulateMethod(classID, "<init>", "(Ljava/lang/String;Ljava/lang/String;ILjava/lang/String;I)"); + prepopulateMethod(classID, "<init>", "(ILjava/lang/Object;)"); + prepopulateField(classID, "lineno"); + prepopulateField(classID, "tokenIndex"); + prepopulateField(classID, "source"); + prepopulateField(classID, "filename"); + prepopulateField(classID, "wrappedExceptionType"); + prepopulateField(classID, "wrappedException"); + + classID = prepopulateClass("netscape/javascript/JSUtil"); + prepopulateMethod(classID, "getStackTrace", "(Ljava/lang/Throwable;)"); + + prepopulateClass("java/lang/Object"); + classID = prepopulateClass("java/lang/Class"); + prepopulateMethod(classID, "getMethods", "()"); + prepopulateMethod(classID, "getConstructors", "()"); + prepopulateMethod(classID, "getFields", "()"); + prepopulateMethod(classID, "getName", "()"); + prepopulateMethod(classID, "isArray", "()"); + prepopulateMethod(classID, "getComponentType", "()"); + prepopulateMethod(classID, "getModifiers", "()"); + + + classID = prepopulateClass("java/lang/reflect/Method"); + prepopulateMethod(classID, "getName", "()"); + prepopulateMethod(classID, "getParameterTypes", "()"); + prepopulateMethod(classID, "getReturnType", "()"); + prepopulateMethod(classID, "getModifiers", "()"); + + classID = prepopulateClass("java/lang/reflect/Constructor"); + prepopulateMethod(classID, "getParameterTypes", "()"); + prepopulateMethod(classID, "getModifiers", "()"); + + classID = prepopulateClass("java/lang/reflect/Field"); + prepopulateMethod(classID, "getName", "()"); + prepopulateMethod(classID, "getType", "()"); + prepopulateMethod(classID, "getModifiers", "()"); + + classID = prepopulateClass("java/lang/reflect/Array"); + prepopulateMethod(classID, "newInstance", "(Ljava/lang/Class;I)"); + + classID = prepopulateClass("java/lang/Throwable"); + prepopulateMethod(classID, "toString", "()"); + prepopulateMethod(classID, "getMessage", "()"); + + classID = prepopulateClass("java/lang/System"); + prepopulateMethod(classID, "identityHashCode", "(Ljava/lang/Object;)"); + + classID = prepopulateClass("java/lang/Boolean"); + prepopulateMethod(classID, "booleanValue", "()"); + prepopulateMethod(classID, "<init>", "(Z)"); + + classID = prepopulateClass("java/lang/Double"); + prepopulateMethod(classID, "doubleValue", "()"); + prepopulateMethod(classID, "<init>", "(D)"); + + classID = prepopulateClass("java/lang/Void"); + prepopulateField(classID, "TYPE"); + + prepopulateClass("java/lang/String"); + prepopulateClass("java/applet/Applet"); + } + + private int prepopulateClass(String name) { + name = name.replace('/', '.'); + ClassLoader cl = liveconnectLoader; + Class c = null; + + try { + c = cl.loadClass(name); + store.reference(c); + } catch (ClassNotFoundException cnfe) { + // do nothing ... this should never happen + cnfe.printStackTrace(); + } + + return store.getIdentifier(c); + } + + private int prepopulateMethod(int classID, String methodName, String signatureStr) { + Signature signature = parseCall(signatureStr, ((Class) store.getObject(classID)).getClassLoader(), Signature.class); + Object[] a = signature.getClassArray(); + + Class c = (Class) store.getObject(classID); + Method m = null; + Constructor cs = null; + Object o = null; + + try { + if (methodName.equals("<init>") + || methodName.equals("<clinit>")) { + o = cs = c.getConstructor(signature.getClassArray()); + store.reference(cs); + } else { + o = m = c.getMethod(methodName, signature.getClassArray()); + store.reference(m); + } + } catch (NoSuchMethodException e) { + // should never happen + e.printStackTrace(); + } + + return store.getIdentifier(m); + } + + private int prepopulateField(int classID, String fieldName) { + + Class c = (Class) store.getObject(classID); + Field f = null; + try { + f = c.getField(fieldName); + } catch (SecurityException e) { + // should never happen + e.printStackTrace(); + } catch (NoSuchFieldException e) { + // should never happen + e.printStackTrace(); + } + + store.reference(f); + return store.getIdentifier(f); + } + + public void dumpStore() { + store.dump(); + } + + public Object getObject(int identifier) { + return store.getObject(identifier); + } + + public int getIdentifier(Object o) { + return store.getIdentifier(o); + } + + public void store(Object o) { + store.reference(o); + } + + /** + * Returns a "closed" AccessControlContext i.e. no permissions to get out of sandbox. + */ + public AccessControlContext getClosedAccessControlContext() { + // Deny everything + Permissions p = new Permissions(); + ProtectionDomain pd = new ProtectionDomain(null, p); + return new AccessControlContext(new ProtectionDomain[] {pd}); + } + + public AccessControlContext getAccessControlContext(String[] nsPrivilegeList, String src) { + +/* + for (int i=0; i < nsPrivilegeList.length; i++) { + String privilege = nsPrivilegeList[i]; + + if (privilege.equals("UniversalAccept")) { + SocketPermission sp = new SocketPermission("*", "accept,resolve"); + grantedPermissions.add(sp); + } else if (privilege.equals("UniversalAwtEventQueueAccess")) { + AWTPermission awtp = new AWTPermission("accessEventQueue"); + grantedPermissions.add(awtp); + } else if (privilege.equals("UniversalConnect")) { + SocketPermission sp = new SocketPermission("*", "connect,resolve"); + grantedPermissions.add(sp); + } else if (privilege.equals("UniversalListen")) { + SocketPermission sp = new SocketPermission("*", "listen,resolve"); + grantedPermissions.add(sp); + } else if (privilege.equals("UniversalExecAccess")) { + FilePermission fp = new FilePermission("<<ALL FILES>>", "execute"); + RuntimePermission rtp = new RuntimePermission("setIO"); + grantedPermissions.add(fp); + grantedPermissions.add(rtp); + } else if (privilege.equals("UniversalExitAccess")) { + // Doesn't matter what the permissions are. Do not allow VM to exit.. we + // use a single VM for the entire browser lifecycle once invoked, we + // cannot let it exit + + //RuntimePermission rtp = new RuntimePermission("exitVM.*"); + //grantedPermissions.add(rtp); + } else if (privilege.equals("UniversalFileDelete")) { + FilePermission fp = new FilePermission("<<ALL FILES>>", "delete"); + grantedPermissions.add(fp); + } else if (privilege.equals("UniversalFileRead")) { + FilePermission fp = new FilePermission("<<ALL FILES>>", "read"); + grantedPermissions.add(fp); + } else if (privilege.equals("UniversalFileWrite")) { + FilePermission fp = new FilePermission("<<ALL FILES>>", "write"); + grantedPermissions.add(fp); + } else if (privilege.equals("UniversalFdRead")) { + RuntimePermission rtp = new RuntimePermission("readFileDescriptor"); + grantedPermissions.add(rtp); + } else if (privilege.equals("UniversalFdWrite")) { + RuntimePermission rtp = new RuntimePermission("writeFileDescriptor"); + grantedPermissions.add(rtp); + } else if (privilege.equals("UniversalLinkAccess")) { + RuntimePermission rtp = new RuntimePermission("loadLibrary.*"); + grantedPermissions.add(rtp); + } else if (privilege.equals("UniversalListen")) { + SocketPermission sp = new SocketPermission("*", "listen"); + grantedPermissions.add(sp); + } else if (privilege.equals("UniversalMulticast")) { + SocketPermission sp = new SocketPermission("*", "accept,connect,resolve"); + grantedPermissions.add(sp); + } else if (privilege.equals("UniversalPackageAccess")) { + RuntimePermission rtp = new RuntimePermission("defineClassInPackage.*"); + grantedPermissions.add(rtp); + } else if (privilege.equals("UniversalPackageDefinition")) { + RuntimePermission rtp = new RuntimePermission("accessClassInPackage.*"); + grantedPermissions.add(rtp); + } else if (privilege.equals("UniversalPrintJobAccess")) { + RuntimePermission rtp = new RuntimePermission("queuePrintJob"); + grantedPermissions.add(rtp); + } else if (privilege.equals("UniversalPropertyRead")) { + PropertyPermission pp = new PropertyPermission("*", "read"); + grantedPermissions.add(pp); + } else if (privilege.equals("UniversalPropertyWrite")) { + PropertyPermission pp = new PropertyPermission("*", "write"); + grantedPermissions.add(pp); + } else if (privilege.equals("UniversalSetFactory")) { + RuntimePermission rtp = new RuntimePermission("setFactory"); + grantedPermissions.add(rtp); + } else if (privilege.equals("UniversalSystemClipboardAccess")) { + AWTPermission awtp = new AWTPermission("accessClipboard"); + grantedPermissions.add(awtp); + } else if (privilege.equals("UniversalThreadAccess")) { + RuntimePermission rtp1 = new RuntimePermission("modifyThread"); + RuntimePermission rtp2 = new RuntimePermission("stopThread"); + grantedPermissions.add(rtp1); + grantedPermissions.add(rtp2); + } else if (privilege.equals("UniversalThreadGroupAccess")) { + RuntimePermission rtp1 = new RuntimePermission("modifyThreadGroup"); + RuntimePermission rtp2 = new RuntimePermission("modifyThread"); + RuntimePermission rtp3 = new RuntimePermission("stopThread"); + grantedPermissions.add(rtp1); + grantedPermissions.add(rtp2); + grantedPermissions.add(rtp3); + } else if (privilege.equals("UniversalTopLevelWindow")) { + AWTPermission awtp = new AWTPermission("topLevelWindow"); + grantedPermissions.add(awtp); + } else if (privilege.equals("UniversalBrowserRead")) { + BrowserReadPermission bp = new BrowserReadPermission(); + grantedPermissions.add(bp); + } else if (privilege.equals("UniversalJavaPermissions")) { + AllPermission ap = new AllPermission(); + grantedPermissions.add(ap); + } + } + + // what to do with these is unknown: UniversalConnectWithRedirect, UniversalDialogModality, UniversalSendMail, LimitedInstall, FullInstall, SilentInstall +*/ + + Permissions grantedPermissions = new Permissions(); + + for (int i=0; i < nsPrivilegeList.length; i++) { + String privilege = nsPrivilegeList[i]; + + if (privilege.equals("UniversalBrowserRead")) { + BrowserReadPermission bp = new BrowserReadPermission(); + grantedPermissions.add(bp); + } else if (privilege.equals("UniversalJavaPermission")) { + AllPermission ap = new AllPermission(); + grantedPermissions.add(ap); + } + } + + CodeSource cs = new CodeSource((URL) null, (java.security.cert.Certificate [])null); + + if (src != null && src.length() > 0) { + try { + cs = new CodeSource(new URL(src + "/"), (java.security.cert.Certificate[]) null); + } catch (MalformedURLException mfue) { + // do nothing + } + + if (src.equals("[System]")) + grantedPermissions.add(new JSObjectCreatePermission()); + + } else { + JSObjectCreatePermission perm = new JSObjectCreatePermission(); + grantedPermissions.add(perm); + } + + ProtectionDomain pd = new ProtectionDomain(cs, grantedPermissions, null, null); + + // Add to hashmap + return new AccessControlContext(new ProtectionDomain[] {pd}); + } + + // private static final == inline + private static final boolean isInt(Object o) { + boolean isInt = false; + + try { + Integer.parseInt((String) o); + isInt = true; + } catch (Exception e) { + // don't care + } + + return isInt; + } + + class BrowserReadPermission extends BasicPermission { + public BrowserReadPermission() { + super("browserRead"); + } + } + +} diff --git a/plugin/icedteanp/java/sun/applet/PluginAppletViewer.java b/plugin/icedteanp/java/sun/applet/PluginAppletViewer.java new file mode 100644 index 0000000..382ff7b --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/PluginAppletViewer.java @@ -0,0 +1,2056 @@ +/* PluginAppletViewer -- Handles embedding of the applet panel + Copyright (C) 2008 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +/* + * Copyright 1995-2004 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + + package sun.applet; + + import java.applet.Applet; +import java.applet.AppletContext; +import java.applet.AudioClip; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Insets; +import java.awt.Label; +import java.awt.Toolkit; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.io.Reader; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.InvocationTargetException; +import java.net.MalformedURLException; +import java.net.SocketPermission; +import java.net.URI; +import java.net.URL; +import java.security.AccessController; +import java.security.AllPermission; +import java.security.PrivilegedAction; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.Vector; + +import javax.swing.SwingUtilities; + +import net.sourceforge.jnlp.NetxPanel; +import net.sourceforge.jnlp.runtime.JNLPClassLoader; +import sun.awt.AppContext; +import sun.awt.SunToolkit; +import sun.awt.X11.XEmbeddedFrame; +import sun.misc.Ref; + +import com.sun.jndi.toolkit.url.UrlUtil; + + /** + * Lets us construct one using unix-style one shot behaviors + */ + + class PluginAppletPanelFactory + { + + public AppletPanel createPanel(PluginStreamHandler streamhandler, + int identifier, + long handle, int x, int y, + final URL doc, final Hashtable atts) { + + AppletViewerPanel panel = (AppletViewerPanel) AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + try { + AppletPanel panel = new NetxPanel(doc, atts, false); + AppletViewerPanel.debug("Using NetX panel"); + PluginDebug.debug(atts.toString()); + return panel; + } catch (Exception ex) { + AppletViewerPanel.debug("Unable to start NetX applet - defaulting to Sun applet", ex); + return new AppletViewerPanel(doc, atts); + } + } + }); + + + + // create the frame. + PluginAppletViewer.reFrame(null, identifier, System.out, handle, panel); + + panel.init(); + + // Start the applet + initEventQueue(panel); + + // Applet initialized. Find out it's classloader and add it to the list + String portComponent = doc.getPort() != -1 ? ":" + doc.getPort() : ""; + String codeBase = doc.getProtocol() + "://" + doc.getHost() + portComponent; + + if (atts.get("codebase") != null) { + try { + URL appletSrcURL = new URL(codeBase + (String) atts.get("codebase")); + codeBase = appletSrcURL.getProtocol() + "://" + appletSrcURL.getHost(); + } catch (MalformedURLException mfue) { + // do nothing + } + } + + + // Wait for the panel to initialize + // (happens in a separate thread) + Applet a; + + // Wait for panel to come alive + int maxWait = PluginAppletViewer.APPLET_TIMEOUT; // wait for panel to come alive + int wait = 0; + while ((panel == null) || (!((NetxPanel) panel).isAlive() && wait < maxWait)) { + try { + Thread.sleep(50); + wait += 50; + } catch (InterruptedException ie) { + // just wait + } + } + + // Wait for the panel to initialize + // (happens in a separate thread) + while (panel.getApplet() == null && + ((NetxPanel) panel).isAlive()) { + try { + Thread.sleep(50); + PluginDebug.debug("Waiting for applet to initialize..."); + } catch (InterruptedException ie) { + // just wait + } + } + + a = panel.getApplet(); + + // Still null? + if (panel.getApplet() == null) { + streamhandler.write("instance " + identifier + " reference " + -1 + " fatalError " + "Initialization failed"); + return null; + } + + PluginDebug.debug("Applet " + a.getClass() + " initialized"); + streamhandler.write("instance " + identifier + " reference 0 initialized"); + + AppletSecurityContextManager.getSecurityContext(0).associateSrc(((NetxPanel) panel).getAppletClassLoader(), doc); + AppletSecurityContextManager.getSecurityContext(0).associateInstance(identifier, ((NetxPanel) panel).getAppletClassLoader()); + + return panel; + } + + public boolean isStandalone() + { + return false; + } + + /** + * Send the initial set of events to the appletviewer event queue. + * On start-up the current behaviour is to load the applet and call + * Applet.init() and Applet.start(). + */ + private void initEventQueue(AppletPanel panel) { + // appletviewer.send.event is an undocumented and unsupported system + // property which is used exclusively for testing purposes. + PrivilegedAction pa = new PrivilegedAction() { + public Object run() { + return System.getProperty("appletviewer.send.event"); + } + }; + String eventList = (String) AccessController.doPrivileged(pa); + + if (eventList == null) { + // Add the standard events onto the event queue. + panel.sendEvent(AppletPanel.APPLET_LOAD); + panel.sendEvent(AppletPanel.APPLET_INIT); + panel.sendEvent(AppletPanel.APPLET_START); + } else { + // We're testing AppletViewer. Force the specified set of events + // onto the event queue, wait for the events to be processed, and + // exit. + + // The list of events that will be executed is provided as a + // ","-separated list. No error-checking will be done on the list. + String [] events = splitSeparator(",", eventList); + + for (int i = 0; i < events.length; i++) { + PluginDebug.debug("Adding event to queue: " + events[i]); + if (events[i].equals("dispose")) + panel.sendEvent(AppletPanel.APPLET_DISPOSE); + else if (events[i].equals("load")) + panel.sendEvent(AppletPanel.APPLET_LOAD); + else if (events[i].equals("init")) + panel.sendEvent(AppletPanel.APPLET_INIT); + else if (events[i].equals("start")) + panel.sendEvent(AppletPanel.APPLET_START); + else if (events[i].equals("stop")) + panel.sendEvent(AppletPanel.APPLET_STOP); + else if (events[i].equals("destroy")) + panel.sendEvent(AppletPanel.APPLET_DESTROY); + else if (events[i].equals("quit")) + panel.sendEvent(AppletPanel.APPLET_QUIT); + else if (events[i].equals("error")) + panel.sendEvent(AppletPanel.APPLET_ERROR); + else + // non-fatal error if we get an unrecognized event + PluginDebug.debug("Unrecognized event name: " + events[i]); + } + + while (!panel.emptyEventQueue()) ; + } + } + + + /** + * Split a string based on the presence of a specified separator. Returns + * an array of arbitrary length. The end of each element in the array is + * indicated by the separator of the end of the string. If there is a + * separator immediately before the end of the string, the final element + * will be empty. None of the strings will contain the separator. Useful + * when separating strings such as "foo/bar/bas" using separator "/". + * + * @param sep The separator. + * @param s The string to split. + * @return An array of strings. Each string in the array is determined + * by the location of the provided sep in the original string, + * s. Whitespace not stripped. + */ + private String [] splitSeparator(String sep, String s) { + Vector v = new Vector(); + int tokenStart = 0; + int tokenEnd = 0; + + while ((tokenEnd = s.indexOf(sep, tokenStart)) != -1) { + v.addElement(s.substring(tokenStart, tokenEnd)); + tokenStart = tokenEnd+1; + } + // Add the final element. + v.addElement(s.substring(tokenStart)); + + String [] retVal = new String[v.size()]; + v.copyInto(retVal); + return retVal; + } + } + + class PluginParseRequest + { + long handle; + String tag; + String documentbase; + } + + /* + */ + // FIXME: declare JSProxy implementation + public class PluginAppletViewer extends XEmbeddedFrame + implements AppletContext, Printable { + /** + * Some constants... + */ + private static String defaultSaveFile = "Applet.ser"; + + private static enum PAV_INIT_STATUS {PRE_INIT, IN_INIT, INIT_COMPLETE, INACTIVE}; + + /** + * The panel in which the applet is being displayed. + */ + AppletViewerPanel panel; + + /** + * The status line. + */ + Label label; + + /** + * output status messages to this stream + */ + + PrintStream statusMsgStream; + + int identifier; + + private static HashMap<Integer, PluginParseRequest> requests = + new HashMap(); + + // Instance identifier -> PluginAppletViewer object. + private static HashMap<Integer, PluginAppletViewer> applets = + new HashMap(); + + private static PluginStreamHandler streamhandler; + + private static PluginCallRequestFactory requestFactory; + + private static HashMap<Integer, PAV_INIT_STATUS> status = + new HashMap<Integer,PAV_INIT_STATUS>(); + + + private long handle = 0; + private WindowListener windowEventListener = null; + private AppletEventListener appletEventListener = null; + + public static final int APPLET_TIMEOUT = 180000; + + private static Long requestIdentityCounter = 0L; + + /** + * Null constructor to allow instantiation via newInstance() + */ + public PluginAppletViewer() { + } + + public static void reFrame(PluginAppletViewer oldFrame, + int identifier, PrintStream statusMsgStream, + long handle, AppletViewerPanel panel) { + + PluginDebug.debug("Reframing " + panel); + + // SecurityManager MUST be set, and only privileged code may call reFrame() + System.getSecurityManager().checkPermission(new AllPermission()); + + // Same handle => nothing to do + if (oldFrame != null && handle == oldFrame.handle) + return; + + PluginAppletViewer newFrame = new PluginAppletViewer(handle, identifier, statusMsgStream, panel); + + if (oldFrame != null) { + applets.remove(oldFrame.identifier); + oldFrame.removeWindowListener(oldFrame.windowEventListener); + panel.removeAppletListener(oldFrame.appletEventListener); + oldFrame.remove(panel); + oldFrame.dispose(); + } + + newFrame.add("Center", panel); + newFrame.pack(); + + newFrame.appletEventListener = new AppletEventListener(newFrame, newFrame); + panel.addAppletListener(newFrame.appletEventListener); + + applets.put(identifier, newFrame); + + // dispose oldframe if necessary + if (oldFrame != null) { + oldFrame.dispose(); + } + + PluginDebug.debug(panel + " reframed"); + } + + /** + * Create new plugin appletviewer frame + */ + private PluginAppletViewer(long handle, final int identifier, + PrintStream statusMsgStream, + AppletViewerPanel appletPanel) { + + super(handle, true); + this.statusMsgStream = statusMsgStream; + this.identifier = identifier; + this.panel = appletPanel; + + if (!appletPanels.contains(panel)) + appletPanels.addElement(panel); + + windowEventListener = new WindowAdapter() { + + public void windowClosing(WindowEvent evt) { + appletClose(); + } + + public void windowIconified(WindowEvent evt) { + appletStop(); + } + + public void windowDeiconified(WindowEvent evt) { + appletStart(); + } + }; + + addWindowListener(windowEventListener); + + } + + private static class AppletEventListener implements AppletListener + { + final Frame frame; + final PluginAppletViewer appletViewer; + + public AppletEventListener(Frame frame, PluginAppletViewer appletViewer) + { + this.frame = frame; + this.appletViewer = appletViewer; + } + + public void appletStateChanged(AppletEvent evt) + { + AppletPanel src = (AppletPanel)evt.getSource(); + + switch (evt.getID()) { + case AppletPanel.APPLET_RESIZE: { + if(src != null) { + appletViewer.resize(appletViewer.preferredSize()); + appletViewer.validate(); + } + break; + } + case AppletPanel.APPLET_LOADING_COMPLETED: { + Applet a = src.getApplet(); // sun.applet.AppletPanel + + // Fixed #4754451: Applet can have methods running on main + // thread event queue. + // + // The cause of this bug is that the frame of the applet + // is created in main thread group. Thus, when certain + // AWT/Swing events are generated, the events will be + // dispatched through the wrong event dispatch thread. + // + // To fix this, we rearrange the AppContext with the frame, + // so the proper event queue will be looked up. + // + // Swing also maintains a Frame list for the AppContext, + // so we will have to rearrange it as well. + // + if (a != null) + AppletPanel.changeFrameAppContext(frame, SunToolkit.targetToAppContext(a)); + else + AppletPanel.changeFrameAppContext(frame, AppContext.getAppContext()); + + status.put(appletViewer.identifier, PAV_INIT_STATUS.INIT_COMPLETE); + + break; + } + } + } + } + + public static void setStreamhandler(PluginStreamHandler sh) { + streamhandler = sh; + } + + public static void setPluginCallRequestFactory(PluginCallRequestFactory rf) { + requestFactory = rf; + } + + /** + * Handle an incoming message from the plugin. + */ + public static void handleMessage(int identifier, int reference, String message) + { + + PluginDebug.debug("PAV handling: " + message); + + try { + if (message.startsWith("handle")) { + + // Extract the information from the message + String[] msgParts = new String[4]; + for (int i=0; i < 3; i++) { + int spaceLocation = message.indexOf(' '); + int nextSpaceLocation = message.indexOf(' ', spaceLocation+1); + msgParts[i] = message.substring(spaceLocation + 1, nextSpaceLocation); + message = message.substring(nextSpaceLocation + 1); + } + + long handle = Long.parseLong(msgParts[0]); + String width = msgParts[1]; + String height = msgParts[2]; + + int spaceLocation = message.indexOf(' ', "tag".length()+1); + String documentBase = + UrlUtil.decode(message.substring("tag".length() + 1, spaceLocation)); + String tag = message.substring(spaceLocation+1); + + PluginDebug.debug ("Handle = " + handle + "\n" + + "Width = " + width + "\n" + + "Height = " + height + "\n" + + "DocumentBase = " + documentBase + "\n" + + "Tag = " + tag); + + status.put(identifier, PAV_INIT_STATUS.PRE_INIT); + PluginAppletViewer.parse + (identifier, handle, width, height, + new StringReader(tag), + new URL(documentBase)); + + + int maxWait = APPLET_TIMEOUT; // wait for applet to fully load + int wait = 0; + while (!status.get(identifier).equals(PAV_INIT_STATUS.INIT_COMPLETE) && + (wait < maxWait)) { + + try { + Thread.sleep(50); + wait += 50; + } catch (InterruptedException ie) { + // just wait + } + } + + if (!status.get(identifier).equals(PAV_INIT_STATUS.INIT_COMPLETE)) + throw new Exception("Applet initialization timeout"); + + } else { + PluginDebug.debug ("Handling message: " + message + " instance " + identifier + " " + Thread.currentThread()); + + // Wait till initialization finishes + while (!applets.containsKey(identifier) && + ( + !status.containsKey(identifier) || + status.get(identifier).equals(PAV_INIT_STATUS.PRE_INIT) + ) + ); + + // don't bother processing further for inactive applets + if (status.get(identifier).equals(PAV_INIT_STATUS.INACTIVE)) + return; + + applets.get(identifier).handleMessage(reference, message); + } + } catch (Exception e) { + + e.printStackTrace(); + + // If an exception happened during pre-init, we need to update status + if (status.get(identifier).equals(PAV_INIT_STATUS.PRE_INIT)) + status.put(identifier, PAV_INIT_STATUS.INACTIVE); + + throw new RuntimeException("Failed to handle message: " + + message + " for instance " + identifier, e); + } + } + + public void handleMessage(int reference, String message) + { + if (message.startsWith("width")) { + + // Wait for panel to come alive + int maxWait = APPLET_TIMEOUT; // wait for panel to come alive + int wait = 0; + while (!status.get(identifier).equals(PAV_INIT_STATUS.INIT_COMPLETE) && wait < maxWait) { + + try { + Thread.sleep(50); + wait += 50; + } catch (InterruptedException ie) { + // just wait + } + } + + + // 0 => width, 1=> width_value, 2 => height, 3=> height_value + String[] dimMsg = message.split(" "); + + final int height = (int) (Integer.parseInt(dimMsg[3])); + final int width = (int) (Integer.parseInt(dimMsg[1])); + + if (panel instanceof NetxPanel) + ((NetxPanel) panel).updateSizeInAtts(height, width); + + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + + setSize(width, height); + + // There is a rather odd drawing bug whereby resizing + // the panel makes no difference on initial call + // because the panel thinks that it is already the + // right size. Validation has no effect there either. + // So we work around by setting size to 1, validating, + // and then setting to the right size and validating + // again. This is not very efficient, and there is + // probably a better way -- but resizing happens + // quite infrequently, so for now this is how we do it + + panel.setSize(1,1); + panel.validate(); + + panel.setSize(width, height); + panel.validate(); + + panel.applet.resize(width, height); + panel.applet.validate(); + } + }); + } catch (InterruptedException e) { + // do nothing + e.printStackTrace(); + } catch (InvocationTargetException e) { + // do nothing + e.printStackTrace(); + } + + } else if (message.startsWith("destroy")) { + dispose(); + status.put(identifier, PAV_INIT_STATUS.INACTIVE); + } else if (message.startsWith("GetJavaObject")) { + + // FIXME: how do we determine what security context this + // object should belong to? + Object o; + + // Wait for panel to come alive + int maxWait = APPLET_TIMEOUT; // wait for panel to come alive + int wait = 0; + while ((panel == null) || (!((NetxPanel) panel).isAlive() && wait < maxWait)) { + try { + Thread.sleep(50); + wait += 50; + } catch (InterruptedException ie) { + // just wait + } + } + + // Wait for the panel to initialize + // (happens in a separate thread) + while (panel.getApplet() == null && + ((NetxPanel) panel).isAlive()) { + try { + Thread.sleep(50); + PluginDebug.debug("Waiting for applet to initialize..."); + } catch (InterruptedException ie) { + // just wait + } + } + + PluginDebug.debug(panel + " -- " + panel.getApplet() + " -- " + ((NetxPanel) panel).isAlive()); + + // Still null? + if (panel.getApplet() == null) { + this.streamhandler.write("instance " + identifier + " reference " + -1 + " fatalError " + "Initialization failed"); + return; + } + + o = panel.getApplet(); + PluginDebug.debug ("Looking for object " + o + " panel is " + panel); + AppletSecurityContextManager.getSecurityContext(0).store(o); + PluginDebug.debug ("WRITING 1: " + "context 0 reference " + reference + " GetJavaObject " + + AppletSecurityContextManager.getSecurityContext(0).getIdentifier(o)); + streamhandler.write("context 0 reference " + reference + " GetJavaObject " + + AppletSecurityContextManager.getSecurityContext(0).getIdentifier(o)); + PluginDebug.debug ("WRITING 1 DONE"); + } + } + + // FIXME: Kind of hackish way to ensure synchronized re-drawing + private synchronized void forceredraw() { + doLayout(); + } + + /* + * Methods for java.applet.AppletContext + */ + + private static Map audioClips = new HashMap(); + + /** + * Get an audio clip. + */ + public AudioClip getAudioClip(URL url) { + checkConnect(url); + synchronized (audioClips) { + AudioClip clip = (AudioClip)audioClips.get(url); + if (clip == null) { + audioClips.put(url, clip = new AppletAudioClip(url)); + } + return clip; + } + } + + private static Map imageRefs = new HashMap(); + + /** + * Get an image. + */ + public Image getImage(URL url) { + return getCachedImage(url); + } + + private Image getCachedImage(URL url) { + // System.getSecurityManager().checkConnection(url.getHost(), url.getPort()); + return (Image)getCachedImageRef(url).get(); + } + + /** + * Get an image ref. + */ + private synchronized Ref getCachedImageRef(URL url) { + PluginDebug.debug("getCachedImageRef() searching for " + url); + + try { + + String originalURL = url.toString(); + String codeBase = panel.getCodeBase().toString(); + + if (originalURL.startsWith(codeBase)) { + + PluginDebug.debug("getCachedImageRef() got URL = " + url); + PluginDebug.debug("getCachedImageRef() plugin codebase = " + codeBase); + + // try to fetch it locally + if (panel instanceof NetxPanel) { + + URL localURL = null; + + String resourceName = originalURL.substring(codeBase.length()); + JNLPClassLoader loader = (JNLPClassLoader) ((NetxPanel) panel).getAppletClassLoader(); + + if (loader.resourceAvailableLocally(resourceName)) + localURL = loader.getResource(resourceName); + + url = localURL != null ? localURL : url; + } + } + + PluginDebug.debug("getCachedImageRef() getting img from URL = " + url); + + synchronized (imageRefs) { + AppletImageRef ref = (AppletImageRef)imageRefs.get(url); + if (ref == null) { + ref = new AppletImageRef(url); + imageRefs.put(url, ref); + } + return ref; + } + } catch (Exception e) { + System.err.println("Error occurred when trying to fetch image:"); + e.printStackTrace(); + return null; + } + } + + /** + * Flush the image cache. + */ + static void flushImageCache() { + imageRefs.clear(); + } + + static Vector appletPanels = new Vector(); + + /** + * Get an applet by name. + */ + public Applet getApplet(String name) { + name = name.toLowerCase(); + SocketPermission panelSp = + new SocketPermission(panel.getCodeBase().getHost(), "connect"); + for (Enumeration e = appletPanels.elements() ; e.hasMoreElements() ;) { + AppletPanel p = (AppletPanel)e.nextElement(); + String param = p.getParameter("name"); + if (param != null) { + param = param.toLowerCase(); + } + if (name.equals(param) && + p.getDocumentBase().equals(panel.getDocumentBase())) { + + SocketPermission sp = + new SocketPermission(p.getCodeBase().getHost(), "connect"); + + if (panelSp.implies(sp)) { + return p.applet; + } + } + } + return null; + } + + /** + * Return an enumeration of all the accessible + * applets on this page. + */ + public Enumeration getApplets() { + Vector v = new Vector(); + SocketPermission panelSp = + new SocketPermission(panel.getCodeBase().getHost(), "connect"); + + for (Enumeration e = appletPanels.elements() ; e.hasMoreElements() ;) { + AppletPanel p = (AppletPanel)e.nextElement(); + if (p.getDocumentBase().equals(panel.getDocumentBase())) { + + SocketPermission sp = + new SocketPermission(p.getCodeBase().getHost(), "connect"); + if (panelSp.implies(sp)) { + v.addElement(p.applet); + } + } + } + return v.elements(); + } + + /** + * Ignore. + */ + public void showDocument(URL url) { + PluginDebug.debug("Showing document..."); + showDocument(url, "_self"); + } + + /** + * Ignore. + */ + public void showDocument(URL url, String target) { + try { + // FIXME: change to postCallRequest + write("url " + UrlUtil.encode(url.toString(), "UTF-8") + " " + target); + } catch (IOException exception) { + // Deliberately ignore IOException. showDocument may be + // called from threads other than the main thread after + // streamhandler.pluginOutputStream has been closed. + } + } + + /** + * Show status. + */ + public void showStatus(String status) { + try { + // FIXME: change to postCallRequest + // For statuses, we cannot have a newline + status = status.replace("\n", " "); + write("status " + status); + } catch (IOException exception) { + // Deliberately ignore IOException. showStatus may be + // called from threads other than the main thread after + // streamhandler.pluginOutputStream has been closed. + } + } + + /** + * Returns an incremental number (unique identifier) for a message. + * If identifier hits Long.MAX_VALUE it loops back starting at 0. + * + * @return A unique Long identifier for the request + */ + private static Long getRequestIdentifier() { + synchronized (requestIdentityCounter) { + + if (requestIdentityCounter == Long.MAX_VALUE) + requestIdentityCounter = 0L; + + return requestIdentityCounter++; + } + } + + public long getWindow() { + PluginDebug.debug ("STARTING getWindow"); + Long reference = getRequestIdentifier(); + + PluginCallRequest request = requestFactory.getPluginCallRequest("window", + "instance " + identifier + " reference " + + + reference + " " + "GetWindow", reference); + + PluginDebug.debug ("STARTING postCallRequest"); + streamhandler.postCallRequest(request); + PluginDebug.debug ("STARTING postCallRequest done"); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait request 1"); + synchronized(request) { + PluginDebug.debug ("wait request 2"); + while ((Long) request.getObject() == 0) + request.wait(); + PluginDebug.debug ("wait request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + + PluginDebug.debug ("STARTING getWindow DONE"); + return (Long) request.getObject(); + } + + // FIXME: make private, access via reflection. + public static Object getMember(long internal, String name) + { + AppletSecurityContextManager.getSecurityContext(0).store(name); + int nameID = AppletSecurityContextManager.getSecurityContext(0).getIdentifier(name); + Long reference = getRequestIdentifier(); + + // Prefix with dummy instance for convenience. + PluginCallRequest request = requestFactory.getPluginCallRequest("member", + "instance " + 0 + " reference " + reference + " GetMember " + + internal + " " + nameID, reference); + + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait getMEM request 1"); + synchronized(request) { + PluginDebug.debug ("wait getMEM request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait getMEM request 3 GOT: " + request.getObject().getClass()); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" getMember DONE"); + return request.getObject(); + } + + public static void setMember(long internal, String name, Object value) { + System.err.println("Setting to class " + value.getClass() + ":" + value.getClass().isPrimitive()); + AppletSecurityContextManager.getSecurityContext(0).store(name); + int nameID = AppletSecurityContextManager.getSecurityContext(0).getIdentifier(name); + Long reference = getRequestIdentifier(); + + // work on a copy of value, as we don't want to be manipulating + // complex objects + String valueToSetTo; + if (value instanceof java.lang.Byte || + value instanceof java.lang.Character || + value instanceof java.lang.Short || + value instanceof java.lang.Integer || + value instanceof java.lang.Long || + value instanceof java.lang.Float || + value instanceof java.lang.Double || + value instanceof java.lang.Boolean) { + + valueToSetTo = "literalreturn " + value.toString(); + + // Character -> Str results in str value.. we need int value as + // per specs. + if (value instanceof java.lang.Character) { + valueToSetTo = "literalreturn " + (int) ((java.lang.Character) value).charValue(); + } else if (value instanceof Float || + value instanceof Double) { + valueToSetTo = "literalreturn " + String.format("%308.308e", value); + } + + } else { + AppletSecurityContextManager.getSecurityContext(0).store(value); + valueToSetTo = Integer.toString(AppletSecurityContextManager.getSecurityContext(0).getIdentifier(value)); + } + + // Prefix with dummy instance for convenience. + PluginCallRequest request = requestFactory.getPluginCallRequest("void", + "instance " + 0 + " reference " + reference + " SetMember " + + internal + " " + nameID + " " + valueToSetTo, reference); + + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait setMem request: " + request.getMessage()); + PluginDebug.debug ("wait setMem request 1"); + synchronized(request) { + PluginDebug.debug ("wait setMem request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait setMem request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" setMember DONE"); + } + + // FIXME: handle long index as well. + public static void setSlot(long internal, int index, Object value) { + AppletSecurityContextManager.getSecurityContext(0).store(value); + Long reference = getRequestIdentifier(); + + // work on a copy of value, as we don't want to be manipulating + // complex objects + String valueToSetTo; + if (value instanceof java.lang.Byte || + value instanceof java.lang.Character || + value instanceof java.lang.Short || + value instanceof java.lang.Integer || + value instanceof java.lang.Long || + value instanceof java.lang.Float || + value instanceof java.lang.Double || + value instanceof java.lang.Boolean) { + + valueToSetTo = "literalreturn " + value.toString(); + + // Character -> Str results in str value.. we need int value as + // per specs. + if (value instanceof java.lang.Character) { + valueToSetTo = "literalreturn " + (int) ((java.lang.Character) value).charValue(); + } else if (value instanceof Float || + value instanceof Double) { + valueToSetTo = "literalreturn " + String.format("%308.308e", value); + } + + } else { + AppletSecurityContextManager.getSecurityContext(0).store(value); + valueToSetTo = Integer.toString(AppletSecurityContextManager.getSecurityContext(0).getIdentifier(value)); + } + + // Prefix with dummy instance for convenience. + PluginCallRequest request = requestFactory.getPluginCallRequest("void", + "instance " + 0 + " reference " + reference + " SetSlot " + + internal + " " + index + " " + valueToSetTo, reference); + + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait setSlot request 1"); + synchronized(request) { + PluginDebug.debug ("wait setSlot request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait setSlot request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" setSlot DONE"); + } + + public static Object getSlot(long internal, int index) + { + Long reference = getRequestIdentifier(); + + // Prefix with dummy instance for convenience. + PluginCallRequest request = requestFactory.getPluginCallRequest("member", + "instance " + 0 + " reference " + reference + " GetSlot " + + internal + " " + index, reference); + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait getSlot request 1"); + synchronized(request) { + PluginDebug.debug ("wait getSlot request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait getSlot request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" getSlot DONE"); + return request.getObject(); + } + + public static Object eval(long internal, String s) + { + AppletSecurityContextManager.getSecurityContext(0).store(s); + int stringID = AppletSecurityContextManager.getSecurityContext(0).getIdentifier(s); + Long reference = getRequestIdentifier(); + + // Prefix with dummy instance for convenience. + // FIXME: rename GetMemberPluginCallRequest ObjectPluginCallRequest. + PluginCallRequest request = requestFactory.getPluginCallRequest("member", + "instance " + 0 + " reference " + reference + " Eval " + + internal + " " + stringID, reference); + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait eval request 1"); + synchronized(request) { + PluginDebug.debug ("wait eval request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait eval request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" getSlot DONE"); + return request.getObject(); + } + + public static void removeMember (long internal, String name) { + AppletSecurityContextManager.getSecurityContext(0).store(name); + int nameID = AppletSecurityContextManager.getSecurityContext(0).getIdentifier(name); + Long reference = getRequestIdentifier(); + + // Prefix with dummy instance for convenience. + PluginCallRequest request = requestFactory.getPluginCallRequest("void", + "instance " + 0 + " reference " + reference + " RemoveMember " + + internal + " " + nameID, reference); + + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait removeMember request 1"); + synchronized(request) { + PluginDebug.debug ("wait removeMember request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait removeMember request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" RemoveMember DONE"); + } + + public static Object call(long internal, String name, Object args[]) + { + // FIXME: when is this removed from the object store? + // FIXME: reference should return the ID. + // FIXME: convenience method for this long line. + AppletSecurityContextManager.getSecurityContext(0).store(name); + int nameID = AppletSecurityContextManager.getSecurityContext(0).getIdentifier(name); + Long reference = getRequestIdentifier(); + + String argIDs = ""; + for (Object arg : args) + { + AppletSecurityContextManager.getSecurityContext(0).store(arg); + argIDs += AppletSecurityContextManager.getSecurityContext(0).getIdentifier(arg) + " "; + } + argIDs = argIDs.trim(); + + // Prefix with dummy instance for convenience. + PluginCallRequest request = requestFactory.getPluginCallRequest("member", + "instance " + 0 + " reference " + reference + " Call " + + internal + " " + nameID + " " + argIDs, reference); + + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait call request 1"); + synchronized(request) { + PluginDebug.debug ("wait call request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait call request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" Call DONE"); + return request.getObject(); + } + + public static Object requestPluginCookieInfo(URI uri) { + + PluginCallRequest request; + Long reference = getRequestIdentifier(); + + try + { + String encodedURI = UrlUtil.encode(uri.toString(), "UTF-8"); + request = requestFactory.getPluginCallRequest("cookieinfo", + "plugin PluginCookieInfo " + "reference " + reference + + " " + encodedURI, reference); + + } catch (UnsupportedEncodingException e) + { + e.printStackTrace(); + return null; + } + + PluginMessageConsumer.registerPriorityWait(reference); + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait cookieinfo request 1"); + synchronized(request) { + PluginDebug.debug ("wait cookieinfo request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait cookieinfo request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for cookieinfo request.", + e); + } + PluginDebug.debug (" Cookieinfo DONE"); + return request.getObject(); + } + + public static Object requestPluginProxyInfo(URI uri) { + + String requestURI = null; + Long reference = getRequestIdentifier(); + + try { + + // there is no easy way to get SOCKS proxy info. So, we tell mozilla that we want proxy for + // an HTTP uri in case of non http/ftp protocols. If we get back a SOCKS proxy, we can + // use that, if we get back an http proxy, we fallback to DIRECT connect + + String scheme = uri.getScheme(); + String port = uri.getPort() != -1 ? ":" + uri.getPort() : ""; + if (!uri.getScheme().startsWith("http") && !uri.getScheme().equals("ftp")) + scheme = "http"; + + requestURI = UrlUtil.encode(scheme + "://" + uri.getHost() + port + "/" + uri.getPath(), "UTF-8"); + } catch (Exception e) { + PluginDebug.debug("Cannot construct URL from " + uri.toString() + " ... falling back to DIRECT proxy"); + e.printStackTrace(); + return null; + } + + PluginCallRequest request = requestFactory.getPluginCallRequest("proxyinfo", + "plugin PluginProxyInfo reference " + reference + " " + + requestURI, reference); + + PluginMessageConsumer.registerPriorityWait(reference); + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait call request 1"); + synchronized(request) { + PluginDebug.debug ("wait call request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait call request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" Call DONE"); + return request.getObject(); + } + + public static void JavaScriptFinalize(long internal) + { + Long reference = getRequestIdentifier(); + + // Prefix with dummy instance for convenience. + PluginCallRequest request = requestFactory.getPluginCallRequest("void", + "instance " + 0 + " reference " + reference + " Finalize " + + internal, reference); + + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait finalize request 1"); + synchronized(request) { + PluginDebug.debug ("wait finalize request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait finalize request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" finalize DONE"); + } + + public static String javascriptToString(long internal) + { + Long reference = getRequestIdentifier(); + + // Prefix with dummy instance for convenience. + PluginCallRequest request = requestFactory.getPluginCallRequest("member", + "instance " + 0 + " reference " + reference + " ToString " + + internal, reference); + + streamhandler.postCallRequest(request); + streamhandler.write(request.getMessage()); + try { + PluginDebug.debug ("wait ToString request 1"); + synchronized(request) { + PluginDebug.debug ("wait ToString request 2"); + while (request.isDone() == false) + request.wait(); + PluginDebug.debug ("wait ToString request 3"); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted waiting for call request.", + e); + } + PluginDebug.debug (" ToString DONE"); + return (String) request.getObject(); + } + + // FIXME: make this private and access it from JSObject using + // reflection. + private void write(String message) throws IOException { + PluginDebug.debug ("WRITING 2: " + "instance " + identifier + " " + message); + streamhandler.write("instance " + identifier + " " + message); + PluginDebug.debug ("WRITING 2 DONE"); + } + + public void setStream(String key, InputStream stream)throws IOException{ + // We do nothing. + } + + public InputStream getStream(String key){ + // We do nothing. + return null; + } + + public Iterator getStreamKeys(){ + // We do nothing. + return null; + } + + /** + * System parameters. + */ + static Hashtable systemParam = new Hashtable(); + + static { + systemParam.put("codebase", "codebase"); + systemParam.put("code", "code"); + systemParam.put("alt", "alt"); + systemParam.put("width", "width"); + systemParam.put("height", "height"); + systemParam.put("align", "align"); + systemParam.put("vspace", "vspace"); + systemParam.put("hspace", "hspace"); + } + + /** + * Print the HTML tag. + */ + public static void printTag(PrintStream out, Hashtable atts) { + out.print("<applet"); + + String v = (String)atts.get("codebase"); + if (v != null) { + out.print(" codebase=\"" + v + "\""); + } + + v = (String)atts.get("code"); + if (v == null) { + v = "applet.class"; + } + out.print(" code=\"" + v + "\""); + v = (String)atts.get("width"); + if (v == null) { + v = "150"; + } + out.print(" width=" + v); + + v = (String)atts.get("height"); + if (v == null) { + v = "100"; + } + out.print(" height=" + v); + + v = (String)atts.get("name"); + if (v != null) { + out.print(" name=\"" + v + "\""); + } + out.println(">"); + + // A very slow sorting algorithm + int len = atts.size(); + String params[] = new String[len]; + len = 0; + for (Enumeration e = atts.keys() ; e.hasMoreElements() ;) { + String param = (String)e.nextElement(); + int i = 0; + for (; i < len ; i++) { + if (params[i].compareTo(param) >= 0) { + break; + } + } + System.arraycopy(params, i, params, i + 1, len - i); + params[i] = param; + len++; + } + + for (int i = 0 ; i < len ; i++) { + String param = params[i]; + if (systemParam.get(param) == null) { + out.println("<param name=" + param + + " value=\"" + atts.get(param) + "\">"); + } + } + out.println("</applet>"); + } + + /** + * Make sure the atrributes are uptodate. + */ + public void updateAtts() { + Dimension d = panel.size(); + Insets in = panel.insets(); + panel.atts.put("width", + new Integer(d.width - (in.left + in.right)).toString()); + panel.atts.put("height", + new Integer(d.height - (in.top + in.bottom)).toString()); + } + + /** + * Restart the applet. + */ + void appletRestart() { + panel.sendEvent(AppletPanel.APPLET_STOP); + panel.sendEvent(AppletPanel.APPLET_DESTROY); + panel.sendEvent(AppletPanel.APPLET_INIT); + panel.sendEvent(AppletPanel.APPLET_START); + } + + /** + * Reload the applet. + */ + void appletReload() { + panel.sendEvent(AppletPanel.APPLET_STOP); + panel.sendEvent(AppletPanel.APPLET_DESTROY); + panel.sendEvent(AppletPanel.APPLET_DISPOSE); + + /** + * Fixed #4501142: Classlaoder sharing policy doesn't + * take "archive" into account. This will be overridden + * by Java Plug-in. [stanleyh] + */ + AppletPanel.flushClassLoader(panel.getClassLoaderCacheKey()); + + /* + * Make sure we don't have two threads running through the event queue + * at the same time. + */ + try { + panel.joinAppletThread(); + panel.release(); + } catch (InterruptedException e) { + return; // abort the reload + } + + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + panel.createAppletThread(); + return null; + } + }); + + panel.sendEvent(AppletPanel.APPLET_LOAD); + panel.sendEvent(AppletPanel.APPLET_INIT); + panel.sendEvent(AppletPanel.APPLET_START); + } + + public int print(Graphics graphics, PageFormat pf, int pageIndex) { + return Printable.NO_SUCH_PAGE; + } + + /** + * Start the applet. + */ + void appletStart() { + panel.sendEvent(AppletPanel.APPLET_START); + } + + /** + * Stop the applet. + */ + void appletStop() { + panel.sendEvent(AppletPanel.APPLET_STOP); + } + + /** + * Shutdown a viewer. + * Stop, Destroy, Dispose and Quit a viewer + */ + private void appletShutdown(AppletPanel p) { + p.sendEvent(AppletPanel.APPLET_STOP); + p.sendEvent(AppletPanel.APPLET_DESTROY); + p.sendEvent(AppletPanel.APPLET_DISPOSE); + p.sendEvent(AppletPanel.APPLET_QUIT); + } + + /** + * Close this viewer. + * Stop, Destroy, Dispose and Quit an AppletView, then + * reclaim resources and exit the program if this is + * the last applet. + */ + void appletClose() { + + // The caller thread is event dispatch thread, so + // spawn a new thread to avoid blocking the event queue + // when calling appletShutdown. + // + final AppletPanel p = panel; + + new Thread(new Runnable() + { + public void run() + { + ThreadGroup tg = ((JNLPClassLoader) p.applet.getClass().getClassLoader()).getApplication().getThreadGroup(); + + appletShutdown(p); + appletPanels.removeElement(p); + dispose(); + + if (tg.activeCount() > 0) + tg.stop(); + + if (countApplets() == 0) { + appletSystemExit(); + } + } + }).start(); + + status.put(identifier, PAV_INIT_STATUS.INACTIVE); + } + + /** + * Exit the program. + * Exit from the program (if not stand alone) - do no clean-up + */ + private void appletSystemExit() { + // Do nothing. Exit is handled by another + // block of code, called when _all_ applets are gone + } + + /** + * How many applets are running? + */ + + public static int countApplets() { + return appletPanels.size(); + } + + + /** + * Scan spaces. + */ + public static void skipSpace(int[] c, Reader in) throws IOException { + while ((c[0] >= 0) && + ((c[0] == ' ') || (c[0] == '\t') || (c[0] == '\n') || (c[0] == '\r'))) { + c[0] = in.read(); + } + } + + /** + * Scan identifier + */ + public static String scanIdentifier(int[] c, Reader in) throws IOException { + StringBuffer buf = new StringBuffer(); + + if (c[0] == '!') { + // Technically, we should be scanning for '!--' but we are reading + // from a stream, and there is no way to peek ahead. That said, + // a ! at this point can only mean comment here afaik, so we + // should be okay + skipComment(c, in); + return ""; + } + + while (true) { + if (((c[0] >= 'a') && (c[0] <= 'z')) || + ((c[0] >= 'A') && (c[0] <= 'Z')) || + ((c[0] >= '0') && (c[0] <= '9')) || (c[0] == '_')) { + buf.append((char)c[0]); + c[0] = in.read(); + } else { + return buf.toString(); + } + } + } + + public static void skipComment(int[] c, Reader in) throws IOException { + StringBuffer buf = new StringBuffer(); + boolean commentHeaderPassed = false; + c[0] = in.read(); + buf.append((char)c[0]); + + while (true) { + if (c[0] == '-' && (c[0] = in.read()) == '-') { + buf.append((char)c[0]); + if (commentHeaderPassed) { + // -- encountered ... is > next? + if ((c[0] = in.read()) == '>') { + buf.append((char)c[0]); + + PluginDebug.debug("Comment skipped: " + buf.toString()); + + // comment skipped. + return; + } + } else { + // first -- is part of <!-- ... , just mark that we have passed it + commentHeaderPassed = true; + } + + } else if (commentHeaderPassed == false) { + buf.append((char)c[0]); + PluginDebug.debug("Warning: Attempted to skip comment, but this tag does not appear to be a comment: " + buf.toString()); + return; + } + + c[0] = in.read(); + buf.append((char)c[0]); + } + } + + /** + * Scan tag + */ + public static Hashtable scanTag(int[] c, Reader in) throws IOException { + Hashtable atts = new Hashtable(); + skipSpace(c, in); + while (c[0] >= 0 && c[0] != '>') { + String att = scanIdentifier(c, in); + String val = ""; + skipSpace(c, in); + if (c[0] == '=') { + int quote = -1; + c[0] = in.read(); + skipSpace(c, in); + if ((c[0] == '\'') || (c[0] == '\"')) { + quote = c[0]; + c[0] = in.read(); + } + StringBuffer buf = new StringBuffer(); + while ((c[0] > 0) && + (((quote < 0) && (c[0] != ' ') && (c[0] != '\t') && + (c[0] != '\n') && (c[0] != '\r') && (c[0] != '>')) + || ((quote >= 0) && (c[0] != quote)))) { + buf.append((char)c[0]); + c[0] = in.read(); + } + if (c[0] == quote) { + c[0] = in.read(); + } + skipSpace(c, in); + val = buf.toString(); + } + + att = att.replace(">", ">"); + att = att.replace("<", "<"); + att = att.replace("&", "&"); + att = att.replace(" ", "\n"); + att = att.replace(" ", "\r"); + + val = val.replace(">", ">"); + val = val.replace("<", "<"); + val = val.replace("&", "&"); + val = val.replace(" ", "\n"); + val = val.replace(" ", "\r"); + + PluginDebug.debug("PUT " + att + " = '" + val + "'"); + atts.put(att.toLowerCase(java.util.Locale.ENGLISH), val); + + while (true) { + if ((c[0] == '>') || (c[0] < 0) || + ((c[0] >= 'a') && (c[0] <= 'z')) || + ((c[0] >= 'A') && (c[0] <= 'Z')) || + ((c[0] >= '0') && (c[0] <= '9')) || (c[0] == '_')) + break; + c[0] = in.read(); + } + //skipSpace(in); + } + return atts; + } + + // private static final == inline + private static final boolean isInt(Object o) { + boolean isInt = false; + + try { + Integer.parseInt((String) o); + isInt = true; + } catch (Exception e) { + // don't care + } + + return isInt; + } + + /* values used for placement of AppletViewer's frames */ + private static int x = 0; + private static int y = 0; + private static final int XDELTA = 30; + private static final int YDELTA = XDELTA; + + static String encoding = null; + + static private Reader makeReader(InputStream is) { + if (encoding != null) { + try { + return new BufferedReader(new InputStreamReader(is, encoding)); + } catch (IOException x) { } + } + InputStreamReader r = new InputStreamReader(is); + encoding = r.getEncoding(); + return new BufferedReader(r); + } + + /** + * Scan an html file for <applet> tags + */ + public static void parse(int identifier, long handle, String width, String height, Reader in, URL url, String enc) + throws IOException { + encoding = enc; + parse(identifier, handle, width, height, in, url, System.out, new PluginAppletPanelFactory()); + } + + public static void parse(int identifier, long handle, String width, String height, Reader in, URL url) + throws IOException { + + final int fIdentifier = identifier; + final long fHandle = handle; + final String fWidth = width; + final String fHeight = height; + final Reader fIn = in; + final URL fUrl = url; + PrivilegedAction pa = new PrivilegedAction() { + public Object run() { + try { + parse(fIdentifier, fHandle, fWidth, fHeight, fIn, fUrl, System.out, new PluginAppletPanelFactory()); + } catch (IOException ioe) { + return ioe; + } + + return null; + } + }; + + Object ret = AccessController.doPrivileged(pa); + if (ret instanceof IOException) { + throw (IOException) ret; + } + } + + public static void parse(int identifier, long handle, String width, + String height, Reader in, URL url, + PrintStream statusMsgStream, + PluginAppletPanelFactory factory) + throws IOException + { + // <OBJECT> <EMBED> tag flags + boolean isAppletTag = false; + boolean isObjectTag = false; + boolean isEmbedTag = false; + boolean objectTagAlreadyParsed = false; + // The current character + // FIXME: This is an evil hack to force pass-by-reference.. the + // parsing code needs to be rewritten from scratch to prevent such + //a need + int[] c = new int[1]; + + // warning messages + String requiresNameWarning = amh.getMessage("parse.warning.requiresname"); + String paramOutsideWarning = amh.getMessage("parse.warning.paramoutside"); + String appletRequiresCodeWarning = amh.getMessage("parse.warning.applet.requirescode"); + String appletRequiresHeightWarning = amh.getMessage("parse.warning.applet.requiresheight"); + String appletRequiresWidthWarning = amh.getMessage("parse.warning.applet.requireswidth"); + String objectRequiresCodeWarning = amh.getMessage("parse.warning.object.requirescode"); + String objectRequiresHeightWarning = amh.getMessage("parse.warning.object.requiresheight"); + String objectRequiresWidthWarning = amh.getMessage("parse.warning.object.requireswidth"); + String embedRequiresCodeWarning = amh.getMessage("parse.warning.embed.requirescode"); + String embedRequiresHeightWarning = amh.getMessage("parse.warning.embed.requiresheight"); + String embedRequiresWidthWarning = amh.getMessage("parse.warning.embed.requireswidth"); + String appNotLongerSupportedWarning = amh.getMessage("parse.warning.appnotLongersupported"); + + java.net.URLConnection conn = url.openConnection(); + /* The original URL may have been redirected - this + * sets it to whatever URL/codebase we ended up getting + */ + url = conn.getURL(); + + int ydisp = 1; + Hashtable atts = null; + + while(true) { + c[0] = in.read(); + if (c[0] == -1) + break; + + if (c[0] == '<') { + c[0] = in.read(); + if (c[0] == '/') { + c[0] = in.read(); + String nm = scanIdentifier(c, in); + if (nm.equalsIgnoreCase("applet") || + nm.equalsIgnoreCase("object") || + nm.equalsIgnoreCase("embed")) { + + // We can't test for a code tag until </OBJECT> + // because it is a parameter, not an attribute. + if(isObjectTag) { + if (atts.get("code") == null && atts.get("object") == null) { + statusMsgStream.println(objectRequiresCodeWarning); + atts = null; + } + } + + if (atts != null) { + // XXX 5/18 In general this code just simply + // shouldn't be part of parsing. It's presence + // causes things to be a little too much of a + // hack. + + // Let user know we are starting up + streamhandler.write("instance " + identifier + " status " + amh.getMessage("status.start")); + factory.createPanel(streamhandler, identifier, handle, x, y, url, atts); + + x += XDELTA; + y += YDELTA; + // make sure we don't go too far! + Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); + if ((x > d.width - 300) || (y > d.height - 300)) { + x = 0; + y = 2 * ydisp * YDELTA; + ydisp++; + } + } + atts = null; + isAppletTag = false; + isObjectTag = false; + isEmbedTag = false; + } + } + else { + String nm = scanIdentifier(c, in); + if (nm.equalsIgnoreCase("param")) { + Hashtable t = scanTag(c, in); + String att = (String)t.get("name"); + + if (atts.containsKey(att)) + continue; + + if (att == null) { + statusMsgStream.println(requiresNameWarning); + } else { + String val = (String)t.get("value"); + if (val == null) { + statusMsgStream.println(requiresNameWarning); + } else if (atts != null) { + att = att.replace(">", ">"); + att = att.replace("<", "<"); + att = att.replace("&", "&"); + att = att.replace(" ", "\n"); + att = att.replace(" ", "\r"); + att = att.replace(""", "\""); + + val = val.replace(">", ">"); + val = val.replace("<", "<"); + val = val.replace("&", "&"); + val = val.replace(" ", "\n"); + val = val.replace(" ", "\r"); + val = val.replace(""", "\""); + PluginDebug.debug("PUT " + att + " = " + val); + atts.put(att.toLowerCase(), val); + } else { + statusMsgStream.println(paramOutsideWarning); + } + } + } + else if (nm.equalsIgnoreCase("applet")) { + isAppletTag = true; + atts = scanTag(c, in); + + // If there is a classid and no code tag present, transform it to code tag + if (atts.get("code") == null && atts.get("classid") != null && !((String) atts.get("classid")).startsWith("clsid:")) { + atts.put("code", atts.get("classid")); + } + + // remove java: from code tag + if (atts.get("code") != null && ((String) atts.get("code")).startsWith("java:")) { + atts.put("code", ((String) atts.get("code")).substring(5)); + } + + if (atts.get("code") == null && atts.get("object") == null) { + statusMsgStream.println(appletRequiresCodeWarning); + atts = null; + } + + if (atts.get("width") == null || !isInt(atts.get("width"))) { + atts.put("width", width); + } + + if (atts.get("height") == null || !isInt(atts.get("height"))) { + atts.put("height", height); + } + } + else if (nm.equalsIgnoreCase("object")) { + isObjectTag = true; + + // Once code is set, additional nested objects are ignored + if (!objectTagAlreadyParsed) { + objectTagAlreadyParsed = true; + atts = scanTag(c, in); + } + + // If there is a classid and no code tag present, transform it to code tag + if (atts.get("code") == null && atts.get("classid") != null && !((String) atts.get("classid")).startsWith("clsid:")) { + atts.put("code", atts.get("classid")); + } + + // remove java: from code tag + if (atts.get("code") != null && ((String) atts.get("code")).startsWith("java:")) { + atts.put("code", ((String) atts.get("code")).substring(5)); + } + + // java_* aliases override older names: + // http://java.sun.com/j2se/1.4.2/docs/guide/plugin/developer_guide/using_tags.html#in-ie + if (atts.get("java_code") != null) { + atts.put("code", ((String) atts.get("java_code"))); + } + + if (atts.containsKey("code")) { + objectTagAlreadyParsed = true; + } + + if (atts.get("java_codebase") != null) { + atts.put("codebase", ((String) atts.get("java_codebase"))); + } + + if (atts.get("java_archive") != null) { + atts.put("archive", ((String) atts.get("java_archive"))); + } + + if (atts.get("java_object") != null) { + atts.put("object", ((String) atts.get("java_object"))); + } + + if (atts.get("java_type") != null) { + atts.put("type", ((String) atts.get("java_type"))); + } + + if (atts.get("width") == null || !isInt(atts.get("width"))) { + atts.put("width", width); + } + + if (atts.get("height") == null || !isInt(atts.get("height"))) { + atts.put("height", height); + } + } + else if (nm.equalsIgnoreCase("embed")) { + isEmbedTag = true; + atts = scanTag(c, in); + + // If there is a classid and no code tag present, transform it to code tag + if (atts.get("code") == null && atts.get("classid") != null && !((String) atts.get("classid")).startsWith("clsid:")) { + atts.put("code", atts.get("classid")); + } + + // remove java: from code tag + if (atts.get("code") != null && ((String) atts.get("code")).startsWith("java:")) { + atts.put("code", ((String) atts.get("code")).substring(5)); + } + + // java_* aliases override older names: + // http://java.sun.com/j2se/1.4.2/docs/guide/plugin/developer_guide/using_tags.html#in-nav + if (atts.get("java_code") != null) { + atts.put("code", ((String) atts.get("java_code"))); + } + + if (atts.get("java_codebase") != null) { + atts.put("codebase", ((String) atts.get("java_codebase"))); + } + + if (atts.get("java_archive") != null) { + atts.put("archive", ((String) atts.get("java_archive"))); + } + + if (atts.get("java_object") != null) { + atts.put("object", ((String) atts.get("java_object"))); + } + + if (atts.get("java_type") != null) { + atts.put("type", ((String) atts.get("java_type"))); + } + + if (atts.get("code") == null && atts.get("object") == null) { + statusMsgStream.println(embedRequiresCodeWarning); + atts = null; + } + + if (atts.get("width") == null || !isInt(atts.get("width"))) { + atts.put("width", width); + } + + if (atts.get("height") == null || !isInt(atts.get("height"))) { + atts.put("height", height); + } + } + } + } + } + in.close(); + } + + + private static AppletMessageHandler amh = new AppletMessageHandler("appletviewer"); + + private static void checkConnect(URL url) + { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + try { + java.security.Permission perm = + url.openConnection().getPermission(); + if (perm != null) + security.checkPermission(perm); + else + security.checkConnect(url.getHost(), url.getPort()); + } catch (java.io.IOException ioe) { + security.checkConnect(url.getHost(), url.getPort()); + } + } + } + } diff --git a/plugin/icedteanp/java/sun/applet/PluginCallRequest.java b/plugin/icedteanp/java/sun/applet/PluginCallRequest.java new file mode 100644 index 0000000..a4f01a7 --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/PluginCallRequest.java @@ -0,0 +1,89 @@ +/* PluginCallRequest -- represent Java-to-JavaScript requests + Copyright (C) 2008 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package sun.applet; + +import java.security.AccessControlContext; +import java.security.ProtectionDomain; + +// FIXME: for each type of request extend a new (anonymous?) +// PluginCallRequest. +public abstract class PluginCallRequest { + String message; + Long reference; + PluginCallRequest next; + boolean done = false; + + public PluginCallRequest(String message, Long reference) { + this.message = message; + this.reference = reference; + } + + public String getMessage() { + return this.message; + } + + public boolean isDone() { + return this.done; + } + + public boolean setDone(boolean done) { + return this.done = done; + } + + public void setNext(PluginCallRequest next) { + this.next = next; + } + + public PluginCallRequest getNext() { + return this.next; + } + + /** + * Returns whether the given message is serviceable by this object + * + * @param message The message to service + * @return boolean indicating if message is serviceable + */ + public boolean serviceable(String message) { + return message.contains("reference " + reference); + } + + public abstract void parseReturn(String message); + + public abstract Object getObject(); +} diff --git a/plugin/icedteanp/java/sun/applet/PluginCallRequestFactory.java b/plugin/icedteanp/java/sun/applet/PluginCallRequestFactory.java new file mode 100644 index 0000000..69cec35 --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/PluginCallRequestFactory.java @@ -0,0 +1,61 @@ +/* VoidPluginCallRequest -- represent Java-to-JavaScript requests + Copyright (C) 2008 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package sun.applet; + + +public class PluginCallRequestFactory { + + public PluginCallRequest getPluginCallRequest(String id, String message, Long reference) { + + if (id == "member") { + return new GetMemberPluginCallRequest(message, reference); + } else if (id == "void") { + return new VoidPluginCallRequest(message, reference); + } else if (id == "window") { + return new GetWindowPluginCallRequest(message, reference); + } else if (id == "proxyinfo") { + return new PluginProxyInfoRequest(message, reference); + } else if (id == "cookieinfo") { + return new PluginCookieInfoRequest(message, reference); + } else { + throw new RuntimeException ("Unknown plugin call request type requested from factory"); + } + } + +} diff --git a/plugin/icedteanp/java/sun/applet/PluginClassLoader.java b/plugin/icedteanp/java/sun/applet/PluginClassLoader.java new file mode 100644 index 0000000..5965d0d --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/PluginClassLoader.java @@ -0,0 +1,51 @@ +/* VoidPluginCallRequest -- represent Java-to-JavaScript requests + Copyright (C) 2008 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package sun.applet; + +public class PluginClassLoader extends ClassLoader { + + public PluginClassLoader() { + super(); + } + + public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { + return super.loadClass(name, resolve); + } + +} diff --git a/plugin/icedteanp/java/sun/applet/PluginCookieInfoRequest.java b/plugin/icedteanp/java/sun/applet/PluginCookieInfoRequest.java new file mode 100644 index 0000000..c08d9f5 --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/PluginCookieInfoRequest.java @@ -0,0 +1,74 @@ +/* PluginCookieInfoRequest -- Object representing a request for cookie information from the browser + Copyright (C) 2009 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package sun.applet; + + +/** + * This class represents a request object for cookie information for a given URI + */ + +public class PluginCookieInfoRequest extends PluginCallRequest { + + String cookieString = new String(); + + public PluginCookieInfoRequest(String message, Long reference) { + super(message, reference); + } + + public void parseReturn(String cookieInfo) { + + // try to parse the proxy information. If things go wrong, do nothing .. + // this will keep internal = null which forces a direct connection + + PluginDebug.debug ("PluginCookieInfoRequest GOT: " + cookieInfo); + + // Skip the first 5 components. We are guaranteed 5 components, + // so no index -1 to worry about + cookieInfo = cookieInfo.substring(cookieInfo.indexOf(' ')+1); + cookieInfo = cookieInfo.substring(cookieInfo.indexOf(' ')+1); + cookieInfo = cookieInfo.substring(cookieInfo.indexOf(' ')+1); + cookieInfo = cookieInfo.substring(cookieInfo.indexOf(' ')+1); + cookieString = cookieInfo.substring(cookieInfo.indexOf(' ')+1); + + setDone(true); + } + + public String getObject() { + return this.cookieString; + } +} diff --git a/plugin/icedteanp/java/sun/applet/PluginCookieManager.java b/plugin/icedteanp/java/sun/applet/PluginCookieManager.java new file mode 100644 index 0000000..233cfa9 --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/PluginCookieManager.java @@ -0,0 +1,88 @@ +/* PluginCookieManager -- Cookie manager for the plugin + Copyright (C) 2009 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package sun.applet; + +import java.io.IOException; +import java.net.CookieManager; +import java.net.HttpCookie; +import java.net.URI; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class PluginCookieManager extends CookieManager +{ + public Map<String, List<String>> get(URI uri, + Map<String, List<String>> requestHeaders) throws IOException { + // pre-condition check + if (uri == null || requestHeaders == null) { + throw new IllegalArgumentException("Argument is null"); + } + + Map<String, List<String>> cookieMap = new java.util.HashMap<String, List<String>>(); + + String cookies = (String) PluginAppletViewer + .requestPluginCookieInfo(uri); + List<String> cookieHeader = new java.util.ArrayList<String>(); + + if (cookies != null && cookies.length() > 0) + cookieHeader.add(cookies); + + // Add anything else that mozilla didn't add + for (HttpCookie cookie : getCookieStore().get(uri)) { + // apply path-matches rule (RFC 2965 sec. 3.3.4) + if (pathMatches(uri.getPath(), cookie.getPath())) { + cookieHeader.add(cookie.toString()); + } + } + + cookieMap.put("Cookie", cookieHeader); + return Collections.unmodifiableMap(cookieMap); + } + + private boolean pathMatches(String path, String pathToMatchWith) { + if (path == pathToMatchWith) + return true; + if (path == null || pathToMatchWith == null) + return false; + if (path.startsWith(pathToMatchWith)) + return true; + + return false; + } +} diff --git a/plugin/icedteanp/java/sun/applet/PluginDebug.java b/plugin/icedteanp/java/sun/applet/PluginDebug.java new file mode 100644 index 0000000..60e2bd0 --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/PluginDebug.java @@ -0,0 +1,51 @@ +/* VoidPluginCallRequest -- represent Java-to-JavaScript requests + Copyright (C) 2008 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package sun.applet; + +import java.io.*; + +public class PluginDebug { + + static final boolean DEBUG = System.getenv().containsKey("ICEDTEAPLUGIN_DEBUG"); + + public static void debug(String message) { + if (DEBUG) + System.err.println(message); + } +} diff --git a/plugin/icedteanp/java/sun/applet/PluginException.java b/plugin/icedteanp/java/sun/applet/PluginException.java new file mode 100644 index 0000000..0f6e660 --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/PluginException.java @@ -0,0 +1,53 @@ +/* VoidPluginCallRequest -- represent Java-to-JavaScript requests + Copyright (C) 2008 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package sun.applet; + + +public class PluginException extends Exception { + + public PluginException (PluginStreamHandler sh, int instance, int reference, Throwable t) { + t.printStackTrace(); + this.setStackTrace(t.getStackTrace()); + + AppletSecurityContextManager.dumpStore(0); + + String message = "instance " + instance + " reference " + reference + " Error " + t.getMessage(); + sh.write(message); + } +} diff --git a/plugin/icedteanp/java/sun/applet/PluginMain.java b/plugin/icedteanp/java/sun/applet/PluginMain.java new file mode 100644 index 0000000..bce87b2 --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/PluginMain.java @@ -0,0 +1,319 @@ +/* VoidPluginCallRequest -- represent Java-to-JavaScript requests + Copyright (C) 2008 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package sun.applet; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.net.Authenticator; +import java.net.CookieHandler; +import java.net.CookieManager; +import java.net.PasswordAuthentication; +import java.net.ProxySelector; +import java.util.Enumeration; +import java.util.Properties; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; + +import net.sourceforge.jnlp.security.VariableX509TrustManager; + +/** + * The main entry point into PluginAppletViewer. + */ +public class PluginMain +{ + + // the files where stdout/stderr are sent to + public static final String PLUGIN_STDERR_FILE = System.getProperty("user.home") + "/.icedteaplugin/java.stderr"; + public static final String PLUGIN_STDOUT_FILE = System.getProperty("user.home") + "/.icedteaplugin/java.stdout"; + + final boolean redirectStreams = System.getenv().containsKey("ICEDTEAPLUGIN_DEBUG"); + static PluginStreamHandler streamHandler; + + // This is used in init(). Getting rid of this is desirable but depends + // on whether the property that uses it is necessary/standard. + public static final String theVersion = System.getProperty("java.version"); + + private PluginAppletSecurityContext securityContext; + + /** + * The main entry point into AppletViewer. + */ + public static void main(String args[]) + throws IOException + { + if (args.length != 2 || !(new File(args[0]).exists()) || !(new File(args[1]).exists())) { + System.err.println("Invalid pipe names provided. Refusing to proceed."); + System.exit(1); + } + + try { + PluginMain pm = new PluginMain(args[0], args[1]); + } catch (Exception e) { + e.printStackTrace(); + System.err.println("Something very bad happened. I don't know what to do, so I am going to exit :("); + System.exit(1); + } + } + + public PluginMain(String inPipe, String outPipe) { + + try { + File errFile = new File(PLUGIN_STDERR_FILE); + File outFile = new File(PLUGIN_STDOUT_FILE); + + System.setErr(new TeeOutputStream(new FileOutputStream(errFile), System.err)); + System.setOut(new TeeOutputStream(new FileOutputStream(outFile), System.out)); + } catch (Exception e) { + PluginDebug.debug("Unable to redirect streams"); + e.printStackTrace(); + } + + connect(inPipe, outPipe); + + securityContext = new PluginAppletSecurityContext(0); + securityContext.prePopulateLCClasses(); + securityContext.setStreamhandler(streamHandler); + AppletSecurityContextManager.addContext(0, securityContext); + + PluginAppletViewer.setStreamhandler(streamHandler); + PluginAppletViewer.setPluginCallRequestFactory(new PluginCallRequestFactory()); + + init(); + + // Streams set. Start processing. + streamHandler.startProcessing(); + } + + public void connect(String inPipe, String outPipe) { + try { + streamHandler = new PluginStreamHandler(new FileInputStream(inPipe), new FileOutputStream(outPipe)); + PluginDebug.debug("Streams initialized"); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + + private static void init() { + Properties avProps = new Properties(); + + // ADD OTHER RANDOM PROPERTIES + // XXX 5/18 need to revisit why these are here, is there some + // standard for what is available? + + // Standard browser properties + avProps.put("browser", "sun.applet.AppletViewer"); + avProps.put("browser.version", "1.06"); + avProps.put("browser.vendor", "Sun Microsystems Inc."); + avProps.put("http.agent", "Java(tm) 2 SDK, Standard Edition v" + theVersion); + + // Define which packages can be extended by applets + // XXX 5/19 probably not needed, not checked in AppletSecurity + avProps.put("package.restrict.definition.java", "true"); + avProps.put("package.restrict.definition.sun", "true"); + + // Define which properties can be read by applets. + // A property named by "key" can be read only when its twin + // property "key.applet" is true. The following ten properties + // are open by default. Any other property can be explicitly + // opened up by the browser user by calling appletviewer with + // -J-Dkey.applet=true + avProps.put("java.version.applet", "true"); + avProps.put("java.vendor.applet", "true"); + avProps.put("java.vendor.url.applet", "true"); + avProps.put("java.class.version.applet", "true"); + avProps.put("os.name.applet", "true"); + avProps.put("os.version.applet", "true"); + avProps.put("os.arch.applet", "true"); + avProps.put("file.separator.applet", "true"); + avProps.put("path.separator.applet", "true"); + avProps.put("line.separator.applet", "true"); + + avProps.put("javaplugin.nodotversion", "160_17"); + avProps.put("javaplugin.version", "1.6.0_17"); + avProps.put("javaplugin.vm.options", ""); + + // Read in the System properties. If something is going to be + // over-written, warn about it. + Properties sysProps = System.getProperties(); + for (Enumeration e = sysProps.propertyNames(); e.hasMoreElements(); ) { + String key = (String) e.nextElement(); + String val = (String) sysProps.getProperty(key); + avProps.setProperty(key, val); + } + + // INSTALL THE PROPERTY LIST + System.setProperties(avProps); + + + try { + SSLSocketFactory sslSocketFactory; + SSLContext context = SSLContext.getInstance("SSL"); + TrustManager[] trust = new TrustManager[] { VariableX509TrustManager.getInstance() }; + context.init(null, trust, null); + sslSocketFactory = context.getSocketFactory(); + + HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory); + } catch (Exception e) { + System.err.println("Unable to set SSLSocketfactory (may _prevent_ access to sites that should be trusted)! Continuing anyway..."); + e.printStackTrace(); + } + + // plug in a custom authenticator and proxy selector + Authenticator.setDefault(new CustomAuthenticator()); + ProxySelector.setDefault(new PluginProxySelector()); + + CookieManager ckManager = new PluginCookieManager(); + CookieHandler.setDefault(ckManager); + } + + static boolean messageAvailable() { + return streamHandler.messageAvailable(); + } + + static String getMessage() { + return streamHandler.getMessage(); + } + + static class CustomAuthenticator extends Authenticator { + + public PasswordAuthentication getPasswordAuthentication() { + + // No security check is required here, because the only way to + // set parameters for which auth info is needed + // (Authenticator:requestPasswordAuthentication()), has a security + // check + + String type = this.getRequestorType() == RequestorType.PROXY ? "proxy" : "web"; + + // request auth info from user + PasswordAuthenticationDialog pwDialog = new PasswordAuthenticationDialog(); + PasswordAuthentication auth = pwDialog.askUser(this.getRequestingHost(), this.getRequestingPort(), this.getRequestingPrompt(), type); + + // send it along + return auth; + } + } + + /** + * Behaves like the 'tee' command, sends output to both actual std stream and a + * file + */ + class TeeOutputStream extends PrintStream { + + // Everthing written to TeeOutputStream is written to this file + PrintStream logFile; + + public TeeOutputStream(FileOutputStream fileOutputStream, + PrintStream stdStream) { + super(stdStream); + logFile = new PrintStream(fileOutputStream); + } + + @Override + public boolean checkError() { + boolean thisError = super.checkError(); + boolean fileError = logFile.checkError(); + + return thisError || fileError; + } + + @Override + public void close() { + logFile.close(); + super.close(); + } + + @Override + public void flush() { + logFile.flush(); + super.flush(); + } + + /* + * The big ones: these do the actual writing + */ + + @Override + public void write(byte[] buf, int off, int len) { + logFile.write(buf, off, len); + + if (!redirectStreams) + super.write(buf, off, len); + } + + @Override + public void write(int b) { + logFile.write(b); + + if (!redirectStreams) + super.write(b); + } + } + +} diff --git a/plugin/icedteanp/java/sun/applet/PluginMessageConsumer.java b/plugin/icedteanp/java/sun/applet/PluginMessageConsumer.java new file mode 100644 index 0000000..0bc5dda --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/PluginMessageConsumer.java @@ -0,0 +1,268 @@ +/* VoidPluginCallRequest -- represent Java-to-JavaScript requests + Copyright (C) 2008 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package sun.applet; + +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Set; + +class PluginMessageConsumer { + + private static int MAX_PARALLEL_INITS = 1; + + // Each initialization requires 5 responses (tag, handle, width, proxy, cookie) + // before the message stack unlocks/collapses. This works out well because we + // want to allow upto 5 parallel tasks anyway + private static int MAX_WORKERS = MAX_PARALLEL_INITS*4; + private static int PRIORITY_WORKERS = MAX_PARALLEL_INITS*2; + + private static Hashtable<Integer, PluginMessageHandlerWorker> initWorkers = new Hashtable<Integer, PluginMessageHandlerWorker>(2); + + LinkedList<String> readQueue = new LinkedList<String>(); + private static LinkedList<String> priorityWaitQueue = new LinkedList<String>(); + ArrayList<PluginMessageHandlerWorker> workers = new ArrayList<PluginMessageHandlerWorker>(); + PluginStreamHandler streamHandler = null; + AppletSecurity as; + ConsumerThread consumerThread = new ConsumerThread(); + private static ArrayList<Integer> processedIds = new ArrayList<Integer>(); + + /** + * Registers a reference to wait for. Responses to registered priority + * references get handled by priority worker if normal workers are busy. + * + * @param reference The reference to give priority to + */ + public static void registerPriorityWait(Long reference) { + PluginDebug.debug("Registering priority for reference " + reference); + registerPriorityWait("reference " + reference.toString()); + } + + /** + * Registers a string to wait for. + * + * @param searchString the string to look for in a response + */ + public static void registerPriorityWait(String searchString) { + PluginDebug.debug("Registering priority for string " + searchString); + synchronized (priorityWaitQueue) { + if (!priorityWaitQueue.contains(searchString)) + priorityWaitQueue.add(searchString); + } + } + + /** + * Unregisters a priority reference to wait for. + * + * @param reference The reference to remove + */ + public static void unRegisterPriorityWait(Long reference) { + unRegisterPriorityWait(reference.toString()); + } + + /** + * Unregisters a priority string to wait for. + * + * @param searchString The string to unregister from the priority list + */ + public static void unRegisterPriorityWait(String searchString) { + synchronized (priorityWaitQueue) { + priorityWaitQueue.remove(searchString); + } + } + + /** + * Returns the reference for this message. This method assumes that + * the message has a reference number. + * + * @param The message + * @return the reference number + */ + private Long getReference(String[] msgParts) { + return Long.parseLong(msgParts[3]); + } + + public PluginMessageConsumer(PluginStreamHandler streamHandler) { + + as = new AppletSecurity(); + this.streamHandler = streamHandler; + this.consumerThread.start(); + } + + private String getPriorityStrIfPriority(String message) { + + synchronized (priorityWaitQueue) { + Iterator<String> it = priorityWaitQueue.iterator(); + + while (it.hasNext()) { + String priorityStr = it.next(); + if (message.indexOf(priorityStr) > 0) + return priorityStr; + } + } + + return null; + } + + private boolean isInInit(Integer instanceNum) { + return initWorkers.containsKey(instanceNum); + } + + private void addToInitWorkers(Integer instanceNum, PluginMessageHandlerWorker worker) { + synchronized(initWorkers) { + initWorkers.put(instanceNum, worker); + } + } + + + public void notifyWorkerIsFree(PluginMessageHandlerWorker worker) { + synchronized (initWorkers) { + Iterator<Integer> i = initWorkers.keySet().iterator(); + while (i.hasNext()) { + Integer key = i.next(); + if (initWorkers.get(key).equals(worker)) { + processedIds.add(key); + initWorkers.remove(key); + } + } + } + + consumerThread.interrupt(); + } + + public void queue(String message) { + synchronized(readQueue) { + readQueue.addLast(message); + } + + // Wake that lazy consumer thread + consumerThread.interrupt(); + } + + protected class ConsumerThread extends Thread { + public void run() { + + while (true) { + + String message = null; + + synchronized(readQueue) { + message = readQueue.poll(); + } + + if (message != null) { + + String[] msgParts = message.split(" "); + + + String priorityStr = getPriorityStrIfPriority(message); + boolean isPriorityResponse = (priorityStr != null); + + //PluginDebug.debug("Message " + message + " (priority=" + isPriorityResponse + ") ready to be processed. Looking for free worker..."); + final PluginMessageHandlerWorker worker = getFreeWorker(isPriorityResponse); + + if (worker == null) { + synchronized(readQueue) { + readQueue.addLast(message); + } + + continue; // re-loop to try next msg + } + + if (msgParts[2].equals("tag")) + addToInitWorkers((new Integer(msgParts[1])), worker); + + if (isPriorityResponse) { + unRegisterPriorityWait(priorityStr); + } + + worker.setmessage(message); + worker.interrupt(); + + } else { + try { + Thread.sleep(1000); + } catch (InterruptedException ie) {} + } + } + } + } + + private PluginMessageHandlerWorker getFreeWorker(boolean prioritized) { + + for (PluginMessageHandlerWorker worker: workers) { + if (worker.isFree(prioritized)) { + PluginDebug.debug("Found free worker (" + worker.isPriority() + ") with id " + worker.getWorkerId()); + // mark it busy before returning + worker.busy(); + return worker; + } + } + + // If we have less than MAX_WORKERS, create a new worker + if (workers.size() <= MAX_WORKERS) { + PluginMessageHandlerWorker worker = null; + + if (workers.size() < (MAX_WORKERS - PRIORITY_WORKERS)) { + PluginDebug.debug("Cannot find free worker, creating worker " + workers.size()); + worker = new PluginMessageHandlerWorker(this, streamHandler, workers.size(), as, false); + } else if (prioritized) { + PluginDebug.debug("Cannot find free worker, creating priority worker " + workers.size()); + worker = new PluginMessageHandlerWorker(this, streamHandler, workers.size(), as, true); + } else { + return null; + } + + worker.start(); + worker.busy(); + workers.add(worker); + return worker; + + } + + // No workers available. Better luck next time! + return null; + } + + private void dumpWorkerStatus() { + for (PluginMessageHandlerWorker worker: workers) { + PluginDebug.debug(worker.toString()); + } + } +} diff --git a/plugin/icedteanp/java/sun/applet/PluginMessageHandlerWorker.java b/plugin/icedteanp/java/sun/applet/PluginMessageHandlerWorker.java new file mode 100644 index 0000000..30714c4 --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/PluginMessageHandlerWorker.java @@ -0,0 +1,147 @@ +/* VoidPluginCallRequest -- represent Java-to-JavaScript requests + Copyright (C) 2008 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package sun.applet; + + +class PluginMessageHandlerWorker extends Thread { + + private boolean free = true; + private boolean isPriorityWorker = false; + private int id; + private String message = null; + private SecurityManager sm; + PluginStreamHandler streamHandler = null; + PluginMessageConsumer consumer = null; + + public PluginMessageHandlerWorker( + PluginMessageConsumer consumer, + PluginStreamHandler streamHandler, int id, + SecurityManager sm, boolean isPriorityWorker) { + + this.id = id; + this.streamHandler = streamHandler; + this.sm = sm; + this.isPriorityWorker = isPriorityWorker; + this.consumer = consumer; + + PluginDebug.debug("Worker " + this.id + " (priority=" + isPriorityWorker + ") created."); + } + + public void setmessage(String message) { + this.message = message; + } + + public void run() { + while (true) { + + if (message != null) { + + PluginDebug.debug("Consumer (priority=" + isPriorityWorker + ") thread " + id + " consuming " + message); + + // ideally, whoever returns things object should mark it + // busy first, but just in case.. + busy(); + + try { + streamHandler.handleMessage(message); + } catch (PluginException pe) { + /* + catch the exception and DO NOTHING. The plugin should take over after + this error and let the user know. We don't quit because otherwise the + exception will spread to the rest of the applets which is a no-no + */ + } + + this.message = null; + + PluginDebug.debug("Consumption (priority=" + isPriorityWorker + ") completed by consumer thread " + id); + + // mark ourselves free again + free(); + + } else { + + // Sleep when there is nothing to do + try { + Thread.sleep(Integer.MAX_VALUE); + PluginDebug.debug("Consumer thread " + id + " sleeping..."); + } catch (InterruptedException ie) { + PluginDebug.debug("Consumer thread " + id + " woken..."); + // nothing.. someone woke us up, see if there + // is work to do + } + } + } + } + + + + public int getWorkerId() { + return id; + } + + public void busy() { + synchronized (this) { + this.free = false; + } + } + + public void free() { + synchronized (this) { + this.free = true; + + // Signal the consumer that we are done in case it was waiting + consumer.notifyWorkerIsFree(this); + } + } + + public boolean isPriority() { + return isPriorityWorker; + } + + public boolean isFree(boolean prioritized) { + synchronized (this) { + return free && (prioritized == isPriorityWorker); + } + } + + public String toString() { + return "Worker #" + this.id + "/IsPriority=" + this.isPriorityWorker + "/IsFree=" + this.free + "/Message=" + message; + } +} diff --git a/plugin/icedteanp/java/sun/applet/PluginObjectStore.java b/plugin/icedteanp/java/sun/applet/PluginObjectStore.java new file mode 100644 index 0000000..b744744 --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/PluginObjectStore.java @@ -0,0 +1,132 @@ +/* PluginObjectStore -- manage identifier-to-object mapping + Copyright (C) 2008 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package sun.applet; + +import java.util.*; +import java.lang.reflect.*; +import java.io.*; + +public class PluginObjectStore +{ + private static HashMap<Integer, Object> objects = new HashMap(); + private static HashMap<Integer, Integer> counts = new HashMap(); + private static HashMap<Object, Integer> identifiers = new HashMap(); + // FIXME: + // + // IF uniqueID == MAX_LONG, uniqueID = + // 0 && wrapped = true + // + // if (wrapped), check if + // objects.get(uniqueID) returns null + // + // if yes, use uniqueID, if no, + // uniqueID++ and keep checking + // or: + // stack of available ids: + // derefed id -> derefed id -> nextUniqueIdentifier + private static int nextUniqueIdentifier = 1; + + public Object getObject(Integer identifier) { + return objects.get(identifier); + } + + public Integer getIdentifier(Object object) { + if (object == null) + return 0; + return identifiers.get(object); + } + + public boolean contains(Object object) { + if (object == null) + return identifiers.containsKey(object); + + return false; + } + + public boolean contains(int identifier) { + return objects.containsKey(identifier); + } + + public void reference(Object object) { + Integer identifier = identifiers.get(object); + if (identifier == null) { + objects.put(nextUniqueIdentifier, object); + counts.put(nextUniqueIdentifier, 1); + identifiers.put(object, nextUniqueIdentifier); + //System.out.println("JAVA ADDED: " + nextUniqueIdentifier); + //System.out.println("JAVA REFERENCED: " + nextUniqueIdentifier + // + " to: 1"); + nextUniqueIdentifier++; + } else { + counts.put(identifier, counts.get(identifier) + 1); + //System.out.println("JAVA REFERENCED: " + identifier + + // " to: " + counts.get(identifier)); + } + } + + public void unreference(int identifier) { + Integer currentCount = counts.get(identifier); + if (currentCount == null) { + //System.out.println("ERROR UNREFERENCING: " + identifier); + return; + } + if (currentCount == 1) { + //System.out.println("JAVA DEREFERENCED: " + identifier + // + " to: 0"); + Object object = objects.get(identifier); + objects.remove(identifier); + counts.remove(identifier); + identifiers.remove(object); + //System.out.println("JAVA REMOVED: " + identifier); + } else { + counts.put(identifier, currentCount - 1); + //System.out.println("JAVA DEREFERENCED: " + + // identifier + " to: " + + // counts.get(identifier)); + } + } + + public void dump() { + Iterator i = objects.keySet().iterator(); + while (i.hasNext()) { + Object key = i.next(); + PluginDebug.debug(key + "::" + objects.get(key)); + } + } +} + diff --git a/plugin/icedteanp/java/sun/applet/PluginProxyInfoRequest.java b/plugin/icedteanp/java/sun/applet/PluginProxyInfoRequest.java new file mode 100644 index 0000000..674f11d --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/PluginProxyInfoRequest.java @@ -0,0 +1,81 @@ +/* PluginProxyInfoRequest -- Object representing a request for proxy information from the browser + Copyright (C) 2009 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package sun.applet; + +import java.net.MalformedURLException; +import java.net.URI; + +/** + * This class represents a request object for proxy information for a given URI + */ + +public class PluginProxyInfoRequest extends PluginCallRequest { + + URI internal = null; + + public PluginProxyInfoRequest(String message, Long reference) { + super(message, reference); + } + + public void parseReturn(String proxyInfo) { + + // try to parse the proxy information. If things go wrong, do nothing .. + // this will keep internal = null which forces a direct connection + + PluginDebug.debug ("PluginProxyInfoRequest GOT: " + proxyInfo); + String[] messageComponents = proxyInfo.split(" "); + + try { + String protocol = messageComponents[4].equals("PROXY") ? "http" : "socks"; + String host = messageComponents[5].split(":")[0]; + int port = Integer.parseInt(messageComponents[5].split(":")[1]); + + internal = new URI(protocol, null, host, port, null, null, null); + } catch (ArrayIndexOutOfBoundsException aioobe) { + // Nothing.. this is expected if there is no proxy + } catch (Exception e) { + e.printStackTrace(); + } + + setDone(true); + } + + public URI getObject() { + return this.internal; + } +} diff --git a/plugin/icedteanp/java/sun/applet/PluginProxySelector.java b/plugin/icedteanp/java/sun/applet/PluginProxySelector.java new file mode 100644 index 0000000..dff8ded --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/PluginProxySelector.java @@ -0,0 +1,195 @@ +/* PluginProxySelector -- proxy selector for all connections from applets and the plugin + Copyright (C) 2009 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package sun.applet; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.SocketAddress; +import java.net.URI; +import java.util.Date; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * Proxy selector implementation for plugin network functions. + * + * This class fetches proxy information from the web browser and + * uses that information in the context of all network connection + * (plugin specific and applet connections) as applicable + * + */ + +public class PluginProxySelector extends ProxySelector { + + private TimedHashMap<String, Proxy> proxyCache = new TimedHashMap<String, Proxy>(); + + + @Override + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { + // If the connection fails, there is little we can do here. Just print the exception + ioe.printStackTrace(); + } + + /** + * Selects the appropriate proxy (or DIRECT connection method) for the given URI + * + * @param uri The URI being accessed + * @return A list of Proxy objects that are usable for this URI + */ + @Override + public List<Proxy> select(URI uri) { + + List<Proxy> proxyList = new ArrayList<Proxy>(); + + // check cache first + Proxy cachedProxy = checkCache(uri); + if (cachedProxy != null) { + proxyList.add(cachedProxy); + return proxyList; + } + + // Nothing usable in cache. Fetch info from browser + Proxy proxy = Proxy.NO_PROXY; + Object o = PluginAppletViewer.requestPluginProxyInfo(uri); + + // If the browser returned anything, try to parse it. If anything in the try block fails, the fallback is direct connection + try { + if (o != null) { + PluginDebug.debug("Proxy URI = " + o); + URI proxyURI = (URI) o; + + // If origin uri is http/ftp, we're good. If origin uri is not that, the proxy _must_ be socks, else we fallback to direct + if (uri.getScheme().startsWith("http") || uri.getScheme().equals("ftp") || proxyURI.getScheme().startsWith("socks")) { + + Proxy.Type type = proxyURI.getScheme().equals("http") ? Proxy.Type.HTTP : Proxy.Type.SOCKS; + InetSocketAddress socketAddr = new InetSocketAddress(proxyURI.getHost(), proxyURI.getPort()); + + proxy = new Proxy(type, socketAddr); + + String uriKey = uri.getScheme() + "://" + uri.getHost(); + proxyCache.put(uriKey, proxy); + } else { + PluginDebug.debug("Proxy " + proxyURI + " cannot be used for " + uri + ". Falling back to DIRECT"); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + proxyList.add(proxy); + + PluginDebug.debug("Proxy for " + uri.toString() + " is " + proxy); + + return proxyList; + } + + /** + * Checks to see if proxy information is already cached. + * + * @param uri The URI to check + * @return The cached Proxy. null if there is no suitable cached proxy. + */ + private Proxy checkCache(URI uri) { + + String uriKey = uri.getScheme() + "://" + uri.getHost(); + if (proxyCache.get(uriKey) != null) { + return proxyCache.get(uriKey); + } + + return null; + } + + /** + * Simple utility class that extends HashMap by adding an expiry to the entries. + * + * This map stores entries, and returns them only if the entries were last accessed within time t=10 seconds + * + * @param <K> The key type + * @param <V> The Object type + */ + + private class TimedHashMap<K,V> extends HashMap<K,V> { + + HashMap<K, Long> timeStamps = new HashMap<K, Long>(); + Long expiry = 10000L; + + /** + * Store the item in the map and associate a timestamp with it + * + * @param key The key + * @param value The value to store + */ + public V put(K key, V value) { + timeStamps.put(key, new Date().getTime()); + return super.put(key, value); + } + + /** + * Return cached item if it has not already expired. + * + * Before returning, this method also resets the "last accessed" + * time for this entry, so it is good for another 10 seconds + * + * @param key The key + */ + public V get(Object key) { + + Long now = new Date().getTime(); + + if (super.containsKey(key)) { + Long age = now - timeStamps.get(key); + + // Item exists. If it has not expired, renew its access time and return it + if (age <= expiry) { + PluginDebug.debug("Returning proxy " + super.get(key) + " from cache for " + key); + timeStamps.put((K) key, (new Date()).getTime()); + return super.get(key); + } else { + PluginDebug.debug("Proxy cache for " + key + " has expired (age=" + age/1000.0 + " seconds)"); + } + } + + return null; + } + } + +} diff --git a/plugin/icedteanp/java/sun/applet/PluginStreamHandler.java b/plugin/icedteanp/java/sun/applet/PluginStreamHandler.java new file mode 100644 index 0000000..eb5b11a --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/PluginStreamHandler.java @@ -0,0 +1,454 @@ +/* VoidPluginCallRequest -- represent Java-to-JavaScript requests + Copyright (C) 2008 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package sun.applet; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.StreamTokenizer; +import java.net.MalformedURLException; +import java.nio.charset.Charset; +import java.util.Date; +import java.util.LinkedList; + +import javax.swing.SwingUtilities; + + +public class PluginStreamHandler { + + private BufferedReader pluginInputReader; + private StreamTokenizer pluginInputTokenizer; + private BufferedWriter pluginOutputWriter; + + private RequestQueue queue = new RequestQueue(); + + private JavaConsole console = new JavaConsole(); + + LinkedList<String> writeQueue = new LinkedList<String>(); + + PluginMessageConsumer consumer; + Boolean shuttingDown = false; + + PluginAppletViewer pav; + + static Date d = new Date(); + static long startTime = d.getTime(); + static long totalWait = 0; + + public PluginStreamHandler(InputStream inputstream, OutputStream outputstream) + throws MalformedURLException, IOException + { + + PluginDebug.debug("Current context CL=" + Thread.currentThread().getContextClassLoader()); + try { + pav = (PluginAppletViewer) ClassLoader.getSystemClassLoader().loadClass("sun.applet.PluginAppletViewer").newInstance(); + PluginDebug.debug("Loaded: " + pav + " CL=" + pav.getClass().getClassLoader()); + } catch (InstantiationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalAccessException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (ClassNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + PluginDebug.debug("Creating consumer..."); + consumer = new PluginMessageConsumer(this); + + // Set up input and output pipes. Use UTF-8 encoding. + pluginInputReader = + new BufferedReader(new InputStreamReader(inputstream, + Charset.forName("UTF-8"))); + /*pluginInputTokenizer = new StreamTokenizer(pluginInputReader); + pluginInputTokenizer.resetSyntax(); + pluginInputTokenizer.whitespaceChars('\u0000', '\u0000'); + pluginInputTokenizer.wordChars('\u0001', '\u00FF');*/ + pluginOutputWriter = + new BufferedWriter(new OutputStreamWriter + (outputstream, Charset.forName("UTF-8"))); + + /* + while(true) { + String message = read(); + PluginDebug.debug(message); + handleMessage(message); + // TODO: + // write(queue.peek()); + } + */ + } + + public void startProcessing() { + + Thread listenerThread = new Thread() { + + public void run() { + + while (true) { + + PluginDebug.debug("Waiting for data..."); + + long b4 = new Date().getTime(); + + String s = read(); + + long after = new Date().getTime(); + + totalWait += (after - b4); + //System.err.println("Total wait time: " + totalWait); + + if (s != null) { + consumer.queue(s); + } else { + try { + // Close input/output channels to plugin. + pluginInputReader.close(); + pluginOutputWriter.close(); + } catch (IOException exception) { + // Deliberately ignore IOException caused by broken + // pipe since plugin may have already detached. + } + AppletSecurityContextManager.dumpStore(0); + PluginDebug.debug("APPLETVIEWER: exiting appletviewer"); + System.exit(0); + } + +/* + int readChar = -1; + // blocking read, discard first character + try { + readChar = pluginInputReader.read(); + } catch (IOException ioe) { + // plugin may have detached + } + + // if not disconnected + if (readChar != -1) { + String s = read(); + PluginDebug.debug("Got data, consuming " + s); + consumer.consume(s); + } else { + try { + // Close input/output channels to plugin. + pluginInputReader.close(); + pluginOutputWriter.close(); + } catch (IOException exception) { + // Deliberately ignore IOException caused by broken + // pipe since plugin may have already detached. + } + AppletSecurityContextManager.dumpStore(0); + PluginDebug.debug("APPLETVIEWER: exiting appletviewer"); + System.exit(0); + } +*/ + } + } + }; + + listenerThread.start(); + } + + public void handleMessage(String message) throws PluginException { + + int nextIndex = 0; + int reference = -1; + String src = null; + String[] privileges = null; + String rest = ""; + + String[] msgComponents = message.split(" "); + + if (msgComponents.length < 2) + return; + + if (msgComponents[0].startsWith("plugin")) { + handlePluginMessage(message); + return; + } + + // type and identifier are guaranteed to be there + String type = msgComponents[0]; + final int identifier = Integer.parseInt(msgComponents[1]); + nextIndex = 2; + + // reference, src and privileges are optional components, + // and are guaranteed to be in that order, if they occur + + // is there a reference ? + if (msgComponents[nextIndex].equals("reference")) { + reference = Integer.parseInt(msgComponents[nextIndex+1]); + nextIndex += 2; + } + + // is there a src? + if (msgComponents[nextIndex].equals("src")) { + src = msgComponents[nextIndex+1]; + nextIndex += 2; + } + + // is there a privileges? + if (msgComponents[nextIndex].equals("privileges")) { + String privs = msgComponents[nextIndex+1]; + privileges = privs.split(","); + nextIndex += 2; + } + + // rest + for (int i=nextIndex; i < msgComponents.length; i++) { + rest += msgComponents[i]; + rest += " "; + } + + rest = rest.trim(); + + try { + + PluginDebug.debug("Breakdown -- type: " + type + " identifier: " + identifier + " reference: " + reference + " src: " + src + " privileges: " + privileges + " rest: \"" + rest + "\""); + + if (rest.contains("JavaScriptGetWindow") + || rest.contains("JavaScriptGetMember") + || rest.contains("JavaScriptSetMember") + || rest.contains("JavaScriptGetSlot") + || rest.contains("JavaScriptSetSlot") + || rest.contains("JavaScriptEval") + || rest.contains("JavaScriptRemoveMember") + || rest.contains("JavaScriptCall") + || rest.contains("JavaScriptFinalize") + || rest.contains("JavaScriptToString")) { + + finishCallRequest("reference " + reference + " " + rest); + return; + } + + final int freference = reference; + final String frest = rest; + + if (type.equals("instance")) { + PluginAppletViewer.handleMessage(identifier, freference,frest); + } else if (type.equals("context")) { + PluginDebug.debug("Sending to PASC: " + identifier + "/" + reference + " and " + rest); + AppletSecurityContextManager.handleMessage(identifier, reference, src, privileges, rest); + } + } catch (Exception e) { + throw new PluginException(this, identifier, reference, e); + } + } + + private void handlePluginMessage(String message) { + if (message.equals("plugin showconsole")) { + showConsole(); + } else if (message.equals("plugin hideconsole")) { + hideConsole(); + } else { + // else this is something that was specifically requested + finishCallRequest(message); + } + } + + public void postCallRequest(PluginCallRequest request) { + synchronized(queue) { + queue.post(request); + } + } + + private void finishCallRequest(String message) { + PluginDebug.debug ("DISPATCHCALLREQUESTS 1"); + synchronized(queue) { + PluginDebug.debug ("DISPATCHCALLREQUESTS 2"); + PluginCallRequest request = queue.pop(); + + // make sure we give the message to the right request + // in the queue.. for the love of God, MAKE SURE! + + // first let's be efficient.. if there was only one + // request in queue, we're already set + if (queue.size() != 0) { + + int size = queue.size(); + int count = 0; + + while (!request.serviceable(message)) { + + PluginDebug.debug(request + " cannot service " + message); + + // something is very wrong.. we have a message to + // process, but no one to service it + if (count >= size) { + throw new RuntimeException("Unable to find processor for message " + message); + } + + // post request at the end of the queue + queue.post(request); + + // Look at the next request + request = queue.pop(); + + count++; + } + + } + + PluginDebug.debug ("DISPATCHCALLREQUESTS 3"); + if (request != null) { + PluginDebug.debug ("DISPATCHCALLREQUESTS 5"); + synchronized(request) { + request.parseReturn(message); + request.notifyAll(); + } + PluginDebug.debug ("DISPATCHCALLREQUESTS 6"); + PluginDebug.debug ("DISPATCHCALLREQUESTS 7"); + } + } + PluginDebug.debug ("DISPATCHCALLREQUESTS 8"); + } + + /** + * Read string from plugin. + * + * @return the read string + * + * @exception IOException if an error occurs + */ + private String read() + { + String message = null; + + try { + message = pluginInputReader.readLine(); + PluginDebug.debug(" PIPE: appletviewer read: " + message); + + if (message == null || message.equals("shutdown")) { + synchronized(shuttingDown) { + shuttingDown = true; + } + try { + // Close input/output channels to plugin. + pluginInputReader.close(); + pluginOutputWriter.close(); + } catch (IOException exception) { + // Deliberately ignore IOException caused by broken + // pipe since plugin may have already detached. + } + AppletSecurityContextManager.dumpStore(0); + PluginDebug.debug("APPLETVIEWER: exiting appletviewer"); + System.exit(0); + } + } catch (IOException e) { + e.printStackTrace(); + } + + return message; + } + + /** + * Write string to plugin. + * + * @param message the message to write + * + * @exception IOException if an error occurs + */ + public void write(String message) + { + + PluginDebug.debug(" PIPE: appletviewer wrote: " + message); + synchronized(pluginOutputWriter) { + try { + pluginOutputWriter.write(message + "\n", 0, message.length()); + pluginOutputWriter.write(0); + pluginOutputWriter.flush(); + } catch (IOException e) { + // if we are shutting down, ignore write failures as + // pipe may have closed + synchronized(shuttingDown) { + if (!shuttingDown) { + e.printStackTrace(); + } + } + + // either ways, if the pipe is broken, there is nothing + // we can do anymore. Don't hang around. + PluginDebug.debug("Unable to write to PIPE. APPLETVIEWER exiting"); + System.exit(1); + } + } + + return; + /* + synchronized(writeQueue) { + writeQueue.add(message); + PluginDebug.debug(" PIPE: appletviewer wrote: " + message); + } + */ + + } + + public boolean messageAvailable() { + return writeQueue.size() != 0; + } + + public String getMessage() { + synchronized(writeQueue) { + String ret = writeQueue.size() > 0 ? writeQueue.poll() : ""; + return ret; + } + } + + private void showConsole() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + console.showConsole(); + } + }); + } + + private void hideConsole() { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + console.hideConsole(); + } + }); + } +} diff --git a/plugin/icedteanp/java/sun/applet/RequestQueue.java b/plugin/icedteanp/java/sun/applet/RequestQueue.java new file mode 100644 index 0000000..87dc54f --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/RequestQueue.java @@ -0,0 +1,77 @@ +/* VoidPluginCallRequest -- represent Java-to-JavaScript requests + Copyright (C) 2008 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package sun.applet; + + +public class RequestQueue { + PluginCallRequest head = null; + PluginCallRequest tail = null; + private int size = 0; + + public void post(PluginCallRequest request) { + PluginDebug.debug("Securitymanager=" + System.getSecurityManager()); + if (head == null) { + head = tail = request; + tail.setNext(null); + } else { + tail.setNext(request); + tail = request; + tail.setNext(null); + } + + size++; + } + + public PluginCallRequest pop() { + if (head == null) + return null; + + PluginCallRequest ret = head; + head = head.getNext(); + ret.setNext(null); + + size--; + + return ret; + } + + public int size() { + return size; + } +} diff --git a/plugin/icedteanp/java/sun/applet/TestEnv.java b/plugin/icedteanp/java/sun/applet/TestEnv.java new file mode 100644 index 0000000..08b74ce --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/TestEnv.java @@ -0,0 +1,172 @@ +/* TestEnv -- test JavaScript-to-Java calls + Copyright (C) 2008 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package sun.applet; + +public class TestEnv +{ + public static int intField = 103; + public int intInstanceField = 7822; + public String stringField = "hello"; + // z <musical G clef> <chinese water> + public String complexStringField = "z\uD834\uDD1E\u6C34"; + + public static void TestIt() { + PluginDebug.debug("TestIt"); + } + + public static void TestItBool(boolean arg) { + PluginDebug.debug("TestItBool: " + arg); + } + + public static void TestItByte(byte arg) { + PluginDebug.debug("TestItByte: " + arg); + } + + public static void TestItChar(char arg) { + PluginDebug.debug("TestItChar: " + arg); + } + + public static void TestItShort(short arg) { + PluginDebug.debug("TestItShort: " + arg); + } + + public static void TestItInt(int arg) { + PluginDebug.debug("TestItInt: " + arg); + } + + public static void TestItLong(long arg) { + PluginDebug.debug("TestItLong: " + arg); + } + + public static void TestItFloat(float arg) { + PluginDebug.debug("TestItFloat: " + arg); + } + + public static void TestItDouble(double arg) { + PluginDebug.debug("TestItDouble: " + arg); + } + + public static void TestItObject(TestEnv arg) { + PluginDebug.debug("TestItObject: " + arg); + } + + public static void TestItObjectString(String arg) { + PluginDebug.debug("TestItObjectString: " + arg); + } + + public static void TestItIntArray(int[] arg) { + PluginDebug.debug("TestItIntArray: " + arg); + for (int i = 0; i < arg.length; i++) + PluginDebug.debug ("ELEMENT: " + i + " " + arg[i]); + } + + public static void TestItObjectArray(String[] arg) { + PluginDebug.debug("TestItObjectArray: " + arg); + for (int i = 0; i < arg.length; i++) + PluginDebug.debug ("ELEMENT: " + i + " " + arg[i]); + } + + public static void TestItObjectArrayMulti(String[][] arg) { + PluginDebug.debug("TestItObjectArrayMulti: " + arg); + for (int i = 0; i < arg.length; i++) + for (int j = 0; j < arg[i].length; j++) + PluginDebug.debug ("ELEMENT: " + i + " " + j + " " + arg[i][j]); + } + + public static boolean TestItBoolReturnTrue() { + return true; + } + + public static boolean TestItBoolReturnFalse() { + return false; + } + + public static byte TestItByteReturn() { + return (byte) 0xfe; + } + + public static char TestItCharReturn() { + return 'K'; + } + + public static char TestItCharUnicodeReturn() { + return '\u6C34'; + } + + public static short TestItShortReturn() { + return 23; + } + + public static int TestItIntReturn() { + return 3445; + } + + public static long TestItLongReturn() { + return 3242883; + } + + public static float TestItFloatReturn() { + return 9.21E4f; + } + + public static double TestItDoubleReturn() { + return 8.33E88; + } + + public static Object TestItObjectReturn() { + return new String("Thomas"); + } + + public static int[] TestItIntArrayReturn() { + return new int[] { 6, 7, 8 }; + } + + public static String[] TestItObjectArrayReturn() { + return new String[] { "Thomas", "Brigitte" }; + } + + public static String[][] TestItObjectArrayMultiReturn() { + return new String[][] { {"Thomas", "Brigitte"}, + {"Lindsay", "Michael"} }; + } + + public int TestItIntInstance(int arg) { + PluginDebug.debug("TestItIntInstance: " + this + " " + arg); + return 899; + } +} diff --git a/plugin/icedteanp/java/sun/applet/VoidPluginCallRequest.java b/plugin/icedteanp/java/sun/applet/VoidPluginCallRequest.java new file mode 100644 index 0000000..0bacc6c --- /dev/null +++ b/plugin/icedteanp/java/sun/applet/VoidPluginCallRequest.java @@ -0,0 +1,54 @@ +/* VoidPluginCallRequest -- represent Java-to-JavaScript requests + Copyright (C) 2008 Red Hat + +This file is part of IcedTea. + +IcedTea is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +IcedTea is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with IcedTea; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package sun.applet; + + +public class VoidPluginCallRequest extends PluginCallRequest { + public VoidPluginCallRequest(String message, Long reference) { + super(message, reference); + PluginDebug.debug ("VoidPluginCall " + message); + } + + public void parseReturn(String message) { + setDone(true); + } + + public Object getObject() { + return null; + } +} |