| /* |
| * 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.dev.shell.BrowserChannel.InvokeOnServerMessage; |
| import com.google.gwt.dev.shell.BrowserChannel.JavaObjectRef; |
| import com.google.gwt.dev.shell.BrowserChannel.ReturnMessage; |
| import com.google.gwt.dev.shell.BrowserChannel.Value; |
| import com.google.gwt.dev.shell.BrowserChannel.SessionHandler.ExceptionOrReturnValue; |
| |
| 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 net.sourceforge.htmlunit.corejs.javascript.Undefined; |
| |
| import java.io.IOException; |
| |
| /** |
| * Class to encapsulate JavaObject on the server side. |
| */ |
| public class JavaObject extends ScriptableObject implements Function { |
| |
| private static final long serialVersionUID = -7923090130737830902L; |
| private static final ExceptionOrReturnValue DEFAULT_VALUE = new ExceptionOrReturnValue( |
| true, new Value()); |
| |
| public static JavaObject getOrCreateJavaObject(JavaObjectRef javaRef, |
| SessionData sessionData, Context context) { |
| return sessionData.getSessionHandler().getOrCreateJavaObject( |
| javaRef.getRefid(), context); |
| } |
| |
| /** |
| * @param cx the Context |
| */ |
| static ExceptionOrReturnValue getReturnFromJavaMethod(Context cx, |
| HtmlUnitSessionHandler sessionHandler, BrowserChannelClient channel, |
| int dispatchId, Value thisValue, Value valueArgs[]) { |
| |
| synchronized (sessionHandler.getSynchronizationObject()) { |
| try { |
| new InvokeOnServerMessage(channel, dispatchId, thisValue, valueArgs).send(); |
| } catch (IOException e) { |
| return DEFAULT_VALUE; |
| } |
| try { |
| ReturnMessage returnMessage = channel.reactToMessagesWhileWaitingForReturn(sessionHandler); |
| return new ExceptionOrReturnValue(returnMessage.isException(), |
| returnMessage.getReturnValue()); |
| } catch (IOException e) { |
| return DEFAULT_VALUE; |
| } catch (BrowserChannelException e) { |
| return DEFAULT_VALUE; |
| } |
| } |
| } |
| |
| /** |
| * @param jsContext the Context |
| */ |
| static boolean isJavaObject(Context jsContext, ScriptableObject javaObject) { |
| return javaObject instanceof JavaObject; |
| } |
| |
| private Context jsContext; |
| |
| private final int objectRef; |
| |
| private final SessionData sessionData; |
| |
| public JavaObject(Context jsContext, SessionData sessionData, int objectRef) { |
| this.objectRef = objectRef; |
| this.sessionData = sessionData; |
| this.jsContext = jsContext; |
| } |
| |
| /* |
| * If this function fails for any reason, we return Undefined instead of |
| * throwing an Exception in all cases except when Java throws an Exception. |
| */ |
| public Object call(Context cx, Scriptable scope, Scriptable thisObj, |
| Object[] args) { |
| |
| if (args.length < 2) { |
| return Undefined.instance; |
| } |
| Value valueArgs[] = new Value[args.length - 2]; |
| for (int i = 0; i < valueArgs.length; i++) { |
| valueArgs[i] = sessionData.getSessionHandler().makeValueFromJsval(cx, |
| args[i + 2]); |
| } |
| |
| /** |
| * Called when the JavaObject is invoked as a function. We ignore the |
| * thisObj argument, which is usually the window object. |
| * |
| * Returns a JS array, with the first element being a boolean indicating |
| * that an exception occured, and the second element is either the return |
| * value or the exception which was thrown. In this case, we always return |
| * false and raise the exception ourselves. |
| */ |
| |
| Value thisValue = sessionData.getSessionHandler().makeValueFromJsval(cx, |
| args[1]); |
| int dispatchId = ((Number) args[0]).intValue(); |
| |
| ExceptionOrReturnValue returnValue = getReturnFromJavaMethod(cx, |
| sessionData.getSessionHandler(), sessionData.getChannel(), dispatchId, |
| thisValue, valueArgs); |
| /* |
| * Return a object array ret. ret[0] is a boolean indicating whether an |
| * exception was thrown or not. ret[1] is the exception or the return value. |
| */ |
| Object ret[] = new Object[2]; |
| ret[0] = returnValue.isException(); |
| ret[1] = sessionData.getSessionHandler().makeJsvalFromValue(cx, |
| returnValue.getReturnValue()); |
| return ret; |
| } |
| |
| public Scriptable construct(Context cx, Scriptable scope, Object[] args) { |
| throw Context.reportRuntimeError("JavaObject can't be used as a " |
| + "constructor"); |
| } |
| |
| // ignoring the 'start' argument. |
| @Override |
| public Object get(int index, Scriptable start) { |
| Value value = ServerMethods.getProperty(sessionData.getChannel(), |
| sessionData.getSessionHandler(), objectRef, index); |
| return sessionData.getSessionHandler().makeJsvalFromValue(jsContext, value); |
| } |
| |
| // ignoring the 'start' argument. |
| @Override |
| public Object get(String name, Scriptable start) { |
| if ("toString".equals(name)) { |
| return sessionData.getSessionHandler().getToStringTearOff(jsContext); |
| } |
| if ("id".equals(name)) { |
| return objectRef; |
| } |
| if ("__noSuchMethod__".equals(name)) { |
| return Undefined.instance; |
| } |
| System.err.println("Unknown property name in get " + name); |
| return Undefined.instance; |
| } |
| |
| @Override |
| public String getClassName() { |
| return "Class JavaObject"; |
| } |
| |
| @Override |
| public void put(int dispatchId, Scriptable start, Object value) { |
| HtmlUnitSessionHandler sessionHandler = sessionData.getSessionHandler(); |
| if (!ServerMethods.setProperty(sessionData.getChannel(), sessionHandler, |
| objectRef, dispatchId, sessionHandler.makeValueFromJsval(jsContext, |
| value))) { |
| // TODO: fix later. |
| throw new RuntimeException("setProperty failed"); |
| } |
| } |
| |
| int getRefId() { |
| return objectRef; |
| } |
| } |