com.google.gwt.dev.shell.HostedModePluginObject Maven / Gradle / Ivy
The newest version!
/*
* 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.WebClient;
import com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine;
import com.gargoylesoftware.htmlunit.javascript.host.Window;
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;
import java.util.Collections;
/**
* 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;
@Override
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");
}
}
@Override
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;
@Override
public Object call(Context context, Scriptable scope, Scriptable thisObj,
Object[] args) {
// Allow extra arguments for forward compatibility
return disconnect();
}
@Override
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";
@Override
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 = (Window) args[0];
return init(VERSION);
} catch (ClassCastException e) {
throw Context.reportRuntimeError("Incorrect parameter types for init: expected Window");
}
}
@Override
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 WebClient webClient;
private final TreeLogger logger;
private BrowserChannelClient browserChannelClient;
/**
* Creates a HostedModePluginObject with the passed-in JavaScriptEngine.
*
* @param jsEngine The JavaScriptEngine.
* @param webClient The WebClient being tested.
* @param logger A logger instance to notify the user of errors.
*/
public HostedModePluginObject(JavaScriptEngine jsEngine, WebClient webClient, TreeLogger logger) {
this.jsEngine = jsEngine;
this.webClient = webClient;
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?
// We know that legacy dev mode is running, we need to tell HtmlUnit that it is safe
// to permit plain Java objects to leak into JS - the JavaObject type will return a
// Object[] with a success boolean and a value, and HtmlUnit will guard against this.
// The simplest way to do that here is to mark java.lang.Object as the java equivalent
// of some JS type - the name of the type doesn't matter.
webClient.setActiveXObjectMap(Collections.singletonMap(
"GwtLegacyDevModeExceptionOrReturnValue", "java.lang.Object"));
try {
HtmlUnitSessionHandler htmlUnitSessionHandler = new HtmlUnitSessionHandler(
window, jsEngine, webClient);
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;
}
}