blob: 2b89c2e87c7ac3e6e146af3570f405ceaf55da8a [file] [log] [blame]
/*
* Copyright 2007 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.moz;
import com.google.gwt.dev.shell.LowLevel;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Vector;
/**
* Various low-level helper methods for dealing with Gecko.
*/
public class LowLevelMoz {
/**
* Provides interface for methods to be exposed on JavaScript side.
*/
public interface DispatchMethod {
/**
* Invoke a Java method from JavaScript.
*
* @param jsthis the wrapped Java object to invoke
* @param jsargs an array of JavaScript values to pass as parameters
* @param returnValue the JavaScript value in which to store the returned
* value
*/
void invoke(int jsthis, int[] jsargs, int returnValue);
}
/**
* Provides interface for objects to be exposed to JavaScript code.
*/
public interface DispatchObject {
/**
* Retrieve a field from an object.
*
* @param name the name of the field
* @param value pointer to the JsRootedValue to receive the field value
*/
void getField(String name, int value);
Object getTarget();
/**
* Set the value of a field on an object.
*
* @param name the name of the field
* @param value pointer to the JsRootedValue to store into the field
*/
void setField(String name, int value);
}
interface ExternalFactory {
ExternalObject createExternalObject();
boolean matchesDOMWindow(int domWindow);
}
/**
* TODO: rip this whole thing out if possible and use DispatchObject like on
* Safari.
*/
interface ExternalObject {
boolean initModule(String moduleName);
boolean gwtOnLoad(int scriptGlobalObject, String moduleName, String version);
}
/**
* Stores a map from DispatchObject/DispatchMethod to the live underlying
* jsval. This is used to both preserve identity for the same Java Object and
* also prevent GC.
*/
static Map<Object, Integer> sObjectToJsval = new IdentityHashMap<Object, Integer>();
private static Vector<ExternalFactory> sExternalFactories = new Vector<ExternalFactory>();
private static boolean sInitialized = false;
/**
* Executes JavaScript code, retaining file and line information.
*
* @param scriptObject An opaque handle to the script frame window
* @param code The JavaScript code to execute
* @param file A file name associated with the code
* @param line A line number associated with the code.
*/
public static void executeScriptWithInfo(int scriptObject, String code,
String file, int line) {
if (!_executeScriptWithInfo(scriptObject, code, file, line)) {
throw new RuntimeException(file + "(" + line
+ "): Failed to execute script: " + code);
}
}
public static synchronized void init() {
// Force LowLevel initialization to load gwt-ll
LowLevel.init();
if (!sInitialized) {
if (!_registerExternalFactoryHandler()) {
throw new RuntimeException(
"Failed to register external factory handler.");
}
sInitialized = true;
}
}
/**
* Invokes a method implemented in JavaScript.
*
* @param scriptObject An opaque handle to the script frame window
* @param methodName the method name on jsthis to call
* @param jsthis A wrapped java object as a JsRootedValue pointer
* @param jsargs the arguments to pass to the method as JsRootedValue pointers
* @param retval the jsvalue to write the result into
* @throws RuntimeException if the invoke fails
*/
public static void invoke(int scriptObject, String methodName, int jsthis,
int[] jsargs, int retval) {
if (!_invoke(scriptObject, methodName, jsthis, jsargs, retval)) {
throw new RuntimeException("Failed to invoke native method: "
+ methodName + " with " + jsargs.length + " arguments.");
}
}
/**
* Call this to raise an exception in JavaScript before returning control.
* Currently, the JavaScript exception throw is always null.
*/
public static void raiseJavaScriptException() {
if (!_raiseJavaScriptException()) {
throw new RuntimeException(
"Failed to raise Java Exception into JavaScript.");
}
}
/**
* BrowserWindows register here so that if their contained window gets a call
* to window.external, the call can be routed correctly by nsIDOMWindow
* pointer.
*
* @param externalFactory the factory to register
*/
public static void registerExternalFactory(ExternalFactory externalFactory) {
synchronized (sExternalFactories) {
sExternalFactories.add(externalFactory);
}
}
/**
* Unregisters an existing registration.
*
* @param externalFactory the factory to unregister
*/
public static void unregisterExternalFactory(ExternalFactory externalFactory) {
synchronized (sExternalFactories) {
sExternalFactories.remove(externalFactory);
}
}
/**
* Called from native code to create an external object for a particular
* window.
*
* @param domWindow an nsIDOMWindow to check against our ExternalFactories map
* @return a new ExternalObject
*/
protected static ExternalObject createExternalObjectForDOMWindow(int domWindow) {
for (ExternalFactory fac : sExternalFactories) {
if (fac.matchesDOMWindow(domWindow)) {
return fac.createExternalObject();
}
}
return null;
}
/**
* Called from native code to do tracing.
*
* @param s the string to trace
*/
protected static void trace(String s) {
System.out.println(s);
System.out.flush();
}
/**
* Native code accessor to remove the mapping upon GC.
*/
static void removeJsvalForObject(Object o) {
sObjectToJsval.remove(o);
}
// CHECKSTYLE_NAMING_OFF: Non JSNI native code may have leading '_'s.
private static native boolean _executeScriptWithInfo(int scriptObject,
String newScript, String file, int line);
/**
* Native method for invoking a JavaScript method.
*
* @param scriptObject nsIScriptGlobalObject* as an int
* @param methodName name of JavaScript method
* @param jsThisInt JavaScript object to invoke the method on, as a
* JsRootedValue int
* @param jsArgsInt array of arguments, as an array of JsRootedValue ints
* @param jsRetValint pointer to JsRootedValue to receive return value
* @return true on success
*/
private static native boolean _invoke(int scriptObject, String methodName,
int jsThisInt, int[] jsArgsInt, int jsRetValInt);
private static native boolean _raiseJavaScriptException();
private static native boolean _registerExternalFactoryHandler();
// CHECKSTYLE_NAMING_ON
/**
* Print debug information for a JS method invocation.
*
* TODO(jat): remove this method
*
* @param methodName the name of the JS method being invoked
* @param jsthis the JS object with the named method
* @param jsargs an array of arguments to the method
*/
@SuppressWarnings("unused")
// kept for future debugging purposes
private static void printInvocationParams(String methodName,
JsValueMoz jsthis, JsValueMoz[] jsargs) {
System.out.println("LowLevelMoz.invoke:");
System.out.println(" method = " + methodName);
System.out.println(" # args = " + (jsargs.length));
System.out.println(" jsthis = " + jsthis.toString());
for (int i = 0; i < jsargs.length; ++i) {
System.out.println(" jsarg[" + i + "] = " + jsargs[i].toString());
}
System.out.println("");
}
/**
* Not instantiable.
*/
private LowLevelMoz() {
}
}