1 38 package com.gargoylesoftware.htmlunit.javascript; 39 40 import com.gargoylesoftware.htmlunit.Assert; 41 import com.gargoylesoftware.htmlunit.ScriptEngine; 42 import com.gargoylesoftware.htmlunit.ScriptException; 43 import com.gargoylesoftware.htmlunit.WebClient; 44 import com.gargoylesoftware.htmlunit.html.HtmlElement; 45 import com.gargoylesoftware.htmlunit.html.HtmlPage; 46 import com.gargoylesoftware.htmlunit.javascript.configuration.ClassConfiguration; 47 import com.gargoylesoftware.htmlunit.javascript.configuration.JavaScriptConfiguration; 48 import com.gargoylesoftware.htmlunit.javascript.host.Window; 49 50 import java.util.Iterator ; 51 import java.util.WeakHashMap ; 52 import java.util.Map ; 53 import java.lang.ref.WeakReference ; 54 55 import org.apache.commons.logging.Log; 56 import org.apache.commons.logging.LogFactory; 57 import org.mozilla.javascript.Context; 58 import org.mozilla.javascript.Function; 59 import org.mozilla.javascript.FunctionObject; 60 import org.mozilla.javascript.Scriptable; 61 import org.mozilla.javascript.ScriptableObject; 62 63 76 public final class JavaScriptEngine extends ScriptEngine { 77 78 79 public static final class PageInfo { 80 81 87 private Context context_; 88 89 90 private Window scope_; 91 92 96 public Window getScope() { 97 return scope_; 98 } 99 } 100 101 102 105 private final Map pageInfos_ = new WeakHashMap (89); 106 107 private static final ThreadLocal WEB_CLIENTS = new ThreadLocal (); 108 109 private static final ThreadLocal javaScriptRunning_ = new ThreadLocal (); 110 111 116 public JavaScriptEngine( final WebClient webClient ) { 117 super( webClient ); 118 } 119 120 124 public void initialize(final HtmlPage page) { 125 try { 127 getPageInfo(page); 128 } 129 catch (final RuntimeException e) { 130 getLog().error("Exception while initializing JavaScript for the page", e); 132 } 133 } 134 135 136 private PageInfo getPageInfo( final HtmlPage htmlPage ) { 137 Assert.notNull( "htmlPage", htmlPage ); 138 139 final WeakReference weakReference = (WeakReference )pageInfos_.get(htmlPage); 140 if( weakReference != null ) { 141 final PageInfo existingPageInfo = (PageInfo)weakReference.get(); 142 if( existingPageInfo != null ) { 143 return existingPageInfo; 144 } 145 } 146 final WebClient webClient = htmlPage.getWebClient(); 147 try { 148 WEB_CLIENTS.set(webClient); 149 150 final Context context = Context.enter(); 151 context.setOptimizationLevel(-1); 152 context.setErrorReporter(new StrictErrorReporter(getScriptEngineLog())); 153 final Scriptable parentScope = context.initStandardObjects(null); 154 155 final JavaScriptConfiguration jsConfig = JavaScriptConfiguration.getInstance(webClient.getBrowserVersion()); 156 final Iterator it = jsConfig.keySet().iterator(); 157 while (it.hasNext()) { 158 final String jsClassName = (String ) it.next(); 159 final ClassConfiguration config = jsConfig.getClassConfiguration(jsClassName); 160 if (config.isJsObject()) { 161 final Class jsHostClass = config.getLinkedClass(); 162 ScriptableObject.defineClass(parentScope, jsHostClass); 163 final ScriptableObject prototype = (ScriptableObject) ScriptableObject 164 .getClassPrototype(parentScope, jsClassName); 165 166 final Iterator propertiesIterator = config.propertyKeys().iterator(); 167 while (propertiesIterator.hasNext()) { 168 final String entryKey = (String ) propertiesIterator.next(); 169 prototype.defineProperty(entryKey, null, config.getPropertyReadMethod(entryKey), 170 config.getPropertyWriteMethod(entryKey), 0); 171 } 172 173 final Iterator functionsIterator = config.functionKeys().iterator(); 174 while (functionsIterator.hasNext()) { 175 final String entryKey = (String ) functionsIterator.next(); 176 final FunctionObject functionObject = new FunctionObject(entryKey, 177 config.getFunctionMethod(entryKey), prototype); 178 prototype.defineProperty(entryKey, functionObject, 0); 179 } 180 181 } 182 } 183 184 ScriptableObject.defineClass(parentScope, ElementArray.class); 185 ScriptableObject.defineClass(parentScope, OptionsArray.class); 186 187 final Window window = (Window) context.newObject( 188 parentScope, "Window", new Object [0]); 189 190 final PageInfo newPageInfo = new PageInfo(); 191 newPageInfo.context_ = context; 192 newPageInfo.scope_ = window; 193 window.setPageInfo(newPageInfo); 194 window.initialize(htmlPage); 195 196 pageInfos_.put( htmlPage, new WeakReference (newPageInfo) ); 197 198 return newPageInfo; 199 } 200 catch( final Exception e ) { 201 throw new ScriptException(e); 202 } 203 } 205 206 207 208 212 protected Log getLog() { 213 return LogFactory.getLog(getClass()); 214 } 215 216 223 private Scriptable getScope( 224 final PageInfo pageInfo, final HtmlElement htmlElementScope ) { 225 226 Scriptable scope; 227 if( htmlElementScope == null ) { 228 scope = pageInfo.getScope(); 229 } 230 else { 231 scope = (Scriptable)htmlElementScope.getScriptObject(); 232 scope.setParentScope(pageInfo.getScope()); 233 } 234 return scope; 235 } 236 237 238 248 public Object execute( 249 final HtmlPage htmlPage, String sourceCode, final String sourceName, final HtmlElement htmlElementScope ) { 250 251 Assert.notNull( "sourceCode", sourceCode ); 252 253 sourceCode = preProcess(htmlPage, sourceCode, sourceName, htmlElementScope); 255 256 sourceCode = sourceCode.trim(); 258 if( sourceCode.startsWith("<!--") ) { 259 int startIndex = 4; 260 261 final int endIndex; 262 if( sourceCode.endsWith("-->") ) { 263 endIndex = sourceCode.length()-3; 264 } 265 else { 266 endIndex = sourceCode.length(); 267 } 268 269 char eachChar = sourceCode.charAt(startIndex); 271 while( startIndex < endIndex && eachChar != '\n' && eachChar != '\r' ) { 272 eachChar = sourceCode.charAt( ++startIndex ); 273 } 274 275 sourceCode = sourceCode.substring(startIndex, endIndex); 276 } 277 278 final PageInfo pageInfo = getPageInfo(htmlPage); 279 280 final int lineNumber = 1; 281 final Object securityDomain = null; 282 283 final Scriptable scope = getScope( pageInfo, htmlElementScope ); 284 285 final Boolean javaScriptAlreadyRunning = (Boolean ) javaScriptRunning_.get(); 286 javaScriptRunning_.set(Boolean.TRUE); 287 try { 288 final Object result = pageInfo.context_.evaluateString( 289 scope, sourceCode, sourceName, lineNumber, securityDomain ); 290 return result; 291 } 292 catch (final Exception e ) { 293 final ScriptException scriptException = new ScriptException( e, sourceCode ); 294 if (getWebClient().isThrowExceptionOnScriptError()) { 295 throw scriptException; 296 } 297 else { 298 getLog().info("Catched script exception", scriptException); 301 return null; 302 } 303 } 304 finally { 305 javaScriptRunning_.set(javaScriptAlreadyRunning); 306 } 307 } 308 309 310 319 public Object callFunction( 320 final HtmlPage htmlPage, 321 final Object javaScriptFunction, 322 final Object thisObject, 323 final Object [] args, 324 final HtmlElement htmlElementScope ) { 325 326 final PageInfo pageInfo = getPageInfo(htmlPage); 327 328 final Scriptable scope = getScope( pageInfo, htmlElementScope ); 329 final Boolean javaScriptAlreadyRunning = (Boolean ) javaScriptRunning_.get(); 332 javaScriptRunning_.set(Boolean.TRUE); 333 final Function function = (Function) javaScriptFunction; 334 try { 335 final Object result = function.call( pageInfo.context_, 336 scope, (Scriptable) thisObject, args ); 337 return result; 338 } 339 catch (final Exception e ) { 340 final ScriptException scriptException = new ScriptException( e, 341 pageInfo.context_.decompileFunction(function, 2)); 342 if (getWebClient().isThrowExceptionOnScriptError()) { 343 throw scriptException; 344 } 345 else { 346 getLog().info("Catched script exception", scriptException); 349 return null; 350 } 351 } 352 finally { 353 javaScriptRunning_.set(javaScriptAlreadyRunning); 354 } 355 } 356 357 358 364 public String toString( final HtmlPage htmlPage, final Object javaScriptObject ) { 365 366 getPageInfo(htmlPage); 367 368 final String result = Context.toString( javaScriptObject ); 369 return result; 370 } 371 372 377 public static WebClient getWebClientForCurrentThread() { 378 return (WebClient)WEB_CLIENTS.get(); 379 } 380 381 386 public boolean isScriptRunning() { 387 return Boolean.TRUE.equals(javaScriptRunning_.get()); 388 } 389 } 390 391 | Popular Tags |