private void init(final WebWindow webWindow, final Context context) throws Exception {
final WebClient webClient = webWindow.getWebClient();
final Map<Class< ? extends SimpleScriptable>, Scriptable> prototypes =
new HashMap<Class< ? extends SimpleScriptable>, Scriptable>();
final Map<String, Scriptable> prototypesPerJSName = new HashMap<String, Scriptable>();
final Window window = new Window();
final JavaScriptConfiguration jsConfig = JavaScriptConfiguration.getInstance(webClient.getBrowserVersion());
context.initStandardObjects(window);
// remove some objects, that Rhino defines in top scope but that we don't want
deleteProperties(window, "javax", "org", "com", "edu", "net", "JavaAdapter", "JavaImporter", "Continuation");
if (webClient.getBrowserVersion().isIE()) {
deleteProperties(window, "Packages", "java", "getClass", "XML", "XMLList", "Namespace", "QName");
}
// put custom object to be called as very last prototype to call the fallback getter (if any)
final Scriptable fallbackCaller = new FallbackCaller();
ScriptableObject.getObjectPrototype(window).setPrototype(fallbackCaller);
for (final String jsClassName : jsConfig.keySet()) {
final ClassConfiguration config = jsConfig.getClassConfiguration(jsClassName);
final boolean isWindow = Window.class.getName().equals(config.getLinkedClass().getName());
if (isWindow) {
configureConstantsPropertiesAndFunctions(config, window);
}
else {
final ScriptableObject prototype = configureClass(config, window);
if (config.isJsObject()) {
// for FF, place object with prototype property in Window scope
if (!getWebClient().getBrowserVersion().isIE()) {
final SimpleScriptable obj = config.getLinkedClass().newInstance();
prototype.defineProperty("__proto__", prototype, ScriptableObject.DONTENUM);
obj.defineProperty("prototype", prototype, ScriptableObject.DONTENUM); // but not setPrototype!
obj.setParentScope(window);
ScriptableObject.defineProperty(window, config.getClassName(), obj, ScriptableObject.DONTENUM);
// this obj won't have prototype, constants need to be configured on it again
configureConstants(config, obj);
if (obj.getClass() == Element.class && webWindow.getEnclosedPage() instanceof HtmlPage) {
final DomNode domNode =
new HtmlDivision(null, "", (SgmlPage) webWindow.getEnclosedPage(), null);
obj.setDomNode(domNode);
}
}
prototypes.put(config.getLinkedClass(), prototype);
}
prototypesPerJSName.put(config.getClassName(), prototype);
}
}
// once all prototypes have been build, it's possible to configure the chains
final Scriptable objectPrototype = ScriptableObject.getObjectPrototype(window);
for (final Map.Entry<String, Scriptable> entry : prototypesPerJSName.entrySet()) {
final String name = entry.getKey();
final ClassConfiguration config = jsConfig.getClassConfiguration(name);
Scriptable prototype = entry.getValue();
if (prototype.getPrototype() != null) {
prototype = prototype.getPrototype(); // "double prototype" hack for FF
}
if (!StringUtils.isEmpty(config.getExtendedClass())) {
final Scriptable parentPrototype = prototypesPerJSName.get(config.getExtendedClass());
prototype.setPrototype(parentPrototype);
}
else {
prototype.setPrototype(objectPrototype);
}
}
// eval hack (cf unit tests testEvalScopeOtherWindow and testEvalScopeLocal)
final Class< ? >[] evalFnTypes = {String.class};
final Member evalFn = Window.class.getMethod("custom_eval", evalFnTypes);
final FunctionObject jsCustomEval = new FunctionObject("eval", evalFn, window);
window.associateValue("custom_eval", jsCustomEval);
for (final String jsClassName : jsConfig.keySet()) {
final ClassConfiguration config = jsConfig.getClassConfiguration(jsClassName);
final Method jsConstructor = config.getJsConstructor();
if (jsConstructor != null) {
final Scriptable prototype = prototypesPerJSName.get(jsClassName);
if (prototype != null) {
final FunctionObject jsCtor = new FunctionObject(jsClassName, jsConstructor, window);
jsCtor.addAsConstructor(window, prototype);
}
}
}
// Rhino defines too much methods for us, particularly since implementation of ECMAScript5
removePrototypeProperties(window, "String", "equals", "equalsIgnoreCase", "trim");
removePrototypeProperties(window, "Function", "bind");
removePrototypeProperties(window, "Date", "toISOString", "toJSON");
// in IE, not all standard methods exists
if (webClient.getBrowserVersion().isIE()) {
deleteProperties(window, "isXMLName", "uneval");
removePrototypeProperties(window, "Object", "__defineGetter__", "__defineSetter__", "__lookupGetter__",
"__lookupSetter__", "toSource");
removePrototypeProperties(window, "Array", "every", "filter", "forEach", "indexOf", "lastIndexOf", "map",
"reduce", "reduceRight", "some", "toSource");
removePrototypeProperties(window, "Date", "toSource");
removePrototypeProperties(window, "Function", "toSource");
removePrototypeProperties(window, "Number", "toSource");
removePrototypeProperties(window, "String", "toSource");
}
else if ("FF2".equals(webClient.getBrowserVersion().getNickname())) {
removePrototypeProperties(window, "Array", "reduce", "reduceRight");
}
window.setPrototypes(prototypes);
window.initialize(webWindow);
}