/* GNU LESSER GENERAL PUBLIC LICENSE Copyright (C) 2006 The Lobo Project This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Contact info: lobochief@users.sourceforge.net */ package org.lobobrowser.html.js; import java.util.logging.Level; import java.util.logging.Logger; import org.lobobrowser.html.domimpl.HTMLDocumentImpl; import org.lobobrowser.html.domimpl.NodeImpl; import org.lobobrowser.js.JavaScript; import org.lobobrowser.ua.UserAgentContext; import org.lobobrowser.ua.UserAgentContext.Request; import org.lobobrowser.ua.UserAgentContext.RequestKind; import org.mozilla.javascript.Context; import org.mozilla.javascript.ContextFactory; import org.mozilla.javascript.Function; import org.mozilla.javascript.RhinoException; import org.mozilla.javascript.Scriptable; import org.w3c.dom.Document; public class Executor { private static final Logger logger = Logger.getLogger(Executor.class.getName()); /** * This method should be invoked instead of <code>Context.enter</code>. * * @param codeSource * @param ucontext */ public static Context createContext(final java.net.URL codeSource, final UserAgentContext ucontext, final ContextFactory factory) { final Context prev = Context.getCurrentContext(); // final Context ctx = Context.enter(); final Context ctx = factory.enterContext(); if (!ctx.isSealed()) { ctx.setOptimizationLevel(ucontext.getScriptingOptimizationLevel()); if (prev == null) { // If there was a previous context, this one must be nested. // We still need to create a context because of exit() but // we cannot set a new security controller. ctx.setSecurityController(new SecurityControllerImpl(codeSource, ucontext.getSecurityPolicy())); } // Sealing is recommended for untrusted scripts ctx.seal(null); } return ctx; } public static boolean executeFunction(final NodeImpl element, final Function f, final Object event, final ContextFactory contextFactory) { return Executor.executeFunction(element, element, f, event, contextFactory); } private static boolean executeFunction(final NodeImpl element, final Object thisObject, final Function f, final Object event, final ContextFactory contextFactory) { final Document doc = element.getOwnerDocument(); if (doc == null) { throw new IllegalStateException("Element does not belong to a document."); } final UserAgentContext uaContext = element.getUserAgentContext(); if (uaContext.isRequestPermitted(new Request(element.getDocumentURL(), RequestKind.JavaScript))) { final Context ctx = createContext(element.getDocumentURL(), element.getUserAgentContext(), contextFactory); // ctx.setGenerateObserverCount(true); try { final Scriptable scope = ((HTMLDocumentImpl) doc).getWindow().getWindowScope(); if (scope == null) { throw new IllegalStateException("Scriptable (scope) instance is null"); } final JavaScript js = JavaScript.getInstance(); final Scriptable thisScope = (Scriptable) js.getJavascriptObject(thisObject, scope); try { // final Scriptable eventScriptable = (Scriptable) js.getJavascriptObject(event, thisScope); final Object eventScriptable = js.getJavascriptObject(event, thisScope); scope.put("event", thisScope, eventScriptable); // ScriptableObject.defineProperty(thisScope, "event", // eventScriptable, // ScriptableObject.READONLY); final Object result = f.call(ctx, thisScope, thisScope, new Object[] { eventScriptable }); if (!(result instanceof Boolean)) { return true; } return ((Boolean) result).booleanValue(); } catch (final Exception thrown) { logJSException(thrown); return true; } } finally { Context.exit(); } } else { // TODO: Should this be true? I am copying the return from the exception clause above. System.out.println("Rejected request to execute script"); return true; } } public static void logJSException(final Throwable err) { logger.log(Level.WARNING, "Unable to evaluate Javascript code", err); if (err instanceof RhinoException) { final RhinoException rhinoException = (RhinoException) err; logger.log(Level.WARNING, "JS Error: " + rhinoException.details() + "\nJS Stack:\n" + rhinoException.getScriptStackTrace()); } } public static boolean executeFunction(final Scriptable thisScope, final Function f, final java.net.URL codeSource, final UserAgentContext ucontext, final ContextFactory contextFactory) { final Context ctx = createContext(codeSource, ucontext, contextFactory); try { try { final Object result = f.call(ctx, thisScope, thisScope, new Object[0]); if (!(result instanceof Boolean)) { return true; } return ((Boolean) result).booleanValue(); } catch (final Exception err) { logJSException(err); return true; } } finally { Context.exit(); } } }