Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8242361: JavaFX Web View crashes with Segmentation Fault, when HTML contains Data-URIs #360

Closed
wants to merge 6 commits into from
@@ -3600,6 +3600,7 @@ project(":systemTests") {
testapp4
testapp5
testapp6
testapp7
testscriptapp1
testscriptapp2
}
@@ -3615,6 +3616,7 @@ project(":systemTests") {
sourceSets.testapp4,
sourceSets.testapp5,
sourceSets.testapp6,
sourceSets.testapp7,
sourceSets.testscriptapp1,
sourceSets.testscriptapp2
]
@@ -3716,7 +3718,7 @@ project(":systemTests") {
}
test.dependsOn(createTestApps);

def modtestapps = [ "testapp2", "testapp3", "testapp4", "testapp5", "testapp6", "testscriptapp1", "testscriptapp2" ]
def modtestapps = [ "testapp2", "testapp3", "testapp4", "testapp5", "testapp6", "testapp7", "testscriptapp1", "testscriptapp2" ]
modtestapps.each { testapp ->
def testappCapital = testapp.capitalize()
def copyTestAppTask = task("copy${testappCapital}", type: Copy) {
@@ -31,25 +31,59 @@
#include <wtf/RunLoop.h>

namespace WTF {
static JGClass jMainThreadCls;
static jmethodID fwkIsMainThread;
static jmethodID fwkScheduleDispatchFunctions;

void scheduleDispatchFunctionsOnMainThread()
{
AttachThreadAsNonDaemonToJavaEnv autoAttach;
JNIEnv* env = autoAttach.env();
static JGClass jMainThreadCls(env->FindClass("com/sun/webkit/MainThread"));
env->CallStaticVoidMethod(jMainThreadCls, fwkScheduleDispatchFunctions);
WTF::CheckAndClearException(env);
}

void initializeMainThreadPlatform()
{
// Initialize the class reference and methodids for the MainThread. The
// initialization has to be done from a context where the class
// com.sun.webkit.MainThread is accessible. When
// scheduleDispatchFunctionsOnMainThread is invoked, the system class loader
// would be used to locate the class, which fails if the JavaFX modules are
// not loaded from the boot module layer.
//
// initializeMainThreadPlatform is called through the chain:
// - com.sun.webkit.WebPage.WebPage
// - com.sun.webkit.WebPage.twkCreatePage
// - WTF::initializeMainThread
// - WTF::initializeMainThreadPlatform
//
// As we are invoked through JNI from java, the class loader, that loaded
// WebPage will be used by FindClass.
//
// WTF::initializeMainThread has a guard, so that initialization is only run
This conversation was marked as resolved by kevinrushforth

This comment has been minimized.

@kevinrushforth

kevinrushforth Dec 14, 2020
Member

I guess you meant to say "is only run once"?

// once

AttachThreadAsNonDaemonToJavaEnv autoAttach;
JNIEnv* env = autoAttach.env();

static JGClass jMainThreadRef(env->FindClass("com/sun/webkit/MainThread"));
jMainThreadCls = jMainThreadRef;

fwkIsMainThread = env->GetStaticMethodID(
jMainThreadCls,
"fwkIsMainThread",
"()Z");

ASSERT(fwkIsMainThread);

static jmethodID mid = env->GetStaticMethodID(
fwkScheduleDispatchFunctions = env->GetStaticMethodID(
jMainThreadCls,
"fwkScheduleDispatchFunctions",
"()V");

ASSERT(mid);
ASSERT(fwkScheduleDispatchFunctions);

env->CallStaticVoidMethod(jMainThreadCls, mid);
WTF::CheckAndClearException(env);
}

void initializeMainThreadPlatform()
{
#if OS(WINDOWS)
RunLoop::registerRunLoopMessageWindowClass();
#endif
@@ -64,16 +98,7 @@ bool isMainThread()
{
AttachThreadAsNonDaemonToJavaEnv autoAttach;
JNIEnv* env = autoAttach.env();
static JGClass jMainThreadCls(env->FindClass("com/sun/webkit/MainThread"));

static jmethodID mid = env->GetStaticMethodID(
jMainThreadCls,
"fwkIsMainThread",
"()Z");

ASSERT(mid);

jboolean isMainThread = env->CallStaticBooleanMethod(jMainThreadCls, mid);
jboolean isMainThread = env->CallStaticBooleanMethod(jMainThreadCls, fwkIsMainThread);
WTF::CheckAndClearException(env);
return isMainThread == JNI_TRUE;
}
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package test.com.sun.webkit;

import java.io.File;
import static java.util.Arrays.asList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import org.junit.Test;

/**
* @test
* @bug 8242361
* @summary Check if webkit main thread <-> java integration works correctly
*/
public class MainThreadTest {
@Test (timeout = 15000)
public void testMainThreadDoesNotSegfault() throws Exception {
// This is an indirect test of the webkit main thread <-> java
// integration. It was observed, that using a data-url caused the
// JVM to segfault. That case is executed by this test:
//
// A new JVM is started with a custom launcher (classpath based). This
// launcher sets up the module layer required for OpenJFX and starts
// the test application. That way the OpenJFX classes are not loaded by
// the system class loader, but by the classloader that is associated
// with the new module layer.
//

final String appModulePath = System.getProperty("launchertest.testapp7.module.path");
final String workerModulePath = System.getProperty("worker.module.path");
final String javaLibraryPath = System.getProperty("java.library.path");
final String workerJavaCmd = System.getProperty("worker.java.cmd");

final List<String> cmd = asList(
workerJavaCmd,
"-cp", appModulePath + "/mymod",
"-Djava.library.path=" + javaLibraryPath,
"-Dmodule.path=" + appModulePath + "/mymod" + File.pathSeparator + workerModulePath,
"myapp7.DataUrlWithModuleLayerLauncher"
);

final ProcessBuilder builder = new ProcessBuilder(cmd);

builder.redirectError(ProcessBuilder.Redirect.INHERIT);
builder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
Process process = builder.start();
int retVal = process.waitFor();

assertEquals("Process did not exit cleanly", 0, retVal);
}
}
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

module mymod {
requires javafx.controls;
requires javafx.media;
requires javafx.web;
requires javafx.graphics;
requires javafx.base;
exports myapp7;
}
@@ -0,0 +1,115 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package myapp7;

import java.lang.module.ModuleDescriptor;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker.State;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class DataUrlWithModuleLayer extends Application {
public static final int ERROR_OK = 0;
public static final int ERROR_ASSUMPTION_VIOLATED = 2;
public static final int ERROR_TIMEOUT = 3;
public static final int ERROR_TITLE_NOT_UPDATED = 4;
public static final int ERROR_UNEXPECTED_EXIT = 5;

@Override
public void start(Stage primaryStage) throws Exception {
Module module = Application.class.getModule();

if (module == null) {
System.err.println("Failure: Module for Application not found");
System.exit(ERROR_ASSUMPTION_VIOLATED);
}

if (! module.isNamed()) {
System.err.println("Failure: Expected named module");
System.exit(ERROR_ASSUMPTION_VIOLATED);
}

ModuleDescriptor moduleDesc = module.getDescriptor();

if (moduleDesc.isAutomatic()) {
System.err.println("Failure: Automatic module found");
System.exit(ERROR_ASSUMPTION_VIOLATED);
}

if (moduleDesc.isOpen()) {
System.err.println("Failure: Open module found");
System.exit(ERROR_ASSUMPTION_VIOLATED);
}

BorderPane root = new BorderPane();
Scene scene = new Scene(root);

WebView webview = new WebView();
root.setCenter(webview);

// The title tag value is made available via the getTitle nethod. The
// JS code changes the value, so that the enclosing application can
// detect, that the JS code was run.
String checkJS = "document.getElementsByTagName(\"title\")[0].textContent='Executed'";
String checkJSEncoded = Base64.getEncoder().encodeToString(checkJS.getBytes(StandardCharsets.UTF_8));

String script = "<html>"
+ "<head>"
+ "<title>Armed</title>"
+ "</head>"
+ "<body>"
+ "<h1>Test for loading a data URL</h1>"
+ "<p>The test is successful, if the JVM does not crash with a SEGFAULT.</p>"
+ "<script src=\"data:application/javascript;base64," + checkJSEncoded + "\"></script>"
+ "</body>"
+ "</html>";
webview.getEngine().getLoadWorker().stateProperty().addListener(
new ChangeListener<State>() {
public void changed(ObservableValue ov, State oldState, State newState) {
if (newState == State.SUCCEEDED) {
String title = webview.getEngine().getTitle();
if ("Executed".equals(title)) {
System.exit(ERROR_OK);
} else {
System.exit(ERROR_TITLE_NOT_UPDATED);
}
}
}
});
webview.getEngine().loadContent(script);

primaryStage.setScene(scene);
primaryStage.setWidth(1024);
primaryStage.setHeight(768);
primaryStage.show();
}
}
ProTip! Use n and p to navigate between commits in a pull request.