diff options
author | andrew <devnull@localhost> | 2010-10-19 17:55:59 +0100 |
---|---|---|
committer | andrew <devnull@localhost> | 2010-10-19 17:55:59 +0100 |
commit | 7603e948d7a0a7eb2e72358cb4a40ae6779f95da (patch) | |
tree | c6441f7d14eafe8119d890cddd09b05b8f88c52a /plugin/icedteanp |
Initial import from IcedTea6.
2010-10-19 Andrew John Hughes <[email protected]>
* .hgignore,
* Makefile.am,
* acinclude.m4,
* autogen.sh,
* configure.ac,
* extra/net/sourceforge/jnlp/about/HTMLPanel.java,
* extra/net/sourceforge/jnlp/about/Main.java,
* extra/net/sourceforge/jnlp/about/resources/about.html,
* extra/net/sourceforge/jnlp/about/resources/applications.html,
* extra/net/sourceforge/jnlp/about/resources/notes.html,
* javac.in,
* javaws.desktop: Imported from IcedTea6.
* launcher/java.c,
* launcher/java.h,
* launcher/java_md.c,
* launcher/java_md.h,
* launcher/jli_util.h,
* launcher/jni.h,
* launcher/jvm.h,
* launcher/jvm_md.h,
* launcher/manifest_info.h,
* launcher/splashscreen.h,
* launcher/splashscreen_stubs.c,
* launcher/version_comp.h,
* launcher/wildcard.h: Imported from OpenJDK.
* netx/javaws.1,
* netx/javax/jnlp/BasicService.java,
* netx/javax/jnlp/ClipboardService.java,
* netx/javax/jnlp/DownloadService.java,
* netx/javax/jnlp/DownloadServiceListener.java,
* netx/javax/jnlp/ExtendedService.java,
* netx/javax/jnlp/ExtensionInstallerService.java,
* netx/javax/jnlp/FileContents.java,
* netx/javax/jnlp/FileOpenService.java,
* netx/javax/jnlp/FileSaveService.java,
* netx/javax/jnlp/JNLPRandomAccessFile.java,
* netx/javax/jnlp/PersistenceService.java,
* netx/javax/jnlp/PrintService.java,
* netx/javax/jnlp/ServiceManager.java,
* netx/javax/jnlp/ServiceManagerStub.java,
* netx/javax/jnlp/SingleInstanceListener.java,
* netx/javax/jnlp/SingleInstanceService.java,
* netx/javax/jnlp/UnavailableServiceException.java,
* netx/net/sourceforge/jnlp/AppletDesc.java,
* netx/net/sourceforge/jnlp/ApplicationDesc.java,
* netx/net/sourceforge/jnlp/AssociationDesc.java,
* netx/net/sourceforge/jnlp/ComponentDesc.java,
* netx/net/sourceforge/jnlp/DefaultLaunchHandler.java,
* netx/net/sourceforge/jnlp/ExtensionDesc.java,
* netx/net/sourceforge/jnlp/IconDesc.java,
* netx/net/sourceforge/jnlp/InformationDesc.java,
* netx/net/sourceforge/jnlp/InstallerDesc.java,
* netx/net/sourceforge/jnlp/JARDesc.java,
* netx/net/sourceforge/jnlp/JNLPFile.java,
* netx/net/sourceforge/jnlp/JNLPSplashScreen.java,
* netx/net/sourceforge/jnlp/JREDesc.java,
* netx/net/sourceforge/jnlp/LaunchException.java,
* netx/net/sourceforge/jnlp/LaunchHandler.java,
* netx/net/sourceforge/jnlp/Launcher.java,
* netx/net/sourceforge/jnlp/MenuDesc.java,
* netx/net/sourceforge/jnlp/NetxPanel.java,
* netx/net/sourceforge/jnlp/Node.java,
* netx/net/sourceforge/jnlp/PackageDesc.java,
* netx/net/sourceforge/jnlp/ParseException.java,
* netx/net/sourceforge/jnlp/Parser.java,
* netx/net/sourceforge/jnlp/PluginBridge.java,
* netx/net/sourceforge/jnlp/PropertyDesc.java,
* netx/net/sourceforge/jnlp/RelatedContentDesc.java,
* netx/net/sourceforge/jnlp/ResourcesDesc.java,
* netx/net/sourceforge/jnlp/SecurityDesc.java,
* netx/net/sourceforge/jnlp/ShortcutDesc.java,
* netx/net/sourceforge/jnlp/StreamEater.java,
* netx/net/sourceforge/jnlp/UpdateDesc.java,
* netx/net/sourceforge/jnlp/Version.java,
* netx/net/sourceforge/jnlp/cache/CacheEntry.java,
* netx/net/sourceforge/jnlp/cache/CacheUtil.java,
* netx/net/sourceforge/jnlp/cache/DefaultDownloadIndicator.java,
* netx/net/sourceforge/jnlp/cache/DownloadIndicator.java,
* netx/net/sourceforge/jnlp/cache/Resource.java,
* netx/net/sourceforge/jnlp/cache/ResourceTracker.java,
* netx/net/sourceforge/jnlp/cache/UpdatePolicy.java,
* netx/net/sourceforge/jnlp/cache/package.html,
* netx/net/sourceforge/jnlp/event/ApplicationEvent.java,
* netx/net/sourceforge/jnlp/event/ApplicationListener.java,
* netx/net/sourceforge/jnlp/event/DownloadEvent.java,
* netx/net/sourceforge/jnlp/event/DownloadListener.java,
* netx/net/sourceforge/jnlp/event/package.html,
* netx/net/sourceforge/jnlp/package.html,
* netx/net/sourceforge/jnlp/resources/Manifest.mf,
* netx/net/sourceforge/jnlp/resources/Messages.properties,
* netx/net/sourceforge/jnlp/resources/about.jnlp,
* netx/net/sourceforge/jnlp/resources/default.jnlp,
* netx/net/sourceforge/jnlp/runtime/AppThreadGroup.java,
* netx/net/sourceforge/jnlp/runtime/AppletAudioClip.java,
* netx/net/sourceforge/jnlp/runtime/AppletEnvironment.java,
* netx/net/sourceforge/jnlp/runtime/AppletInstance.java,
* netx/net/sourceforge/jnlp/runtime/ApplicationInstance.java,
* netx/net/sourceforge/jnlp/runtime/Boot.java,
* netx/net/sourceforge/jnlp/runtime/Boot13.java,
* netx/net/sourceforge/jnlp/runtime/JNLPClassLoader.java,
* netx/net/sourceforge/jnlp/runtime/JNLPPolicy.java,
* netx/net/sourceforge/jnlp/runtime/JNLPRuntime.java,
* netx/net/sourceforge/jnlp/runtime/JNLPSecurityManager.java,
* netx/net/sourceforge/jnlp/runtime/package.html,
* netx/net/sourceforge/jnlp/security/AccessWarningPane.java,
* netx/net/sourceforge/jnlp/security/AppletWarningPane.java,
* netx/net/sourceforge/jnlp/security/CertVerifier.java,
* netx/net/sourceforge/jnlp/security/CertWarningPane.java,
* netx/net/sourceforge/jnlp/security/CertsInfoPane.java,
* netx/net/sourceforge/jnlp/security/HttpsCertVerifier.java,
* netx/net/sourceforge/jnlp/security/MoreInfoPane.java,
* netx/net/sourceforge/jnlp/security/NotAllSignedWarningPane.java,
* netx/net/sourceforge/jnlp/security/SecurityDialogPanel.java,
* netx/net/sourceforge/jnlp/security/SecurityUtil.java,
* netx/net/sourceforge/jnlp/security/SecurityWarningDialog.java,
* netx/net/sourceforge/jnlp/security/SingleCertInfoPane.java,
* netx/net/sourceforge/jnlp/security/VariableX509TrustManager.java,
* netx/net/sourceforge/jnlp/security/viewer/CertificatePane.java,
* netx/net/sourceforge/jnlp/security/viewer/CertificateViewer.java,
* netx/net/sourceforge/jnlp/services/ExtendedSingleInstanceService.java,
* netx/net/sourceforge/jnlp/services/InstanceExistsException.java,
* netx/net/sourceforge/jnlp/services/ServiceUtil.java,
* netx/net/sourceforge/jnlp/services/SingleInstanceLock.java,
* netx/net/sourceforge/jnlp/services/XBasicService.java,
* netx/net/sourceforge/jnlp/services/XClipboardService.java,
* netx/net/sourceforge/jnlp/services/XDownloadService.java,
* netx/net/sourceforge/jnlp/services/XExtendedService.java,
* netx/net/sourceforge/jnlp/services/XExtensionInstallerService.java,
* netx/net/sourceforge/jnlp/services/XFileContents.java,
* netx/net/sourceforge/jnlp/services/XFileOpenService.java,
* netx/net/sourceforge/jnlp/services/XFileSaveService.java,
* netx/net/sourceforge/jnlp/services/XJNLPRandomAccessFile.java,
* netx/net/sourceforge/jnlp/services/XPersistenceService.java,
* netx/net/sourceforge/jnlp/services/XPrintService.java,
* netx/net/sourceforge/jnlp/services/XServiceManagerStub.java,
* netx/net/sourceforge/jnlp/services/XSingleInstanceService.java,
* netx/net/sourceforge/jnlp/services/package.html,
* netx/net/sourceforge/jnlp/tools/CharacterEncoder.java,
* netx/net/sourceforge/jnlp/tools/HexDumpEncoder.java,
* netx/net/sourceforge/jnlp/tools/JarRunner.java,
* netx/net/sourceforge/jnlp/tools/JarSigner.java,
* netx/net/sourceforge/jnlp/tools/JarSignerResources.java,
* netx/net/sourceforge/jnlp/tools/KeyStoreUtil.java,
* netx/net/sourceforge/jnlp/tools/KeyTool.java,
* netx/net/sourceforge/jnlp/util/FileUtils.java,
* netx/net/sourceforge/jnlp/util/PropertiesFile.java,
* netx/net/sourceforge/jnlp/util/Reflect.java,
* netx/net/sourceforge/jnlp/util/WeakList.java,
* netx/net/sourceforge/jnlp/util/XDesktopEntry.java,
* netx/net/sourceforge/nanoxml/XMLElement.java,
* netx/net/sourceforge/nanoxml/XMLParseException.java,
* plugin/icedteanp/IcedTeaJavaRequestProcessor.cc,
* plugin/icedteanp/IcedTeaJavaRequestProcessor.h,
* plugin/icedteanp/IcedTeaNPPlugin.cc,
* plugin/icedteanp/IcedTeaNPPlugin.h,
* plugin/icedteanp/IcedTeaPluginRequestProcessor.cc,
* plugin/icedteanp/IcedTeaPluginRequestProcessor.h,
* plugin/icedteanp/IcedTeaPluginUtils.cc,
* plugin/icedteanp/IcedTeaPluginUtils.h,
* plugin/icedteanp/IcedTeaRunnable.cc,
* plugin/icedteanp/IcedTeaRunnable.h,
* plugin/icedteanp/IcedTeaScriptablePluginObject.cc,
* plugin/icedteanp/IcedTeaScriptablePluginObject.h,
* plugin/icedteanp/java/netscape/javascript/JSException.java,
* plugin/icedteanp/java/netscape/javascript/JSObject.java,
* plugin/icedteanp/java/netscape/javascript/JSObjectCreatePermission.java,
* plugin/icedteanp/java/netscape/javascript/JSProxy.java,
* plugin/icedteanp/java/netscape/javascript/JSRunnable.java,
* plugin/icedteanp/java/netscape/javascript/JSUtil.java,
* plugin/icedteanp/java/netscape/security/ForbiddenTargetException.java,
* plugin/icedteanp/java/sun/applet/AppletSecurityContextManager.java,
* plugin/icedteanp/java/sun/applet/GetMemberPluginCallRequest.java,
* plugin/icedteanp/java/sun/applet/GetWindowPluginCallRequest.java,
* plugin/icedteanp/java/sun/applet/JavaConsole.java,
* plugin/icedteanp/java/sun/applet/MethodOverloadResolver.java,
* plugin/icedteanp/java/sun/applet/PasswordAuthenticationDialog.java,
* plugin/icedteanp/java/sun/applet/PluginAppletSecurityContext.java,
* plugin/icedteanp/java/sun/applet/PluginAppletViewer.java,
* plugin/icedteanp/java/sun/applet/PluginCallRequest.java,
* plugin/icedteanp/java/sun/applet/PluginCallRequestFactory.java,
* plugin/icedteanp/java/sun/applet/PluginClassLoader.java,
* plugin/icedteanp/java/sun/applet/PluginCookieInfoRequest.java,
* plugin/icedteanp/java/sun/applet/PluginCookieManager.java,
* plugin/icedteanp/java/sun/applet/PluginDebug.java,
* plugin/icedteanp/java/sun/applet/PluginException.java,
* plugin/icedteanp/java/sun/applet/PluginMain.java,
* plugin/icedteanp/java/sun/applet/PluginMessageConsumer.java,
* plugin/icedteanp/java/sun/applet/PluginMessageHandlerWorker.java,
* plugin/icedteanp/java/sun/applet/PluginObjectStore.java,
* plugin/icedteanp/java/sun/applet/PluginProxyInfoRequest.java,
* plugin/icedteanp/java/sun/applet/PluginProxySelector.java,
* plugin/icedteanp/java/sun/applet/PluginStreamHandler.java,
* plugin/icedteanp/java/sun/applet/RequestQueue.java,
* plugin/icedteanp/java/sun/applet/TestEnv.java,
* plugin/icedteanp/java/sun/applet/VoidPluginCallRequest.java,
* plugin/tests/LiveConnect/DummyObject.java,
* plugin/tests/LiveConnect/OverloadTestHelper1.java,
* plugin/tests/LiveConnect/OverloadTestHelper2.java,
* plugin/tests/LiveConnect/OverloadTestHelper3.java,
* plugin/tests/LiveConnect/PluginTest.java,
* plugin/tests/LiveConnect/build,
* plugin/tests/LiveConnect/common.js,
* plugin/tests/LiveConnect/index.html,
* plugin/tests/LiveConnect/jjs_eval_test.js,
* plugin/tests/LiveConnect/jjs_func_parameters_tests.js,
* plugin/tests/LiveConnect/jjs_func_rettype_tests.js,
* plugin/tests/LiveConnect/jjs_get_tests.js,
* plugin/tests/LiveConnect/jjs_set_tests.js,
* plugin/tests/LiveConnect/jsj_func_overload_tests.js,
* plugin/tests/LiveConnect/jsj_func_parameters_tests.js,
* plugin/tests/LiveConnect/jsj_func_rettype_tests.js,
* plugin/tests/LiveConnect/jsj_get_tests.js,
* plugin/tests/LiveConnect/jsj_set_tests.js,
* plugin/tests/LiveConnect/jsj_type_casting_tests.js,
* plugin/tests/LiveConnect/jsj_type_conversion_tests.js:
Initial import from IcedTea6.
* AUTHORS,
* COPYING
* INSTALL,
* NEWS,
* README: New documentation.
Diffstat (limited to 'plugin/icedteanp')
44 files changed, 16132 insertions, 0 deletions
diff --git a/plugin/icedteanp/IcedTeaJavaRequestProcessor.cc b/plugin/icedteanp/IcedTeaJavaRequestProcessor.cc new file mode 100644 index 0000000..8b5b8a3 --- /dev/null +++ b/plugin/icedteanp/IcedTeaJavaRequestProcessor.cc @@ -0,0 +1,1392 @@ +/* IcedTeaJavaRequestProcessor.cc + + Copyright (C) 2009, 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. */ + +#include <typeinfo> + +#include "IcedTeaJavaRequestProcessor.h" +#include "IcedTeaScriptablePluginObject.h" + +/* + * This class processes LiveConnect requests from JavaScript to Java. + * + * It sends the requests to Java, gets the return information, and sends it + * back to the browser/JavaScript + */ + +/** + * Processes return information from JavaSide (return messages of requests) + * + * @param message The message request to process + * @return boolean indicating whether the message is serviceable by this object + */ + +bool +JavaRequestProcessor::newMessageOnBus(const char* message) +{ + + // Anything we are waiting for _MUST_ have and instance id and reference # + std::vector<std::string*>* message_parts = IcedTeaPluginUtilities::strSplit(message, " "); + + IcedTeaPluginUtilities::printStringPtrVector("JavaRequest::newMessageOnBus:", message_parts); + + if (*(message_parts->at(0)) == "context" && *(message_parts->at(2)) == "reference") + if (atoi(message_parts->at(1)->c_str()) == this->instance && atoi(message_parts->at(3)->c_str()) == this->reference) + { + // Gather the results + + // Let's get errors out of the way first + if (!message_parts->at(4)->find("Error")) + { + for (int i=5; i < message_parts->size(); i++) + { + result->error_msg->append(*(message_parts->at(i))); + result->error_msg->append(" "); + } + + printf("Error on Java side: %s\n", result->error_msg->c_str()); + + result->error_occurred = true; + result_ready = true; + } + else if (!message_parts->at(4)->find("GetStringUTFChars") || + !message_parts->at(4)->find("GetToStringValue")) + { + // first item is length, and it is radix 10 + int length = strtol(message_parts->at(5)->c_str(), NULL, 10); + + IcedTeaPluginUtilities::getUTF8String(length, 6 /* start at */, message_parts, result->return_string); + result_ready = true; + } + else if (!message_parts->at(4)->find("GetStringChars")) // GetStringChars (UTF-16LE/UCS-2) + { + // first item is length, and it is radix 10 + int length = strtol(message_parts->at(5)->c_str(), NULL, 10); + + IcedTeaPluginUtilities::getUTF16LEString(length, 6 /* start at */, message_parts, result->return_wstring); + result_ready = true; + } else if (!message_parts->at(4)->find("FindClass") || + !message_parts->at(4)->find("GetClassName") || + !message_parts->at(4)->find("GetClassID") || + !message_parts->at(4)->find("GetMethodID") || + !message_parts->at(4)->find("GetStaticMethodID") || + !message_parts->at(4)->find("GetObjectClass") || + !message_parts->at(4)->find("NewObject") || + !message_parts->at(4)->find("NewStringUTF") || + !message_parts->at(4)->find("HasPackage") || + !message_parts->at(4)->find("HasMethod") || + !message_parts->at(4)->find("HasField") || + !message_parts->at(4)->find("GetStaticFieldID") || + !message_parts->at(4)->find("GetFieldID") || + !message_parts->at(4)->find("GetJavaObject") || + !message_parts->at(4)->find("IsInstanceOf") || + !message_parts->at(4)->find("NewArray")) + { + result->return_identifier = atoi(message_parts->at(5)->c_str()); + result->return_string->append(*(message_parts->at(5))); // store it as a string as well, for easy access + result_ready = true; + } else if (!message_parts->at(4)->find("DeleteLocalRef") || + !message_parts->at(4)->find("NewGlobalRef")) + { + result_ready = true; // nothing else to do + } else if (!message_parts->at(4)->find("CallMethod") || + !message_parts->at(4)->find("CallStaticMethod") || + !message_parts->at(4)->find("GetField") || + !message_parts->at(4)->find("GetStaticField") || + !message_parts->at(4)->find("GetValue") || + !message_parts->at(4)->find("GetObjectArrayElement")) + { + + if (!message_parts->at(5)->find("literalreturn")) + { + // literal returns don't have a corresponding jni id + result->return_identifier = 0; + result->return_string->append(*(message_parts->at(5))); + result->return_string->append(" "); + result->return_string->append(*(message_parts->at(6))); + + } else + { + // Else it is a complex object + + result->return_identifier = atoi(message_parts->at(5)->c_str()); + result->return_string->append(*(message_parts->at(5))); // store it as a string as well, for easy access + } + + result_ready = true; + } else if (!message_parts->at(4)->find("GetArrayLength")) + { + result->return_identifier = 0; // length is not an "identifier" + result->return_string->append(*(message_parts->at(5))); + result_ready = true; + } else if (!message_parts->at(4)->find("SetField") || + !message_parts->at(4)->find("SetObjectArrayElement")) + { + + // nothing to do + + result->return_identifier = 0; + result_ready = true; + } + + IcedTeaPluginUtilities::freeStringPtrVector(message_parts); + return true; + } + + IcedTeaPluginUtilities::freeStringPtrVector(message_parts); + return false; +} + +/** + * Constructor. + * + * Initializes the result data structure (heap) + */ + +JavaRequestProcessor::JavaRequestProcessor() +{ + PLUGIN_DEBUG("JavaRequestProcessor constructor\n"); + + // caller frees this + result = new JavaResultData(); + result->error_msg = new std::string(); + result->return_identifier = 0; + result->return_string = new std::string(); + result->return_wstring = new std::wstring(); + result->error_occurred = false; + + result_ready = false; +} + +/** + * Destructor + * + * Frees memory used by the result struct + */ + +JavaRequestProcessor::~JavaRequestProcessor() +{ + PLUGIN_DEBUG("JavaRequestProcessor::~JavaRequestProcessor\n"); + + if (result) + { + if (result->error_msg) + delete result->error_msg; + + if (result->return_string) + delete result->return_string; + + if (result->return_wstring) + delete result->return_wstring; + + delete result; + } +} + +/** + * Resets the results + */ +void +JavaRequestProcessor::resetResult() +{ + // caller frees this + result->error_msg->clear(); + result->return_identifier = 0; + result->return_string->clear(); + result->return_wstring->clear(); + result->error_occurred = false; + + result_ready = false; +} + +void +JavaRequestProcessor::postAndWaitForResponse(std::string message) +{ + struct timespec t; + clock_gettime(CLOCK_REALTIME, &t); + t.tv_sec += REQUESTTIMEOUT; // 1 minute timeout + + // Clear the result + resetResult(); + + java_to_plugin_bus->subscribe(this); + plugin_to_java_bus->post(message.c_str()); + + // Wait for result to be filled in. + struct timespec curr_t; + + do + { + clock_gettime(CLOCK_REALTIME, &curr_t); + + if (!result_ready && (curr_t.tv_sec < t.tv_sec)) + { + if (g_main_context_pending(NULL)) + g_main_context_iteration(NULL, false); + else + usleep(200); + } + else + break; + + } while (1); + + if (curr_t.tv_sec >= t.tv_sec) + { + result->error_occurred = true; + result->error_msg->append("Error: Timed out when waiting for response"); + + // Report error + PLUGIN_DEBUG("Error: Timed out when waiting for response to %s\n", message.c_str()); + } + + java_to_plugin_bus->unSubscribe(this); +} + +/** + * Given an object id, fetches the toString() value from Java + * + * @param object_id The ID of the object + * @return A JavaResultData struct containing the result of the request + */ + +JavaResultData* +JavaRequestProcessor::getToStringValue(std::string object_id) +{ + std::string message = std::string(); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + + message.append(" GetToStringValue "); // get it in UTF8 + message.append(object_id); + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + return result; +} + +/** + * Given an object id, fetches the value of that ID from Java + * + * @param object_id The ID of the object + * @return A JavaResultData struct containing the result of the request + */ + +JavaResultData* +JavaRequestProcessor::getValue(std::string object_id) +{ + std::string message = std::string(); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + + message.append(" GetValue "); // get it in UTF8 + message.append(object_id); + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + return result; +} + +/** + * Given a string id, fetches the actual string from Java side + * + * @param string_id The ID of the string + * @return A JavaResultData struct containing the result of the request + */ + +JavaResultData* +JavaRequestProcessor::getString(std::string string_id) +{ + std::string message = std::string(); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + + message.append(" GetStringUTFChars "); // get it in UTF8 + message.append(string_id); + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + return result; +} + +/** + * Decrements reference count by 1 + * + * @param object_id The ID of the object + */ + +void +JavaRequestProcessor::deleteReference(std::string object_id) +{ + std::string message = std::string(); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + + message.append(" DeleteLocalRef "); + message.append(object_id); + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); +} + +/** + * Increments reference count by 1 + * + * @param object_id The ID of the object + */ + +void +JavaRequestProcessor::addReference(std::string object_id) +{ + std::string message = std::string(); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + + message.append(" NewGlobalRef "); + message.append(object_id); + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + +} + +JavaResultData* +JavaRequestProcessor::findClass(int plugin_instance_id, + std::string name) +{ + std::string message = std::string(); + std::string plugin_instance_id_str = std::string(); + + IcedTeaPluginUtilities::itoa(plugin_instance_id, &plugin_instance_id_str); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + + message.append(" FindClass "); + message.append(plugin_instance_id_str); + message.append(" "); + message.append(name); + + postAndWaitForResponse(message); + + return result; +} + +JavaResultData* +JavaRequestProcessor::getClassName(std::string objectID) +{ + std::string message = std::string(); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + + message.append(" GetClassName "); + message.append(objectID); + + postAndWaitForResponse(message); + + return result; +} + +JavaResultData* +JavaRequestProcessor::getClassID(std::string objectID) +{ + std::string message = std::string(); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + + message.append(" GetClassID "); + message.append(objectID); + + postAndWaitForResponse(message); + + return result; +} + +JavaResultData* +JavaRequestProcessor::getArrayLength(std::string objectID) +{ + std::string message = std::string(); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + + message.append(" GetArrayLength "); + message.append(objectID); + + postAndWaitForResponse(message); + + return result; +} + +JavaResultData* +JavaRequestProcessor::getSlot(std::string objectID, std::string index) +{ + std::string message = std::string(); + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + + message.append(" GetObjectArrayElement "); + message.append(objectID); + message.append(" "); + message.append(index); + + postAndWaitForResponse(message); + + return result; +} + +JavaResultData* +JavaRequestProcessor::setSlot(std::string objectID, + std::string index, + std::string value_id) +{ + std::string message = std::string(); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + + message.append(" SetObjectArrayElement "); + message.append(objectID); + message.append(" "); + message.append(index); + message.append(" "); + message.append(value_id); + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + return result; +} + +JavaResultData* +JavaRequestProcessor::newArray(std::string array_class, + std::string length) +{ + std::string message = std::string(); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + + message.append(" NewArray "); + message.append(array_class); + message.append(" "); + message.append(length); + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + return result; +} + +JavaResultData* +JavaRequestProcessor::getFieldID(std::string classID, std::string fieldName) +{ + JavaResultData* java_result; + JavaRequestProcessor* java_request = new JavaRequestProcessor(); + std::string message = std::string(); + + java_result = java_request->newString(fieldName); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + message.append(" GetFieldID "); + message.append(classID); + message.append(" "); + message.append(java_result->return_string->c_str()); + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + delete java_request; + + return result; +} + +JavaResultData* +JavaRequestProcessor::getStaticFieldID(std::string classID, std::string fieldName) +{ + JavaResultData* java_result; + JavaRequestProcessor* java_request = new JavaRequestProcessor(); + std::string message = std::string(); + + java_result = java_request->newString(fieldName); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + message.append(" GetStaticFieldID "); + message.append(classID); + message.append(" "); + message.append(java_result->return_string->c_str()); + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + delete java_request; + + return result; +} + +JavaResultData* +JavaRequestProcessor::getField(std::string source, + std::string classID, + std::string objectID, + std::string fieldName) +{ + JavaResultData* java_result; + JavaRequestProcessor* java_request = new JavaRequestProcessor(); + std::string message = std::string(); + + java_result = java_request->getFieldID(classID, fieldName); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, source, &message); + message.append(" GetField "); + message.append(objectID); + message.append(" "); + message.append(java_result->return_string->c_str()); + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + delete java_request; + + return result; +} + +JavaResultData* +JavaRequestProcessor::getStaticField(std::string source, std::string classID, + std::string fieldName) +{ + JavaResultData* java_result; + JavaRequestProcessor* java_request = new JavaRequestProcessor(); + std::string message = std::string(); + + java_result = java_request->getStaticFieldID(classID, fieldName); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, source, &message); + message.append(" GetStaticField "); + message.append(classID); + message.append(" "); + message.append(java_result->return_string->c_str()); + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + delete java_request; + + return result; +} + + +JavaResultData* +JavaRequestProcessor::set(std::string source, + bool isStatic, + std::string classID, + std::string objectID, + std::string fieldName, + std::string value_id) +{ + JavaResultData* java_result; + JavaRequestProcessor java_request = JavaRequestProcessor(); + std::string message = std::string(); + + java_result = java_request.getFieldID(classID, fieldName); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, source, &message); + + if (isStatic) + { + message.append(" SetStaticField "); + message.append(classID); + } else + { + message.append(" SetField "); + message.append(objectID); + } + + message.append(" "); + message.append(java_result->return_string->c_str()); + message.append(" "); + message.append(value_id); + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + return result; +} + +JavaResultData* +JavaRequestProcessor::setStaticField(std::string source, + std::string classID, + std::string fieldName, + std::string value_id) +{ + return set(source, true, classID, "", fieldName, value_id); +} + +JavaResultData* +JavaRequestProcessor::setField(std::string source, + std::string classID, + std::string objectID, + std::string fieldName, + std::string value_id) +{ + return set(source, false, classID, objectID, fieldName, value_id); +} + +JavaResultData* +JavaRequestProcessor::getMethodID(std::string classID, NPIdentifier methodName, + std::vector<std::string> args) +{ + JavaRequestProcessor* java_request; + std::string message = std::string(); + std::string* signature; + + signature = new std::string(); + *signature += "("; + + // FIXME: Need to determine how to extract array types and complex java objects + for (int i=0; i < args.size(); i++) + { + *signature += args[i]; + } + + *signature += ")"; + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + message += " GetMethodID "; + message += classID; + message += " "; + message += browser_functions.utf8fromidentifier(methodName); + message += " "; + message += *signature; + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + delete signature; + + return result; +} + +JavaResultData* +JavaRequestProcessor::getStaticMethodID(std::string classID, NPIdentifier methodName, + std::vector<std::string> args) +{ + JavaRequestProcessor* java_request; + std::string message = std::string(); + std::string* signature; + + signature = new std::string(); + *signature += "("; + + // FIXME: Need to determine how to extract array types and complex java objects + for (int i=0; i < args.size(); i++) + { + *signature += args[i]; + } + + *signature += ")"; + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + message += " GetStaticMethodID "; + message += classID; + message += " "; + message += browser_functions.utf8fromidentifier(methodName); + message += " "; + message += *signature; + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + delete signature; + + return result; +} + +void +getArrayTypeForJava(NPP instance, NPVariant element, std::string* type) +{ + + if (NPVARIANT_IS_BOOLEAN(element)) { + type->append("string"); + } else if (NPVARIANT_IS_INT32(element)) { + type->append("string"); + } else if (NPVARIANT_IS_DOUBLE(element)) { + type->append("string"); + } else if (NPVARIANT_IS_STRING(element)) { + type->append("string"); + } else if (NPVARIANT_IS_OBJECT(element)) { + + NPObject* first_element_obj = NPVARIANT_TO_OBJECT(element); + if (IcedTeaScriptableJavaPackageObject::is_valid_java_object(first_element_obj)) + { + std::string class_id = std::string(((IcedTeaScriptableJavaObject*) first_element_obj)->getClassID()); + type->append(class_id); + } else + { + type->append("jsobject"); + } + } else { + type->append("jsobject"); // Else it is a string + } +} + +void +createJavaObjectFromVariant(NPP instance, NPVariant variant, std::string* id) +{ + JavaResultData* java_result; + + std::string className; + std::string jsObjectClassID = std::string(); + std::string jsObjectConstructorID = std::string(); + + std::string stringArg = std::string(); + std::vector<std::string> args = std::vector<std::string>(); + + JavaRequestProcessor java_request = JavaRequestProcessor(); + bool alreadyCreated = false; + + if (NPVARIANT_IS_VOID(variant)) + { + PLUGIN_DEBUG("VOID %d\n", variant); + id->append("0"); + return; // no need to go further + } else if (NPVARIANT_IS_NULL(variant)) + { + PLUGIN_DEBUG("NULL\n", variant); + id->append("0"); + return; // no need to go further + } else if (NPVARIANT_IS_BOOLEAN(variant)) + { + className = "java.lang.Boolean"; + + if (NPVARIANT_TO_BOOLEAN(variant)) + stringArg = "true"; + else + stringArg = "false"; + + } else if (NPVARIANT_IS_INT32(variant)) + { + className = "java.lang.Integer"; + + char* valueStr = (char*) malloc(sizeof(char)*32); + sprintf(valueStr, "%d", NPVARIANT_TO_INT32(variant)); + stringArg += valueStr; + free(valueStr); + } else if (NPVARIANT_IS_DOUBLE(variant)) + { + className = "java.lang.Double"; + + char* valueStr = (char*) malloc(sizeof(char)*1024); + sprintf(valueStr, "%f", NPVARIANT_TO_DOUBLE(variant)); + stringArg += valueStr; + free(valueStr); + } else if (NPVARIANT_IS_STRING(variant)) + { + className = "java.lang.String"; +#if MOZILLA_VERSION_COLLAPSED < 1090200 + stringArg += NPVARIANT_TO_STRING(variant).utf8characters; +#else + stringArg += NPVARIANT_TO_STRING(variant).UTF8Characters; +#endif + } else if (NPVARIANT_IS_OBJECT(variant)) + { + + NPObject* obj = NPVARIANT_TO_OBJECT(variant); + if (IcedTeaScriptableJavaPackageObject::is_valid_java_object(obj)) + { + PLUGIN_DEBUG("NPObject is a Java object\n"); + alreadyCreated = true; + } else + { + PLUGIN_DEBUG("NPObject is not a Java object\n"); + NPIdentifier length_id = browser_functions.getstringidentifier("length"); + + // FIXME: We currently only handle <= 2 dim arrays. Do we really need more though? + + // Is it an array? + if (IcedTeaPluginUtilities::isObjectJSArray(instance, obj)) { + PLUGIN_DEBUG("NPObject is an array\n"); + + std::string array_id = std::string(); + std::string java_array_type = std::string(); + NPVariant length = NPVariant(); + browser_functions.getproperty(instance, obj, length_id, &length); + + std::string length_str = std::string(); + IcedTeaPluginUtilities::itoa(NPVARIANT_TO_INT32(length), &length_str); + + if (NPVARIANT_TO_INT32(length) > 0) + { + NPIdentifier id_0 = browser_functions.getintidentifier(0); + NPVariant first_element = NPVariant(); + browser_functions.getproperty(instance, obj, id_0, &first_element); + + // Check for multi-dim array + if (NPVARIANT_IS_OBJECT(first_element) && + IcedTeaPluginUtilities::isObjectJSArray(instance, NPVARIANT_TO_OBJECT(first_element))) { + + NPVariant first_nested_element = NPVariant(); + browser_functions.getproperty(instance, NPVARIANT_TO_OBJECT(first_element), id_0, &first_nested_element); + + getArrayTypeForJava(instance, first_nested_element, &java_array_type); + + length_str.append(" 0"); // secondary array is created on the fly + } else + { + getArrayTypeForJava(instance, first_element, &java_array_type); + } + } else + java_array_type.append("jsobject"); + + java_result = java_request.newArray(java_array_type, length_str); + + if (java_result->error_occurred) { + printf("Unable to create array\n"); + id->append("-1"); + return; + } + + id->append(*(java_result->return_string)); + + NPIdentifier index_id = NPIdentifier(); + for (int i=0; i < NPVARIANT_TO_INT32(length); i++) + { + NPVariant value = NPVariant(); + + index_id = browser_functions.getintidentifier(i); + browser_functions.getproperty(instance, obj, index_id, &value); + + std::string value_id = std::string(); + createJavaObjectFromVariant(instance, value, &value_id); + + if (value_id == "-1") { + printf("Unable to populate array\n"); + id->clear(); + id->append("-1"); + return; + } + + std::string value_str = std::string(); + IcedTeaPluginUtilities::itoa(i, &value_str); + java_result = java_request.setSlot(*id, value_str, value_id); + + } + + // Got here => no errors above. We're good to return! + return; + } else // Else it is not an array + { + + NPVariant* variant_copy = new NPVariant(); + OBJECT_TO_NPVARIANT(NPVARIANT_TO_OBJECT(variant), *variant_copy); + + className = "netscape.javascript.JSObject"; + IcedTeaPluginUtilities::JSIDToString(variant_copy, &stringArg); + browser_functions.retainobject(NPVARIANT_TO_OBJECT(variant)); + + std::string jsObjectClassID = std::string(); + std::string jsObjectConstructorID = std::string(); + std::vector<std::string> args = std::vector<std::string>(); + + java_result = java_request.findClass(0, "netscape.javascript.JSObject"); + + // the result we want is in result_string (assuming there was no error) + if (java_result->error_occurred) + { + printf("Unable to get JSObject class id\n"); + id->clear(); + id->append("-1"); + return; + } + + jsObjectClassID.append(*(java_result->return_string)); + args.push_back("J"); + + java_result = java_request.getMethodID(jsObjectClassID, + browser_functions.getstringidentifier("<init>"), + args); + + // the result we want is in result_string (assuming there was no error) + if (java_result->error_occurred) + { + printf("Unable to get JSObject constructor id\n"); + id->clear(); + id->append("-1"); + return; + } + + jsObjectConstructorID.append(*(java_result->return_string)); + + // We have the method id. Now create a new object. + + args.clear(); + args.push_back(stringArg); + java_result = java_request.newObjectWithConstructor("", + jsObjectClassID, + jsObjectConstructorID, + args); + + // Store the instance ID for future reference + IcedTeaPluginUtilities::storeInstanceID(variant_copy, instance); + + // the result we want is in result_string (assuming there was no error) + // the result we want is in result_string (assuming there was no error) + if (java_result->error_occurred) + { + printf("Unable to create JSObject\n"); + id->clear(); + id->append("-1"); + return; + } + + id->append(*(java_result->return_string)); + return; + } + } + } + + if (!alreadyCreated) { + java_result = java_request.findClass(0, className); + + // the result we want is in result_string (assuming there was no error) + if (java_result->error_occurred) { + printf("Unable to find classid for %s\n", className.c_str()); + id->append("-1"); + return; + } + + jsObjectClassID.append(*(java_result->return_string)); + + std::string stringClassName = "Ljava/lang/String;"; + args.push_back(stringClassName); + + java_result = java_request.getMethodID(jsObjectClassID, + browser_functions.getstringidentifier("<init>"), args); + + // the result we want is in result_string (assuming there was no error) + if (java_result->error_occurred) { + printf("Unable to find string constructor for %s\n", className.c_str()); + id->append("-1"); + return; + } + + jsObjectConstructorID.append(*(java_result->return_string)); + + // We have class id and constructor ID. So we know we can create the + // object.. now create the string that will be provided as the arg + java_result = java_request.newString(stringArg); + + if (java_result->error_occurred) { + printf("Unable to create requested object\n"); + id->append("-1"); + return; + } + + // Create the object + args.clear(); + std::string arg = std::string(); + arg.append(*(java_result->return_string)); + args.push_back(arg); + java_result = java_request.newObjectWithConstructor("[System]", jsObjectClassID, jsObjectConstructorID, args); + + if (java_result->error_occurred) { + printf("Unable to create requested object\n"); + id->append("-1"); + return; + } + + + id->append(*(java_result->return_string)); + + } else { + // Else already created + + std::string classId = std::string(((IcedTeaScriptableJavaObject*) NPVARIANT_TO_OBJECT(variant))->getClassID()); + std::string instanceId = std::string(((IcedTeaScriptableJavaObject*) NPVARIANT_TO_OBJECT(variant))->getInstanceID()); + + if (instanceId.length() == 0) + id->append(classId.c_str()); + else + id->append(instanceId.c_str()); + } + +} + +JavaResultData* +JavaRequestProcessor::callStaticMethod(std::string source, std::string classID, + std::string methodName, + std::vector<std::string> args) +{ + return call(source, true, classID, methodName, args); +} + +JavaResultData* +JavaRequestProcessor::callMethod(std::string source, + std::string objectID, std::string methodName, + std::vector<std::string> args) +{ + return call(source, false, objectID, methodName, args); +} + +JavaResultData* +JavaRequestProcessor::call(std::string source, + bool isStatic, std::string objectID, + std::string methodName, + std::vector<std::string> args) +{ + std::string message = std::string(); + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, source, &message); + + if (isStatic) + message += " CallStaticMethod "; + else + message += " CallMethod "; + + message += objectID; + message += " "; + message += methodName; + message += " "; + + for (int i=0; i < args.size(); i++) + { + message += args[i]; + message += " "; + } + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + return result; +} + +JavaResultData* +JavaRequestProcessor::getObjectClass(std::string objectID) +{ + JavaRequestProcessor* java_request; + std::string message = std::string(); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + message += " GetObjectClass "; + message += objectID; + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + return result; +} + +JavaResultData* +JavaRequestProcessor::newObject(std::string source, std::string classID, + std::vector<std::string> args) +{ + JavaRequestProcessor* java_request; + std::string message = std::string(); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, source, &message); + message += " NewObject "; + message += classID; + message += " "; + + for (int i=0; i < args.size(); i++) + { + message += args[i]; + message += " "; + } + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + return result; +} + +JavaResultData* +JavaRequestProcessor::newObjectWithConstructor(std::string source, std::string classID, + std::string methodID, + std::vector<std::string> args) +{ + JavaRequestProcessor* java_request; + std::string message = std::string(); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, source, &message); + message += " NewObjectWithConstructor "; + message += classID; + message += " "; + message += methodID; + message += " "; + + for (int i=0; i < args.size(); i++) + { + message += args[i]; + message += " "; + } + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + return result; +} + +JavaResultData* +JavaRequestProcessor::newString(std::string str) +{ + std::string utf_string = std::string(); + std::string message = std::string(); + + IcedTeaPluginUtilities::convertStringToUTF8(&str, &utf_string); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + message.append(" NewStringUTF "); + message.append(utf_string); + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + return result; +} + +JavaResultData* +JavaRequestProcessor::hasPackage(int plugin_instance_id, + std::string package_name) +{ + JavaResultData* java_result; + JavaRequestProcessor* java_request = new JavaRequestProcessor(); + std::string message = std::string(); + std::string plugin_instance_id_str = std::string(); + IcedTeaPluginUtilities::itoa(plugin_instance_id, &plugin_instance_id_str); + + java_result = java_request->newString(package_name); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + message.append(" HasPackage "); + message.append(plugin_instance_id_str); + message.append(" "); + message.append(java_result->return_string->c_str()); + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + delete java_request; + + return result; +} + +JavaResultData* +JavaRequestProcessor::hasMethod(std::string classID, std::string method_name) +{ + JavaResultData* java_result; + JavaRequestProcessor* java_request = new JavaRequestProcessor(); + std::string message = std::string(); + + java_result = java_request->newString(method_name); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + message.append(" HasMethod "); + message.append(classID); + message.append(" "); + message.append(java_result->return_string->c_str()); + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + delete java_request; + + return result; +} + +JavaResultData* +JavaRequestProcessor::hasField(std::string classID, std::string method_name) +{ + JavaResultData* java_result; + JavaRequestProcessor java_request = JavaRequestProcessor(); + std::string message = std::string(); + + java_result = java_request.newString(method_name); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + message.append(" HasField "); + message.append(classID); + message.append(" "); + message.append(java_result->return_string->c_str()); + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + return result; +} + +JavaResultData* +JavaRequestProcessor::isInstanceOf(std::string objectID, std::string classID) +{ + std::string message = std::string(); + + this->instance = 0; // context is always 0 (needed for java-side backwards compat.) + this->reference = IcedTeaPluginUtilities::getReference(); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &message); + message.append(" IsInstanceOf "); + message.append(objectID); + message.append(" "); + message.append(classID); + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + return result; +} + +JavaResultData* +JavaRequestProcessor::getAppletObjectInstance(std::string instanceID) +{ + std::string message = std::string(); + std::string ref_str = std::string(); + + this->instance = 0; + this->reference = IcedTeaPluginUtilities::getReference(); + IcedTeaPluginUtilities::itoa(reference, &ref_str); + + message = "instance "; + message += instanceID; + message += " reference "; + message += ref_str; + message += " GetJavaObject"; + + postAndWaitForResponse(message); + + IcedTeaPluginUtilities::releaseReference(); + + return result; +} + diff --git a/plugin/icedteanp/IcedTeaJavaRequestProcessor.h b/plugin/icedteanp/IcedTeaJavaRequestProcessor.h new file mode 100644 index 0000000..63223fd --- /dev/null +++ b/plugin/icedteanp/IcedTeaJavaRequestProcessor.h @@ -0,0 +1,233 @@ +/* IcedTeaJavaRequestProcessor.h + + Copyright (C) 2009, 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. */ + +#ifndef ICEDTEAJAVAREQUEST_H_ +#define ICEDTEAJAVAREQUEST_H_ + +#include <errno.h> +#include <stdlib.h> +#include <vector> + +#include "IcedTeaNPPlugin.h" +#include "IcedTeaPluginUtils.h" + +#define REQUESTTIMEOUT 180 + +/* + * This struct holds data specific to a Java operation requested by the plugin + */ +typedef struct java_request +{ + // Instance id (if applicable) + int instance; + + // Context id (if applicable) + int context; + + // request specific data + std::vector<std::string>* data; + + // source of the request + std::string* source; + +} JavaRequest; + +/* Creates a argument on java-side with appropriate type */ +void createJavaObjectFromVariant(NPP instance, NPVariant variant, std::string* id); + +/* Returns the type of array based on the given element */ +void getArrayTypeForJava(NPP instance, NPVariant element, std::string* type); + +class JavaRequestProcessor : BusSubscriber +{ + private: + // instance and references are constant throughout this objects + // lifecycle + int instance; + int reference; + bool result_ready; + JavaResultData* result; + + /* Post message on bus and wait */ + void postAndWaitForResponse(std::string message); + + // Call a method, static or otherwise, depending on supplied arg + JavaResultData* call(std::string source, bool isStatic, + std::string objectID, std::string methodName, + std::vector<std::string> args); + + // Set a static/non-static field to given value + JavaResultData* set(std::string source, + bool isStatic, + std::string classID, + std::string objectID, + std::string fieldName, + std::string value_id); + + /* Resets the results */ + void resetResult(); + + public: + JavaRequestProcessor(); + ~JavaRequestProcessor(); + virtual bool newMessageOnBus(const char* message); + + /* Increments reference count by 1 */ + void addReference(std::string object_id); + + /* Decrements reference count by 1 */ + void deleteReference(std::string object_id); + + /* Returns the toString() value, given an object identifier */ + JavaResultData* getToStringValue(std::string object_id); + + /* Returns the value, given an object identifier */ + JavaResultData* getValue(std::string object_id); + + /* Returns the string, given the identifier */ + JavaResultData* getString(std::string string_id); + + /* Returns the field object */ + JavaResultData* getField(std::string source, + std::string classID, + std::string objectID, + std::string fieldName); + + /* Returns the static field object */ + JavaResultData* getStaticField(std::string source, + std::string classID, + std::string fieldName); + + /* Sets the field object */ + JavaResultData* setField(std::string source, + std::string classID, + std::string objectID, + std::string fieldName, + std::string value_id); + + /* Sets the static field object */ + JavaResultData* setStaticField(std::string source, + std::string classID, + std::string fieldName, + std::string value_id); + + /* Returns the field id */ + JavaResultData* getFieldID(std::string classID, std::string fieldName); + + /* Returns the static field id */ + JavaResultData* getStaticFieldID(std::string classID, std::string fieldName); + + /* Returns the method id */ + JavaResultData* getMethodID(std::string classID, NPIdentifier methodName, + std::vector<std::string> args); + + /* Returns the static method id */ + JavaResultData* getStaticMethodID(std::string classID, NPIdentifier methodName, + std::vector<std::string> args); + + /* Calls a static method */ + JavaResultData* callStaticMethod(std::string source, + std::string classID, + std::string methodName, + std::vector<std::string> args); + + /* Calls a method on an instance */ + JavaResultData* callMethod(std::string source, + std::string objectID, + std::string methodName, + std::vector<std::string> args); + + /* Returns the class of the given object */ + JavaResultData* getObjectClass(std::string objectID); + + /* Creates a new object with choosable constructor */ + JavaResultData* newObject(std::string source, + std::string classID, + std::vector<std::string> args); + + /* Creates a new object when constructor is undetermined */ + JavaResultData* newObjectWithConstructor(std::string source, std::string classID, + std::string methodID, + std::vector<std::string> args); + + /* Returns the class ID */ + JavaResultData* findClass(int plugin_instance_id, + std::string name); + + /* Returns the type class name */ + JavaResultData* getClassName(std::string objectID); + + /* Returns the type class id */ + JavaResultData* getClassID(std::string objectID); + + /* Returns the length of the array object. -1 if not found */ + JavaResultData* getArrayLength(std::string objectID); + + /* Returns the item at the given index for the array */ + JavaResultData* getSlot(std::string objectID, std::string index); + + /* Sets the item at the given index to the given value */ + JavaResultData* setSlot(std::string objectID, + std::string index, + std::string value_id); + + /* Creates a new array of given length */ + JavaResultData* newArray(std::string component_class, + std::string length); + + /* Creates a new string in the Java store */ + JavaResultData* newString(std::string str); + + /* Check if package exists */ + JavaResultData* hasPackage(int plugin_instance_id, + std::string package_name); + + /* Check if method exists */ + JavaResultData* hasMethod(std::string classID, std::string method_name); + + /* Check if field exists */ + JavaResultData* hasField(std::string classID, std::string method_name); + + /* Check if given object is instance of given class */ + JavaResultData* isInstanceOf(std::string objectID, std::string classID); + + /* Returns the instance ID of the java applet */ + JavaResultData* getAppletObjectInstance(std::string instanceID); +}; + +#endif /* ICEDTEAJAVAREQUESTPROCESSOR_H_ */ diff --git a/plugin/icedteanp/IcedTeaNPPlugin.cc b/plugin/icedteanp/IcedTeaNPPlugin.cc new file mode 100644 index 0000000..ac40103 --- /dev/null +++ b/plugin/icedteanp/IcedTeaNPPlugin.cc @@ -0,0 +1,2453 @@ +/* IcedTeaNPPlugin.cc -- web browser plugin to execute Java applets + Copyright (C) 2003, 2004, 2006, 2007 Free Software Foundation, Inc. + Copyright (C) 2009, 2010 Red Hat + +This file is part of GNU Classpath. + +GNU Classpath 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. + +GNU Classpath 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 GNU Classpath; 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. */ + +// System includes. +#include <dlfcn.h> +#include <errno.h> +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +// Liveconnect extension +#include "IcedTeaScriptablePluginObject.h" +#include "IcedTeaNPPlugin.h" + +#if MOZILLA_VERSION_COLLAPSED < 1090100 +// Documentbase retrieval includes. +#include <nsIPluginInstance.h> +#include <nsIPluginInstancePeer.h> +#include <nsIPluginTagInfo2.h> + +// API's into Mozilla +#include <nsCOMPtr.h> +#include <nsICookieService.h> +#include <nsIDNSRecord.h> +#include <nsIDNSService.h> +#include <nsINetUtil.h> +#include <nsIProxyInfo.h> +#include <nsIProtocolProxyService.h> +#include <nsIScriptSecurityManager.h> +#include <nsIIOService.h> +#include <nsIURI.h> +#include <nsNetCID.h> +#include <nsStringAPI.h> +#include <nsServiceManagerUtils.h> +#endif + +// Error reporting macros. +#define PLUGIN_ERROR(message) \ + g_printerr ("%s:%d: thread %p: Error: %s\n", __FILE__, __LINE__, \ + g_thread_self (), message) + +#define PLUGIN_ERROR_TWO(first, second) \ + g_printerr ("%s:%d: thread %p: Error: %s: %s\n", __FILE__, __LINE__, \ + g_thread_self (), first, second) + +#define PLUGIN_ERROR_THREE(first, second, third) \ + g_printerr ("%s:%d: thread %p: Error: %s: %s: %s\n", __FILE__, \ + __LINE__, g_thread_self (), first, second, third) + +// Plugin information passed to about:plugins. +#define PLUGIN_NAME "IcedTea NPR Web Browser Plugin (using " PLUGIN_VERSION ")" +#define PLUGIN_DESC "The " PLUGIN_NAME " executes Java applets." + +#define PLUGIN_MIME_DESC \ + "application/x-java-vm:class,jar:IcedTea;" \ + "application/x-java-applet:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.1:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.1.1:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.1.2:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.1.3:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.2:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.2.1:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.2.2:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.3:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.3.1:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.4:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.4.1:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.4.2:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.5:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.6:class,jar:IcedTea;" \ + "application/x-java-applet;jpi-version=1.6.0_" JDK_UPDATE_VERSION ":class,jar:IcedTea;" \ + "application/x-java-bean:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.1:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.1.1:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.1.2:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.1.3:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.2:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.2.1:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.2.2:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.3:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.3.1:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.4:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.4.1:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.4.2:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.5:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.6:class,jar:IcedTea;" \ + "application/x-java-bean;jpi-version=1.6.0_" JDK_UPDATE_VERSION ":class,jar:IcedTea;" \ + "application/x-java-vm-npruntime::IcedTea;" + +#define PLUGIN_URL NS_INLINE_PLUGIN_CONTRACTID_PREFIX NS_JVM_MIME_TYPE +#define PLUGIN_MIME_TYPE "application/x-java-vm" +#define PLUGIN_FILE_EXTS "class,jar,zip" +#define PLUGIN_MIME_COUNT 1 + +#define FAILURE_MESSAGE "icedteanp plugin error: Failed to run %s." \ + " For more detail rerun \"firefox -g\" in a terminal window." + +#if MOZILLA_VERSION_COLLAPSED < 1090100 +// Documentbase retrieval required definition. +static NS_DEFINE_IID (kIPluginTagInfo2IID, NS_IPLUGINTAGINFO2_IID); +#endif + +// Data directory for plugin. +static gchar* data_directory = NULL; + +// Fully-qualified appletviewer executable. +static gchar* appletviewer_executable = NULL; + +// Applet viewer input channel (needs to be static because it is used in plugin_in_pipe_callback) +static GIOChannel* in_from_appletviewer = NULL; + +// Applet viewer input pipe name. +gchar* in_pipe_name; + +// Applet viewer input watch source. +gint in_watch_source; + +// Applet viewer output pipe name. +gchar* out_pipe_name; + +// Applet viewer output watch source. +gint out_watch_source; + +// Applet viewer output channel. +GIOChannel* out_to_appletviewer; + +// Tracks jvm status +gboolean jvm_up = FALSE; + +// Keeps track of initialization. NP_Initialize should only be +// called once. +gboolean initialized = false; + +// browser functions into mozilla +NPNetscapeFuncs browser_functions; + +// Various message buses carrying information to/from Java, and internally +MessageBus* plugin_to_java_bus; +MessageBus* java_to_plugin_bus; +//MessageBus* internal_bus = new MessageBus(); + +// Processor for plugin requests +PluginRequestProcessor* plugin_req_proc; + +// Sends messages to Java over the bus +JavaMessageSender* java_req_proc; + +#if MOZILLA_VERSION_COLLAPSED < 1090100 +// Documentbase retrieval type-punning union. +typedef union +{ + void** void_field; + nsIPluginTagInfo2** info_field; +} info_union; +#endif + +// Static instance helper functions. +// Have the browser allocate a new ITNPPluginData structure. +static void plugin_data_new (ITNPPluginData** data); +// Retrieve the current document's documentbase. +static gchar* plugin_get_documentbase (NPP instance); +// Notify the user that the appletviewer is not installed correctly. +static void plugin_display_failure_dialog (); +// Callback used to monitor input pipe status. +static gboolean plugin_in_pipe_callback (GIOChannel* source, + GIOCondition condition, + gpointer plugin_data); +// Callback used to monitor output pipe status. +static gboolean plugin_out_pipe_callback (GIOChannel* source, + GIOCondition condition, + gpointer plugin_data); +static NPError plugin_start_appletviewer (ITNPPluginData* data); +static gchar* plugin_create_applet_tag (int16_t argc, char* argn[], + char* argv[]); +static void plugin_stop_appletviewer (); +// Uninitialize ITNPPluginData structure +static void plugin_data_destroy (NPP instance); + +NPError get_cookie_info(const char* siteAddr, char** cookieString, uint32_t* len); +NPError get_proxy_info(const char* siteAddr, char** proxy, uint32_t* len); +void consume_message(gchar* message); +void start_jvm_if_needed(); +static void appletviewer_monitor(GPid pid, gint status, gpointer data); + +// Global instance counter. +// Mutex to protect plugin_instance_counter. +static GMutex* plugin_instance_mutex = NULL; +// A global variable for reporting GLib errors. This must be free'd +// and set to NULL after each use. +static GError* channel_error = NULL; + +static GHashTable* instance_to_id_map = g_hash_table_new(NULL, NULL); +static GHashTable* id_to_instance_map = g_hash_table_new(NULL, NULL); +static gint instance_counter = 1; +static GPid appletviewer_pid = -1; +static guint appletviewer_watch_id = -1; + +int plugin_debug = getenv ("ICEDTEAPLUGIN_DEBUG") != NULL; + +pthread_cond_t cond_message_available = PTHREAD_COND_INITIALIZER; + +// Functions prefixed by ITNP_ are instance functions. They are called +// by the browser and operate on instances of ITNPPluginData. +// Functions prefixed by plugin_ are static helper functions. +// Functions prefixed by NP_ are factory functions. They are called +// by the browser and provide functionality needed to create plugin +// instances. + +// INSTANCE FUNCTIONS + +// Creates a new icedtea np plugin instance. This function creates a +// ITNPPluginData* and stores it in instance->pdata. The following +// ITNPPluginData fiels are initialized: instance_id, in_pipe_name, +// in_from_appletviewer, in_watch_source, out_pipe_name, +// out_to_appletviewer, out_watch_source, appletviewer_mutex, owner, +// appletviewer_alive. In addition two pipe files are created. All +// of those fields must be properly destroyed, and the pipes deleted, +// by ITNP_Destroy. If an error occurs during initialization then this +// function will free anything that's been allocated so far, set +// instance->pdata to NULL and return an error code. +NPError +ITNP_New (NPMIMEType pluginType, NPP instance, uint16_t mode, + int16_t argc, char* argn[], char* argv[], + NPSavedData* saved) +{ + PLUGIN_DEBUG("ITNP_New\n"); + + static NPObject *window_ptr; + NPIdentifier identifier; + NPVariant member_ptr; + browser_functions.getvalue(instance, NPNVWindowNPObject, &window_ptr); + identifier = browser_functions.getstringidentifier("document"); + if (!browser_functions.hasproperty(instance, window_ptr, identifier)) + { + printf("%s not found!\n", "document"); + } + browser_functions.getproperty(instance, window_ptr, identifier, &member_ptr); + + PLUGIN_DEBUG("Got variant %p\n", &member_ptr); + + + NPError np_error = NPERR_NO_ERROR; + ITNPPluginData* data = NULL; + + gchar* documentbase = NULL; + gchar* read_message = NULL; + gchar* applet_tag = NULL; + gchar* cookie_info = NULL; + + NPObject* npPluginObj = NULL; + + if (!instance) + { + PLUGIN_ERROR ("Browser-provided instance pointer is NULL."); + np_error = NPERR_INVALID_INSTANCE_ERROR; + goto cleanup_done; + } + + // data + plugin_data_new (&data); + if (data == NULL) + { + PLUGIN_ERROR ("Failed to allocate plugin data."); + np_error = NPERR_OUT_OF_MEMORY_ERROR; + goto cleanup_done; + } + + // start the jvm if needed + start_jvm_if_needed(); + + // Initialize data->instance_id. + // + // instance_id should be unique for this process so we use a + // combination of getpid and plugin_instance_counter. + // + // Critical region. Reference and increment plugin_instance_counter + // global. + g_mutex_lock (plugin_instance_mutex); + + // data->instance_id + data->instance_id = g_strdup_printf ("%d", + instance_counter); + + g_mutex_unlock (plugin_instance_mutex); + + // data->appletviewer_mutex + data->appletviewer_mutex = g_mutex_new (); + + g_mutex_lock (data->appletviewer_mutex); + + // Documentbase retrieval. + documentbase = plugin_get_documentbase (instance); + if (documentbase && argc != 0) + { + // Send applet tag message to appletviewer. + applet_tag = plugin_create_applet_tag (argc, argn, argv); + + data->applet_tag = (gchar*) malloc(strlen(applet_tag)*sizeof(gchar) + strlen(documentbase)*sizeof(gchar) + 32); + g_sprintf(data->applet_tag, "tag %s %s", documentbase, applet_tag); + + data->is_applet_instance = true; + } + + if (argc == 0) + { + data->is_applet_instance = false; + } + + g_mutex_unlock (data->appletviewer_mutex); + + // If initialization succeeded entirely then we store the plugin + // data in the instance structure and return. Otherwise we free the + // data we've allocated so far and set instance->pdata to NULL. + + // Set back-pointer to owner instance. + data->owner = instance; + + // source of this instance + // don't use documentbase, it is cleared later + data->source = plugin_get_documentbase(instance); + + instance->pdata = data; + + goto cleanup_done; + + cleanup_appletviewer_mutex: + g_free (data->appletviewer_mutex); + data->appletviewer_mutex = NULL; + + // cleanup_instance_string: + g_free (data->instance_id); + data->instance_id = NULL; + + // cleanup applet tag: + g_free (data->applet_tag); + data->applet_tag = NULL; + + // cleanup_data: + // Eliminate back-pointer to plugin instance. + data->owner = NULL; + (*browser_functions.memfree) (data); + data = NULL; + + // Initialization failed so return a NULL pointer for the browser + // data. + instance->pdata = NULL; + + cleanup_done: + g_free (applet_tag); + applet_tag = NULL; + g_free (read_message); + read_message = NULL; + g_free (documentbase); + documentbase = NULL; + + // store an identifier for this plugin + PLUGIN_DEBUG("Mapping id %d and instance %p\n", instance_counter, instance); + g_hash_table_insert(instance_to_id_map, instance, GINT_TO_POINTER(instance_counter)); + g_hash_table_insert(id_to_instance_map, GINT_TO_POINTER(instance_counter), instance); + instance_counter++; + + PLUGIN_DEBUG ("ITNP_New return\n"); + + return np_error; +} + +// Starts the JVM if it is not already running +void start_jvm_if_needed() +{ + + // This is asynchronized function. It must + // have exclusivity when running. + + GMutex *vm_start_mutex = g_mutex_new(); + g_mutex_lock(vm_start_mutex); + + PLUGIN_DEBUG("Checking JVM status...\n"); + + // If the jvm is already up, do nothing + if (jvm_up) + { + PLUGIN_DEBUG("JVM is up. Returning.\n"); + return; + } + + PLUGIN_DEBUG("No JVM is running. Attempting to start one...\n"); + + NPError np_error = NPERR_NO_ERROR; + ITNPPluginData* data = NULL; + + // Create appletviewer-to-plugin pipe which we refer to as the input + // pipe. + + // in_pipe_name + in_pipe_name = g_strdup_printf ("%s/%d-icedteanp-appletviewer-to-plugin", + data_directory, getpid()); + if (!in_pipe_name) + { + PLUGIN_ERROR ("Failed to create input pipe name."); + np_error = NPERR_OUT_OF_MEMORY_ERROR; + // If in_pipe_name is NULL then the g_free at + // cleanup_in_pipe_name will simply return. + goto cleanup_in_pipe_name; + } + + // clean up any older pip + unlink (in_pipe_name); + + PLUGIN_DEBUG ("ITNP_New: creating input fifo: %s\n", in_pipe_name); + if (mkfifo (in_pipe_name, 0600) == -1 && errno != EEXIST) + { + PLUGIN_ERROR_TWO ("Failed to create input pipe", strerror (errno)); + np_error = NPERR_GENERIC_ERROR; + goto cleanup_in_pipe_name; + } + PLUGIN_DEBUG ("ITNP_New: created input fifo: %s\n", in_pipe_name); + + // Create plugin-to-appletviewer pipe which we refer to as the + // output pipe. + + // out_pipe_name + out_pipe_name = g_strdup_printf ("%s/%d-icedteanp-plugin-to-appletviewer", + data_directory, getpid()); + + if (!out_pipe_name) + { + PLUGIN_ERROR ("Failed to create output pipe name."); + np_error = NPERR_OUT_OF_MEMORY_ERROR; + goto cleanup_out_pipe_name; + } + + // clean up any older pip + unlink (out_pipe_name); + + PLUGIN_DEBUG ("ITNP_New: creating output fifo: %s\n", out_pipe_name); + if (mkfifo (out_pipe_name, 0600) == -1 && errno != EEXIST) + { + PLUGIN_ERROR_TWO ("Failed to create output pipe", strerror (errno)); + np_error = NPERR_GENERIC_ERROR; + goto cleanup_out_pipe_name; + } + PLUGIN_DEBUG ("ITNP_New: created output fifo: %s\n", out_pipe_name); + + // Start a separate appletviewer process for each applet, even if + // there are multiple applets in the same page. We may need to + // change this behaviour if we find pages with multiple applets that + // rely on being run in the same VM. + + np_error = plugin_start_appletviewer (data); + + // Create plugin-to-appletviewer channel. The default encoding for + // the file is UTF-8. + // out_to_appletviewer + out_to_appletviewer = g_io_channel_new_file (out_pipe_name, + "w", &channel_error); + if (!out_to_appletviewer) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to create output channel", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to create output channel"); + + np_error = NPERR_GENERIC_ERROR; + goto cleanup_out_to_appletviewer; + } + + // Watch for hangup and error signals on the output pipe. + out_watch_source = + g_io_add_watch (out_to_appletviewer, + (GIOCondition) (G_IO_ERR | G_IO_HUP), + plugin_out_pipe_callback, (gpointer) out_to_appletviewer); + + // Create appletviewer-to-plugin channel. The default encoding for + // the file is UTF-8. + // in_from_appletviewer + in_from_appletviewer = g_io_channel_new_file (in_pipe_name, + "r", &channel_error); + if (!in_from_appletviewer) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to create input channel", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to create input channel"); + + np_error = NPERR_GENERIC_ERROR; + goto cleanup_in_from_appletviewer; + } + + // Watch for hangup and error signals on the input pipe. + in_watch_source = + g_io_add_watch (in_from_appletviewer, + (GIOCondition) (G_IO_IN | G_IO_ERR | G_IO_HUP), + plugin_in_pipe_callback, (gpointer) in_from_appletviewer); + + jvm_up = TRUE; + + goto done; + + // Free allocated data + + cleanup_in_watch_source: + // Removing a source is harmless if it fails since it just means the + // source has already been removed. + g_source_remove (in_watch_source); + in_watch_source = 0; + + cleanup_in_from_appletviewer: + if (in_from_appletviewer) + g_io_channel_unref (in_from_appletviewer); + in_from_appletviewer = NULL; + + // cleanup_out_watch_source: + g_source_remove (out_watch_source); + out_watch_source = 0; + + cleanup_out_to_appletviewer: + if (out_to_appletviewer) + g_io_channel_unref (out_to_appletviewer); + out_to_appletviewer = NULL; + + // cleanup_out_pipe: + // Delete output pipe. + PLUGIN_DEBUG ("ITNP_New: deleting input fifo: %s\n", in_pipe_name); + unlink (out_pipe_name); + PLUGIN_DEBUG ("ITNP_New: deleted input fifo: %s\n", in_pipe_name); + + cleanup_out_pipe_name: + g_free (out_pipe_name); + out_pipe_name = NULL; + + // cleanup_in_pipe: + // Delete input pipe. + PLUGIN_DEBUG ("ITNP_New: deleting output fifo: %s\n", out_pipe_name); + unlink (in_pipe_name); + PLUGIN_DEBUG ("ITNP_New: deleted output fifo: %s\n", out_pipe_name); + + cleanup_in_pipe_name: + g_free (in_pipe_name); + in_pipe_name = NULL; + + done: + + // Now other threads may re-enter.. unlock the mutex + g_mutex_unlock(vm_start_mutex); + +} + +NPError +ITNP_GetValue (NPP instance, NPPVariable variable, void* value) +{ + PLUGIN_DEBUG ("ITNP_GetValue\n"); + + NPError np_error = NPERR_NO_ERROR; + + switch (variable) + { + // This plugin needs XEmbed support. + case NPPVpluginNeedsXEmbed: + { + PLUGIN_DEBUG ("ITNP_GetValue: returning TRUE for NeedsXEmbed.\n"); + bool* bool_value = (bool*) value; + *bool_value = true; + } + break; + case NPPVpluginScriptableNPObject: + { + *(NPObject **)value = get_scriptable_object(instance); + } + break; + default: + PLUGIN_ERROR ("Unknown plugin value requested."); + np_error = NPERR_GENERIC_ERROR; + break; + } + + PLUGIN_DEBUG ("ITNP_GetValue return\n"); + + return np_error; +} + +NPError +ITNP_Destroy (NPP instance, NPSavedData** save) +{ + PLUGIN_DEBUG ("ITNP_Destroy %p\n", instance); + + ITNPPluginData* data = (ITNPPluginData*) instance->pdata; + + int id = get_id_from_instance(instance); + + // Let Java know that this applet needs to be destroyed + gchar* msg = (gchar*) g_malloc(512*sizeof(gchar)); // 512 is more than enough. We need < 100 + g_sprintf(msg, "instance %d destroy", id); + plugin_send_message_to_appletviewer(msg); + g_free(msg); + msg = NULL; + + if (data) + { + // Free plugin data. + plugin_data_destroy (instance); + } + + g_hash_table_remove(instance_to_id_map, instance); + g_hash_table_remove(id_to_instance_map, GINT_TO_POINTER(id)); + + IcedTeaPluginUtilities::invalidateInstance(instance); + + PLUGIN_DEBUG ("ITNP_Destroy return\n"); + + return NPERR_NO_ERROR; +} + +NPError +ITNP_SetWindow (NPP instance, NPWindow* window) +{ + PLUGIN_DEBUG ("ITNP_SetWindow\n"); + + if (instance == NULL) + { + PLUGIN_ERROR ("Invalid instance."); + + return NPERR_INVALID_INSTANCE_ERROR; + } + + gpointer id_ptr = g_hash_table_lookup(instance_to_id_map, instance); + + gint id = 0; + if (id_ptr) + { + id = GPOINTER_TO_INT(id_ptr); + } + + ITNPPluginData* data = (ITNPPluginData*) instance->pdata; + + // Simply return if we receive a NULL window. + if ((window == NULL) || (window->window == NULL)) + { + PLUGIN_DEBUG ("ITNP_SetWindow: got NULL window.\n"); + + return NPERR_NO_ERROR; + } + + if (data->window_handle) + { + // The window already exists. + if (data->window_handle == window->window) + { + // The parent window is the same as in previous calls. + PLUGIN_DEBUG ("ITNP_SetWindow: window already exists.\n"); + + // Critical region. Read data->appletviewer_mutex and send + // a message to the appletviewer. + g_mutex_lock (data->appletviewer_mutex); + + if (jvm_up) + { + gboolean dim_changed = FALSE; + + // The window is the same as it was for the last + // SetWindow call. + if (window->width != data->window_width) + { + PLUGIN_DEBUG ("ITNP_SetWindow: window width changed.\n"); + // The width of the plugin window has changed. + + // Store the new width. + data->window_width = window->width; + dim_changed = TRUE; + } + + if (window->height != data->window_height) + { + PLUGIN_DEBUG ("ITNP_SetWindow: window height changed.\n"); + // The height of the plugin window has changed. + + // Store the new height. + data->window_height = window->height; + + dim_changed = TRUE; + } + + if (dim_changed) { + gchar* message = g_strdup_printf ("instance %d width %d height %d", + id, window->width, window->height); + plugin_send_message_to_appletviewer (message); + g_free (message); + message = NULL; + } + + + } + else + { + // The appletviewer is not running. + PLUGIN_DEBUG ("ITNP_SetWindow: appletviewer is not running.\n"); + } + + g_mutex_unlock (data->appletviewer_mutex); + } + else + { + // The parent window has changed. This branch does run but + // doing nothing in response seems to be sufficient. + PLUGIN_DEBUG ("ITNP_SetWindow: parent window changed.\n"); + } + } + else + { + // Else this is initialization + PLUGIN_DEBUG ("ITNP_SetWindow: setting window.\n"); + + // Critical region. Send messages to appletviewer. + g_mutex_lock (data->appletviewer_mutex); + + // Store the window handle and dimensions + data->window_handle = window->window; + data->window_width = window->width; + data->window_height = window->height; + + // Now we have everything. Send this data to the Java side + + gchar* instance_msg = g_strdup_printf ("instance %s handle %ld width %d height %d %s", + data->instance_id, + (gulong) data->window_handle, + data->window_width, + data->window_height, + data->applet_tag); + + plugin_send_message_to_appletviewer (instance_msg); + + g_free(instance_msg); + instance_msg = NULL; + + g_mutex_unlock (data->appletviewer_mutex); + + } + + PLUGIN_DEBUG ("ITNP_SetWindow return\n"); + + return NPERR_NO_ERROR; +} + +NPError +ITNP_NewStream (NPP instance, NPMIMEType type, NPStream* stream, + NPBool seekable, uint16_t* stype) +{ + PLUGIN_DEBUG ("ITNP_NewStream\n"); + + PLUGIN_DEBUG ("ITNP_NewStream return\n"); + + return NPERR_GENERIC_ERROR; +} + +void +ITNP_StreamAsFile (NPP instance, NPStream* stream, const char* filename) +{ + PLUGIN_DEBUG ("ITNP_StreamAsFile\n"); + + PLUGIN_DEBUG ("ITNP_StreamAsFile return\n"); +} + +NPError +ITNP_DestroyStream (NPP instance, NPStream* stream, NPReason reason) +{ + PLUGIN_DEBUG ("ITNP_DestroyStream\n"); + + PLUGIN_DEBUG ("ITNP_DestroyStream return\n"); + + return NPERR_NO_ERROR; +} + +int32_t +ITNP_WriteReady (NPP instance, NPStream* stream) +{ + PLUGIN_DEBUG ("ITNP_WriteReady\n"); + + PLUGIN_DEBUG ("ITNP_WriteReady return\n"); + + return 0; +} + +int32_t +ITNP_Write (NPP instance, NPStream* stream, int32_t offset, int32_t len, + void* buffer) +{ + PLUGIN_DEBUG ("ITNP_Write\n"); + + PLUGIN_DEBUG ("ITNP_Write return\n"); + + return 0; +} + +void +ITNP_Print (NPP instance, NPPrint* platformPrint) +{ + PLUGIN_DEBUG ("ITNP_Print\n"); + + PLUGIN_DEBUG ("ITNP_Print return\n"); +} + +int16_t +ITNP_HandleEvent (NPP instance, void* event) +{ + PLUGIN_DEBUG ("ITNP_HandleEvent\n"); + + PLUGIN_DEBUG ("ITNP_HandleEvent return\n"); + + return 0; +} + +void +ITNP_URLNotify (NPP instance, const char* url, NPReason reason, + void* notifyData) +{ + PLUGIN_DEBUG ("ITNP_URLNotify\n"); + + PLUGIN_DEBUG ("ITNP_URLNotify return\n"); +} + +NPError +get_cookie_info(const char* siteAddr, char** cookieString, uint32_t* len) +{ +#if MOZILLA_VERSION_COLLAPSED < 1090100 + nsresult rv; + nsCOMPtr<nsIScriptSecurityManager> sec_man = + do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); + + if (!sec_man) { + return NPERR_GENERIC_ERROR; + } + + nsCOMPtr<nsIIOService> io_svc = do_GetService("@mozilla.org/network/io-service;1", &rv); + + if (NS_FAILED(rv) || !io_svc) { + return NPERR_GENERIC_ERROR; + } + + nsCOMPtr<nsIURI> uri; + io_svc->NewURI(nsCString(siteAddr), NULL, NULL, getter_AddRefs(uri)); + + nsCOMPtr<nsICookieService> cookie_svc = do_GetService("@mozilla.org/cookieService;1", &rv); + + if (NS_FAILED(rv) || !cookie_svc) { + return NPERR_GENERIC_ERROR; + } + + rv = cookie_svc->GetCookieString(uri, NULL, cookieString); + + if (NS_FAILED(rv) || !*cookieString) { + return NPERR_GENERIC_ERROR; + } + +#else + + // getvalueforurl needs an NPP instance. Quite frankly, there is no easy way + // to know which instance needs the information, as applets on Java side can + // be multi-threaded and the thread making a proxy.cookie request cannot be + // easily tracked. + + // Fortunately, XULRunner does not care about the instance as long as it is + // valid. So we just pick the first valid one and use it. Proxy/Cookie + // information is not instance specific anyway, it is URL specific. + + if (browser_functions.getvalueforurl) + { + GHashTableIter iter; + gpointer id, instance; + + g_hash_table_iter_init (&iter, instance_to_id_map); + g_hash_table_iter_next (&iter, &instance, &id); + + return browser_functions.getvalueforurl((NPP) instance, NPNURLVCookie, siteAddr, cookieString, len); + } else + { + return NPERR_GENERIC_ERROR; + } + +#endif + + return NPERR_NO_ERROR; +} + +// HELPER FUNCTIONS + +static void +plugin_data_new (ITNPPluginData** data) +{ + PLUGIN_DEBUG ("plugin_data_new\n"); + + *data = (ITNPPluginData*) + (*browser_functions.memalloc) (sizeof (struct ITNPPluginData)); + + // appletviewer_alive is false until the applet viewer is spawned. + if (*data) + memset (*data, 0, sizeof (struct ITNPPluginData)); + + PLUGIN_DEBUG ("plugin_data_new return\n"); +} + + + +// Documentbase retrieval. This function gets the current document's +// documentbase. This function relies on browser-private data so it +// will only work when the plugin is loaded in a Mozilla-based +// browser. We could not find a way to retrieve the documentbase +// using the original Netscape plugin API so we use the XPCOM API +// instead. +#if MOZILLA_VERSION_COLLAPSED < 1090100 +static gchar* +plugin_get_documentbase (NPP instance) +{ + PLUGIN_DEBUG ("plugin_get_documentbase\n"); + + nsIPluginInstance* xpcom_instance = NULL; + nsIPluginInstancePeer* peer = NULL; + nsresult result = 0; + nsIPluginTagInfo2* pluginTagInfo2 = NULL; + info_union u = { NULL }; + char const* documentbase = NULL; + gchar* documentbase_copy = NULL; + + xpcom_instance = (nsIPluginInstance*) (instance->ndata); + if (!xpcom_instance) + { + PLUGIN_ERROR ("xpcom_instance is NULL."); + goto cleanup_done; + } + + xpcom_instance->GetPeer (&peer); + if (!peer) + { + PLUGIN_ERROR ("peer is NULL."); + goto cleanup_done; + } + + u.info_field = &pluginTagInfo2; + + result = peer->QueryInterface (kIPluginTagInfo2IID, + u.void_field); + if (result || !pluginTagInfo2) + { + PLUGIN_ERROR ("pluginTagInfo2 retrieval failed."); + goto cleanup_peer; + } + + pluginTagInfo2->GetDocumentBase (&documentbase); + + if (!documentbase) + { + // NULL => dummy instantiation for LiveConnect + goto cleanup_plugintaginfo2; + } + + documentbase_copy = g_strdup (documentbase); + + // Release references. + cleanup_plugintaginfo2: + NS_RELEASE (pluginTagInfo2); + + cleanup_peer: + NS_RELEASE (peer); + + cleanup_done: + PLUGIN_DEBUG ("plugin_get_documentbase return\n"); + + PLUGIN_DEBUG("plugin_get_documentbase returning: %s\n", documentbase_copy); + return documentbase_copy; +} +#else +static gchar* +plugin_get_documentbase (NPP instance) +{ + PLUGIN_DEBUG ("plugin_get_documentbase\n"); + + char const* documentbase = NULL; + gchar* documentbase_copy = NULL; + + // FIXME: This method is not ideal, but there are no known NPAPI call + // for this. See thread for more information: + // http://www.mail-archive.com/[email protected]/msg04844.html + + // Additionally, since it is insecure, we cannot use it for making + // security decisions. + NPObject* window; + browser_functions.getvalue(instance, NPNVWindowNPObject, &window); + + NPVariant location; + NPIdentifier location_id = browser_functions.getstringidentifier("location"); + browser_functions.getproperty(instance, window, location_id, &location); + + NPVariant href; + NPIdentifier href_id = browser_functions.getstringidentifier("href"); + browser_functions.getproperty(instance, NPVARIANT_TO_OBJECT(location), + href_id, &href); + + // Strip everything after the last "/" +#if MOZILLA_VERSION_COLLAPSED < 1090200 + gchar** parts = g_strsplit (NPVARIANT_TO_STRING(href).utf8characters, "/", -1); +#else + gchar** parts = g_strsplit (NPVARIANT_TO_STRING(href).UTF8Characters, "/", -1); +#endif + guint parts_sz = g_strv_length (parts); + + std::string location_str; + for (int i=0; i < parts_sz - 1; i++) + { + location_str += parts[i]; + location_str += "/"; + } + + documentbase_copy = g_strdup (location_str.c_str()); + + // Release references. + browser_functions.releasevariantvalue(&href); + browser_functions.releasevariantvalue(&location); + cleanup_done: + PLUGIN_DEBUG ("plugin_get_documentbase return\n"); + PLUGIN_DEBUG("plugin_get_documentbase returning: %s\n", documentbase_copy); + + return documentbase_copy; +} +#endif + +// This function displays an error message if the appletviewer has not +// been installed correctly. +static void +plugin_display_failure_dialog () +{ + GtkWidget* dialog = NULL; + + PLUGIN_DEBUG ("plugin_display_failure_dialog\n"); + + dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + FAILURE_MESSAGE, + appletviewer_executable); + gtk_widget_show_all (dialog); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + PLUGIN_DEBUG ("plugin_display_failure_dialog return\n"); +} + + + +// plugin_in_pipe_callback is called when data is available on the +// input pipe, or when the appletviewer crashes or is killed. It may +// be called after data has been destroyed in which case it simply +// returns FALSE to remove itself from the glib main loop. +static gboolean +plugin_in_pipe_callback (GIOChannel* source, + GIOCondition condition, + gpointer plugin_data) +{ + PLUGIN_DEBUG ("plugin_in_pipe_callback\n"); + + gboolean keep_installed = TRUE; + + if (condition & G_IO_IN) + { + gchar* message = NULL; + + if (g_io_channel_read_line (in_from_appletviewer, + &message, NULL, NULL, + &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to read line from input channel", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to read line from input channel"); + } else + { + consume_message(message); + } + + g_free (message); + message = NULL; + + keep_installed = TRUE; + } + + if (condition & (G_IO_ERR | G_IO_HUP)) + { + PLUGIN_DEBUG ("appletviewer has stopped.\n"); + keep_installed = FALSE; + } + + PLUGIN_DEBUG ("plugin_in_pipe_callback return\n"); + + return keep_installed; +} + +void consume_message(gchar* message) { + + PLUGIN_DEBUG (" PIPE: plugin read: %s\n", message); + + if (g_str_has_prefix (message, "instance")) + { + + ITNPPluginData* data; + gchar** parts = g_strsplit (message, " ", -1); + guint parts_sz = g_strv_length (parts); + + int instance_id = atoi(parts[1]); + NPP instance = (NPP) g_hash_table_lookup(id_to_instance_map, + GINT_TO_POINTER(instance_id)); + + if (instance_id > 0 && !instance) + { + PLUGIN_DEBUG("Instance %d is not active. Refusing to consume message \"%s\"\n", instance_id, message); + return; + } + else if (instance) + { + data = (ITNPPluginData*) instance->pdata; + } + + if (g_str_has_prefix (parts[2], "url")) + { + // Open the URL in a new browser window. + gchar* decoded_url = (gchar*) calloc(strlen(parts[3]) + 1, sizeof(gchar)); + IcedTeaPluginUtilities::decodeURL(parts[3], &decoded_url); + + PLUGIN_DEBUG ("plugin_in_pipe_callback: opening URL %s\n", decoded_url); + PLUGIN_DEBUG ("plugin_in_pipe_callback: URL target %s\n", parts[4]); + + NPError np_error = + (*browser_functions.geturl) (data->owner, decoded_url, parts[4]); + + + if (np_error != NPERR_NO_ERROR) + PLUGIN_ERROR ("Failed to load URL."); + + g_free(decoded_url); + decoded_url = NULL; + } + else if (g_str_has_prefix (parts[2], "status")) + { + + // clear the "instance X status" parts + sprintf(parts[0], ""); + sprintf(parts[1], ""); + sprintf(parts[2], ""); + + // join the rest + gchar* status_message = g_strjoinv(" ", parts); + PLUGIN_DEBUG ("plugin_in_pipe_callback: setting status %s\n", status_message); + (*browser_functions.status) (data->owner, status_message); + + g_free(status_message); + status_message = NULL; + } + else if (g_str_has_prefix (parts[1], "internal")) + { + //s->post(message); + } + else + { + // All other messages are posted to the bus, and subscribers are + // expected to take care of them. They better! + + java_to_plugin_bus->post(message); + } + + g_strfreev (parts); + parts = NULL; + } + else if (g_str_has_prefix (message, "context")) + { + java_to_plugin_bus->post(message); + } + else if (g_str_has_prefix (message, "plugin ")) + { + // internal plugin related message + gchar** parts = g_strsplit (message, " ", 5); + if (g_str_has_prefix(parts[1], "PluginProxyInfo")) + { + gchar* proxy; + uint32_t len; + + gchar* decoded_url = (gchar*) calloc(strlen(parts[4]) + 1, sizeof(gchar)); + IcedTeaPluginUtilities::decodeURL(parts[4], &decoded_url); + PLUGIN_DEBUG("parts[0]=%s, parts[1]=%s, reference, parts[3]=%s, parts[4]=%s -- decoded_url=%s\n", parts[0], parts[1], parts[3], parts[4], decoded_url); + + gchar* proxy_info; + +#if MOZILLA_VERSION_COLLAPSED < 1090100 + proxy = (char*) malloc(sizeof(char)*2048); +#endif + + proxy_info = g_strconcat ("plugin PluginProxyInfo reference ", parts[3], " ", NULL); + if (get_proxy_info(decoded_url, &proxy, &len) == NPERR_NO_ERROR) + { + proxy_info = g_strconcat (proxy_info, proxy, NULL); + } + + PLUGIN_DEBUG("Proxy info: %s\n", proxy_info); + plugin_send_message_to_appletviewer(proxy_info); + + g_free(decoded_url); + decoded_url = NULL; + g_free(proxy_info); + proxy_info = NULL; + +#if MOZILLA_VERSION_COLLAPSED < 1090100 + g_free(proxy); + proxy = NULL; +#endif + + } else if (g_str_has_prefix(parts[1], "PluginCookieInfo")) + { + gchar* decoded_url = (gchar*) calloc(strlen(parts[4])+1, sizeof(gchar)); + IcedTeaPluginUtilities::decodeURL(parts[4], &decoded_url); + + gchar* cookie_info = g_strconcat ("plugin PluginCookieInfo reference ", parts[3], " ", NULL); + gchar* cookie_string; + uint32_t len; + if (get_cookie_info(decoded_url, &cookie_string, &len) == NPERR_NO_ERROR) + { + cookie_info = g_strconcat (cookie_info, cookie_string, NULL); + } + + PLUGIN_DEBUG("Cookie info: %s\n", cookie_info); + plugin_send_message_to_appletviewer(cookie_info); + + g_free(decoded_url); + decoded_url = NULL; + g_free(cookie_info); + cookie_info = NULL; + } + } + else + { + g_print (" Unable to handle message: %s\n", message); + } +} + +void get_instance_from_id(int id, NPP& instance) +{ + instance = (NPP) g_hash_table_lookup(id_to_instance_map, + GINT_TO_POINTER(id)); +} + +int get_id_from_instance(NPP instance) +{ + int id = GPOINTER_TO_INT(g_hash_table_lookup(instance_to_id_map, + instance)); + PLUGIN_DEBUG("Returning id %d for instance %p\n", id, instance); + return id; +} + +NPError +get_proxy_info(const char* siteAddr, char** proxy, uint32_t* len) +{ +#if MOZILLA_VERSION_COLLAPSED < 1090100 + nsresult rv; + + // Initialize service variables + nsCOMPtr<nsIProtocolProxyService> proxy_svc = do_GetService("@mozilla.org/network/protocol-proxy-service;1", &rv); + + if (!proxy_svc) { + printf("Cannot initialize proxy service\n"); + return NPERR_GENERIC_ERROR; + } + + nsCOMPtr<nsIIOService> io_svc = do_GetService("@mozilla.org/network/io-service;1", &rv); + + if (NS_FAILED(rv) || !io_svc) { + printf("Cannot initialize io service\n"); + return NPERR_GENERIC_ERROR; + } + + // uri which needs to be accessed + nsCOMPtr<nsIURI> uri; + io_svc->NewURI(nsCString(siteAddr), NULL, NULL, getter_AddRefs(uri)); + + // find the proxy address if any + nsCOMPtr<nsIProxyInfo> info; + proxy_svc->Resolve(uri, 0, getter_AddRefs(info)); + + // if there is no proxy found, return immediately + if (!info) { + PLUGIN_DEBUG("%s does not need a proxy\n", siteAddr); + return NPERR_GENERIC_ERROR; + } + + // if proxy info is available, extract it + nsCString phost; + PRInt32 pport; + nsCString ptype; + + info->GetHost(phost); + info->GetPort(&pport); + info->GetType(ptype); + + // resolve the proxy address to an IP + nsCOMPtr<nsIDNSService> dns_svc = do_GetService("@mozilla.org/network/dns-service;1", &rv); + + if (!dns_svc) { + printf("Cannot initialize DNS service\n"); + return NPERR_GENERIC_ERROR; + } + + nsCOMPtr<nsIDNSRecord> record; + dns_svc->Resolve(phost, 0U, getter_AddRefs(record)); + + // TODO: Add support for multiple ips + nsDependentCString ipAddr; + record->GetNextAddrAsString(ipAddr); + + if (!strcmp(ptype.get(), "http")) + { + snprintf(*proxy, sizeof(char)*1024, "%s %s:%d", "PROXY", ipAddr.get(), pport); + } else + { + snprintf(*proxy, sizeof(char)*1024, "%s %s:%d", "SOCKS", ipAddr.get(), pport); + } + + *len = strlen(*proxy); + + PLUGIN_DEBUG("Proxy info for %s: %s\n", siteAddr, *proxy); + +#else + + if (browser_functions.getvalueforurl) + { + + // As in get_cookie_info, we use the first active instance + GHashTableIter iter; + gpointer id, instance; + + g_hash_table_iter_init (&iter, instance_to_id_map); + g_hash_table_iter_next (&iter, &instance, &id); + + browser_functions.getvalueforurl((NPP) instance, NPNURLVProxy, siteAddr, proxy, len); + } else + { + return NPERR_GENERIC_ERROR; + } +#endif + + return NPERR_NO_ERROR; +} + +// plugin_out_pipe_callback is called when the appletviewer crashes or +// is killed. It may be called after data has been destroyed in which +// case it simply returns FALSE to remove itself from the glib main +// loop. +static gboolean +plugin_out_pipe_callback (GIOChannel* source, + GIOCondition condition, + gpointer plugin_data) +{ + PLUGIN_DEBUG ("plugin_out_pipe_callback\n"); + + ITNPPluginData* data = (ITNPPluginData*) plugin_data; + + PLUGIN_DEBUG ("plugin_out_pipe_callback: appletviewer has stopped.\n"); + + PLUGIN_DEBUG ("plugin_out_pipe_callback return\n"); + + return FALSE; +} + +// remove all components from LD_LIBRARY_PATH, which start with +// MOZILLA_FIVE_HOME; firefox has its own NSS based security provider, +// which conflicts with the one configured in nss.cfg. +static gchar* +plugin_filter_ld_library_path(gchar *path_old) +{ + gchar *moz_home = g_strdup (g_getenv ("MOZILLA_FIVE_HOME")); + gchar *moz_prefix; + gchar *path_new; + gchar** components; + int i1, i2; + + if (moz_home == NULL || path_old == NULL || strlen (path_old) == 0) + return path_old; + if (g_str_has_suffix (moz_home, "/")) + moz_home[strlen (moz_home - 1)] = '\0'; + moz_prefix = g_strconcat (moz_home, "/", NULL); + + components = g_strsplit (path_old, ":", -1); + for (i1 = 0, i2 = 0; components[i1] != NULL; i1++) + { + if (g_strcmp0 (components[i1], moz_home) == 0 + || g_str_has_prefix (components[i1], moz_home)) + components[i2] = components[i1]; + else + components[i2++] = components[i1]; + } + components[i2] = NULL; + + if (i1 > i2) + path_new = g_strjoinv (":", components); + g_strfreev (components); + g_free (moz_home); + g_free (moz_prefix); + g_free (path_old); + + if (path_new == NULL || strlen (path_new) == 0) + { + PLUGIN_DEBUG("Unset LD_LIBRARY_PATH\n"); + return NULL; + } + else + { + PLUGIN_DEBUG ("Set LD_LIBRARY_PATH: %s\n", path_new); + return path_new; + } +} + +// build the environment to pass to the external plugin process +static gchar** +plugin_filter_environment(void) +{ + gchar **var_names = g_listenv(); + gchar **new_env = (gchar**) malloc(sizeof(gchar*) * (g_strv_length (var_names) + 1)); + int i_var, i_env; + + for (i_var = 0, i_env = 0; var_names[i_var] != NULL; i_var++) + { + gchar *env_value = g_strdup (g_getenv (var_names[i_var])); + + if (g_str_has_prefix (var_names[i_var], "LD_LIBRARY_PATH")) + env_value = plugin_filter_ld_library_path (env_value); + if (env_value != NULL) + { + new_env[i_env++] = g_strdup_printf ("%s=%s", var_names[i_var], env_value); + g_free (env_value); + } + } + new_env[i_env] = NULL; + return new_env; +} + +static NPError +plugin_test_appletviewer () +{ + PLUGIN_DEBUG ("plugin_test_appletviewer: %s\n", appletviewer_executable); + NPError error = NPERR_NO_ERROR; + + gchar* command_line[3] = { NULL, NULL, NULL }; + gchar** environment; + + command_line[0] = g_strdup (appletviewer_executable); + command_line[1] = g_strdup("-version"); + command_line[2] = NULL; + + environment = plugin_filter_environment(); + + if (!g_spawn_async (NULL, command_line, environment, + (GSpawnFlags) 0, + NULL, NULL, NULL, &channel_error)) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to spawn applet viewer", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to spawn applet viewer"); + error = NPERR_GENERIC_ERROR; + } + + g_strfreev (environment); + + g_free (command_line[0]); + command_line[0] = NULL; + g_free (command_line[1]); + command_line[1] = NULL; + g_free (command_line[2]); + command_line[2] = NULL; + + PLUGIN_DEBUG ("plugin_test_appletviewer return\n"); + return error; +} + +static NPError +plugin_start_appletviewer (ITNPPluginData* data) +{ + PLUGIN_DEBUG ("plugin_start_appletviewer\n"); + NPError error = NPERR_NO_ERROR; + + gchar** command_line; + gchar** environment; + + if (plugin_debug) + { + command_line = (gchar**) malloc(sizeof(gchar*)*8); + command_line[0] = g_strdup(appletviewer_executable); + command_line[1] = g_strdup("-Xdebug"); + command_line[2] = g_strdup("-Xnoagent"); + command_line[3] = g_strdup("-Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n"); + command_line[4] = g_strdup("sun.applet.PluginMain"); + command_line[5] = g_strdup(out_pipe_name); + command_line[6] = g_strdup(in_pipe_name); + command_line[7] = NULL; + } else + { + command_line = (gchar**) malloc(sizeof(gchar*)*5); + command_line[0] = g_strdup(appletviewer_executable); + command_line[1] = g_strdup("sun.applet.PluginMain"); + command_line[2] = g_strdup(out_pipe_name); + command_line[3] = g_strdup(in_pipe_name); + command_line[4] = NULL; + } + + environment = plugin_filter_environment(); + + if (!g_spawn_async (NULL, command_line, environment, + (GSpawnFlags) G_SPAWN_DO_NOT_REAP_CHILD, + NULL, NULL, &appletviewer_pid, &channel_error)) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to spawn applet viewer", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to spawn applet viewer"); + error = NPERR_GENERIC_ERROR; + } + + g_strfreev (environment); + + g_free (command_line[0]); + command_line[0] = NULL; + g_free (command_line[1]); + command_line[1] = NULL; + + if (plugin_debug) + { + g_free (command_line[2]); + command_line[2] = NULL; + g_free (command_line[3]); + command_line[3] = NULL; + g_free (command_line[4]); + command_line[4] = NULL; + g_free (command_line[5]); + command_line[5] = NULL; + } + + g_free(command_line); + command_line = NULL; + + if (appletviewer_pid) + { + PLUGIN_DEBUG("Initialized VM with pid=%d\n", appletviewer_pid); + appletviewer_watch_id = g_child_watch_add(appletviewer_pid, (GChildWatchFunc) appletviewer_monitor, (gpointer) appletviewer_pid); + } + + + PLUGIN_DEBUG ("plugin_start_appletviewer return\n"); + return error; +} + +// Build up the applet tag string that we'll send to the applet +// viewer. +static gchar* +plugin_create_applet_tag (int16_t argc, char* argn[], char* argv[]) +{ + PLUGIN_DEBUG ("plugin_create_applet_tag\n"); + + gchar* applet_tag = g_strdup ("<EMBED "); + gchar* parameters = g_strdup (""); + + for (int16_t i = 0; i < argc; i++) + { + if (!g_ascii_strcasecmp (argn[i], "code")) + { + gchar* code = g_strdup_printf ("CODE=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, code, NULL); + g_free (code); + code = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "java_code")) + { + gchar* java_code = g_strdup_printf ("JAVA_CODE=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, java_code, NULL); + g_free (java_code); + java_code = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "codebase")) + { + gchar* codebase = g_strdup_printf ("CODEBASE=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, codebase, NULL); + g_free (codebase); + codebase = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "java_codebase")) + { + gchar* java_codebase = g_strdup_printf ("JAVA_CODEBASE=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, java_codebase, NULL); + g_free (java_codebase); + java_codebase = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "classid")) + { + gchar* classid = g_strdup_printf ("CLASSID=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, classid, NULL); + g_free (classid); + classid = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "archive")) + { + gchar* archive = g_strdup_printf ("ARCHIVE=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, archive, NULL); + g_free (archive); + archive = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "java_archive")) + { + gchar* java_archive = g_strdup_printf ("JAVA_ARCHIVE=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, java_archive, NULL); + g_free (java_archive); + java_archive = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "width")) + { + gchar* width = g_strdup_printf ("width=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, width, NULL); + g_free (width); + width = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "height")) + { + gchar* height = g_strdup_printf ("height=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, height, NULL); + g_free (height); + height = NULL; + } + else + { + // Escape the parameter value so that line termination + // characters will pass through the pipe. + if (argv[i] != '\0') + { + // worst case scenario -> all characters are newlines or + // returns, each of which translates to 5 substitutions + char* escaped = (char*) calloc(((strlen(argv[i])*5)+1), sizeof(char)); + + strcpy(escaped, ""); + for (int j=0; j < strlen(argv[i]); j++) + { + if (argv[i][j] == '\r') + strcat(escaped, " "); + else if (argv[i][j] == '\n') + strcat(escaped, " "); + else if (argv[i][j] == '>') + strcat(escaped, ">"); + else if (argv[i][j] == '<') + strcat(escaped, "<"); + else if (argv[i][j] == '&') + strcat(escaped, "&"); + else + { + char* orig_char = (char*) calloc(2, sizeof(char)); + orig_char[0] = argv[i][j]; + orig_char[1] = '\0'; + + strcat(escaped, orig_char); + + free(orig_char); + orig_char = NULL; + } + } + + parameters = g_strconcat (parameters, "<PARAM NAME=\"", argn[i], + "\" VALUE=\"", escaped, "\">", NULL); + + free (escaped); + escaped = NULL; + } + } + } + + applet_tag = g_strconcat (applet_tag, ">", parameters, "</EMBED>", NULL); + + g_free (parameters); + parameters = NULL; + + PLUGIN_DEBUG ("plugin_create_applet_tag return\n"); + + return applet_tag; +} + +// plugin_send_message_to_appletviewer must be called while holding +// data->appletviewer_mutex. +void +plugin_send_message_to_appletviewer (gchar const* message) +{ + PLUGIN_DEBUG ("plugin_send_message_to_appletviewer\n"); + + if (jvm_up) + { + gchar* newline_message = NULL; + gsize bytes_written = 0; + + // Send message to appletviewer. + newline_message = g_strdup_printf ("%s\n", message); + + // g_io_channel_write_chars will return something other than + // G_IO_STATUS_NORMAL if not all the data is written. In that + // case we fail rather than retrying. + if (g_io_channel_write_chars (out_to_appletviewer, + newline_message, -1, &bytes_written, + &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to write bytes to output channel", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to write bytes to output channel"); + } + + if (g_io_channel_flush (out_to_appletviewer, &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to flush bytes to output channel", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to flush bytes to output channel"); + } + g_free (newline_message); + newline_message = NULL; + + PLUGIN_DEBUG (" PIPE: plugin wrote: %s\n", message); + } + + PLUGIN_DEBUG ("plugin_send_message_to_appletviewer return\n"); +} + +// Stop the appletviewer process. When this is called the +// appletviewer can be in any of three states: running, crashed or +// hung. If the appletviewer is running then sending it "shutdown" +// will cause it to exit. This will cause +// plugin_out_pipe_callback/plugin_in_pipe_callback to be called and +// the input and output channels to be shut down. If the appletviewer +// has crashed then plugin_out_pipe_callback/plugin_in_pipe_callback +// would already have been called and data->appletviewer_alive cleared +// in which case this function simply returns. If the appletviewer is +// hung then this function will be successful and the input and output +// watches will be removed by plugin_data_destroy. +// plugin_stop_appletviewer must be called with +// data->appletviewer_mutex held. +static void +plugin_stop_appletviewer () +{ + PLUGIN_DEBUG ("plugin_stop_appletviewer\n"); + + if (jvm_up) + { + // Shut down the appletviewer. + gsize bytes_written = 0; + + if (out_to_appletviewer) + { + if (g_io_channel_write_chars (out_to_appletviewer, "shutdown", + -1, &bytes_written, &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to write shutdown message to" + " appletviewer", channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to write shutdown message to"); + } + + if (g_io_channel_flush (out_to_appletviewer, &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to write shutdown message to" + " appletviewer", channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to write shutdown message to"); + } + + if (g_io_channel_shutdown (out_to_appletviewer, + TRUE, &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to shut down appletviewer" + " output channel", channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to shut down appletviewer"); + } + } + + if (in_from_appletviewer) + { + if (g_io_channel_shutdown (in_from_appletviewer, + TRUE, &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to shut down appletviewer" + " input channel", channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to shut down appletviewer"); + } + } + } + + jvm_up = FALSE; + sleep(2); /* Needed to prevent crashes during debug (when JDWP port is not freed by the kernel right away) */ + + PLUGIN_DEBUG ("plugin_stop_appletviewer return\n"); +} + +static void appletviewer_monitor(GPid pid, gint status, gpointer data) +{ + PLUGIN_DEBUG ("appletviewer_monitor\n"); + jvm_up = FALSE; + pid = -1; + PLUGIN_DEBUG ("appletviewer_monitor return\n"); +} + +static void +plugin_data_destroy (NPP instance) +{ + PLUGIN_DEBUG ("plugin_data_destroy\n"); + + ITNPPluginData* tofree = (ITNPPluginData*) instance->pdata; + + // Remove instance from map + gpointer id_ptr = g_hash_table_lookup(instance_to_id_map, instance); + + if (id_ptr) + { + gint id = GPOINTER_TO_INT(id_ptr); + g_hash_table_remove(instance_to_id_map, instance); + g_hash_table_remove(id_to_instance_map, id_ptr); + } + + tofree->window_handle = NULL; + tofree->window_height = 0; + tofree->window_width = 0; + + // cleanup_appletviewer_mutex: + g_free (tofree->appletviewer_mutex); + tofree->appletviewer_mutex = NULL; + + // cleanup_instance_string: + g_free (tofree->instance_id); + tofree->instance_id = NULL; + + // cleanup applet tag + g_free (tofree->applet_tag); + tofree->applet_tag = NULL; + + g_free(tofree->source); + tofree->source = NULL; + + // cleanup_data: + // Eliminate back-pointer to plugin instance. + tofree->owner = NULL; + (*browser_functions.memfree) (tofree); + tofree = NULL; + + PLUGIN_DEBUG ("plugin_data_destroy return\n"); +} + +// FACTORY FUNCTIONS + +// Provides the browser with pointers to the plugin functions that we +// implement and initializes a local table with browser functions that +// we may wish to call. Called once, after browser startup and before +// the first plugin instance is created. +// The field 'initialized' is set to true once this function has +// finished. If 'initialized' is already true at the beginning of +// this function, then it is evident that NP_Initialize has already +// been called. There is no need to call this function more than once and +// this workaround avoids any duplicate calls. +NPError +NP_Initialize (NPNetscapeFuncs* browserTable, NPPluginFuncs* pluginTable) +{ + PLUGIN_DEBUG ("NP_Initialize\n"); + + if (initialized) + return NPERR_NO_ERROR; + else if ((browserTable == NULL) || (pluginTable == NULL)) + { + PLUGIN_ERROR ("Browser or plugin function table is NULL."); + + return NPERR_INVALID_FUNCTABLE_ERROR; + } + + // Ensure that the major version of the plugin API that the browser + // expects is not more recent than the major version of the API that + // we've implemented. + if ((browserTable->version >> 8) > NP_VERSION_MAJOR) + { + PLUGIN_ERROR ("Incompatible version."); + + return NPERR_INCOMPATIBLE_VERSION_ERROR; + } + + // Ensure that the plugin function table we've received is large + // enough to store the number of functions that we may provide. + if (pluginTable->size < sizeof (NPPluginFuncs)) + { + PLUGIN_ERROR ("Invalid plugin function table."); + + return NPERR_INVALID_FUNCTABLE_ERROR; + } + + // Ensure that the browser function table is large enough to store + // the number of browser functions that we may use. + if (browserTable->size < sizeof (NPNetscapeFuncs)) + { + fprintf (stderr, "ERROR: Invalid browser function table. Some functionality may be restricted.\n"); + } + + // Store in a local table the browser functions that we may use. + browser_functions.size = browserTable->size; + browser_functions.version = browserTable->version; + browser_functions.geturlnotify = browserTable->geturlnotify; + browser_functions.geturl = browserTable->geturl; + browser_functions.posturlnotify = browserTable->posturlnotify; + browser_functions.posturl = browserTable->posturl; + browser_functions.requestread = browserTable->requestread; + browser_functions.newstream = browserTable->newstream; + browser_functions.write = browserTable->write; + browser_functions.destroystream = browserTable->destroystream; + browser_functions.status = browserTable->status; + browser_functions.uagent = browserTable->uagent; + browser_functions.memalloc = browserTable->memalloc; + browser_functions.memfree = browserTable->memfree; + browser_functions.memflush = browserTable->memflush; + browser_functions.reloadplugins = browserTable->reloadplugins; + browser_functions.getJavaEnv = browserTable->getJavaEnv; + browser_functions.getJavaPeer = browserTable->getJavaPeer; + browser_functions.getvalue = browserTable->getvalue; + browser_functions.setvalue = browserTable->setvalue; + browser_functions.invalidaterect = browserTable->invalidaterect; + browser_functions.invalidateregion = browserTable->invalidateregion; + browser_functions.forceredraw = browserTable->forceredraw; + browser_functions.getstringidentifier = browserTable->getstringidentifier; + browser_functions.getstringidentifiers = browserTable->getstringidentifiers; + browser_functions.getintidentifier = browserTable->getintidentifier; + browser_functions.identifierisstring = browserTable->identifierisstring; + browser_functions.utf8fromidentifier = browserTable->utf8fromidentifier; + browser_functions.intfromidentifier = browserTable->intfromidentifier; + browser_functions.createobject = browserTable->createobject; + browser_functions.retainobject = browserTable->retainobject; + browser_functions.releaseobject = browserTable->releaseobject; + browser_functions.invoke = browserTable->invoke; + browser_functions.invokeDefault = browserTable->invokeDefault; + browser_functions.evaluate = browserTable->evaluate; + browser_functions.getproperty = browserTable->getproperty; + browser_functions.setproperty = browserTable->setproperty; + browser_functions.removeproperty = browserTable->removeproperty; + browser_functions.hasproperty = browserTable->hasproperty; + browser_functions.hasmethod = browserTable->hasmethod; + browser_functions.releasevariantvalue = browserTable->releasevariantvalue; + browser_functions.setexception = browserTable->setexception; + browser_functions.pluginthreadasynccall = browserTable->pluginthreadasynccall; +#if MOZILLA_VERSION_COLLAPSED >= 1090100 + browser_functions.getvalueforurl = browserTable->getvalueforurl; + browser_functions.setvalueforurl = browserTable->setvalueforurl; +#endif + + // Return to the browser the plugin functions that we implement. + pluginTable->version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR; + pluginTable->size = sizeof (NPPluginFuncs); + +#if MOZILLA_VERSION_COLLAPSED < 1090100 + pluginTable->newp = NewNPP_NewProc (ITNP_New); + pluginTable->destroy = NewNPP_DestroyProc (ITNP_Destroy); + pluginTable->setwindow = NewNPP_SetWindowProc (ITNP_SetWindow); + pluginTable->newstream = NewNPP_NewStreamProc (ITNP_NewStream); + pluginTable->destroystream = NewNPP_DestroyStreamProc (ITNP_DestroyStream); + pluginTable->asfile = NewNPP_StreamAsFileProc (ITNP_StreamAsFile); + pluginTable->writeready = NewNPP_WriteReadyProc (ITNP_WriteReady); + pluginTable->write = NewNPP_WriteProc (ITNP_Write); + pluginTable->print = NewNPP_PrintProc (ITNP_Print); + pluginTable->urlnotify = NewNPP_URLNotifyProc (ITNP_URLNotify); + pluginTable->getvalue = NewNPP_GetValueProc (ITNP_GetValue); +#else + pluginTable->newp = NPP_NewProcPtr (ITNP_New); + pluginTable->destroy = NPP_DestroyProcPtr (ITNP_Destroy); + pluginTable->setwindow = NPP_SetWindowProcPtr (ITNP_SetWindow); + pluginTable->newstream = NPP_NewStreamProcPtr (ITNP_NewStream); + pluginTable->destroystream = NPP_DestroyStreamProcPtr (ITNP_DestroyStream); + pluginTable->asfile = NPP_StreamAsFileProcPtr (ITNP_StreamAsFile); + pluginTable->writeready = NPP_WriteReadyProcPtr (ITNP_WriteReady); + pluginTable->write = NPP_WriteProcPtr (ITNP_Write); + pluginTable->print = NPP_PrintProcPtr (ITNP_Print); + pluginTable->urlnotify = NPP_URLNotifyProcPtr (ITNP_URLNotify); + pluginTable->getvalue = NPP_GetValueProcPtr (ITNP_GetValue); +#endif + + // Make sure the plugin data directory exists, creating it if + // necessary. + data_directory = g_strconcat (P_tmpdir, NULL); + if (!data_directory) + { + PLUGIN_ERROR ("Failed to create data directory name."); + return NPERR_OUT_OF_MEMORY_ERROR; + } + NPError np_error = NPERR_NO_ERROR; + gchar* filename = NULL; + + // If P_tmpdir does not exist, try /tmp directly + + if (!g_file_test (data_directory, + (GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) + { + int file_error = 0; + + data_directory = g_strconcat ("/tmp", NULL); + if (!data_directory) + { + PLUGIN_ERROR ("Failed to create data directory name."); + return NPERR_OUT_OF_MEMORY_ERROR; + } + + } + + data_directory = g_strconcat (data_directory, "/icedteaplugin-", getenv("USER"), NULL); + + if (!data_directory) + { + PLUGIN_ERROR ("Failed to create data directory name."); + return NPERR_OUT_OF_MEMORY_ERROR; + } + + // Now create a icedteaplugin subdir + if (!g_file_test (data_directory, + (GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) + { + int file_error = 0; + + file_error = g_mkdir (data_directory, 0700); + if (file_error != 0) + { + PLUGIN_ERROR_THREE ("Failed to create data directory", + data_directory, + strerror (errno)); + np_error = NPERR_GENERIC_ERROR; + goto cleanup_data_directory; + } + } + + + // If data directory doesn't exit by this point, bail + if (!g_file_test (data_directory, + (GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) + { + PLUGIN_ERROR_THREE ("Temp directory does not exist: ", + data_directory, + strerror (errno)); + + np_error = NPERR_GENERIC_ERROR; + goto cleanup_data_directory; + + } + + // Set appletviewer_executable. + Dl_info info; + int filename_size; + if (dladdr ((const void*) ITNP_New, &info) == 0) + { + PLUGIN_ERROR_TWO ("Failed to determine plugin shared object filename", + dlerror ()); + np_error = NPERR_GENERIC_ERROR; + goto cleanup_data_directory; + } + filename = (gchar*) malloc(sizeof(gchar)*1024); + filename_size = readlink(info.dli_fname, filename, 1023); + if (filename_size >= 0) + { + filename[filename_size] = '\0'; + } + + if (!filename) + { + PLUGIN_ERROR ("Failed to create plugin shared object filename."); + np_error = NPERR_OUT_OF_MEMORY_ERROR; + goto cleanup_data_directory; + } + + if (filename_size <= 0) + { + free(filename); + filename = g_strdup(info.dli_fname); + } + + appletviewer_executable = g_strdup_printf ("%s/../../bin/java", + dirname (filename)); + PLUGIN_DEBUG(".so is located at: %s and the link points to: %s. Executing java from dir %s to run %s\n", info.dli_fname, filename, dirname (filename), appletviewer_executable); + if (!appletviewer_executable) + { + PLUGIN_ERROR ("Failed to create appletviewer executable name."); + np_error = NPERR_OUT_OF_MEMORY_ERROR; + goto cleanup_filename; + } + + np_error = plugin_test_appletviewer (); + if (np_error != NPERR_NO_ERROR) + { + plugin_display_failure_dialog (); + goto cleanup_appletviewer_executable; + } + g_free (filename); + + initialized = true; + + // Initialize threads (needed for mutexes). + if (!g_thread_supported ()) + g_thread_init (NULL); + + plugin_instance_mutex = g_mutex_new (); + + PLUGIN_DEBUG ("NP_Initialize: using %s\n", appletviewer_executable); + + PLUGIN_DEBUG ("NP_Initialize return\n"); + + plugin_req_proc = new PluginRequestProcessor(); + java_req_proc = new JavaMessageSender(); + + java_to_plugin_bus = new MessageBus(); + plugin_to_java_bus = new MessageBus(); + + java_to_plugin_bus->subscribe(plugin_req_proc); + plugin_to_java_bus->subscribe(java_req_proc); + + pthread_create (&plugin_request_processor_thread1, NULL, &queue_processor, (void*) plugin_req_proc); + pthread_create (&plugin_request_processor_thread2, NULL, &queue_processor, (void*) plugin_req_proc); + pthread_create (&plugin_request_processor_thread3, NULL, &queue_processor, (void*) plugin_req_proc); + + return NPERR_NO_ERROR; + + cleanup_appletviewer_executable: + if (appletviewer_executable) + { + g_free (appletviewer_executable); + appletviewer_executable = NULL; + } + + cleanup_filename: + if (filename) + { + g_free (filename); + filename = NULL; + } + + cleanup_data_directory: + if (data_directory) + { + g_free (data_directory); + data_directory = NULL; + } + + + return np_error; +} + +// Returns a string describing the MIME type that this plugin +// handles. +char* +NP_GetMIMEDescription () +{ + PLUGIN_DEBUG ("NP_GetMIMEDescription\n"); + + PLUGIN_DEBUG ("NP_GetMIMEDescription return\n"); + + return (char*) PLUGIN_MIME_DESC; +} + +// Returns a value relevant to the plugin as a whole. The browser +// calls this function to obtain information about the plugin. +NPError +NP_GetValue (void* future, NPPVariable variable, void* value) +{ + PLUGIN_DEBUG ("NP_GetValue\n"); + + NPError result = NPERR_NO_ERROR; + gchar** char_value = (gchar**) value; + + switch (variable) + { + case NPPVpluginNameString: + PLUGIN_DEBUG ("NP_GetValue: returning plugin name.\n"); + *char_value = g_strdup (PLUGIN_NAME); + break; + + case NPPVpluginDescriptionString: + PLUGIN_DEBUG ("NP_GetValue: returning plugin description.\n"); + *char_value = g_strdup (PLUGIN_DESC); + break; + + default: + PLUGIN_ERROR ("Unknown plugin value requested."); + result = NPERR_GENERIC_ERROR; + break; + } + + PLUGIN_DEBUG ("NP_GetValue return\n"); + + return result; +} + +// Shuts down the plugin. Called after the last plugin instance is +// destroyed. +NPError +NP_Shutdown (void) +{ + PLUGIN_DEBUG ("NP_Shutdown\n"); + + // Free mutex. + if (plugin_instance_mutex) + { + g_mutex_free (plugin_instance_mutex); + plugin_instance_mutex = NULL; + } + + if (data_directory) + { + g_free (data_directory); + data_directory = NULL; + } + + if (appletviewer_executable) + { + g_free (appletviewer_executable); + appletviewer_executable = NULL; + } + + // stop the appletviewer + plugin_stop_appletviewer(); + + // remove monitor + if (appletviewer_watch_id != -1) + g_source_remove(appletviewer_watch_id); + + // Removing a source is harmless if it fails since it just means the + // source has already been removed. + g_source_remove (in_watch_source); + in_watch_source = 0; + + // cleanup_in_from_appletviewer: + if (in_from_appletviewer) + g_io_channel_unref (in_from_appletviewer); + in_from_appletviewer = NULL; + + // cleanup_out_watch_source: + g_source_remove (out_watch_source); + out_watch_source = 0; + + // cleanup_out_to_appletviewer: + if (out_to_appletviewer) + g_io_channel_unref (out_to_appletviewer); + out_to_appletviewer = NULL; + + // cleanup_out_pipe: + // Delete output pipe. + PLUGIN_DEBUG ("NP_Shutdown: deleting output fifo: %s\n", out_pipe_name); + unlink (out_pipe_name); + PLUGIN_DEBUG ("NP_Shutdown: deleted output fifo: %s\n", out_pipe_name); + + // cleanup_out_pipe_name: + g_free (out_pipe_name); + out_pipe_name = NULL; + + // cleanup_in_pipe: + // Delete input pipe. + PLUGIN_DEBUG ("NP_Shutdown: deleting input fifo: %s\n", in_pipe_name); + unlink (in_pipe_name); + PLUGIN_DEBUG ("NP_Shutdown: deleted input fifo: %s\n", in_pipe_name); + + // cleanup_in_pipe_name: + g_free (in_pipe_name); + in_pipe_name = NULL; + + initialized = false; + + pthread_cancel(plugin_request_processor_thread1); + pthread_cancel(plugin_request_processor_thread2); + pthread_cancel(plugin_request_processor_thread3); + + java_to_plugin_bus->unSubscribe(plugin_req_proc); + plugin_to_java_bus->unSubscribe(java_req_proc); + //internal_bus->unSubscribe(java_req_proc); + //internal_bus->unSubscribe(plugin_req_proc); + + delete plugin_req_proc; + delete java_req_proc; + delete java_to_plugin_bus; + delete plugin_to_java_bus; + //delete internal_bus; + + PLUGIN_DEBUG ("NP_Shutdown return\n"); + + return NPERR_NO_ERROR; +} + +NPObject* +get_scriptable_object(NPP instance) +{ + NPObject* obj; + ITNPPluginData* data = (ITNPPluginData*) instance->pdata; + + if (data->is_applet_instance) // dummy instance/package? + { + JavaRequestProcessor java_request = JavaRequestProcessor(); + JavaResultData* java_result; + std::string instance_id = std::string(); + std::string applet_class_id = std::string(); + + int id = get_id_from_instance(instance); + gchar* id_str = g_strdup_printf ("%d", id); + + // Some browsers.. (e.g. chromium) don't call NPP_SetWindow + // for 0x0 plugins and therefore require initialization with + // a 0 handle + if (!data->window_handle) + { + data->window_handle = 0; + gchar *window_message = g_strdup_printf ("instance %s handle %d", + id_str, 0); + plugin_send_message_to_appletviewer (window_message); + g_free (window_message); + } + + java_result = java_request.getAppletObjectInstance(id_str); + + g_free(id_str); + + if (java_result->error_occurred) + { + printf("Error: Unable to fetch applet instance id from Java side.\n"); + return NULL; + } + + instance_id.append(*(java_result->return_string)); + + java_result = java_request.getClassID(instance_id); + + if (java_result->error_occurred) + { + printf("Error: Unable to fetch applet instance id from Java side.\n"); + return NULL; + } + + applet_class_id.append(*(java_result->return_string)); + + obj = IcedTeaScriptableJavaPackageObject::get_scriptable_java_object(instance, applet_class_id, instance_id, false); + + } else + { + obj = IcedTeaScriptablePluginObject::get_scriptable_java_package_object(instance, ""); + } + + return obj; +} + +NPObject* +allocate_scriptable_object(NPP npp, NPClass *aClass) +{ + PLUGIN_DEBUG("Allocating new scriptable object\n"); + return new IcedTeaScriptablePluginObject(npp); +} diff --git a/plugin/icedteanp/IcedTeaNPPlugin.h b/plugin/icedteanp/IcedTeaNPPlugin.h new file mode 100644 index 0000000..18d1765 --- /dev/null +++ b/plugin/icedteanp/IcedTeaNPPlugin.h @@ -0,0 +1,136 @@ +/* IcedTeaNPPlugin.h + + Copyright (C) 2009, 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. */ + +#ifndef __ICEDTEANPPLUGIN_H__ +#define __ICEDTEANPPLUGIN_H__ + +#if MOZILLA_VERSION_COLLAPSED < 1090100 +#include <nsThreadUtils.h> +#else +#include <npapi.h> +#include <npruntime.h> +#include <npfunctions.h> +#endif + +// GLib includes. +#include <glib.h> +#include <glib/gstdio.h> + +// GTK includes. +#include <gtk/gtk.h> + +#include "IcedTeaPluginUtils.h" +#include "IcedTeaPluginRequestProcessor.h" + +// Work around across some chromium issues +#define CHROMIUM_WORKAROUND + +// ITNPPluginData stores all the data associated with a single plugin +// instance. A separate plugin instance is created for each <APPLET> +// tag. For now, each plugin instance spawns its own applet viewer +// process but this may need to change if we find pages containing +// multiple applets that expect to be running in the same VM. +struct ITNPPluginData +{ + // A unique identifier for this plugin window. + gchar* instance_id; + // The applet tag sent to Java side + gchar* applet_tag; + // Mutex to protect appletviewer_alive. + GMutex* appletviewer_mutex; + // Back-pointer to the plugin instance to which this data belongs. + // This should not be freed but instead simply set to NULL. + NPP owner; + // The address of the plugin window. This should not be freed but + // instead simply set to NULL. + gpointer window_handle; + // The last plugin window width sent to us by the browser. + guint32 window_width; + // The last plugin window height sent to us by the browser. + guint32 window_height; + // The source location for this instance + gchar* source; + // If this is an actual applet instance, or a dummy instance for static calls + bool is_applet_instance; +}; + +// Queue processing threads +static pthread_t plugin_request_processor_thread1; +static pthread_t plugin_request_processor_thread2; +static pthread_t plugin_request_processor_thread3; + +// Condition on which the queue processor waits +extern pthread_cond_t cond_message_available; + +// debug switch +extern int plugin_debug; + +// Browser function table. +extern NPNetscapeFuncs browser_functions; + +// messages to the java side +extern MessageBus* plugin_to_java_bus; + +// messages from the java side +extern MessageBus* java_to_plugin_bus; + +// internal messages (e.g ones that need processing in main thread) +//extern MessageBus* internal_bus; + +// subscribes to plugin_to_java_bus and sends messages over the link +extern JavaMessageSender java_request_processor; + +// processes requests made to the plugin +extern PluginRequestProcessor plugin_request_processor; + +/* Given an instance pointer, return its id */ +void get_instance_from_id(int id, NPP& instance); + +/* Given an instance id, return its pointer */ +int get_id_from_instance(NPP instance); + +/* Sends a message to the appletviewer */ +void plugin_send_message_to_appletviewer(gchar const* message); + +/* Returns an appropriate (package/object) scriptable npobject */ +NPObject* get_scriptable_object(NPP instance); + +/* Creates a new scriptable plugin object and returns it */ +NPObject* allocate_scriptable_object(NPP npp, NPClass *aClass); + +#endif /* __ICEDTEANPPLUGIN_H__ */ diff --git a/plugin/icedteanp/IcedTeaPluginRequestProcessor.cc b/plugin/icedteanp/IcedTeaPluginRequestProcessor.cc new file mode 100644 index 0000000..373be59 --- /dev/null +++ b/plugin/icedteanp/IcedTeaPluginRequestProcessor.cc @@ -0,0 +1,988 @@ +/* IcedTeaPluginRequestProcessor.cc + + Copyright (C) 2009, 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. */ + +#include <typeinfo> + +#include "IcedTeaScriptablePluginObject.h" +#include "IcedTeaNPPlugin.h" +#include "IcedTeaPluginRequestProcessor.h" + +/* + * This class processes requests made by Java. The requests include pointer + * information, script execution and variable get/set + */ + +// Initialize static members used by the queue processing framework +pthread_mutex_t message_queue_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t syn_write_mutex = PTHREAD_MUTEX_INITIALIZER; +std::vector< std::vector<std::string*>* >* message_queue = new std::vector< std::vector<std::string*>* >(); + +/** + * PluginRequestProcessor constructor. + * + * Initializes various complex data structures used by the class. + */ + +PluginRequestProcessor::PluginRequestProcessor() +{ + this->pendingRequests = new std::map<pthread_t, uintmax_t>(); + + internal_req_ref_counter = 0; +} + +/** + * PluginRequestProcessor destructor. + * + * Frees memory used by complex objects. + */ + +PluginRequestProcessor::~PluginRequestProcessor() +{ + PLUGIN_DEBUG("PluginRequestProcessor::~PluginRequestProcessor\n"); + + if (pendingRequests) + delete pendingRequests; +} + +/** + * Processes plugin (C++ side) requests from the Java side, and internally. + * + * @param message The message request to process + * @return boolean indicating whether the message is serviceable by this object + */ + +bool +PluginRequestProcessor::newMessageOnBus(const char* message) +{ + PLUGIN_DEBUG("PluginRequestProcessor processing %s\n", message); + + std::string* type; + std::string* command; + int counter = 0; + + std::vector<std::string*>* message_parts = IcedTeaPluginUtilities::strSplit(message, " "); + + std::vector<std::string*>::iterator the_iterator; + the_iterator = message_parts->begin(); + + IcedTeaPluginUtilities::printStringPtrVector("PluginRequestProcessor::newMessageOnBus:", message_parts); + + type = message_parts->at(0); + command = message_parts->at(4); + + if (!type->find("instance")) + { + if (!command->find("GetWindow")) + { + // Window can be queried from the main thread only. And this call + // returns immediately, so we do it in the same thread. + this->sendWindow(message_parts); + return true; + } else if (!command->find("GetMember") || + !command->find("SetMember") || + !command->find("ToString") || + !command->find("Call") || + !command->find("GetSlot") || + !command->find("SetSlot") || + !command->find("Eval") || + !command->find("Finalize")) + { + + // Update queue synchronously + pthread_mutex_lock(&message_queue_mutex); + message_queue->push_back(message_parts); + pthread_mutex_unlock(&message_queue_mutex); + + // Broadcast that a message is now available + pthread_cond_broadcast(&cond_message_available); + + return true; + } + + } + + IcedTeaPluginUtilities::freeStringPtrVector(message_parts); + + // If we got here, it means we couldn't process the message. Let the caller know. + return false; +} + +/** + * Sends the window pointer to the Java side. + * + * @param message_parts The request message. + */ + +void +PluginRequestProcessor::sendWindow(std::vector<std::string*>* message_parts) +{ + std::string* type; + std::string* command; + int reference; + std::string response = std::string(); + std::string window_ptr_str = std::string(); + NPVariant* variant = new NPVariant(); + static NPObject* window_ptr; + int id; + + type = message_parts->at(0); + id = atoi(message_parts->at(1)->c_str()); + reference = atoi(message_parts->at(3)->c_str()); + command = message_parts->at(4); + + NPP instance; + get_instance_from_id(id, instance); + + browser_functions.getvalue(instance, NPNVWindowNPObject, &window_ptr); + PLUGIN_DEBUG("ID=%d, Instance=%p, WindowPTR = %p\n", id, instance, window_ptr); + + OBJECT_TO_NPVARIANT(window_ptr, *variant); + browser_functions.retainobject(window_ptr); + IcedTeaPluginUtilities::JSIDToString(variant, &window_ptr_str); + + // We need the context 0 for backwards compatibility with the Java side + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &response); + response += " JavaScriptGetWindow "; + response += window_ptr_str; + + plugin_to_java_bus->post(response.c_str()); + + // store the instance pointer for future reference + IcedTeaPluginUtilities::storeInstanceID(variant, instance); +} + +/** + * Evaluates the given script + * + * @param message_parts The request message. + */ + +void +PluginRequestProcessor::eval(std::vector<std::string*>* message_parts) +{ + JavaRequestProcessor request_processor = JavaRequestProcessor(); + JavaResultData* java_result; + + NPVariant* window_ptr; + NPP instance; + std::string script; + NPVariant result; + int reference; + std::string response = std::string(); + std::string return_type = std::string(); + int id; + + reference = atoi(message_parts->at(3)->c_str()); + window_ptr = (NPVariant*) IcedTeaPluginUtilities::stringToJSID(message_parts->at(5)); + instance = IcedTeaPluginUtilities::getInstanceFromMemberPtr(window_ptr); + + java_result = request_processor.getString(*(message_parts->at(6))); + CHECK_JAVA_RESULT(java_result); + script.append(*(java_result->return_string)); + + AsyncCallThreadData thread_data = AsyncCallThreadData(); + thread_data.result_ready = false; + thread_data.parameters = std::vector<void*>(); + thread_data.result = std::string(); + + thread_data.parameters.push_back(instance); + thread_data.parameters.push_back(NPVARIANT_TO_OBJECT(*window_ptr)); + thread_data.parameters.push_back(&script); + +#ifdef CHROMIUM_WORKAROUND + // Workaround for chromium + _eval(&thread_data); + + if (!thread_data.call_successful) + { +#endif + thread_data.result_ready = false; + browser_functions.pluginthreadasynccall(instance, &_eval, &thread_data); + + while (!thread_data.result_ready) usleep(2000); // Wait till result is ready +#ifdef CHROMIUM_WORKAROUND + } +#endif + + NPVariant* result_variant = (NPVariant*) IcedTeaPluginUtilities::stringToJSID(thread_data.result); + std::string result_variant_jniid = std::string(); + createJavaObjectFromVariant(instance, *result_variant, &result_variant_jniid); + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &response); + response += " JavaScriptEval "; + response += result_variant_jniid; + + plugin_to_java_bus->post(response.c_str()); +} + +/** + * Calls the given javascript script + * + * @param message_parts The request message. + */ + +void +PluginRequestProcessor::call(std::vector<std::string*>* message_parts) +{ + NPP instance; + std::string* window_ptr_str; + NPVariant* window_ptr; + int reference; + std::string window_function_name; + std::vector<NPVariant> args = std::vector<NPVariant>(); + std::vector<std::string> arg_ids = std::vector<std::string>(); + int arg_count; + std::string response = std::string(); + JavaRequestProcessor java_request = JavaRequestProcessor(); + JavaResultData* java_result; + + reference = atoi(message_parts->at(3)->c_str()); + + // window + window_ptr_str = message_parts->at(5); + window_ptr = (NPVariant*) IcedTeaPluginUtilities::stringToJSID(window_ptr_str); + + // instance + instance = IcedTeaPluginUtilities::getInstanceFromMemberPtr(window_ptr); + + // function name + java_result = java_request.getString(*(message_parts->at(6))); + CHECK_JAVA_RESULT(java_result); + window_function_name.append(*(java_result->return_string)); + + // arguments + for (int i=7; i < message_parts->size(); i++) + { + arg_ids.push_back(*(message_parts->at(i))); + } + + // determine arguments + for (int i=0; i < arg_ids.size(); i++) + { + NPVariant* variant = new NPVariant(); + java_result = java_request.getValue(arg_ids[i]); + CHECK_JAVA_RESULT(java_result); + + IcedTeaPluginUtilities::javaResultToNPVariant(instance, java_result->return_string, variant); + + args.push_back(*variant); + } + + arg_count = args.size(); + NPVariant *args_array = (NPVariant*) malloc(sizeof(NPVariant)*args.size()); + for (int i=0; i < args.size(); i++) + args_array[i] = args[i]; + + AsyncCallThreadData thread_data = AsyncCallThreadData(); + thread_data.result_ready = false; + thread_data.parameters = std::vector<void*>(); + thread_data.result = std::string(); + + thread_data.parameters.push_back(instance); + thread_data.parameters.push_back(NPVARIANT_TO_OBJECT(*window_ptr)); + thread_data.parameters.push_back(&window_function_name); + thread_data.parameters.push_back(&arg_count); + thread_data.parameters.push_back(args_array); + +#ifdef CHROMIUM_WORKAROUND + // Workaround for chromium + _call(&thread_data); + + if (!thread_data.call_successful) + { +#endif + thread_data.result_ready = false; + browser_functions.pluginthreadasynccall(instance, &_call, &thread_data); + + while (!thread_data.result_ready) usleep(2000); // wait till ready +#ifdef CHROMIUM_WORKAROUND + } +#endif + + NPVariant* result_variant = (NPVariant*) IcedTeaPluginUtilities::stringToJSID(thread_data.result); + std::string result_variant_jniid = std::string(); + + if (result_variant) + { + createJavaObjectFromVariant(instance, *result_variant, &result_variant_jniid); + } else + { + result_variant_jniid = "0"; + } + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &response); + response += " JavaScriptCall "; + response += result_variant_jniid; + + plugin_to_java_bus->post(response.c_str()); + + cleanup: + free(args_array); +} + +/** + * Sends the string value of the requested variable + * + * @param message_parts The request message. + */ +void +PluginRequestProcessor::sendString(std::vector<std::string*>* message_parts) +{ + std::string variant_ptr; + NPVariant* variant; + JavaRequestProcessor java_request = JavaRequestProcessor(); + JavaResultData* java_result; + int reference; + std::string response = std::string(); + + reference = atoi(message_parts->at(3)->c_str()); + variant_ptr = *(message_parts->at(5)); + + variant = (NPVariant*) IcedTeaPluginUtilities::stringToJSID(variant_ptr); + AsyncCallThreadData thread_data = AsyncCallThreadData(); + thread_data.result_ready = false; + thread_data.parameters = std::vector<void*>(); + thread_data.result = std::string(); + + NPP instance = IcedTeaPluginUtilities::getInstanceFromMemberPtr(variant); + thread_data.parameters.push_back(instance); + thread_data.parameters.push_back(variant); + +#ifdef CHROMIUM_WORKAROUND + // Workaround for chromium + _getString(&thread_data); + + if (!thread_data.call_successful) + { +#endif + thread_data.result_ready = false; + browser_functions.pluginthreadasynccall(instance, &_getString, &thread_data); + while (!thread_data.result_ready) usleep(2000); // wait till ready +#ifdef CHROMIUM_WORKAROUND + } +#endif + + // We need the context 0 for backwards compatibility with the Java side + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &response); + response += " JavaScriptToString "; + response += thread_data.result; + + plugin_to_java_bus->post(response.c_str()); + + cleanup: + + pthread_mutex_lock(&tc_mutex); + thread_count--; + pthread_mutex_unlock(&tc_mutex); +} + +/** + * Sets variable to given value + * + * @param message_parts The request message. + */ + +void +PluginRequestProcessor::setMember(std::vector<std::string*>* message_parts) +{ + std::string propertyNameID; + std::string value = std::string(); + std::string response = std::string(); + int reference; + + NPP instance; + NPVariant* member; + NPIdentifier property_identifier; + + JavaRequestProcessor java_request = JavaRequestProcessor(); + JavaResultData* java_result; + + IcedTeaPluginUtilities::printStringPtrVector("PluginRequestProcessor::_setMember - ", message_parts); + + reference = atoi(message_parts->at(3)->c_str()); + + member = (NPVariant*) (IcedTeaPluginUtilities::stringToJSID(*(message_parts->at(5)))); + propertyNameID = *(message_parts->at(6)); + + if (*(message_parts->at(7)) == "literalreturn") + { + value.append(*(message_parts->at(7))); + value.append(" "); + value.append(*(message_parts->at(8))); + } else + { + value.append(*(message_parts->at(7))); + } + + instance = IcedTeaPluginUtilities::getInstanceFromMemberPtr(member); + + if (*(message_parts->at(4)) == "SetSlot") + { + property_identifier = browser_functions.getintidentifier(atoi(message_parts->at(6)->c_str())); + } else + { + java_result = java_request.getString(propertyNameID); + + // the result we want is in result_string (assuming there was no error) + if (java_result->error_occurred) + { + printf("Unable to get member name for setMember. Error occurred: %s\n", java_result->error_msg); + //goto cleanup; + } + + property_identifier = browser_functions.getstringidentifier(java_result->return_string->c_str()); + } + + AsyncCallThreadData thread_data = AsyncCallThreadData(); + thread_data.result_ready = false; + thread_data.parameters = std::vector<void*>(); + thread_data.result = std::string(); + + thread_data.parameters.push_back(instance); + thread_data.parameters.push_back(NPVARIANT_TO_OBJECT(*member)); + thread_data.parameters.push_back(&property_identifier); + thread_data.parameters.push_back(&value); + +#ifdef CHROMIUM_WORKAROUND + // Workaround for chromium + _setMember(&thread_data); + + if (!thread_data.call_successful) + { +#endif + thread_data.result_ready = false; + browser_functions.pluginthreadasynccall(instance, &_setMember, &thread_data); + + while (!thread_data.result_ready) usleep(2000); // wait till ready +#ifdef CHROMIUM_WORKAROUND + } +#endif + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &response); + response.append(" JavaScriptSetMember "); + plugin_to_java_bus->post(response.c_str()); + + cleanup: + + // property_name, type and value are deleted by _setMember + pthread_mutex_lock(&tc_mutex); + thread_count--; + pthread_mutex_unlock(&tc_mutex); +} + +/** + * Sends request member pointer to the Java side. + * + * This is a static function, called in another thread. Since certain data + * can only be requested from the main thread in Mozilla, this function + * does whatever it can seperately, and then makes an internal request that + * causes _sendMember to do the rest of the work. + * + * @param message_parts The request message + */ + +void +PluginRequestProcessor::sendMember(std::vector<std::string*>* message_parts) +{ + // member initialization + std::vector<std::string> args; + JavaRequestProcessor java_request = JavaRequestProcessor(); + JavaResultData* java_result; + NPVariant* parent_ptr; + + //int reference; + std::string member_id = std::string(); + std::string jsObjectClassID = std::string(); + std::string jsObjectConstructorID = std::string(); + std::string response = std::string(); + + NPIdentifier member_identifier; + + int method_id; + int instance_id; + int reference; + + // debug printout of parent thread data + IcedTeaPluginUtilities::printStringPtrVector("PluginRequestProcessor::getMember:", message_parts); + + reference = atoi(message_parts->at(3)->c_str()); + + // store info in local variables for easy access + instance_id = atoi(message_parts->at(1)->c_str()); + parent_ptr = (NPVariant*) (IcedTeaPluginUtilities::stringToJSID(message_parts->at(5))); + member_id.append(*(message_parts->at(6))); + + /** Request data from Java if necessary **/ + if (*(message_parts->at(4)) == "GetSlot") + { + member_identifier = browser_functions.getintidentifier(atoi(member_id.c_str())); + } else + { + // make a new request for getString, to get the name of the identifier + java_result = java_request.getString(member_id); + + // the result we want is in result_string (assuming there was no error) + if (java_result->error_occurred) + { + printf("Unable to process getMember request. Error occurred: %s\n", java_result->error_msg); + //goto cleanup; + } + + member_identifier = browser_functions.getstringidentifier(java_result->return_string->c_str()); + } + + AsyncCallThreadData thread_data = AsyncCallThreadData(); + thread_data.result_ready = false; + thread_data.parameters = std::vector<void*>(); + thread_data.result = std::string(); + + NPP instance = IcedTeaPluginUtilities::getInstanceFromMemberPtr(parent_ptr); + thread_data.parameters.push_back(instance); + thread_data.parameters.push_back(NPVARIANT_TO_OBJECT(*parent_ptr)); + thread_data.parameters.push_back(&member_identifier); + +#ifdef CHROMIUM_WORKAROUND + // Workaround for chromium + _getMember(&thread_data); + + if (!thread_data.call_successful) + { +#endif + thread_data.result_ready = false; + browser_functions.pluginthreadasynccall(instance, &_getMember, &thread_data); + + while (!thread_data.result_ready) usleep(2000); // wait till ready + +#ifdef CHROMIUM_WORKAROUND + } +#endif + + PLUGIN_DEBUG("Member PTR after internal request: %s\n", thread_data.result.c_str()); + + java_result = java_request.findClass(0, "netscape.javascript.JSObject"); + + // the result we want is in result_string (assuming there was no error) + if (java_result->error_occurred) + { + printf("Unable to process getMember request. Error occurred: %s\n", java_result->error_msg); + //goto cleanup; + } + + jsObjectClassID.append(*(java_result->return_string)); + + args = std::vector<std::string>(); + std::string longArg = "J"; + args.push_back(longArg); + + java_result = java_request.getMethodID(jsObjectClassID, + browser_functions.getstringidentifier("<init>"), + args); + + // the result we want is in result_string (assuming there was no error) + if (java_result->error_occurred) + { + printf("Unable to process getMember request. Error occurred: %s\n", java_result->error_msg); + //goto cleanup; + } + + jsObjectConstructorID.append(*(java_result->return_string)); + + // We have the method id. Now create a new object. + + args.clear(); + args.push_back(thread_data.result); + java_result = java_request.newObjectWithConstructor("", + jsObjectClassID, + jsObjectConstructorID, + args); + + // the result we want is in result_string (assuming there was no error) + if (java_result->error_occurred) + { + printf("Unable to process getMember request. Error occurred: %s\n", java_result->error_msg); + //goto cleanup; + } + + + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &response); + if (*(message_parts->at(2)) == "GetSlot") + { + response.append(" JavaScriptGetMember "); + } else { + response.append(" JavaScriptGetSlot "); + } + response.append(java_result->return_string->c_str()); + plugin_to_java_bus->post(response.c_str()); + + + // Now be a good citizen and help keep the heap free of garbage + cleanup: + + pthread_mutex_lock(&tc_mutex); + thread_count--; + pthread_mutex_unlock(&tc_mutex); +} + +/** + * Decrements reference count to given object + * + * @param message_parts The request message. + */ + +void +PluginRequestProcessor::finalize(std::vector<std::string*>* message_parts) +{ + std::string* type; + std::string* command; + int reference; + std::string response = std::string(); + std::string* variant_ptr_str; + NPVariant* variant_ptr; + NPObject* window_ptr; + int id; + + type = message_parts->at(0); + id = atoi(message_parts->at(1)->c_str()); + reference = atoi(message_parts->at(3)->c_str()); + variant_ptr_str = message_parts->at(5); + + NPP instance; + get_instance_from_id(id, instance); + + variant_ptr = (NPVariant*) IcedTeaPluginUtilities::stringToJSID(variant_ptr_str); + window_ptr = NPVARIANT_TO_OBJECT(*variant_ptr); + browser_functions.releaseobject(window_ptr); + + // remove reference + IcedTeaPluginUtilities::removeInstanceID(variant_ptr); + + // clear memory + free(variant_ptr); + + // We need the context 0 for backwards compatibility with the Java side + IcedTeaPluginUtilities::constructMessagePrefix(0, reference, &response); + response += " JavaScriptFinalize"; + + plugin_to_java_bus->post(response.c_str()); +} + + +void* +queue_processor(void* data) +{ + + PluginRequestProcessor* processor = (PluginRequestProcessor*) data; + std::vector<std::string*>* message_parts = NULL; + std::string command; + pthread_mutex_t wait_mutex = PTHREAD_MUTEX_INITIALIZER; // This is needed for API compat. and is unused + + PLUGIN_DEBUG("Queue processor initialized. Queue = %p\n", message_queue); + + while (true) + { + pthread_mutex_lock(&message_queue_mutex); + if (message_queue->size() > 0) + { + message_parts = message_queue->front(); + message_queue->erase(message_queue->begin()); + } + pthread_mutex_unlock(&message_queue_mutex); + + if (message_parts) + { + command = *(message_parts->at(4)); + + if (command == "GetMember") + { + processor->sendMember(message_parts); + } else if (command == "ToString") + { + processor->sendString(message_parts); + } else if (command == "SetMember") + { + // write methods are synchronized + pthread_mutex_lock(&syn_write_mutex); + processor->setMember(message_parts); + pthread_mutex_unlock(&syn_write_mutex); + } else if (command == "Call") + { + // write methods are synchronized + pthread_mutex_lock(&syn_write_mutex); + processor->call(message_parts); + pthread_mutex_unlock(&syn_write_mutex); + } else if (command == "Eval") + { + // write methods are synchronized + pthread_mutex_lock(&syn_write_mutex); + processor->eval(message_parts); + pthread_mutex_unlock(&syn_write_mutex); + } else if (command == "GetSlot") + { + // write methods are synchronized + pthread_mutex_lock(&syn_write_mutex); + processor->sendMember(message_parts); + pthread_mutex_unlock(&syn_write_mutex); + } else if (command == "SetSlot") + { + // write methods are synchronized + pthread_mutex_lock(&syn_write_mutex); + processor->setMember(message_parts); + pthread_mutex_unlock(&syn_write_mutex); + } else if (command == "Finalize") + { + // write methods are synchronized + pthread_mutex_lock(&syn_write_mutex); + processor->finalize(message_parts); + pthread_mutex_unlock(&syn_write_mutex); + } else + { + // Nothing matched + IcedTeaPluginUtilities::printStringPtrVector("Error: Unable to process message: ", message_parts); + } + + // Free memory for message_parts + IcedTeaPluginUtilities::freeStringPtrVector(message_parts); + + } else + { + pthread_cond_wait(&cond_message_available, &wait_mutex); + pthread_testcancel(); + } + + message_parts = NULL; + } + + PLUGIN_DEBUG("Queue processing stopped.\n"); +} + +/****************************************** + * Functions delegated to the main thread * + ******************************************/ + +void +_setMember(void* data) +{ + std::string* value; + + NPP instance; + NPVariant value_variant = NPVariant(); + NPObject* member; + NPIdentifier* property; + + std::vector<void*> parameters = ((AsyncCallThreadData*) data)->parameters; + instance = (NPP) parameters.at(0); + member = (NPObject*) parameters.at(1); + property = (NPIdentifier*) parameters.at(2); + value = (std::string*) parameters.at(3); + + PLUGIN_DEBUG("Setting %s on instance %p, object %p to value %s\n", browser_functions.utf8fromidentifier(*property), instance, member, value->c_str()); + + IcedTeaPluginUtilities::javaResultToNPVariant(instance, value, &value_variant); + + ((AsyncCallThreadData*) data)->call_successful = browser_functions.setproperty(instance, member, *property, &value_variant); + + ((AsyncCallThreadData*) data)->result_ready = true; +} + +void +_getMember(void* data) +{ + NPObject* parent_ptr; + NPVariant* member_ptr = new NPVariant(); + std::string member_ptr_str = std::string(); + NPP instance; + + std::vector<void*> parameters = ((AsyncCallThreadData*) data)->parameters; + + instance = (NPP) parameters.at(0); + parent_ptr = (NPObject*) parameters.at(1); + NPIdentifier* member_identifier = (NPIdentifier*) parameters.at(2); + + // Get the NPVariant corresponding to this member + PLUGIN_DEBUG("Looking for %p %p %p (%s)\n", instance, parent_ptr, member_identifier, browser_functions.utf8fromidentifier(*member_identifier)); + + if (!browser_functions.hasproperty(instance, parent_ptr, *member_identifier)) + { + printf("%s not found!\n", browser_functions.utf8fromidentifier(*member_identifier)); + } + ((AsyncCallThreadData*) data)->call_successful = browser_functions.getproperty(instance, parent_ptr, *member_identifier, member_ptr); + + IcedTeaPluginUtilities::printNPVariant(*member_ptr); + + if (((AsyncCallThreadData*) data)->call_successful) + { + IcedTeaPluginUtilities::JSIDToString(member_ptr, &member_ptr_str); + ((AsyncCallThreadData*) data)->result.append(member_ptr_str); + } + ((AsyncCallThreadData*) data)->result_ready = true; + + // store member -> instance link + IcedTeaPluginUtilities::storeInstanceID(member_ptr, instance); + + PLUGIN_DEBUG("_getMember returning.\n"); +} + +void +_eval(void* data) +{ + NPP instance; + NPObject* window_ptr; + std::string* script_str; + NPIdentifier script_identifier; + NPString script = NPString(); + NPVariant* eval_result = new NPVariant(); + std::string eval_result_ptr_str = std::string(); + + PLUGIN_DEBUG("_eval called\n"); + + std::vector<void*>* call_data = (std::vector<void*>*) data; + + instance = (NPP) call_data->at(0); + window_ptr = (NPObject*) call_data->at(1); + script_str = (std::string*) call_data->at(2); + +#if MOZILLA_VERSION_COLLAPSED < 1090200 + script.utf8characters = script_str->c_str(); + script.utf8length = script_str->size(); + + PLUGIN_DEBUG("Evaluating: %s\n", script.utf8characters); +#else + script.UTF8Characters = script_str->c_str(); + script.UTF8Length = script_str->size(); + + PLUGIN_DEBUG("Evaluating: %s\n", script.UTF8Characters); +#endif + + ((AsyncCallThreadData*) data)->call_successful = browser_functions.evaluate(instance, window_ptr, &script, eval_result); + IcedTeaPluginUtilities::printNPVariant(*eval_result); + + if (((AsyncCallThreadData*) data)->call_successful) + { + IcedTeaPluginUtilities::JSIDToString(eval_result, &eval_result_ptr_str); + ((AsyncCallThreadData*) data)->result.append(eval_result_ptr_str); + } + ((AsyncCallThreadData*) data)->result_ready = true; + + PLUGIN_DEBUG("_eval returning\n"); +} + + +void +_call(void* data) +{ + NPP instance; + NPObject* window_ptr; + std::string* function_name; + NPIdentifier function; + int* arg_count; + NPVariant* args; + NPVariant* call_result = new NPVariant(); + std::string call_result_ptr_str = std::string(); + + PLUGIN_DEBUG("_call called\n"); + + std::vector<void*>* call_data = (std::vector<void*>*) data; + + instance = (NPP) call_data->at(0); + window_ptr = (NPObject*) call_data->at(1); + function_name = (std::string*) call_data->at(2); + + function = browser_functions.getstringidentifier(function_name->c_str()); + arg_count = (int*) call_data->at(3); + args = (NPVariant*) call_data->at(4); + + for (int i=0; i < *arg_count; i++) { + IcedTeaPluginUtilities::printNPVariant(args[i]); + } + + PLUGIN_DEBUG("_calling\n"); + ((AsyncCallThreadData*) data)->call_successful = browser_functions.invoke(instance, window_ptr, function, args, *arg_count, call_result); + PLUGIN_DEBUG("_called\n"); + + IcedTeaPluginUtilities::printNPVariant(*call_result); + + if (((AsyncCallThreadData*) data)->call_successful) + { + IcedTeaPluginUtilities::JSIDToString(call_result, &call_result_ptr_str); + ((AsyncCallThreadData*) data)->result.append(call_result_ptr_str); + } + + ((AsyncCallThreadData*) data)->result_ready = true; + + PLUGIN_DEBUG("_call returning\n"); +} + +void +_getString(void* data) +{ + NPP instance; + NPObject* object; + NPIdentifier toString = browser_functions.getstringidentifier("toString"); + NPVariant tostring_result; + std::string result = std::string(); + + std::vector<void*>* call_data = (std::vector<void*>*) data; + instance = (NPP) call_data->at(0); + NPVariant* variant = (NPVariant*) call_data->at(1); + + PLUGIN_DEBUG("_getString called with %p and %p\n", instance, variant); + + if (NPVARIANT_IS_OBJECT(*variant)) + { + ((AsyncCallThreadData*) data)->call_successful = browser_functions.invoke(instance, NPVARIANT_TO_OBJECT(*variant), toString, NULL, 0, &tostring_result); + } + else + { + IcedTeaPluginUtilities::NPVariantToString(*variant, &result); + tostring_result = NPVariant(); + STRINGZ_TO_NPVARIANT(result.c_str(), tostring_result); + ((AsyncCallThreadData*) data)->call_successful = true; + } + + PLUGIN_DEBUG("ToString result: "); + IcedTeaPluginUtilities::printNPVariant(tostring_result); + + if (((AsyncCallThreadData*) data)->call_successful) + { + createJavaObjectFromVariant(instance, tostring_result, &(((AsyncCallThreadData*) data)->result)); + } + ((AsyncCallThreadData*) data)->result_ready = true; + + PLUGIN_DEBUG("_getString returning\n"); +} + diff --git a/plugin/icedteanp/IcedTeaPluginRequestProcessor.h b/plugin/icedteanp/IcedTeaPluginRequestProcessor.h new file mode 100644 index 0000000..4d31248 --- /dev/null +++ b/plugin/icedteanp/IcedTeaPluginRequestProcessor.h @@ -0,0 +1,146 @@ +/* IcedTeaPluginRequestProcessor.h + + Copyright (C) 2009, 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. */ + +#ifndef __ICEDTEAPLUGINREQUESTPROCESSOR_H__ +#define __ICEDTEAPLUGINREQUESTPROCESSOR_H__ + +#include <map> +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <time.h> + +#include <npapi.h> + +#if MOZILLA_VERSION_COLLAPSED < 1090100 +#include <npupp.h> +#else +#include <npapi.h> +#include <npruntime.h> +#endif + +#include "IcedTeaPluginUtils.h" +#include "IcedTeaJavaRequestProcessor.h" + +/** + * Data structure passed to functions called in a new thread. + */ + +typedef struct async_call_thread_data +{ + std::vector<void*> parameters; + std::string result; + bool result_ready; + bool call_successful; +} AsyncCallThreadData; + +/* Internal request reference counter */ +static long internal_req_ref_counter; + +/* Given a value and type, performs the appropriate Java->JS type + * mapping and puts it in the given variant */ + +static void convertToNPVariant(std::string value, std::string type, NPVariant* result_variant); + +// Internal methods that need to run in main thread +void _getMember(void* data); +void _setMember(void* data); +void _call(void* data); +void _eval(void* data); +void _getString(void* data); + +static pthread_mutex_t tc_mutex = PTHREAD_MUTEX_INITIALIZER; +static int thread_count = 0; + +void* queue_processor(void* data); + +/* Mutex to ensure that the request queue is accessed synchronously */ +extern pthread_mutex_t message_queue_mutex; + +/* Mutex to ensure synchronized writes */ +extern pthread_mutex_t syn_write_mutex; + +/* Queue for holding messages that get processed in a separate thread */ +extern std::vector< std::vector<std::string*>* >* message_queue; + +/** + * Processes requests made TO the plugin (by java or anyone else) + */ +class PluginRequestProcessor : public BusSubscriber +{ + private: + + /* Requests that are still pending */ + std::map<pthread_t, uintmax_t>* pendingRequests; + + /* Dispatch request processing to a new thread for asynch. processing */ + void dispatch(void* func_ptr (void*), std::vector<std::string>* message, std::string* src); + + /* Send main window pointer to Java */ + void sendWindow(std::vector<std::string*>* message_parts); + + /* Stores the variant on java side */ + void storeVariantInJava(NPVariant variant, std::string* result); + + public: + PluginRequestProcessor(); /* Constructor */ + ~PluginRequestProcessor(); /* Destructor */ + + /* Process new requests (if applicable) */ + virtual bool newMessageOnBus(const char* message); + + /* Send member ID to Java */ + void sendMember(std::vector<std::string*>* message_parts); + + /* Set member to given value */ + void setMember(std::vector<std::string*>* message_parts); + + /* Send string value of requested object */ + void sendString(std::vector<std::string*>* message_parts); + + /* Evaluate the given script */ + void eval(std::vector<std::string*>* message_parts); + + /* Evaluate the given script */ + void call(std::vector<std::string*>* message_parts); + + /* Decrements reference count for given object */ + void finalize(std::vector<std::string*>* message_parts); +}; + +#endif // __ICEDTEAPLUGINREQUESTPROCESSOR_H__ diff --git a/plugin/icedteanp/IcedTeaPluginUtils.cc b/plugin/icedteanp/IcedTeaPluginUtils.cc new file mode 100644 index 0000000..501cd32 --- /dev/null +++ b/plugin/icedteanp/IcedTeaPluginUtils.cc @@ -0,0 +1,1047 @@ +/* IcedTeaPluginUtils.cc + + Copyright (C) 2009, 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. */ + +#include "IcedTeaNPPlugin.h" +#include "IcedTeaScriptablePluginObject.h" +#include "IcedTeaPluginUtils.h" + +/** + * Misc. utility functions used by the plugin + */ + +/*********************************************** + * Begin IcedTeaPluginUtilities implementation * +************************************************/ + +// Initialize static variables +int IcedTeaPluginUtilities::reference = -1; +pthread_mutex_t IcedTeaPluginUtilities::reference_mutex = PTHREAD_MUTEX_INITIALIZER; +std::map<void*, NPP>* IcedTeaPluginUtilities::instance_map = new std::map<void*, NPP>(); +std::map<std::string, NPObject*>* IcedTeaPluginUtilities::object_map = new std::map<std::string, NPObject*>(); + +/** + * Given a context number, constructs a message prefix to send to Java + * + * @param context The context of the request + * @return The string prefix (allocated on heap) + */ + +void +IcedTeaPluginUtilities::constructMessagePrefix(int context, std::string *result) +{ + std::string context_str = std::string(); + + itoa(context, &context_str); + + result->append("context "); + result->append(context_str); + result->append(" reference -1"); + +} + +/** + * Given a context number, and reference number, constructs a message prefix to + * send to Java + * + * @param context The context of the request + * @param rerefence The reference number of the request + * @param result The message + */ + +void +IcedTeaPluginUtilities::constructMessagePrefix(int context, int reference, std::string* result) +{ + // Until security is implemented, use file:// source for _everything_ + + std::string context_str = std::string(); + std::string reference_str = std::string(); + + itoa(context, &context_str); + itoa(reference, &reference_str); + + *result += "context "; + result->append(context_str); + *result += " reference "; + result->append(reference_str); +} + +/** + * Given a context number, reference number, and source location, constructs + * a message prefix to send to Java + * + * @param context The context of the request + * @param rerefence The reference number of the request + * @param address The address for the script that made the request + * @param result The message + */ + +void +IcedTeaPluginUtilities::constructMessagePrefix(int context, int reference, + std::string address, + std::string* result) +{ + std::string context_str = std::string(); + std::string reference_str = std::string(); + + itoa(context, &context_str); + itoa(reference, &reference_str); + + *result += "context "; + result->append(context_str); + *result += " reference "; + result->append(reference_str); + + if (address.length() > 0) + { + *result += " src "; + result->append(address); + } +} + +/** + * Returns a string representation of a void pointer + * + * @param id The pointer + * @param result The string representation + */ + +void +IcedTeaPluginUtilities::JSIDToString(void* id, std::string* result) +{ + + char* id_str = (char*) malloc(sizeof(char)*20); // max = long long = 8446744073709551615 == 19 chars + + if (sizeof(void*) == sizeof(long long)) + { + sprintf(id_str, "%llu", id); + } + else + { + sprintf(id_str, "%lu", id); // else use long + } + + result->append(id_str); + + PLUGIN_DEBUG("Converting pointer %p to %s\n", id, id_str); + free(id_str); +} + +/** + * Returns a void pointer from a string representation + * + * @param id_str The string representation + * @return The pointer + */ + +void* +IcedTeaPluginUtilities::stringToJSID(std::string id_str) +{ + void* ptr; + if (sizeof(void*) == sizeof(long long)) + { + PLUGIN_DEBUG("Casting (long long) \"%s\" -- %llu\n", id_str.c_str(), strtoull(id_str.c_str(), NULL, 0)); + ptr = reinterpret_cast <void*> ((unsigned long long) strtoull(id_str.c_str(), NULL, 0)); + } else + { + PLUGIN_DEBUG("Casting (long) \"%s\" -- %lu\n", id_str.c_str(), strtoul(id_str.c_str(), NULL, 0)); + ptr = reinterpret_cast <void*> ((unsigned long) strtoul(id_str.c_str(), NULL, 0)); + } + + PLUGIN_DEBUG("Casted: %p\n", ptr); + + return ptr; +} + +/** + * Returns a void pointer from a string representation + * + * @param id_str The pointer to the string representation + * @return The pointer + */ + +void* +IcedTeaPluginUtilities::stringToJSID(std::string* id_str) +{ + void* ptr; + if (sizeof(void*) == sizeof(long long)) + { + PLUGIN_DEBUG("Casting (long long) \"%s\" -- %llu\n", id_str->c_str(), strtoull(id_str->c_str(), NULL, 0)); + ptr = reinterpret_cast <void*> ((unsigned long long) strtoull(id_str->c_str(), NULL, 0)); + } else + { + PLUGIN_DEBUG("Casting (long) \"%s\" -- %lu\n", id_str->c_str(), strtoul(id_str->c_str(), NULL, 0)); + ptr = reinterpret_cast <void*> ((unsigned long) strtoul(id_str->c_str(), NULL, 0)); + } + + PLUGIN_DEBUG("Casted: %p\n", ptr); + + return ptr; +} + +/** + * Increments the global reference number and returns it. + * + * This function is thread-safe. + */ +int +IcedTeaPluginUtilities::getReference() +{ + pthread_mutex_lock(&reference_mutex); + + // If we are nearing the max, reset + if (reference < -0x7FFFFFFF + 10) { + reference = -1; + } + + reference--; + pthread_mutex_unlock(&reference_mutex); + + return reference; +} + +/** + * Decrements the global reference number. + * + * This function is thread-safe. + */ +void +IcedTeaPluginUtilities::releaseReference() +{ + // do nothing for now +} + +/** + * Converts integer to char* + * + * @param i The integer to convert to ascii + * @param result The resulting string + */ +void +IcedTeaPluginUtilities::itoa(int i, std::string* result) +{ + // largest possible integer is 10 digits long + char* int_str = (char*) malloc(sizeof(char)*11); + sprintf(int_str, "%d", i); + result->append(int_str); + + free(int_str); +} + +/** + * Frees memory from a string* vector + * + * The vector deconstructor will only delete string pointers upon being + * called. This function frees the associated string memory as well. + * + * @param v The vector whose strings are to be freed + */ +void +IcedTeaPluginUtilities::freeStringPtrVector(std::vector<std::string*>* v) +{ + if (v) + { + for (int i=0; i < v->size(); i++) { + delete v->at(i); + } + + delete v; + } + +} + +/** + * Given a string, splits it on the given delimiters. + * + * @param str The string to split + * @param The delimiters to split on + * @return A string vector containing the aplit components + */ + +std::vector<std::string*>* +IcedTeaPluginUtilities::strSplit(const char* str, const char* delim) +{ + std::vector<std::string*>* v = new std::vector<std::string*>(); + v->reserve(strlen(str)/2); + char* copy; + + // Tokening is done on a copy + copy = (char*) malloc (sizeof(char)*strlen(str) + 1); + strcpy(copy, str); + + char* tok_ptr; + tok_ptr = strtok (copy, delim); + + while (tok_ptr != NULL) + { + // Allocation on heap since caller has no way to knowing how much will + // be needed. Make sure caller cleans up! + std::string* s = new std::string(); + s->append(tok_ptr); + v->push_back(s); + tok_ptr = strtok (NULL, " "); + } + + return v; +} + +/** + * Given a unicode byte array, converts it to a UTF8 string + * + * The actual contents in the array may be surrounded by other data. + * + * e.g. with length 5, begin = 3, + * unicode_byte_array = "37 28 5 48 45 4c 4c 4f 9e 47": + * + * We'd start at 3 i.e. "48" and go on for 5 i.e. upto and including "4f". + * So we convert "48 45 4c 4c 4f" which is "hello" + * + * @param length The length of the string + * @param begin Where in the array to begin conversion + * @param result_unicode_str The return variable in which the + * converted string is placed + */ + +void +IcedTeaPluginUtilities::getUTF8String(int length, int begin, std::vector<std::string*>* unicode_byte_array, std::string* result_unicode_str) +{ + result_unicode_str->clear(); + result_unicode_str->reserve(unicode_byte_array->size()/2); + for (int i = begin; i < begin+length; i++) + result_unicode_str->push_back((char) strtol(unicode_byte_array->at(i)->c_str(), NULL, 16)); + + PLUGIN_DEBUG("Converted UTF-8 string: %s. Length=%d\n", result_unicode_str->c_str(), result_unicode_str->length()); +} + +/** + * Given a UTF8 string, converts it to a space delimited string of hex characters + * + * The first element in the return array is the length of the string + * + * e.g. "hello" would convert to: "5 48 45 4c 4c 4f" + * + * @param str The string to convert + * @param urt_str The result + */ + +void +IcedTeaPluginUtilities::convertStringToUTF8(std::string* str, std::string* utf_str) +{ + std::ostringstream ostream; + + std::string length = std::string(); + itoa(str->length(), &length); + + ostream << length; + + // UTF-8 characters are 4-bytes max + space + '\0' + char* hex_value = (char*) malloc(sizeof(char)*10); + + for (int i = 0; i < str->length(); i++) + { + sprintf(hex_value, " %hx", str->at(i)); + ostream << hex_value; + } + + utf_str->clear(); + *utf_str = ostream.str(); + + free(hex_value); + PLUGIN_DEBUG("Converted %s to UTF-8 string %s\n", str->c_str(), utf_str->c_str()); +} + +/** + * Given a unicode byte array, converts it to a UTF16LE/UCS-2 string + * + * This works in a manner similar to getUTF8String, except that it reads 2 + * slots for each byte. + * + * @param length The length of the string + * @param begin Where in the array to begin conversion + * @param result_unicode_str The return variable in which the + * converted string is placed + */ +void +IcedTeaPluginUtilities::getUTF16LEString(int length, int begin, std::vector<std::string*>* unicode_byte_array, std::wstring* result_unicode_str) +{ + + wchar_t c; + + if (plugin_debug) printf("Converted UTF-16LE string: "); + + result_unicode_str->clear(); + for (int i = begin; i < begin+length; i+=2) + { + int low = strtol(unicode_byte_array->at(i)->c_str(), NULL, 16); + int high = strtol(unicode_byte_array->at(i+1)->c_str(), NULL, 16); + + c = ((high << 8) | low); + + if ((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9')) + { + if (plugin_debug) printf("%c", c); + } + + result_unicode_str->push_back(c); + } + + // not routing via debug print macros due to wide-string issues + if (plugin_debug) printf(". Length=%d\n", result_unicode_str->length()); +} + +/* + * Prints the given string vector (if debug is true) + * + * @param prefix The prefix to print before printing the vector contents + * @param cv The string vector whose contents are to be printed + */ +void +IcedTeaPluginUtilities::printStringVector(const char* prefix, std::vector<std::string>* str_vector) +{ + + // This is a CPU intensive function. Run only if debugging + if (!plugin_debug) + return; + + std::string* str = new std::string(); + *str += "{ "; + for (int i=0; i < str_vector->size(); i++) + { + *str += str_vector->at(i); + + if (i != str_vector->size() - 1) + *str += ", "; + } + + *str += " }"; + + PLUGIN_DEBUG("%s %s\n", prefix, str->c_str()); + + delete str; +} + +const gchar* +IcedTeaPluginUtilities::getSourceFromInstance(NPP instance) +{ + // At the moment, src cannot be securely fetched via NPAPI + // See: + // http://www.mail-archive.com/[email protected]/msg04872.html + + // Since we use the insecure window.location.href attribute to compute + // source, we cannot use it to make security decisions. Therefore, + // instance associated source will always return empty + + //ITNPPluginData* data = (ITNPPluginData*) instance->pdata; + //return (data->source) ? data->source : ""; + + return "http://null.null"; +} + +/** + * Stores a window pointer <-> instance mapping + * + * @param member_ptr The pointer key + * @param instance The instance to associate with this pointer + */ + +void +IcedTeaPluginUtilities::storeInstanceID(void* member_ptr, NPP instance) +{ + PLUGIN_DEBUG("Storing instance %p with key %p\n", instance, member_ptr); + instance_map->insert(std::make_pair(member_ptr, instance)); +} + +/** + * Removes a window pointer <-> instance mapping + * + * @param member_ptr The key to remove + */ + +void +IcedTeaPluginUtilities::removeInstanceID(void* member_ptr) +{ + PLUGIN_DEBUG("Removing key %p from instance map\n", member_ptr); + instance_map->erase(member_ptr); +} + +/** + * Removes all mappings to a given instance, and all associated objects + */ +void +IcedTeaPluginUtilities::invalidateInstance(NPP instance) +{ + PLUGIN_DEBUG("Invalidating instance %p\n", instance); + + std::map<void*,NPP>::iterator iterator; + + for (iterator = instance_map->begin(); iterator != instance_map->end(); iterator++) + { + if ((*iterator).second == instance) + { + instance_map->erase((*iterator).first); + } + } +} + +/** + * Given the window pointer, returns the instance associated with it + * + * @param member_ptr The pointer key + * @return The associated instance + */ + +NPP +IcedTeaPluginUtilities::getInstanceFromMemberPtr(void* member_ptr) +{ + + NPP instance = NULL; + PLUGIN_DEBUG("getInstanceFromMemberPtr looking for %p\n", member_ptr); + + std::map<void*, NPP>::iterator iterator = instance_map->find(member_ptr); + + if (iterator != instance_map->end()) + { + instance = instance_map->find(member_ptr)->second; + PLUGIN_DEBUG("getInstanceFromMemberPtr found %p. Instance = %p\n", member_ptr, instance); + } + + return instance; +} + +/** + * Given a java id key ('classid:instanceid'), returns the associated valid NPObject, if any + * + * @param key the key + * @return The associated active NPObject, NULL otherwise + */ + +NPObject* +IcedTeaPluginUtilities::getNPObjectFromJavaKey(std::string key) +{ + + NPObject* object = NULL; + PLUGIN_DEBUG("getNPObjectFromJavaKey looking for %s\n", key.c_str()); + + std::map<std::string, NPObject*>::iterator iterator = object_map->find(key); + + if (iterator != object_map->end()) + { + NPObject* mapped_object = object_map->find(key)->second; + + if (getInstanceFromMemberPtr(mapped_object) != NULL) + { + object = mapped_object; + PLUGIN_DEBUG("getNPObjectFromJavaKey found %s. NPObject = %p\n", key.c_str(), object); + } + } + + return object; +} + +/** + * Stores a java id key <-> NPObject mapping + * + * @param key The Java ID Key + * @param object The object to map to + */ + +void +IcedTeaPluginUtilities::storeObjectMapping(std::string key, NPObject* object) +{ + PLUGIN_DEBUG("Storing object %p with key %s\n", object, key.c_str()); + object_map->insert(std::make_pair(key, object)); +} + +/** + * Removes a java id key <-> NPObject mapping + * + * @param key The key to remove + */ + +void +IcedTeaPluginUtilities::removeObjectMapping(std::string key) +{ + PLUGIN_DEBUG("Removing key %s from object map\n", key.c_str()); + object_map->erase(key); +} + +/* + * Similar to printStringVector, but takes a vector of string pointers instead + * + * @param prefix The prefix to print before printing the vector contents + * @param cv The string* vector whose contents are to be printed + */ + +void +IcedTeaPluginUtilities::printStringPtrVector(const char* prefix, std::vector<std::string*>* str_ptr_vector) +{ + // This is a CPU intensive function. Run only if debugging + if (!plugin_debug) + return; + + std::string* str = new std::string(); + *str += "{ "; + for (int i=0; i < str_ptr_vector->size(); i++) + { + *str += *(str_ptr_vector->at(i)); + + if (i != str_ptr_vector->size() - 1) + *str += ", "; + } + + *str += " }"; + + PLUGIN_DEBUG("%s %s\n", prefix, str->c_str()); + + delete str; +} + +void +IcedTeaPluginUtilities::printNPVariant(NPVariant variant) +{ + // This is a CPU intensive function. Run only if debugging + if (!plugin_debug) + return; + + if (NPVARIANT_IS_VOID(variant)) + { + PLUGIN_DEBUG("VOID %d\n", variant); + } + else if (NPVARIANT_IS_NULL(variant)) + { + PLUGIN_DEBUG("NULL\n", variant); + } + else if (NPVARIANT_IS_BOOLEAN(variant)) + { + PLUGIN_DEBUG("BOOL: %d\n", NPVARIANT_TO_BOOLEAN(variant)); + } + else if (NPVARIANT_IS_INT32(variant)) + { + PLUGIN_DEBUG("INT32: %d\n", NPVARIANT_TO_INT32(variant)); + } + else if (NPVARIANT_IS_DOUBLE(variant)) + { + PLUGIN_DEBUG("DOUBLE: %f\n", NPVARIANT_TO_DOUBLE(variant)); + } + else if (NPVARIANT_IS_STRING(variant)) + { +#if MOZILLA_VERSION_COLLAPSED < 1090200 + PLUGIN_DEBUG("STRING: %s\n", NPVARIANT_TO_STRING(variant).utf8characters); +#else + PLUGIN_DEBUG("STRING: %s\n", NPVARIANT_TO_STRING(variant).UTF8Characters); +#endif + } + else + { + PLUGIN_DEBUG("OBJ: %p\n", NPVARIANT_TO_OBJECT(variant)); + } +} + +void +IcedTeaPluginUtilities::NPVariantToString(NPVariant variant, std::string* result) +{ + char* str = (char*) malloc(sizeof(char)*32); // enough for everything except string + + if (NPVARIANT_IS_VOID(variant)) + { + sprintf(str, "%p", variant); + } + else if (NPVARIANT_IS_NULL(variant)) + { + sprintf(str, "NULL"); + } + else if (NPVARIANT_IS_BOOLEAN(variant)) + { + if (NPVARIANT_TO_BOOLEAN(variant)) + sprintf(str, "true"); + else + sprintf(str, "false"); + } + else if (NPVARIANT_IS_INT32(variant)) + { + sprintf(str, "%d", NPVARIANT_TO_INT32(variant)); + } + else if (NPVARIANT_IS_DOUBLE(variant)) + { + sprintf(str, "%f", NPVARIANT_TO_DOUBLE(variant));; + } + else if (NPVARIANT_IS_STRING(variant)) + { + free(str); +#if MOZILLA_VERSION_COLLAPSED < 1090200 + str = (char*) malloc(sizeof(char)*NPVARIANT_TO_STRING(variant).utf8length); + sprintf(str, "%s", NPVARIANT_TO_STRING(variant).utf8characters); +#else + str = (char*) malloc(sizeof(char)*NPVARIANT_TO_STRING(variant).UTF8Length); + sprintf(str, "%s", NPVARIANT_TO_STRING(variant).UTF8Characters); +#endif + } + else + { + sprintf(str, "[Object %p]", variant); + } + + result->append(str); + free(str); +} + +bool +IcedTeaPluginUtilities::javaResultToNPVariant(NPP instance, + std::string* java_value, + NPVariant* variant) +{ + JavaRequestProcessor java_request = JavaRequestProcessor(); + JavaResultData* java_result; + + if (java_value->find("literalreturn") == 0) + { + // 'literalreturn ' == 14 to skip + std::string value = java_value->substr(14); + + // VOID/BOOLEAN/NUMBER + + if (value == "void") + { + PLUGIN_DEBUG("Method call returned void\n"); + VOID_TO_NPVARIANT(*variant); + } else if (value == "null") + { + PLUGIN_DEBUG("Method call returned null\n"); + NULL_TO_NPVARIANT(*variant); + }else if (value == "true") + { + PLUGIN_DEBUG("Method call returned a boolean (true)\n"); + BOOLEAN_TO_NPVARIANT(true, *variant); + } else if (value == "false") + { + PLUGIN_DEBUG("Method call returned a boolean (false)\n"); + BOOLEAN_TO_NPVARIANT(false, *variant); + } else + { + double d = strtod(value.c_str(), NULL); + + // See if it is convertible to int + if (value.find(".") != std::string::npos || + d < -(0x7fffffffL - 1L) || + d > 0x7fffffffL) + { + PLUGIN_DEBUG("Method call returned a double %f\n", d); + DOUBLE_TO_NPVARIANT(d, *variant); + } else + { + int32_t i = (int32_t) d; + PLUGIN_DEBUG("Method call returned an int %d\n", i); + INT32_TO_NPVARIANT(i, *variant); + } + } + } else { + // Else this is a complex java object + + // To keep code a little bit cleaner, we create variables with proper descriptive names + std::string return_obj_instance_id = std::string(); + std::string return_obj_class_id = std::string(); + std::string return_obj_class_name = std::string(); + return_obj_instance_id.append(*java_value); + + // Find out the class name first, because string is a special case + java_result = java_request.getClassName(return_obj_instance_id); + + if (java_result->error_occurred) + { + return false; + } + + return_obj_class_name.append(*(java_result->return_string)); + + if (return_obj_class_name == "java.lang.String") + { + // String is a special case as NPVariant can handle it directly + java_result = java_request.getString(return_obj_instance_id); + + if (java_result->error_occurred) + { + return false; + } + + // needs to be on the heap + NPUTF8* return_str = (NPUTF8*) malloc(sizeof(NPUTF8)*java_result->return_string->size() + 1); + strcpy(return_str, java_result->return_string->c_str()); + + PLUGIN_DEBUG("Method call returned a string: \"%s\"\n", return_str); + STRINGZ_TO_NPVARIANT(return_str, *variant); + + } else { + + // Else this is a regular class. Reference the class object so + // we can construct an NPObject with it and the instance + java_result = java_request.getClassID(return_obj_instance_id); + + if (java_result->error_occurred) + { + return false; + } + + return_obj_class_id.append(*(java_result->return_string)); + + NPObject* obj; + + if (return_obj_class_name.find('[') == 0) // array + obj = IcedTeaScriptableJavaPackageObject::get_scriptable_java_object( + instance, + return_obj_class_id, return_obj_instance_id, true); + else + obj = IcedTeaScriptableJavaPackageObject::get_scriptable_java_object( + instance, + return_obj_class_id, return_obj_instance_id, false); + + OBJECT_TO_NPVARIANT(obj, *variant); + } + } + + return true; +} + +bool +IcedTeaPluginUtilities::isObjectJSArray(NPP instance, NPObject* object) +{ + + NPVariant constructor_v = NPVariant(); + NPIdentifier constructor_id = browser_functions.getstringidentifier("constructor"); + browser_functions.getproperty(instance, object, constructor_id, &constructor_v); + IcedTeaPluginUtilities::printNPVariant(constructor_v); + + // void constructor => not an array + if (NPVARIANT_IS_VOID(constructor_v)) + return false; + + NPObject* constructor = NPVARIANT_TO_OBJECT(constructor_v); + + NPVariant constructor_str; + NPIdentifier toString = browser_functions.getstringidentifier("toString"); + browser_functions.invoke(instance, constructor, toString, NULL, 0, &constructor_str); + IcedTeaPluginUtilities::printNPVariant(constructor_str); + + std::string constructor_name = std::string(); + +#if MOZILLA_VERSION_COLLAPSED < 1090200 + constructor_name.append(NPVARIANT_TO_STRING(constructor_str).utf8characters); +#else + constructor_name.append(NPVARIANT_TO_STRING(constructor_str).UTF8Characters); +#endif + + PLUGIN_DEBUG("Constructor for NPObject is %s\n", constructor_name.c_str()); + + return constructor_name.find("function Array") == 0; +} + +void +IcedTeaPluginUtilities::decodeURL(const gchar* url, gchar** decoded_url) +{ + + PLUGIN_DEBUG("GOT URL: %s -- %s\n", url, *decoded_url); + int length = strlen(url); + for (int i=0; i < length; i++) + { + if (url[i] == '%' && i < length - 2) + { + unsigned char code1 = (unsigned char) url[i+1]; + unsigned char code2 = (unsigned char) url[i+2]; + + if (!IS_VALID_HEX(&code1) || !IS_VALID_HEX(&code2)) + continue; + + // Convert hex value to integer + int converted1 = HEX_TO_INT(&code1); + int converted2 = HEX_TO_INT(&code2); + + // bitshift 4 to simulate *16 + int value = (converted1 << 4) + converted2; + char decoded = value; + + strncat(*decoded_url, &decoded, 1); + + i += 2; + } else + { + strncat(*decoded_url, &url[i], 1); + } + } + + PLUGIN_DEBUG("SENDING URL: %s\n", *decoded_url); +} + +/****************************************** + * Begin JavaMessageSender implementation * + ****************************************** + * + * This implementation is very simple and is therefore folded into this file + * rather than a new one. + */ + +/** + * Sends to the Java side + * + * @param message The message to send. + * @param returns whether the message was consumable (always true) + */ + +bool +JavaMessageSender::newMessageOnBus(const char* message) +{ + char* msg = (char*) malloc(sizeof(char)*strlen(message) + 1); + strcpy(msg, message); + plugin_send_message_to_appletviewer(msg); + + free(msg); + msg = NULL; + + // Always successful + return true; +} + +/*********************************** + * Begin MessageBus implementation * + ***********************************/ + +/** + * Constructor. + * + * Initializes the mutexes needed by the other functions. + */ +MessageBus::MessageBus() +{ + int ret; + + ret = pthread_mutex_init(&subscriber_mutex, NULL); + + if(ret) + PLUGIN_DEBUG("Error: Unable to initialize subscriber mutex: %d\n", ret); + + ret = pthread_mutex_init(&msg_queue_mutex, NULL); + if(ret) + PLUGIN_DEBUG("Error: Unable to initialize message queue mutex: %d\n", ret); + + PLUGIN_DEBUG("Mutexs %p and %p initialized\n", &subscriber_mutex, &msg_queue_mutex); +} + +/** + * Destructor. + * + * Destroy the mutexes initialized by the constructor. + */ + +MessageBus::~MessageBus() +{ + PLUGIN_DEBUG("MessageBus::~MessageBus\n"); + + int ret; + + ret = pthread_mutex_destroy(&subscriber_mutex); + if(ret) + PLUGIN_DEBUG("Error: Unable to destroy subscriber mutex: %d\n", ret); + + ret = pthread_mutex_destroy(&msg_queue_mutex); + if(ret) + PLUGIN_DEBUG("Error: Unable to destroy message queue mutex: %d\n", ret); +} + +/** + * Adds the given BusSubscriber as a subscriber to self + * + * @param b The BusSubscriber to subscribe + */ +void +MessageBus::subscribe(BusSubscriber* b) +{ + // Applets may initialize in parallel. So lock before pushing. + + PLUGIN_DEBUG("Subscribing %p to bus %p\n", b, this); + pthread_mutex_lock(&subscriber_mutex); + subscribers.push_back(b); + pthread_mutex_unlock(&subscriber_mutex); +} + +/** + * Removes the given BusSubscriber from the subscriber list + * + * @param b The BusSubscriber to ubsubscribe + */ +void +MessageBus::unSubscribe(BusSubscriber* b) +{ + // Applets may initialize in parallel. So lock before pushing. + + PLUGIN_DEBUG("Un-subscribing %p from bus %p\n", b, this); + pthread_mutex_lock(&subscriber_mutex); + subscribers.remove(b); + pthread_mutex_unlock(&subscriber_mutex); +} + +/** + * Notifies all subscribers with the given message + * + * @param message The message to send to the subscribers + */ +void +MessageBus::post(const char* message) +{ + char* msg = (char*) malloc(sizeof(char)*strlen(message) + 1); + bool message_consumed = false; + + // consumer frees this memory + strcpy(msg, message); + + PLUGIN_DEBUG("Trying to lock %p...\n", &msg_queue_mutex); + pthread_mutex_lock(&subscriber_mutex); + + PLUGIN_DEBUG("Message %s received on bus. Notifying subscribers.\n", msg); + + std::list<BusSubscriber*>::const_iterator i; + for( i = subscribers.begin(); i != subscribers.end() && !message_consumed; ++i ) { + PLUGIN_DEBUG("Notifying subscriber %p of %s\n", *i, msg); + message_consumed = ((BusSubscriber*) *i)->newMessageOnBus(msg); + } + + pthread_mutex_unlock(&subscriber_mutex); + + if (!message_consumed) + PLUGIN_DEBUG("Warning: No consumer found for message %s\n", msg); + + PLUGIN_DEBUG("%p unlocked...\n", &msg_queue_mutex); +} diff --git a/plugin/icedteanp/IcedTeaPluginUtils.h b/plugin/icedteanp/IcedTeaPluginUtils.h new file mode 100644 index 0000000..8584fb8 --- /dev/null +++ b/plugin/icedteanp/IcedTeaPluginUtils.h @@ -0,0 +1,296 @@ +/* IcedTeaPluginUtils.h + + Copyright (C) 2009, 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. */ + +/** + * Utility classes for the IcedTeaPlugin + */ + +#ifndef __ICEDTEAPLUGINUTILS_H__ +#define __ICEDTEAPLUGINUTILS_H__ + +#include <pthread.h> +#include <stdio.h> + +#include <cstring> +#include <iostream> +#include <list> +#include <map> +#include <queue> +#include <sstream> +#include <string> +#include <vector> + +#include <npapi.h> + +#if MOZILLA_VERSION_COLLAPSED < 1090100 +#include <npupp.h> +#else +#include <npapi.h> +#include <npruntime.h> +#endif + +#include "IcedTeaNPPlugin.h" + +#define PLUGIN_DEBUG(...) \ + do \ + { \ + if (plugin_debug) \ + { \ + fprintf (stderr, "ITNPP Thread# %ld: ", pthread_self()); \ + fprintf (stderr, __VA_ARGS__); \ + } \ + } while (0) + +#define CHECK_JAVA_RESULT(result_data) \ +{ \ + if (((JavaResultData*) result_data)->error_occurred) \ + { \ + printf("Error: Error occurred on Java side: %s.\n", \ + ((JavaResultData*) result_data)->error_msg->c_str()); \ + return; \ + } \ +} + +#define HEX_TO_INT(c) \ + ((*c >= 'a') ? *c - 'a' + 10 : \ + (*c >= 'A') ? *c - 'A' + 10 : \ + *c - '0') + +#define IS_VALID_HEX(c) \ + ((*c >= '0' && *c <= '9') || \ + (*c >= 'a' && *c <= 'f') || \ + (*c >= 'A' && *c <= 'F')) + +/* + * This struct holds data specific to a Java operation requested by the plugin + */ +typedef struct java_result_data +{ + + // Return identifier (if applicable) + int return_identifier; + + // Return string (if applicable) + std::string* return_string; + + // Return wide/mb string (if applicable) + std::wstring* return_wstring; + + // Error message (if an error occurred) + std::string* error_msg; + + // Boolean indicating if an error occurred + bool error_occurred; + +} JavaResultData; + +/* + * Misc. utility functions + * + * This class is never instantiated and should contain static functions only + */ + +class IcedTeaPluginUtilities +{ + + private: + static int reference; /* Reference count */ + + /* Mutex lock for updating reference count */ + static pthread_mutex_t reference_mutex; + + /* Map holding window pointer<->instance relationships */ + static std::map<void*, NPP>* instance_map; + + /* Map holding java-side-obj-key->NPObject relationship */ + static std::map<std::string, NPObject*>* object_map; + + public: + + /* Constructs message prefix with given context */ + static void constructMessagePrefix(int context, + std::string* result); + + /* Constructs message prefix with given context and reference */ + static void constructMessagePrefix(int context, int reference, + std::string* result); + + /* Constructs message prefix with given context, reference and src */ + static void constructMessagePrefix(int context, int reference, + std::string address, + std::string* result); + + /* Converts given pointer to a string representation */ + static void JSIDToString(void* id, std::string* result); + + /* Converts the given string representation to a pointer */ + static void* stringToJSID(std::string id_str); + static void* stringToJSID(std::string* id_str); + + /* Increments reference count and returns it */ + static int getReference(); + + /* Decrements reference count */ + static void releaseReference(); + + /* Converts the given integer to a string */ + static void itoa(int i, std::string* result); + + /* Frees the given vector and the strings that its contents point to */ + static void freeStringPtrVector(std::vector<std::string*>* v); + + /* Splits the given string based on the delimiter provided */ + static std::vector<std::string*>* strSplit(const char* str, + const char* delim); + + /* Converts given unicode integer byte array to UTF8 string */ + static void getUTF8String(int length, int begin, + std::vector<std::string*>* unicode_byte_array, + std::string* result_unicode_str); + + /* Converts given UTF8 string to unicode integer byte array */ + static void convertStringToUTF8(std::string* str, + std::string* utf_str); + + /* Converts given unicode integer byte array to UTF16LE/UCS-2 string */ + static void getUTF16LEString(int length, int begin, + std::vector<std::string*>* unicode_byte_array, + std::wstring* result_unicode_str); + + /* Prints contents of given string vector */ + static void printStringVector(const char* prefix, std::vector<std::string>* cv); + + /* Prints contents of given string pointer vector */ + static void printStringPtrVector(const char* prefix, std::vector<std::string*>* cv); + + static std::string* variantToClassName(NPVariant variant); + + static void printNPVariant(NPVariant variant); + + static void NPVariantToString(NPVariant variant, std::string* result); + + static bool javaResultToNPVariant(NPP instance, + std::string* java_result, + NPVariant* variant); + + static const gchar* getSourceFromInstance(NPP instance); + + static void storeInstanceID(void* member_ptr, NPP instance); + + static void removeInstanceID(void* member_ptr); + + static NPP getInstanceFromMemberPtr(void* member_ptr); + + static NPObject* getNPObjectFromJavaKey(std::string key); + + static void storeObjectMapping(std::string key, NPObject* object); + + static void removeObjectMapping(std::string key); + + static void invalidateInstance(NPP instance); + + static bool isObjectJSArray(NPP instance, NPObject* object); + + static void decodeURL(const char* url, char** decoded_url); +}; + +/* + * A bus subscriber interface. Implementors must implement the newMessageOnBus + * method. + */ +class BusSubscriber +{ + private: + + public: + BusSubscriber() {} + + /* Notifies this subscriber that a new message as arrived */ + virtual bool newMessageOnBus(const char* message) = 0; +}; + +/* + * This implementation is very simple and is therefore folded into this file + * rather than a new one. + */ +class JavaMessageSender : public BusSubscriber +{ + private: + public: + + /* Sends given message to Java side */ + virtual bool newMessageOnBus(const char* message); +}; + +/* + * Represents a message bus. + * The bus can also have subscribers who are notified when a new message + * arrives. + */ +class MessageBus +{ + private: + /* Mutex for locking the message queue */ + pthread_mutex_t msg_queue_mutex; + + /* Mutex used when adjusting subscriber list */ + pthread_mutex_t subscriber_mutex; + + /* Subscriber list */ + std::list<BusSubscriber*> subscribers; + + /* Queued messages */ + std::queue<char*> msgQueue; + + public: + MessageBus(); + + ~MessageBus(); + + /* subscribe to this bus */ + void subscribe(BusSubscriber* b); + + /* unsubscribe from this bus */ + void unSubscribe(BusSubscriber* b); + + /* Post a message on to the bus (it is safe to free the message pointer + after this function returns) */ + void post(const char* message); +}; + +#endif // __ICEDTEAPLUGINUTILS_H__ diff --git a/plugin/icedteanp/IcedTeaRunnable.cc b/plugin/icedteanp/IcedTeaRunnable.cc new file mode 100644 index 0000000..6f92c89 --- /dev/null +++ b/plugin/icedteanp/IcedTeaRunnable.cc @@ -0,0 +1,75 @@ +/* IcedTeaRunnable.cc + + Copyright (C) 2009, 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. */ + +#include <stdio.h> +#include "IcedTeaRunnable.h" + +NS_IMPL_ISUPPORTS1 (IcedTeaRunnable, nsIRunnable) + +IcedTeaRunnable::IcedTeaRunnable () +{ +} + +IcedTeaRunnable::~IcedTeaRunnable () +{ +} + +NS_IMETHODIMP +IcedTeaRunnable::Run () +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +IcedTeaRunnableMethod::IcedTeaRunnableMethod (Method method, void* thread_data, void* result) +: method (method), + thread_data(thread_data), + result(result) +{ +} + +IcedTeaRunnableMethod::~IcedTeaRunnableMethod () +{ + } + +NS_IMETHODIMP +IcedTeaRunnableMethod::Run () +{ + printf("Running method...\n"); + (*method) (thread_data, result); + return NS_OK; +} diff --git a/plugin/icedteanp/IcedTeaRunnable.h b/plugin/icedteanp/IcedTeaRunnable.h new file mode 100644 index 0000000..d6e7a82 --- /dev/null +++ b/plugin/icedteanp/IcedTeaRunnable.h @@ -0,0 +1,104 @@ +/* IcedTeaRunnable.h + + Copyright (C) 2009, 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. */ + +#ifndef __ICEDTEARUNNABLE_H__ +#define __ICEDTEARUNNABLE_H__ + +#define MOZILLA 1 +#if MOZILLA + +#if MOZILLA_VERSION_COLLAPSED < 1090100 +#include <nsIRunnable.h> +#include <string> +#endif + +/* + * This struct holds the result from the main-thread dispatched method + */ +typedef struct result_data +{ + // Return identifier (if applicable) + int return_identifier; + + // Return string (if applicable) + std::string* return_string; + + // Return wide/mb string (if applicable) + std::wstring* return_wstring; + + // Error message (if an error occurred) + std::string* error_msg; + + // Boolean indicating if an error occurred + bool error_occured; + + // If this result is ready + bool result_ready; + +} ResultData; + +class IcedTeaRunnable : public nsIRunnable +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIRUNNABLE + + IcedTeaRunnable (); + + ~IcedTeaRunnable (); +}; + +class IcedTeaRunnableMethod : public IcedTeaRunnable +{ +public: + + typedef void* (*Method) (void*, void*); + + IcedTeaRunnableMethod (Method, void* thread_data, void* result); + NS_IMETHOD Run (); + + ~IcedTeaRunnableMethod (); + + Method method; + void* thread_data; + void* result; +}; + +#endif /* MOZILLA */ + +#endif /* __ICEDTEARUNNABLE_H__ */ diff --git a/plugin/icedteanp/IcedTeaScriptablePluginObject.cc b/plugin/icedteanp/IcedTeaScriptablePluginObject.cc new file mode 100644 index 0000000..e482941 --- /dev/null +++ b/plugin/icedteanp/IcedTeaScriptablePluginObject.cc @@ -0,0 +1,897 @@ +/* IcedTeaScriptablePluginObject.cc + + Copyright (C) 2009, 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. */ + +#include <typeinfo> + +#include "IcedTeaScriptablePluginObject.h" + +IcedTeaScriptablePluginObject::IcedTeaScriptablePluginObject(NPP instance) +{ + this->instance = instance; + IcedTeaPluginUtilities::storeInstanceID(this, instance); +} + +void +IcedTeaScriptablePluginObject::deAllocate(NPObject *npobj) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::deAllocate %p\n", npobj); +} + +void +IcedTeaScriptablePluginObject::invalidate(NPObject *npobj) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::invalidate %p\n", npobj); +} + +bool +IcedTeaScriptablePluginObject::hasMethod(NPObject *npobj, NPIdentifier name) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::hasMethod %p\n", npobj); + return false; +} + +bool +IcedTeaScriptablePluginObject::invoke(NPObject *npobj, NPIdentifier name, const NPVariant *args, + uint32_t argCount,NPVariant *result) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::invoke %p\n", npobj); + return false; +} + +bool +IcedTeaScriptablePluginObject::invokeDefault(NPObject *npobj, const NPVariant *args, + uint32_t argCount, NPVariant *result) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::invokeDefault %p\n", npobj); + return false; +} + +bool +IcedTeaScriptablePluginObject::hasProperty(NPObject *npobj, NPIdentifier name) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::hasProperty %p\n", npobj); + return false; +} + +bool +IcedTeaScriptablePluginObject::getProperty(NPObject *npobj, NPIdentifier name, NPVariant *result) +{ + // Package request? + if (!strcmp(browser_functions.utf8fromidentifier(name), "java")) + { + //NPObject* obj = IcedTeaScriptablePluginObject::get_scriptable_java_package_object(getInstanceFromMemberPtr(npobj), name); + //OBJECT_TO_NPVARIANT(obj, *result); + + //printf ("Filling variant %p with object %p\n", result); + } + + return false; +} + +bool +IcedTeaScriptablePluginObject::setProperty(NPObject *npobj, NPIdentifier name, const NPVariant *value) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::setProperty %p\n", npobj); + return false; +} + +bool +IcedTeaScriptablePluginObject::removeProperty(NPObject *npobj, NPIdentifier name) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::removeProperty %p\n", npobj); + return false; +} + +bool +IcedTeaScriptablePluginObject::enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::enumerate %p\n", npobj); + return false; +} + +bool +IcedTeaScriptablePluginObject::construct(NPObject *npobj, const NPVariant *args, uint32_t argCount, + NPVariant *result) +{ + printf ("** Unimplemented: IcedTeaScriptablePluginObject::construct %p\n", npobj); + return false; +} + +NPObject* +allocate_scriptable_jp_object(NPP npp, NPClass *aClass) +{ + PLUGIN_DEBUG("Allocating new scriptable Java Package object\n"); + return new IcedTeaScriptableJavaPackageObject(npp); +} + +NPObject* +IcedTeaScriptablePluginObject::get_scriptable_java_package_object(NPP instance, const NPUTF8* name) +{ + + NPObject* scriptable_object; + + NPClass* np_class = new NPClass(); + np_class->structVersion = NP_CLASS_STRUCT_VERSION; + np_class->allocate = allocate_scriptable_jp_object; + np_class->deallocate = IcedTeaScriptableJavaPackageObject::deAllocate; + np_class->invalidate = IcedTeaScriptableJavaPackageObject::invalidate; + np_class->hasMethod = IcedTeaScriptableJavaPackageObject::hasMethod; + np_class->invoke = IcedTeaScriptableJavaPackageObject::invoke; + np_class->invokeDefault = IcedTeaScriptableJavaPackageObject::invokeDefault; + np_class->hasProperty = IcedTeaScriptableJavaPackageObject::hasProperty; + np_class->getProperty = IcedTeaScriptableJavaPackageObject::getProperty; + np_class->setProperty = IcedTeaScriptableJavaPackageObject::setProperty; + np_class->removeProperty = IcedTeaScriptableJavaPackageObject::removeProperty; + np_class->enumerate = IcedTeaScriptableJavaPackageObject::enumerate; + np_class->construct = IcedTeaScriptableJavaPackageObject::construct; + + scriptable_object = browser_functions.createobject(instance, np_class); + PLUGIN_DEBUG("Returning new scriptable package class: %p from instance %p with name %s\n", scriptable_object, instance, name); + + ((IcedTeaScriptableJavaPackageObject*) scriptable_object)->setPackageName(name); + + IcedTeaPluginUtilities::storeInstanceID(scriptable_object, instance); + + return scriptable_object; +} + +IcedTeaScriptableJavaPackageObject::IcedTeaScriptableJavaPackageObject(NPP instance) +{ + PLUGIN_DEBUG("Constructing new scriptable java package object\n"); + this->instance = instance; + this->package_name = new std::string(); +} + +IcedTeaScriptableJavaPackageObject::~IcedTeaScriptableJavaPackageObject() +{ + delete this->package_name; +} + +void +IcedTeaScriptableJavaPackageObject::setPackageName(const NPUTF8* name) +{ + this->package_name->append(name); +} + +std::string +IcedTeaScriptableJavaPackageObject::getPackageName() +{ + return this->package_name->c_str(); +} + +void +IcedTeaScriptableJavaPackageObject::deAllocate(NPObject *npobj) +{ + browser_functions.releaseobject(npobj); +} + +void +IcedTeaScriptableJavaPackageObject::invalidate(NPObject *npobj) +{ + // nothing to do for these +} + +bool +IcedTeaScriptableJavaPackageObject::hasMethod(NPObject *npobj, NPIdentifier name) +{ + // Silly caller. Methods are for objects! + return false; +} + +bool +IcedTeaScriptableJavaPackageObject::invoke(NPObject *npobj, NPIdentifier name, const NPVariant *args, + uint32_t argCount,NPVariant *result) +{ + printf ("** Unimplemented: IcedTeaScriptableJavaPackageObject::invoke %p\n", npobj); + return false; +} + +bool +IcedTeaScriptableJavaPackageObject::invokeDefault(NPObject *npobj, const NPVariant *args, + uint32_t argCount, NPVariant *result) +{ + printf ("** Unimplemented: IcedTeaScriptableJavaPackageObject::invokeDefault %p\n", npobj); + return false; +} + +bool +IcedTeaScriptableJavaPackageObject::hasProperty(NPObject *npobj, NPIdentifier name) +{ + PLUGIN_DEBUG("IcedTeaScriptableJavaPackageObject::hasProperty %s\n", browser_functions.utf8fromidentifier(name)); + + bool hasProperty = false; + JavaResultData* java_result; + JavaRequestProcessor* java_request = new JavaRequestProcessor(); + NPP instance = IcedTeaPluginUtilities::getInstanceFromMemberPtr(npobj); + int plugin_instance_id = get_id_from_instance(instance); + + PLUGIN_DEBUG("Object package name: \"%s\"\n", ((IcedTeaScriptableJavaPackageObject*) npobj)->getPackageName().c_str()); + + // "^java" is always a package + if (((IcedTeaScriptableJavaPackageObject*) npobj)->getPackageName().length() == 0 && + ( !strcmp(browser_functions.utf8fromidentifier(name), "java") || + !strcmp(browser_functions.utf8fromidentifier(name), "javax"))) + { + return true; + } + + std::string property_name = ((IcedTeaScriptableJavaPackageObject*) npobj)->getPackageName(); + if (property_name.length() > 0) + property_name += "."; + property_name += browser_functions.utf8fromidentifier(name); + + PLUGIN_DEBUG("Looking for name \"%s\"\n", property_name.c_str()); + + java_result = java_request->hasPackage(plugin_instance_id, property_name); + + if (!java_result->error_occurred && java_result->return_identifier != 0) hasProperty = true; + + // No such package. Do we have a class with that name? + if (!hasProperty) + { + java_result = java_request->findClass(plugin_instance_id, property_name); + } + + if (java_result->return_identifier != 0) hasProperty = true; + + delete java_request; + + return hasProperty; +} + +bool +IcedTeaScriptableJavaPackageObject::getProperty(NPObject *npobj, NPIdentifier name, NPVariant *result) +{ + + PLUGIN_DEBUG("IcedTeaScriptableJavaPackageObject::getProperty %s\n", browser_functions.utf8fromidentifier(name)); + + if (!browser_functions.utf8fromidentifier(name)) + return false; + + bool isPropertyClass = false; + JavaResultData* java_result; + JavaRequestProcessor java_request = JavaRequestProcessor(); + NPP instance = IcedTeaPluginUtilities::getInstanceFromMemberPtr(npobj); + int plugin_instance_id = get_id_from_instance(instance); + + std::string property_name = ((IcedTeaScriptableJavaPackageObject*) npobj)->getPackageName(); + if (property_name.length() > 0) + property_name += "."; + property_name += browser_functions.utf8fromidentifier(name); + + java_result = java_request.findClass(plugin_instance_id, property_name); + isPropertyClass = (java_result->return_identifier == 0); + + //NPIdentifier property = browser_functions.getstringidentifier(property_name.c_str()); + + NPObject* obj; + + if (isPropertyClass) + { + PLUGIN_DEBUG("Returning package object\n"); + obj = IcedTeaScriptablePluginObject::get_scriptable_java_package_object( + IcedTeaPluginUtilities::getInstanceFromMemberPtr(npobj), + property_name.c_str()); + } + else + { + PLUGIN_DEBUG("Returning Java object\n"); + obj = IcedTeaScriptableJavaPackageObject::get_scriptable_java_object( + IcedTeaPluginUtilities::getInstanceFromMemberPtr(npobj), + *(java_result->return_string), "0", false); + } + + OBJECT_TO_NPVARIANT(obj, *result); + + return true; +} + +bool +IcedTeaScriptableJavaPackageObject::setProperty(NPObject *npobj, NPIdentifier name, const NPVariant *value) +{ + // Can't be going around setting properties on namespaces.. that's madness! + return false; +} + +bool +IcedTeaScriptableJavaPackageObject::removeProperty(NPObject *npobj, NPIdentifier name) +{ + printf ("** Unimplemented: IcedTeaScriptableJavaPackageObject::removeProperty %p\n", npobj); + return false; +} + +bool +IcedTeaScriptableJavaPackageObject::enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) +{ + printf ("** Unimplemented: IcedTeaScriptableJavaPackageObject::enumerate %p\n", npobj); + return false; +} + +bool +IcedTeaScriptableJavaPackageObject::construct(NPObject *npobj, const NPVariant *args, uint32_t argCount, + NPVariant *result) +{ + printf ("** Unimplemented: IcedTeaScriptableJavaPackageObject::construct %p\n", npobj); + return false; +} + +NPObject* +allocate_scriptable_java_object(NPP npp, NPClass *aClass) +{ + PLUGIN_DEBUG("Allocating new scriptable Java object\n"); + return new IcedTeaScriptableJavaObject(npp); +} + +NPObject* +IcedTeaScriptableJavaPackageObject::get_scriptable_java_object(NPP instance, + std::string class_id, + std::string instance_id, + bool isArray) +{ + NPObject* scriptable_object; + + std::string obj_key = std::string(); + obj_key += class_id; + obj_key += ":"; + obj_key += instance_id; + + PLUGIN_DEBUG("get_scriptable_java_object searching for %s...\n", obj_key.c_str()); + scriptable_object = IcedTeaPluginUtilities::getNPObjectFromJavaKey(obj_key); + + if (scriptable_object != NULL) + { + PLUGIN_DEBUG("Returning existing object %p\n", scriptable_object); + browser_functions.retainobject(scriptable_object); + return scriptable_object; + } + + + NPClass* np_class = new NPClass(); + np_class->structVersion = NP_CLASS_STRUCT_VERSION; + np_class->allocate = allocate_scriptable_java_object; + np_class->deallocate = IcedTeaScriptableJavaObject::deAllocate; + np_class->invalidate = IcedTeaScriptableJavaObject::invalidate; + np_class->hasMethod = IcedTeaScriptableJavaObject::hasMethod; + np_class->invoke = IcedTeaScriptableJavaObject::invoke; + np_class->invokeDefault = IcedTeaScriptableJavaObject::invokeDefault; + np_class->hasProperty = IcedTeaScriptableJavaObject::hasProperty; + np_class->getProperty = IcedTeaScriptableJavaObject::getProperty; + np_class->setProperty = IcedTeaScriptableJavaObject::setProperty; + np_class->removeProperty = IcedTeaScriptableJavaObject::removeProperty; + np_class->enumerate = IcedTeaScriptableJavaObject::enumerate; + np_class->construct = IcedTeaScriptableJavaObject::construct; + + // try to create normally + scriptable_object = browser_functions.createobject(instance, np_class); + + // didn't work? try creating asynch + if (!scriptable_object) + { + AsyncCallThreadData thread_data = AsyncCallThreadData(); + thread_data.result_ready = false; + thread_data.parameters = std::vector<void*>(); + thread_data.result = std::string(); + + thread_data.parameters.push_back(instance); + thread_data.parameters.push_back(np_class); + thread_data.parameters.push_back(&scriptable_object); + + browser_functions.pluginthreadasynccall(instance, &_createAndRetainJavaObject, &thread_data); + + while (!thread_data.result_ready) usleep(2000); // wait till ready + } else + { + // Else retain object and continue + browser_functions.retainobject(scriptable_object); + } + + PLUGIN_DEBUG("Constructed new Java Object with classid=%s, instanceid=%s, isArray=%d and scriptable_object=%p\n", class_id.c_str(), instance_id.c_str(), isArray, scriptable_object); + + ((IcedTeaScriptableJavaObject*) scriptable_object)->setClassIdentifier(class_id); + ((IcedTeaScriptableJavaObject*) scriptable_object)->setIsArray(isArray); + + if (instance_id != "0") + ((IcedTeaScriptableJavaObject*) scriptable_object)->setInstanceIdentifier(instance_id); + + IcedTeaPluginUtilities::storeInstanceID(scriptable_object, instance); + IcedTeaPluginUtilities::storeObjectMapping(obj_key, scriptable_object); + + PLUGIN_DEBUG("Inserting into object_map key %s->%p\n", obj_key.c_str(), scriptable_object); + return scriptable_object; +} + +/* Creates and retains a scriptable java object (intended to be called asynch.) */ +void +_createAndRetainJavaObject(void* data) +{ + PLUGIN_DEBUG("Asynchronously creating/retaining object ...\n"); + + std::vector<void*> parameters = ((AsyncCallThreadData*) data)->parameters; + NPP instance = (NPP) parameters.at(0); + NPClass* np_class = (NPClass*) parameters.at(1); + NPObject** scriptable_object = (NPObject**) parameters.at(2); + + *scriptable_object = browser_functions.createobject(instance, np_class); + browser_functions.retainobject(*scriptable_object); + + ((AsyncCallThreadData*) data)->result_ready = true; +} + +bool +IcedTeaScriptableJavaPackageObject::is_valid_java_object(NPObject* object_ptr) { + return IcedTeaPluginUtilities::getInstanceFromMemberPtr(object_ptr) != NULL; +} + +IcedTeaScriptableJavaObject::IcedTeaScriptableJavaObject(NPP instance) +{ + this->instance = instance; + this->class_id = new std::string(); + this->instance_id = new std::string(); +} + +IcedTeaScriptableJavaObject::~IcedTeaScriptableJavaObject() +{ + delete this->class_id; + delete this->instance_id; +} + +void +IcedTeaScriptableJavaObject::setClassIdentifier(std::string class_id) +{ + this->class_id->append(class_id); +} + +void +IcedTeaScriptableJavaObject::setInstanceIdentifier(std::string instance_id) +{ + this->instance_id->append(instance_id); +} + +void +IcedTeaScriptableJavaObject::setIsArray(bool isArray) +{ + this->isObjectArray = isArray; +} + +void +IcedTeaScriptableJavaObject::deAllocate(NPObject *npobj) +{ + browser_functions.releaseobject(npobj); +} + +void +IcedTeaScriptableJavaObject::invalidate(NPObject *npobj) +{ + IcedTeaPluginUtilities::removeInstanceID(npobj); + + std::string obj_key = std::string(); + obj_key += ((IcedTeaScriptableJavaObject*) npobj)->getClassID(); + obj_key += ":"; + obj_key += ((IcedTeaScriptableJavaObject*) npobj)->getInstanceID(); + + IcedTeaPluginUtilities::removeObjectMapping(obj_key); +} + +bool +IcedTeaScriptableJavaObject::hasMethod(NPObject *npobj, NPIdentifier name) +{ + PLUGIN_DEBUG("IcedTeaScriptableJavaObject::hasMethod %s (ival=%d)\n", browser_functions.utf8fromidentifier(name), browser_functions.intfromidentifier(name)); + bool hasMethod = false; + + // If object is an array and requested "method" may be a number, check for it first + if ( !((IcedTeaScriptableJavaObject*) npobj)->isArray() || + (browser_functions.intfromidentifier(name) < 0)) + { + + if (!browser_functions.utf8fromidentifier(name)) + return false; + + JavaResultData* java_result; + JavaRequestProcessor java_request = JavaRequestProcessor(); + + std::string classId = std::string(((IcedTeaScriptableJavaObject*) npobj)->getClassID()); + std::string methodName = browser_functions.utf8fromidentifier(name); + + java_result = java_request.hasMethod(classId, methodName); + hasMethod = java_result->return_identifier != 0; + } + + PLUGIN_DEBUG("IcedTeaScriptableJavaObject::hasMethod returning %d\n", hasMethod); + return hasMethod; +} + +bool +IcedTeaScriptableJavaObject::invoke(NPObject *npobj, NPIdentifier name, const NPVariant *args, + uint32_t argCount, NPVariant *result) +{ + NPUTF8* method_name = browser_functions.utf8fromidentifier(name); + + // Extract arg type array + PLUGIN_DEBUG("IcedTeaScriptableJavaObject::invoke %s. Args follow.\n", method_name); + for (int i=0; i < argCount; i++) + { + IcedTeaPluginUtilities::printNPVariant(args[i]); + } + + JavaResultData* java_result; + JavaRequestProcessor java_request = JavaRequestProcessor(); + + NPObject* obj; + std::string instance_id = ((IcedTeaScriptableJavaObject*) npobj)->getInstanceID(); + std::string class_id = ((IcedTeaScriptableJavaObject*) npobj)->getClassID(); + std::string callee; + std::string source; + + NPP instance = IcedTeaPluginUtilities::getInstanceFromMemberPtr(npobj); + + // First, load the arguments into the java-side table + std::string id = std::string(); + std::vector<std::string> arg_ids = std::vector<std::string>(); + for (int i=0; i < argCount; i++) { + id.clear(); + createJavaObjectFromVariant(instance, args[i], &id); + + if (id == "-1") + { + printf("Unable to create arguments on Java side\n"); + return false; + } + + arg_ids.push_back(id); + } + + if (instance_id.length() == 0) // Static + { + PLUGIN_DEBUG("Calling static method\n"); + callee = ((IcedTeaScriptableJavaObject*) npobj)->getClassID(); + java_result = java_request.callStaticMethod( + IcedTeaPluginUtilities::getSourceFromInstance(instance), + callee, browser_functions.utf8fromidentifier(name), arg_ids); + } else + { + PLUGIN_DEBUG("Calling method normally\n"); + callee = ((IcedTeaScriptableJavaObject*) npobj)->getInstanceID(); + java_result = java_request.callMethod( + IcedTeaPluginUtilities::getSourceFromInstance(instance), + callee, browser_functions.utf8fromidentifier(name), arg_ids); + } + + if (java_result->error_occurred) + { + // error message must be allocated on heap + char* error_msg = (char*) malloc(java_result->error_msg->length()*sizeof(char)); + strcpy(error_msg, java_result->error_msg->c_str()); + browser_functions.setexception(npobj, error_msg); + return false; + } + + PLUGIN_DEBUG("IcedTeaScriptableJavaObject::invoke converting and returning.\n"); + return IcedTeaPluginUtilities::javaResultToNPVariant(instance, java_result->return_string, result); +} + +bool +IcedTeaScriptableJavaObject::invokeDefault(NPObject *npobj, const NPVariant *args, + uint32_t argCount, NPVariant *result) +{ + printf ("** Unimplemented: IcedTeaScriptableJavaObject::invokeDefault %p\n", npobj); + return false; +} + +bool +IcedTeaScriptableJavaObject::hasProperty(NPObject *npobj, NPIdentifier name) +{ + PLUGIN_DEBUG("IcedTeaScriptableJavaObject::hasProperty %s (ival=%d)\n", browser_functions.utf8fromidentifier(name), browser_functions.intfromidentifier(name)); + bool hasProperty = false; + + // If it is an array, only length and indexes are valid + if (((IcedTeaScriptableJavaObject*) npobj)->isArray()) + { + if (browser_functions.intfromidentifier(name) >= 0 || + !strcmp(browser_functions.utf8fromidentifier(name), "length")) + hasProperty = true; + + } else + { + + if (!browser_functions.utf8fromidentifier(name)) + return false; + + if (!strcmp(browser_functions.utf8fromidentifier(name), "Packages")) + { + hasProperty = true; + } else { + + JavaResultData* java_result; + JavaRequestProcessor java_request = JavaRequestProcessor(); + + std::string class_id = std::string(((IcedTeaScriptableJavaObject*) npobj)->getClassID()); + std::string fieldName = browser_functions.utf8fromidentifier(name); + + java_result = java_request.hasField(class_id, fieldName); + + hasProperty = java_result->return_identifier != 0; + } + } + + PLUGIN_DEBUG("IcedTeaScriptableJavaObject::hasProperty returning %d\n", hasProperty); + return hasProperty; +} + +bool +IcedTeaScriptableJavaObject::getProperty(NPObject *npobj, NPIdentifier name, NPVariant *result) +{ + PLUGIN_DEBUG("IcedTeaScriptableJavaObject::getProperty %s (ival=%d)\n", browser_functions.utf8fromidentifier(name), browser_functions.intfromidentifier(name)); + + bool isPropertyClass = false; + JavaResultData* java_result; + JavaRequestProcessor java_request = JavaRequestProcessor(); + + NPObject* obj; + std::string instance_id = ((IcedTeaScriptableJavaObject*) npobj)->getInstanceID(); + std::string class_id = ((IcedTeaScriptableJavaObject*) npobj)->getClassID(); + NPP instance = ((IcedTeaScriptableJavaObject*) npobj)->getInstance(); + + if (instance_id.length() > 0) // Could be an array or a simple object + { + // If array and requesting length + if ( ((IcedTeaScriptableJavaObject*) npobj)->isArray() && + browser_functions.utf8fromidentifier(name) && + !strcmp(browser_functions.utf8fromidentifier(name), "length")) + { + java_result = java_request.getArrayLength(instance_id); + } else if ( ((IcedTeaScriptableJavaObject*) npobj)->isArray() && + browser_functions.intfromidentifier(name) >= 0) // else if array and requesting index + { + + java_result = java_request.getArrayLength(instance_id); + if (java_result->error_occurred) + { + printf("ERROR: Couldn't fetch array length\n"); + return false; + } + + int length = atoi(java_result->return_string->c_str()); + + // Access beyond size? + if (browser_functions.intfromidentifier(name) >= length) + { + VOID_TO_NPVARIANT(*result); + return true; + } + + std::string index = std::string(); + IcedTeaPluginUtilities::itoa(browser_functions.intfromidentifier(name), &index); + java_result = java_request.getSlot(instance_id, index); + + } else // Everything else + { + if (!browser_functions.utf8fromidentifier(name)) + return false; + + if (!strcmp(browser_functions.utf8fromidentifier(name), "Packages")) + { + NPObject* pkgObject = IcedTeaScriptablePluginObject::get_scriptable_java_package_object(instance, ""); + OBJECT_TO_NPVARIANT(pkgObject, *result); + return true; + } + + java_result = java_request.getField( + IcedTeaPluginUtilities::getSourceFromInstance(instance), + class_id, instance_id, browser_functions.utf8fromidentifier(name)); + } + } + else + { + if (!browser_functions.utf8fromidentifier(name)) + return true; + + java_result = java_request.getStaticField( + IcedTeaPluginUtilities::getSourceFromInstance(instance), + class_id, browser_functions.utf8fromidentifier(name)); + } + + if (java_result->error_occurred) + { + return false; + } + + PLUGIN_DEBUG("IcedTeaScriptableJavaObject::getProperty converting and returning.\n"); + return IcedTeaPluginUtilities::javaResultToNPVariant(instance, java_result->return_string, result); +} + +bool +IcedTeaScriptableJavaObject::setProperty(NPObject *npobj, NPIdentifier name, const NPVariant *value) +{ + PLUGIN_DEBUG("IcedTeaScriptableJavaObject::setProperty %s (ival=%d) to:\n", browser_functions.utf8fromidentifier(name), browser_functions.intfromidentifier(name)); + IcedTeaPluginUtilities::printNPVariant(*value); + + bool isPropertyClass = false; + JavaResultData* java_result; + JavaRequestProcessor java_request = JavaRequestProcessor(); + + NPObject* obj; + std::string instance_id = ((IcedTeaScriptableJavaObject*) npobj)->getInstanceID(); + std::string class_id = ((IcedTeaScriptableJavaObject*) npobj)->getClassID(); + + NPP instance = IcedTeaPluginUtilities::getInstanceFromMemberPtr(npobj); + + if (instance_id.length() > 0) // Could be an array or a simple object + { + // If array + if ( ((IcedTeaScriptableJavaObject*) npobj)->isArray() && + browser_functions.utf8fromidentifier(name) && + !strcmp(browser_functions.utf8fromidentifier(name), "length")) + { + printf("ERROR: Array length is not a modifiable property\n"); + return false; + } else if ( ((IcedTeaScriptableJavaObject*) npobj)->isArray() && + browser_functions.intfromidentifier(name) >= 0) // else if array and requesting index + { + + java_result = java_request.getArrayLength(instance_id); + if (java_result->error_occurred) + { + printf("ERROR: Couldn't fetch array length\n"); + return false; + } + + int length = atoi(java_result->return_string->c_str()); + + // Access beyond size? + if (browser_functions.intfromidentifier(name) >= length) + { + return true; + } + + std::string index = std::string(); + IcedTeaPluginUtilities::itoa(browser_functions.intfromidentifier(name), &index); + + std::string value_id = std::string(); + createJavaObjectFromVariant(instance, *value, &value_id); + + java_result = java_request.setSlot(instance_id, index, value_id); + + } else // Everything else + { + std::string value_id = std::string(); + createJavaObjectFromVariant(instance, *value, &value_id); + + java_result = java_request.setField( + IcedTeaPluginUtilities::getSourceFromInstance(instance), + class_id, instance_id, browser_functions.utf8fromidentifier(name), value_id); + } + } + else + { + std::string value_id = std::string(); + createJavaObjectFromVariant(instance, *value, &value_id); + + java_result = java_request.setStaticField( + IcedTeaPluginUtilities::getSourceFromInstance(instance), + class_id, browser_functions.utf8fromidentifier(name), value_id); + } + + if (java_result->error_occurred) + { + return false; + } + + PLUGIN_DEBUG("IcedTeaScriptableJavaObject::setProperty returning.\n"); + return true; +} + +bool +IcedTeaScriptableJavaObject::removeProperty(NPObject *npobj, NPIdentifier name) +{ + printf ("** Unimplemented: IcedTeaScriptableJavaObject::removeProperty %p\n", npobj); + return false; +} + +bool +IcedTeaScriptableJavaObject::enumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count) +{ + printf ("** Unimplemented: IcedTeaScriptableJavaObject::enumerate %p\n", npobj); + return false; +} + +bool +IcedTeaScriptableJavaObject::construct(NPObject *npobj, const NPVariant *args, uint32_t argCount, + NPVariant *result) +{ + // Extract arg type array + PLUGIN_DEBUG("IcedTeaScriptableJavaObject::construct %s. Args follow.\n", ((IcedTeaScriptableJavaObject*) npobj)->getClassID().c_str()); + for (int i=0; i < argCount; i++) + { + IcedTeaPluginUtilities::printNPVariant(args[i]); + } + + JavaResultData* java_result; + JavaRequestProcessor java_request = JavaRequestProcessor(); + + NPObject* obj; + std::string class_id = ((IcedTeaScriptableJavaObject*) npobj)->getClassID(); + NPP instance = IcedTeaPluginUtilities::getInstanceFromMemberPtr(npobj); + + // First, load the arguments into the java-side table + std::string id = std::string(); + std::vector<std::string> arg_ids = std::vector<std::string>(); + for (int i=0; i < argCount; i++) { + id.clear(); + createJavaObjectFromVariant(instance, args[i], &id); + if (id == "0") + { + // error message must be allocated on heap + char* error_msg = (char*) malloc(1024*sizeof(char)); + strcpy(error_msg, "Unable to create argument on Java side"); + + browser_functions.setexception(npobj, error_msg); + return false; + } + + arg_ids.push_back(id); + } + + java_result = java_request.newObject( + IcedTeaPluginUtilities::getSourceFromInstance(instance), + class_id, + arg_ids); + + if (java_result->error_occurred) + { + // error message must be allocated on heap + int length = java_result->error_msg->length(); + char* error_msg = (char*) malloc((length+1)*sizeof(char)); + strcpy(error_msg, java_result->error_msg->c_str()); + + browser_functions.setexception(npobj, error_msg); + return false; + } + + std::string return_obj_instance_id = std::string(); + std::string return_obj_class_id = class_id; + return_obj_instance_id.append(*(java_result->return_string)); + + obj = IcedTeaScriptableJavaPackageObject::get_scriptable_java_object( + IcedTeaPluginUtilities::getInstanceFromMemberPtr(npobj), + return_obj_class_id, return_obj_instance_id, false); + + OBJECT_TO_NPVARIANT(obj, *result); + + PLUGIN_DEBUG("IcedTeaScriptableJavaObject::construct returning.\n"); + return true; +} diff --git a/plugin/icedteanp/IcedTeaScriptablePluginObject.h b/plugin/icedteanp/IcedTeaScriptablePluginObject.h new file mode 100644 index 0000000..a4933ed --- /dev/null +++ b/plugin/icedteanp/IcedTeaScriptablePluginObject.h @@ -0,0 +1,212 @@ +/* IcedTeaScriptablePluginObject.h + + Copyright (C) 2009, 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. */ + +#ifndef __ICEDTEASCRIPTABLEPLUGINOBJECT_H_ +#define __ICEDTEASCRIPTABLEPLUGINOBJECT_H_ + +#if MOZILLA_VERSION_COLLAPSED < 1090100 +#include "npupp.h" +#else +#include <npapi.h> +#include <npruntime.h> +#endif + +#include "IcedTeaJavaRequestProcessor.h" +#include "IcedTeaNPPlugin.h" + +/** + * IcedTeaScriptablePluginObject, an extended NPObject that implements + * static functions whose pointers are supplied to NPClass. + */ + +class IcedTeaScriptablePluginObject: public NPObject +{ + + private: + NPP instance; + + public: + IcedTeaScriptablePluginObject(NPP instance); + + static void deAllocate(NPObject *npobj); + + static void invalidate(NPObject *npobj); + + static bool hasMethod(NPObject *npobj, NPIdentifier name); + + static bool invoke(NPObject *npobj, NPIdentifier name, + const NPVariant *args, uint32_t argCount, NPVariant *result); + + static bool invokeDefault(NPObject *npobj, const NPVariant *args, + uint32_t argCount, NPVariant *result); + + static bool hasProperty(NPObject *npobj, NPIdentifier name); + + static bool getProperty(NPObject *npobj, NPIdentifier name, + NPVariant *result); + + static bool setProperty(NPObject *npobj, NPIdentifier name, + const NPVariant *value); + + static bool removeProperty(NPObject *npobj, NPIdentifier name); + + static bool enumerate(NPObject *npobj, NPIdentifier **value, + uint32_t *count); + + static bool construct(NPObject *npobj, const NPVariant *args, + uint32_t argCount, NPVariant *result); + + static NPObject* get_scriptable_java_package_object(NPP instance, const NPUTF8* name); +}; + +NPObject* allocate_scriptable_jp_object(NPP npp, NPClass *aClass); + +class IcedTeaScriptableJavaPackageObject: public NPObject +{ + + private: + NPP instance; + std::string* package_name; + + public: + IcedTeaScriptableJavaPackageObject(NPP instance); + + ~IcedTeaScriptableJavaPackageObject(); + + void setPackageName(const NPUTF8* name); + + std::string getPackageName(); + + static void deAllocate(NPObject *npobj); + + static void invalidate(NPObject *npobj); + + static bool hasMethod(NPObject *npobj, NPIdentifier name); + + static bool invoke(NPObject *npobj, NPIdentifier name, + const NPVariant *args, uint32_t argCount, NPVariant *result); + + static bool invokeDefault(NPObject *npobj, const NPVariant *args, + uint32_t argCount, NPVariant *result); + + static bool hasProperty(NPObject *npobj, NPIdentifier name); + + static bool getProperty(NPObject *npobj, NPIdentifier name, + NPVariant *result); + + static bool setProperty(NPObject *npobj, NPIdentifier name, + const NPVariant *value); + + static bool removeProperty(NPObject *npobj, NPIdentifier name); + + static bool enumerate(NPObject *npobj, NPIdentifier **value, + uint32_t *count); + + static bool construct(NPObject *npobj, const NPVariant *args, + uint32_t argCount, NPVariant *result); + + static NPObject* get_scriptable_java_object(NPP instance, + std::string class_id, + std::string instance_id, + bool isArray); + + static bool is_valid_java_object(NPObject* object_ptr); +}; + +class IcedTeaScriptableJavaObject: public NPObject +{ + + private: + NPP instance; + bool isObjectArray; + std::string* class_id; + std::string* instance_id; + + public: + IcedTeaScriptableJavaObject(NPP instance); + + ~IcedTeaScriptableJavaObject(); + + void setClassIdentifier(std::string class_id); + + void setInstanceIdentifier(std::string instance_id); + + void setIsArray(bool isArray); + + std::string getClassID() { return *class_id; } + + std::string getInstanceID() { return *instance_id; } + + NPP getInstance() { return instance; } + + bool isArray() { return isObjectArray; } + + static void deAllocate(NPObject *npobj); + + static void invalidate(NPObject *npobj); + + static bool hasMethod(NPObject *npobj, NPIdentifier name); + + static bool invoke(NPObject *npobj, NPIdentifier name, + const NPVariant *args, uint32_t argCount, NPVariant *result); + + static bool invokeDefault(NPObject *npobj, const NPVariant *args, + uint32_t argCount, NPVariant *result); + + static bool hasProperty(NPObject *npobj, NPIdentifier name); + + static bool getProperty(NPObject *npobj, NPIdentifier name, + NPVariant *result); + + static bool setProperty(NPObject *npobj, NPIdentifier name, + const NPVariant *value); + + static bool removeProperty(NPObject *npobj, NPIdentifier name); + + static bool enumerate(NPObject *npobj, NPIdentifier **value, + uint32_t *count); + + static bool construct(NPObject *npobj, const NPVariant *args, + uint32_t argCount, NPVariant *result); +}; + +/* Creates and retains a scriptable java object (intended to be called asynch.) */ + +void _createAndRetainJavaObject(void* data); + +#endif /* __ICEDTEASCRIPTABLEPLUGINOBJECT_H_ */ 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; + } +} |