/* * Copyright 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.google.gwt.dev.shell; import com.google.gwt.core.ext.TreeLogger; import com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine; import com.gargoylesoftware.htmlunit.javascript.host.Window; import com.gargoylesoftware.htmlunit.javascript.host.WindowProxy; import net.sourceforge.htmlunit.corejs.javascript.Context; import net.sourceforge.htmlunit.corejs.javascript.Function; import net.sourceforge.htmlunit.corejs.javascript.Scriptable; import net.sourceforge.htmlunit.corejs.javascript.ScriptableObject; import java.io.IOException; /** * HTMLUnit object that represents the hosted-mode plugin. */ public class HostedModePluginObject extends ScriptableObject { /** * Function object which implements the connect method on the hosted-mode * plugin. */ private class ConnectMethod extends ScriptableObject implements Function { private static final long serialVersionUID = -8799481412144205779L; private static final int EXPECTED_NUM_ARGS = 5; public Object call(Context context, Scriptable scope, Scriptable thisObj, Object[] args) { // Allow extra arguments for forward compatibility if (args.length < EXPECTED_NUM_ARGS) { throw Context.reportRuntimeError("Bad number of parameters for function" + " connect: expected " + EXPECTED_NUM_ARGS + ", got " + args.length); } try { /* * connect arguments: url, sessionKey, ipAddress:port, moduleName, * hostedHtmlVersion */ return connect((String) args[0], (String) args[1], (String) args[2], (String) args[3], (String) args[4]); } catch (ClassCastException e) { throw Context.reportRuntimeError("Incorrect parameter types for " + " connect: expected String/String/String/String/String"); } } public Scriptable construct(Context context, Scriptable scope, Object[] args) { throw Context.reportRuntimeError("Function connect can't be used as a " + "constructor"); } @Override public String getClassName() { return "function HostedModePluginObject.connect"; } } /** * Function object which implements the disconnect method on the hosted-mode * plugin. */ private class DisconnectMethod extends ScriptableObject implements Function { private static final long serialVersionUID = -8799481412144519779L; public Object call(Context context, Scriptable scope, Scriptable thisObj, Object[] args) { // Allow extra arguments for forward compatibility return disconnect(); } public Scriptable construct(Context context, Scriptable scope, Object[] args) { throw Context.reportRuntimeError("Function disconnect can't be used as a " + "constructor"); } @Override public String getClassName() { return "function HostedModePluginObject.disconnect"; } } /** * Function object which implements the init method on the hosted-mode plugin. */ private class InitMethod extends ScriptableObject implements Function { private static final long serialVersionUID = -8799481412144205779L; private static final String VERSION = "2.0"; public Object call(Context context, Scriptable scope, Scriptable thisObj, Object[] args) { // Allow extra arguments for forward compatibility if (args.length < 1) { throw Context.reportRuntimeError("Bad number of parameters for function" + " init: expected 1, got " + args.length); } try { window = ((WindowProxy) args[0]).getDelegee(); return init(VERSION); } catch (ClassCastException e) { throw Context.reportRuntimeError("Incorrect parameter types for " + " initt: expected String"); } } public Scriptable construct(Context context, Scriptable scope, Object[] args) { throw Context.reportRuntimeError("Function init can't be used as a " + "constructor"); } @Override public String getClassName() { return "function HostedModePluginObject.init"; } } private static final long serialVersionUID = -1815031145376726799L; private Scriptable connectMethod; private Scriptable disconnectMethod; private Scriptable initMethod; private Window window; private final JavaScriptEngine jsEngine; private final TreeLogger logger; private BrowserChannelClient browserChannelClient; /** * Creates a HostedModePluginObject with the passed-in JavaScriptEngine. * * @param jsEngine The JavaScriptEngine. */ public HostedModePluginObject(JavaScriptEngine jsEngine, TreeLogger logger) { this.jsEngine = jsEngine; this.logger = logger; } /** * Initiate a hosted mode connection to the requested port and load the * requested module. * * @param url the complete url * @param sessionKey a length 16 string to identify a "session" * @param address "host:port" or "ipAddress:port" to use for the OOPHM server * @param module module name to load * @param version version string * @return true if the connection succeeds */ public boolean connect(String url, String sessionKey, String address, String module, String version) { String addressParts[] = address.split(":"); if (addressParts.length < 2) { logger.log(TreeLogger.ERROR, "connect failed because address " + address + " was not of the form foo.com:8080"); return false; } // TODO: add whitelist and default-port support? try { HtmlUnitSessionHandler htmlUnitSessionHandler = new HtmlUnitSessionHandler( window, jsEngine); browserChannelClient = new BrowserChannelClient(addressParts, url, sessionKey, module, version, htmlUnitSessionHandler); htmlUnitSessionHandler.setSessionData(new SessionData( htmlUnitSessionHandler, browserChannelClient)); return browserChannelClient.process(); } catch (BrowserChannelException e) { logger.log(TreeLogger.ERROR, "BrowserChannelException returned from connect " + e.getMessage(), e); return false; } catch (IOException e) { logger.log(TreeLogger.ERROR, "IOException returned from connect " + e.getMessage(), e); return false; } } public boolean disconnect() { try { return browserChannelClient.disconnectFromHost(); } catch (IOException e) { logger.log(TreeLogger.ERROR, "IOException returned from disconnect " + e.getMessage(), e); return false; } } @Override public Object get(String name, Scriptable start) { if ("connect".equals(name)) { if (connectMethod == null) { connectMethod = new ConnectMethod(); } return connectMethod; } else if ("disconnect".equals(name)) { if (disconnectMethod == null) { disconnectMethod = new DisconnectMethod(); } return disconnectMethod; } else if ("init".equals(name)) { if (initMethod == null) { initMethod = new InitMethod(); } return initMethod; } return NOT_FOUND; } @Override public String getClassName() { return "HostedModePluginObject"; } /** * Verify that the plugin can be initialized properly and supports the * requested version. * * @param version hosted mode protocol version * @return true if initialization succeeds, otherwise false */ public boolean init(String version) { // TODO: what needs to be done here? return true; } }