diff --git a/.github/workflows/full-check.yml b/.github/workflows/full-check.yml index 2e7a2644bc..27bf3b861f 100644 --- a/.github/workflows/full-check.yml +++ b/.github/workflows/full-check.yml @@ -31,7 +31,8 @@ jobs: - name: Checkout GWT tools into a sibling directory uses: actions/checkout@v4 with: - repository: 'gwtproject/tools' + repository: 'zbynek/tools' + ref: 'htmlunit-4' path: 'tools' - name: Set up JDK ${{ matrix.java-version }} # GWT requires Java 11+ to build diff --git a/.github/workflows/quick-check.yml b/.github/workflows/quick-check.yml index 09977bd4d6..fcdd80d037 100644 --- a/.github/workflows/quick-check.yml +++ b/.github/workflows/quick-check.yml @@ -19,7 +19,8 @@ jobs: - name: Checkout GWT tools into a sibling directory uses: actions/checkout@v4 with: - repository: 'gwtproject/tools' + repository: 'zbynek/tools' + ref: 'htmlunit-4' path: 'tools' - name: Set up JDK ${{ matrix.java-version }} # GWT presently requires Java 11+ to build diff --git a/dev/build.xml b/dev/build.xml index 2884648095..bf5c0354c8 100755 --- a/dev/build.xml +++ b/dev/build.xml @@ -85,19 +85,20 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + @@ -155,20 +156,20 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/dev/core/src/com/google/gwt/dev/shell/HostedModePluginObject.java b/dev/core/src/com/google/gwt/dev/shell/HostedModePluginObject.java index 98d5051d8f..c9aeaef585 100644 --- a/dev/core/src/com/google/gwt/dev/shell/HostedModePluginObject.java +++ b/dev/core/src/com/google/gwt/dev/shell/HostedModePluginObject.java @@ -17,17 +17,15 @@ import com.google.gwt.core.ext.TreeLogger; -import com.gargoylesoftware.htmlunit.WebClient; -import com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine; -import com.gargoylesoftware.htmlunit.javascript.host.Window; - -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 org.htmlunit.WebClient; +import org.htmlunit.corejs.javascript.Context; +import org.htmlunit.corejs.javascript.Function; +import org.htmlunit.corejs.javascript.Scriptable; +import org.htmlunit.corejs.javascript.ScriptableObject; +import org.htmlunit.javascript.JavaScriptEngine; +import org.htmlunit.javascript.host.Window; import java.io.IOException; -import java.util.Collections; /** * HTMLUnit object that represents the hosted-mode plugin. @@ -187,15 +185,6 @@ public boolean connect(String url, String sessionKey, String address, return false; } // TODO: add whitelist and default-port support? - - // We know that legacy dev mode is running, we need to tell HtmlUnit that it is safe - // to permit plain Java objects to leak into JS - the JavaObject type will return a - // Object[] with a success boolean and a value, and HtmlUnit will guard against this. - // The simplest way to do that here is to mark java.lang.Object as the java equivalent - // of some JS type - the name of the type doesn't matter. - webClient.setActiveXObjectMap(Collections.singletonMap( - "GwtLegacyDevModeExceptionOrReturnValue", "java.lang.Object")); - try { HtmlUnitSessionHandler htmlUnitSessionHandler = new HtmlUnitSessionHandler( window, jsEngine, webClient); diff --git a/dev/core/src/com/google/gwt/dev/shell/HtmlUnitSessionHandler.java b/dev/core/src/com/google/gwt/dev/shell/HtmlUnitSessionHandler.java index 75abb3aea3..8b93757bcc 100644 --- a/dev/core/src/com/google/gwt/dev/shell/HtmlUnitSessionHandler.java +++ b/dev/core/src/com/google/gwt/dev/shell/HtmlUnitSessionHandler.java @@ -23,23 +23,21 @@ import com.google.gwt.dev.shell.BrowserChannelClient.SessionHandlerClient; import com.google.gwt.dev.util.log.PrintWriterTreeLogger; -import com.gargoylesoftware.htmlunit.ScriptException; -import com.gargoylesoftware.htmlunit.ScriptResult; -import com.gargoylesoftware.htmlunit.WebClient; -import com.gargoylesoftware.htmlunit.WebWindow; -import com.gargoylesoftware.htmlunit.html.HtmlPage; -import com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine; -import com.gargoylesoftware.htmlunit.javascript.SimpleScriptable; -import com.gargoylesoftware.htmlunit.javascript.SimpleScriptableProxy; -import com.gargoylesoftware.htmlunit.javascript.host.Window; - -import net.sourceforge.htmlunit.corejs.javascript.ConsString; -import net.sourceforge.htmlunit.corejs.javascript.Context; -import net.sourceforge.htmlunit.corejs.javascript.Function; -import net.sourceforge.htmlunit.corejs.javascript.JavaScriptException; -import net.sourceforge.htmlunit.corejs.javascript.Scriptable; -import net.sourceforge.htmlunit.corejs.javascript.ScriptableObject; -import net.sourceforge.htmlunit.corejs.javascript.Undefined; +import org.htmlunit.ScriptException; +import org.htmlunit.ScriptResult; +import org.htmlunit.WebClient; +import org.htmlunit.WebWindow; +import org.htmlunit.corejs.javascript.ConsString; +import org.htmlunit.corejs.javascript.Context; +import org.htmlunit.corejs.javascript.Function; +import org.htmlunit.corejs.javascript.JavaScriptException; +import org.htmlunit.corejs.javascript.Scriptable; +import org.htmlunit.corejs.javascript.ScriptableObject; +import org.htmlunit.corejs.javascript.Undefined; +import org.htmlunit.html.HtmlPage; +import org.htmlunit.javascript.HtmlUnitScriptableProxy; +import org.htmlunit.javascript.JavaScriptEngine; +import org.htmlunit.javascript.host.Window; import java.util.Arrays; import java.util.HashMap; @@ -185,13 +183,14 @@ public ExceptionOrReturnValue invoke(BrowserChannelClient channel, Value thisObj jsThis = window; } else { Object obj = makeJsvalFromValue(jsContext, thisObj); + if (obj instanceof HtmlUnitScriptableProxy) { + obj = ((HtmlUnitScriptableProxy) obj).getDelegee(); + } if (obj instanceof ScriptableObject) { jsThis = (ScriptableObject) obj; - } else if (obj instanceof SimpleScriptableProxy) { - jsThis = ((SimpleScriptableProxy) obj).getDelegee(); } else { logger.log(TreeLogger.ERROR, "Unable to convert " + obj + " to either " - + " ScriptableObject or SimpleScriptableProxy"); + + " ScriptableObject or HtmlUnitScriptableProxy"); return new ExceptionOrReturnValue(true, new Value(null)); } } @@ -289,14 +288,14 @@ public Value makeValueFromJsval(Context jsContext, Object value) { return returnVal; } if (value instanceof Scriptable) { - if (value instanceof SimpleScriptableProxy) { + if (value instanceof HtmlUnitScriptableProxy) { // HtmlUnit will return proxies to java for the window/document objects, // so that those objects can work after navigating away from the page. // However, GWTTestCase operates inside a single page session, so we // can unwrap these proxies to get the real instance. Without doing // this, the refToJsObject mapping would indicate that an object might // not equal itself - value = ((SimpleScriptableProxy) value).getDelegee(); + value = ((HtmlUnitScriptableProxy) value).getDelegee(); } if (value instanceof ScriptableObject) { /* diff --git a/dev/core/src/com/google/gwt/dev/shell/JavaObject.java b/dev/core/src/com/google/gwt/dev/shell/JavaObject.java index 19a31f9604..86a91fcfa1 100644 --- a/dev/core/src/com/google/gwt/dev/shell/JavaObject.java +++ b/dev/core/src/com/google/gwt/dev/shell/JavaObject.java @@ -21,11 +21,11 @@ import com.google.gwt.dev.shell.BrowserChannel.SessionHandler.ExceptionOrReturnValue; import com.google.gwt.dev.shell.BrowserChannel.Value; -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 org.htmlunit.corejs.javascript.Context; +import org.htmlunit.corejs.javascript.Function; +import org.htmlunit.corejs.javascript.Scriptable; +import org.htmlunit.corejs.javascript.ScriptableObject; +import org.htmlunit.corejs.javascript.Undefined; import java.io.IOException; diff --git a/dev/core/test/com/google/gwt/core/ext/linker/impl/SelectionScriptJavaScriptTest.java b/dev/core/test/com/google/gwt/core/ext/linker/impl/SelectionScriptJavaScriptTest.java index b1b1e513ca..0bbc97169e 100644 --- a/dev/core/test/com/google/gwt/core/ext/linker/impl/SelectionScriptJavaScriptTest.java +++ b/dev/core/test/com/google/gwt/core/ext/linker/impl/SelectionScriptJavaScriptTest.java @@ -18,14 +18,14 @@ import com.google.gwt.core.ext.linker.LinkerUtils; -import com.gargoylesoftware.htmlunit.AlertHandler; -import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; -import com.gargoylesoftware.htmlunit.MockWebConnection; -import com.gargoylesoftware.htmlunit.Page; -import com.gargoylesoftware.htmlunit.WebClient; - import junit.framework.TestCase; +import org.htmlunit.AlertHandler; +import org.htmlunit.FailingHttpStatusCodeException; +import org.htmlunit.MockWebConnection; +import org.htmlunit.Page; +import org.htmlunit.WebClient; + import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; diff --git a/maven/poms/gwt/gwt-dev/pom-template.xml b/maven/poms/gwt/gwt-dev/pom-template.xml index c8f3419350..70d0c0655f 100644 --- a/maven/poms/gwt/gwt-dev/pom-template.xml +++ b/maven/poms/gwt/gwt-dev/pom-template.xml @@ -95,7 +95,7 @@ - net.sourceforge.htmlunit + org.htmlunit htmlunit diff --git a/maven/poms/gwt/pom-template.xml b/maven/poms/gwt/pom-template.xml index b165744e3e..0e6d70be8d 100644 --- a/maven/poms/gwt/pom-template.xml +++ b/maven/poms/gwt/pom-template.xml @@ -141,9 +141,9 @@ 63.1 - net.sourceforge.htmlunit + org.htmlunit htmlunit - 2.55.0 + 4.11.1 org.w3c.css diff --git a/user/src/com/google/gwt/junit/RunStyleHtmlUnit.java b/user/src/com/google/gwt/junit/RunStyleHtmlUnit.java index 73815427f5..1a09f21233 100644 --- a/user/src/com/google/gwt/junit/RunStyleHtmlUnit.java +++ b/user/src/com/google/gwt/junit/RunStyleHtmlUnit.java @@ -20,30 +20,26 @@ import com.google.gwt.thirdparty.guava.common.collect.ImmutableSet; import com.google.gwt.thirdparty.guava.common.collect.Maps; -import com.gargoylesoftware.htmlunit.AlertHandler; -import com.gargoylesoftware.htmlunit.BrowserVersion; -import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; -import com.gargoylesoftware.htmlunit.IncorrectnessListener; -import com.gargoylesoftware.htmlunit.OnbeforeunloadHandler; -import com.gargoylesoftware.htmlunit.Page; -import com.gargoylesoftware.htmlunit.ScriptException; -import com.gargoylesoftware.htmlunit.WebClient; -import com.gargoylesoftware.htmlunit.WebWindow; -import com.gargoylesoftware.htmlunit.html.HtmlPage; -import com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine; -import com.gargoylesoftware.htmlunit.javascript.JavaScriptErrorListener; -import com.gargoylesoftware.htmlunit.javascript.host.Window; -import com.gargoylesoftware.htmlunit.util.WebClientUtils; - -import net.sourceforge.htmlunit.corejs.javascript.Context; -import net.sourceforge.htmlunit.corejs.javascript.Function; -import net.sourceforge.htmlunit.corejs.javascript.JavaScriptException; -import net.sourceforge.htmlunit.corejs.javascript.ScriptableObject; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.htmlunit.AlertHandler; +import org.htmlunit.BrowserVersion; +import org.htmlunit.FailingHttpStatusCodeException; +import org.htmlunit.IncorrectnessListener; +import org.htmlunit.OnbeforeunloadHandler; +import org.htmlunit.Page; +import org.htmlunit.ScriptException; +import org.htmlunit.WebClient; +import org.htmlunit.WebWindow; +import org.htmlunit.corejs.javascript.Context; +import org.htmlunit.corejs.javascript.ScriptableObject; +import org.htmlunit.html.HtmlPage; +import org.htmlunit.javascript.HtmlUnitContextFactory; +import org.htmlunit.javascript.JavaScriptEngine; +import org.htmlunit.javascript.JavaScriptErrorListener; +import org.htmlunit.javascript.host.Window; +import org.htmlunit.util.WebClientUtils; import java.io.IOException; +import java.lang.reflect.Field; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; @@ -52,6 +48,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TimeZone; /** * Launches a web-mode test via HTMLUnit. @@ -105,6 +102,7 @@ public void run() { webClient.setAlertHandler(this); webClient.setIncorrectnessListener(this); webClient.getOptions().setThrowExceptionOnFailingStatusCode(false); + webClient.getOptions().setFetchPolyfillEnabled(true); // To receive exceptions from js side in the development mode, we need set this to 'true'. // However, as htmlunit dies after throwing the exception, we still want it to be 'false' // for web mode. @@ -138,7 +136,7 @@ public void malformedScriptURL(HtmlPage htmlPage, String url, @Override public void scriptException(HtmlPage htmlPage, ScriptException scriptException) { - treeLogger.log(TreeLogger.DEBUG, + treeLogger.log(TreeLogger.ERROR, "Script Exception: " + scriptException.getLocalizedMessage() + ", line " + scriptException.getFailingLine()); } @@ -177,7 +175,7 @@ protected void setupWebClient(WebClient webClient) { treeLogger); webClient.setJavaScriptEngine(hostedEngine); } else { - JavaScriptEngine webEngine = new WebJavaScriptEngine(webClient); + JavaScriptEngine webEngine = new JavaScriptEngine(webClient); webClient.setJavaScriptEngine(webEngine); } if (System.getProperty("gwt.htmlunit.debug") != null) { @@ -196,108 +194,57 @@ private static class HostedJavaScriptEngine extends JavaScriptEngine { private static final long serialVersionUID = 3594816610842448691L; private final WebClient webClient; private final TreeLogger logger; + private final HtmlUnitContextFactory htmlUnitContextFactory; - public HostedJavaScriptEngine(WebClient webClient, TreeLogger logger) { + HostedJavaScriptEngine(WebClient webClient, TreeLogger logger) { super(webClient); this.webClient = webClient; this.logger = logger; + this.htmlUnitContextFactory = new HtmlUnitContextFactory(webClient) { + protected Context makeContext() { + Context ctx = super.makeContext(); + try { + Field hasShutter = Context.class.getDeclaredField("hasClassShutter"); + hasShutter.setAccessible(true); + hasShutter.setBoolean(ctx, false); + } catch (IllegalAccessException | NoSuchFieldException e) { + throw new RuntimeException(e); + } + ctx.setClassShutter(any -> true); + return ctx; + } + }; + } + + @Override + public HtmlUnitContextFactory getContextFactory() { + return htmlUnitContextFactory; } @Override public void initialize(WebWindow webWindow, Page page) { // Hook in the hosted-mode plugin after initializing the JS engine. super.initialize(webWindow, page); - Window window = (Window) webWindow.getScriptableObject(); + Window window = webWindow.getScriptableObject(); window.defineProperty("__gwt_HostedModePlugin", new HostedModePluginObject(this, webClient, logger), ScriptableObject.READONLY); } } - /** - * JavaScriptEngine subclass that fixes a bug when calling {@code window.onerror}. - * Make sure to remove when updating HtmlUnit. - * - * @see HtmlUnit bug #1924 - */ - private static class WebJavaScriptEngine extends JavaScriptEngine { - private static final Log LOG = LogFactory.getLog(JavaScriptEngine.class); - private final WebClient webClient; - - public WebJavaScriptEngine(WebClient webClient) { - super(webClient); - this.webClient = webClient; - } - - @Override - protected void handleJavaScriptException(ScriptException scriptException, - boolean triggerOnError) { - // XXX(tbroyer): copied from JavaScriptEngine to call below triggerOnError - // instead of Window's triggerOnError. - - // Trigger window.onerror, if it has been set. - final HtmlPage page = scriptException.getPage(); - if (triggerOnError && page != null) { - final WebWindow window = page.getEnclosingWindow(); - if (window != null) { - final Window w = (Window) window.getScriptableObject(); - if (w != null) { - try { - triggerOnError(w, scriptException); - } catch (final Exception e) { - handleJavaScriptException(new ScriptException(page, e, null), false); - } - } - } - } - final JavaScriptErrorListener javaScriptErrorListener = - webClient.getJavaScriptErrorListener(); - if (javaScriptErrorListener != null) { - javaScriptErrorListener.scriptException(page, scriptException); - } - // Throw a Java exception if the user wants us to. - if (webClient.getOptions().isThrowExceptionOnScriptError()) { - throw scriptException; - } - // Log the error; ScriptException instances provide good debug info. - LOG.info("Caught script exception", scriptException); - } - - private void triggerOnError(Window w, ScriptException e) { - // XXX(tbroyer): copied from HtmlUnit's javascript.host.Window - // with fix unwrapping the JS exception before passing it back to JS. - final Object o = w.getOnerror(); - if (o instanceof Function) { - final Function f = (Function) o; - final String msg = e.getMessage(); - final String url = e.getPage().getUrl().toExternalForm(); - final int line = e.getFailingLineNumber(); - - final int column = e.getFailingColumnNumber(); - - Object jsError = null; - if (e.getCause() instanceof JavaScriptException) { - jsError = ((JavaScriptException) e.getCause()).getValue(); - } - - Object[] args = new Object[]{msg, url, line, column, jsError}; - - f.call(Context.getCurrentContext(), w, w, args); - } - } - } - private static final Map BROWSER_MAP = Maps.newHashMap(); private static final Map USER_AGENT_MAP = Maps.newHashMap(); static { - // “Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 Edge/12.0″ + // “Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) + // Chrome/39.0.2171.71 Safari/537.36 Edge/12.0″ addBrowser(BrowserVersion.EDGE, "safari"); addBrowser(BrowserVersion.FIREFOX, "gecko1_8"); addBrowser(BrowserVersion.CHROME, "safari"); - addBrowser(BrowserVersion.INTERNET_EXPLORER, "gecko1_8"); } - private static void addBrowser(BrowserVersion browser, String userAgent) { + private static void addBrowser(BrowserVersion baseBrowser, String userAgent) { + BrowserVersion browser = new BrowserVersion.BrowserVersionBuilder(baseBrowser) + .setSystemTimezone(TimeZone.getDefault()).build(); BROWSER_MAP.put(browser.getNickname(), browser); USER_AGENT_MAP.put(browser, userAgent); } diff --git a/user/test/com/google/gwt/emultest/java/util/DateTest.java b/user/test/com/google/gwt/emultest/java/util/DateTest.java index 452e1f53f9..ba1507c0df 100644 --- a/user/test/com/google/gwt/emultest/java/util/DateTest.java +++ b/user/test/com/google/gwt/emultest/java/util/DateTest.java @@ -571,15 +571,17 @@ public void testToLocaleString() { // ///////////////////////////// // Past // ///////////////////////////// - Date accum1 = create(PAST); + Date accum1 = createLocal("1/5/1890"); String a1 = accum1.toLocaleString(); - assertTrue(a1.indexOf("1890") != -1); + assertTrue(a1 + " should describe 1/5/1890", + a1.contains("1890") || a1.contains("1/5/90")); // ///////////////////////////// // Future // ///////////////////////////// - Date accum2 = create(FUTURE); + Date accum2 = createLocal("12/30/2030 3:4:5"); String a2 = accum2.toLocaleString(); - assertTrue(a2.indexOf("2030") != -1); + assertTrue(a2 + " should describe 12/30/2030", + a2.contains("2030") || a2.contains("12/30/30")); } } @@ -895,6 +897,11 @@ Date create(String s) { } } + Date createLocal(String s) { + // aligned with the TZ used in CI + return new Date(s + " GMT-8:00"); + } + private String createString(String s) { if (s.equals(FUTURE)) { return "12/30/2030 3:4:5 GMT";