FULL PRODUCT VERSION :
java version "1.7.0"
Java(TM) SE Runtime Environment (build 1.7.0-b147)
Java HotSpot(TM) Client VM (build 21.0-b17, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]
A DESCRIPTION OF THE PROBLEM :
If a JavaScript-to-Java call is made in Firefox, the JavaScript engine becomes "available" for any calls while the original JavaScript-to-Java call is not yet terminated. This behavior is not consistent with the LiveConnect Specifications.
According to the LiveConnect Specifications, if a JavaScript-to-Java call is made, the JavaScript engine becomes "available" for Java-to-JavaScript calls and NOT for any other calls.
IE 8.x & 9.x (classic & next-generation Java Plug-In) and Firefox 2.x (classic Java Plug-In) behave correctly and ensure that the JavaScript engine is accessed only in the required single-threaded fashion.
This implementation could lead to unexpected behaviors, for example a JavaScript library which delegates the implementation to a Java Applet can't guarantee the required single-threaded fashion of the JavaScript engine. Users of the JavaScript library will expect that any function call triggered for example from a button click will run to the end without interruption before any other function call will be allowed. Current implementation will allow clicking the button once again and trigger the JavaScript library function call while the initial call is not yet terminated.
This implementation also makes implementing a small utility function in Java dangerous, since during the JavaScript-to-Java call, the user of the function has to set suitable synchronization mechanisms to bypass violating the Single Threaded Model of the JavaScript engine from the Java Plug-in.
This behavior could be reproduced with the attached test case with the following results:
The attached test case is a HTML page that includes a java applet, this test page will make a JavaScript-to-Java call in the body onload event, the java implementation will delay the call for a period of time (default 10 seconds), if we click the button "Call a JavaScript function" or the href "Call a JavaScript function" during this interval, we’ll see that the JavaScript engine is "available" for processing other events, while the onload event is not completely processed.
After the body onload event is completely processed, a JavaScript-to-Java call could be made by clicking the href "Call a JavaScript function" or the button "Call a JavaScript function", again the java implementation will delay the call, if we click the button "Call a JavaScript function" or the href "Call a JavaScript function", we’ll see that the JavaScript engine is "available" for processing other events, while the JavaScript-to-Java call is not completely processed. This behavior is always reproducible when the JavaScript-to-Java call is triggered from a href, while it is occasional when it is triggered from a button.
During the body onunload event, the test page will make a JavaScript-to-Java call, the java implementation will again delay the call, during this time we could click a button and trigger a JS function, but we can't trigger a href click event.
This behavior was investigated by the Firefox developers and the result is the browser thread does block waiting for a call to plugins to return. But If the Java plugin allows the browser to process messages while it's waiting for a synchronous call to complete and return, that could lead UI events to trigger additional JS to run while the JS code that called into Java is still waiting for the call to return. In other words, if the plugin spins the event loop, then the browser can reenter through events such as additional mouse clicks.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Make a JavaScript-to-Java call.
2. Trigger another JavaScript function while the JavaScript-to-Java call is not yet terminated.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The second JavaScript shouldn't be allowed while the initial JavaScript-to-Java call is not yet terminated.
ACTUAL -
The second JavaScript call will be executed while the initial JavaScript-to-Java call is not yet terminated.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
Test.java
-------------
import java.applet.Applet;
public class Test extends Applet {
protected static final long DEFAULT_DURATION = 10000;
protected static final String DURATION_PARAM = "duration";
public String doSomething(String name) {
try {
long duration = getDuration();
debug("applet about to sleep for " + duration + " ms.");
Thread.sleep(duration);
debug("applet after sleep.");
} catch (InterruptedException ex) {
ex.printStackTrace();
}
return "Output String";
}
protected long getDuration() {
long duration = DEFAULT_DURATION;
String durationStr = getParameter(DURATION_PARAM);
if (durationStr != null) {
try {
duration = Long.parseLong(durationStr);
} catch (Throwable th) {
th.printStackTrace();
}
}
return duration;
}
@Override
public void init() {
debug("applet init.");
super.init();
}
@Override
public void start() {
debug("applet start.");
super.start();
}
@Override
public void stop() {
debug("applet stop.");
super.stop();
}
@Override
public void destroy() {
debug("applet destroy.");
super.destroy();
}
protected void debug(String s) {
System.out.println(s);
}
}
-----------
test.html
-----------
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>test.html</title>
</head>
<SCRIPT TYPE="text/javascript">
<!-- to hide script contents from old browsers
var appletCallActive = false;
function callApplet() {
appendLog("about to make an applet call.");
appletCallActive = true;
var result = document.getElementById("testApplet").doSomething('Input String');
appletCallActive = false;
appendLog("after applet call, result = " + result);
return;
}
function callJavaScript() {
if (appletCallActive) {
appendLog("JavaScript function called while an applet call is active.");
} else {
appendLog("JavaScript function called while an applet call is not active.");
}
return;
}
function reload() {
self.location.reload();
}
function appendLog(message) {
document.getElementById("logger").innerHTML += message;
document.getElementById("logger").innerHTML += "<br>";
return;
}
function clearLog() {
document.getElementById("logger").innerHTML = "";
return;
}
// end hiding contents from old browsers -->
</SCRIPT>
<body onload="javascript:callApplet()" onunload="javascript:callApplet()">
<p><applet code="Test" codebase="." width="1" height="1"
id="testApplet"> Your browser does not support the <code>
applet </code> tag.
<param name="scriptable" value="true">
</applet></p>
<p>
<button onclick="javascript:callJavaScript()">Call a JavaScript
function</button>
<button onclick="javascript:callApplet()">Call an Applet
function</button>
<button onclick="javascript:reload()">Reload</button>
<button onclick="javascript:clearLog()">Clear Log</button>
</p>
<p>
<a href="javascript:callJavaScript()">Call a JavaScript
function</a>
<a href="javascript:callApplet()">Call an Applet
function</a>
<a href="javascript:reload()">Reload</a>
<a href="javascript:clearLog()">Clear Log</a>
</p>
<p id="logger" />
</body>
</html>
---------- END SOURCE ----------
java version "1.7.0"
Java(TM) SE Runtime Environment (build 1.7.0-b147)
Java HotSpot(TM) Client VM (build 21.0-b17, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]
A DESCRIPTION OF THE PROBLEM :
If a JavaScript-to-Java call is made in Firefox, the JavaScript engine becomes "available" for any calls while the original JavaScript-to-Java call is not yet terminated. This behavior is not consistent with the LiveConnect Specifications.
According to the LiveConnect Specifications, if a JavaScript-to-Java call is made, the JavaScript engine becomes "available" for Java-to-JavaScript calls and NOT for any other calls.
IE 8.x & 9.x (classic & next-generation Java Plug-In) and Firefox 2.x (classic Java Plug-In) behave correctly and ensure that the JavaScript engine is accessed only in the required single-threaded fashion.
This implementation could lead to unexpected behaviors, for example a JavaScript library which delegates the implementation to a Java Applet can't guarantee the required single-threaded fashion of the JavaScript engine. Users of the JavaScript library will expect that any function call triggered for example from a button click will run to the end without interruption before any other function call will be allowed. Current implementation will allow clicking the button once again and trigger the JavaScript library function call while the initial call is not yet terminated.
This implementation also makes implementing a small utility function in Java dangerous, since during the JavaScript-to-Java call, the user of the function has to set suitable synchronization mechanisms to bypass violating the Single Threaded Model of the JavaScript engine from the Java Plug-in.
This behavior could be reproduced with the attached test case with the following results:
The attached test case is a HTML page that includes a java applet, this test page will make a JavaScript-to-Java call in the body onload event, the java implementation will delay the call for a period of time (default 10 seconds), if we click the button "Call a JavaScript function" or the href "Call a JavaScript function" during this interval, we’ll see that the JavaScript engine is "available" for processing other events, while the onload event is not completely processed.
After the body onload event is completely processed, a JavaScript-to-Java call could be made by clicking the href "Call a JavaScript function" or the button "Call a JavaScript function", again the java implementation will delay the call, if we click the button "Call a JavaScript function" or the href "Call a JavaScript function", we’ll see that the JavaScript engine is "available" for processing other events, while the JavaScript-to-Java call is not completely processed. This behavior is always reproducible when the JavaScript-to-Java call is triggered from a href, while it is occasional when it is triggered from a button.
During the body onunload event, the test page will make a JavaScript-to-Java call, the java implementation will again delay the call, during this time we could click a button and trigger a JS function, but we can't trigger a href click event.
This behavior was investigated by the Firefox developers and the result is the browser thread does block waiting for a call to plugins to return. But If the Java plugin allows the browser to process messages while it's waiting for a synchronous call to complete and return, that could lead UI events to trigger additional JS to run while the JS code that called into Java is still waiting for the call to return. In other words, if the plugin spins the event loop, then the browser can reenter through events such as additional mouse clicks.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Make a JavaScript-to-Java call.
2. Trigger another JavaScript function while the JavaScript-to-Java call is not yet terminated.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The second JavaScript shouldn't be allowed while the initial JavaScript-to-Java call is not yet terminated.
ACTUAL -
The second JavaScript call will be executed while the initial JavaScript-to-Java call is not yet terminated.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
Test.java
-------------
import java.applet.Applet;
public class Test extends Applet {
protected static final long DEFAULT_DURATION = 10000;
protected static final String DURATION_PARAM = "duration";
public String doSomething(String name) {
try {
long duration = getDuration();
debug("applet about to sleep for " + duration + " ms.");
Thread.sleep(duration);
debug("applet after sleep.");
} catch (InterruptedException ex) {
ex.printStackTrace();
}
return "Output String";
}
protected long getDuration() {
long duration = DEFAULT_DURATION;
String durationStr = getParameter(DURATION_PARAM);
if (durationStr != null) {
try {
duration = Long.parseLong(durationStr);
} catch (Throwable th) {
th.printStackTrace();
}
}
return duration;
}
@Override
public void init() {
debug("applet init.");
super.init();
}
@Override
public void start() {
debug("applet start.");
super.start();
}
@Override
public void stop() {
debug("applet stop.");
super.stop();
}
@Override
public void destroy() {
debug("applet destroy.");
super.destroy();
}
protected void debug(String s) {
System.out.println(s);
}
}
-----------
test.html
-----------
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>test.html</title>
</head>
<SCRIPT TYPE="text/javascript">
<!-- to hide script contents from old browsers
var appletCallActive = false;
function callApplet() {
appendLog("about to make an applet call.");
appletCallActive = true;
var result = document.getElementById("testApplet").doSomething('Input String');
appletCallActive = false;
appendLog("after applet call, result = " + result);
return;
}
function callJavaScript() {
if (appletCallActive) {
appendLog("JavaScript function called while an applet call is active.");
} else {
appendLog("JavaScript function called while an applet call is not active.");
}
return;
}
function reload() {
self.location.reload();
}
function appendLog(message) {
document.getElementById("logger").innerHTML += message;
document.getElementById("logger").innerHTML += "<br>";
return;
}
function clearLog() {
document.getElementById("logger").innerHTML = "";
return;
}
// end hiding contents from old browsers -->
</SCRIPT>
<body onload="javascript:callApplet()" onunload="javascript:callApplet()">
<p><applet code="Test" codebase="." width="1" height="1"
id="testApplet"> Your browser does not support the <code>
applet </code> tag.
<param name="scriptable" value="true">
</applet></p>
<p>
<button onclick="javascript:callJavaScript()">Call a JavaScript
function</button>
<button onclick="javascript:callApplet()">Call an Applet
function</button>
<button onclick="javascript:reload()">Reload</button>
<button onclick="javascript:clearLog()">Clear Log</button>
</p>
<p>
<a href="javascript:callJavaScript()">Call a JavaScript
function</a>
<a href="javascript:callApplet()">Call an Applet
function</a>
<a href="javascript:reload()">Reload</a>
<a href="javascript:clearLog()">Clear Log</a>
</p>
<p id="logger" />
</body>
</html>
---------- END SOURCE ----------
- relates to
-
JDK-8133523 _releaseObject called from wrong thread
- Closed