JDK-6764341 : Program hangs when calling JS in Firefox 3 on Linux
  • Type: Bug
  • Component: deploy
  • Sub-Component: plugin
  • Affected Version: 6u10
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: linux
  • CPU: x86
  • Submitted: 2008-10-28
  • Updated: 2010-04-04
  • Resolved: 2008-11-11
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
Tried with 1.6.0_7 and 1.6.0_10 with both plugins

ADDITIONAL OS VERSION INFORMATION :
Have tried it on recent Suse and Mandriva versions. Does not appear to depend on Linux distribution or version

EXTRA RELEVANT SYSTEM CONFIGURATION :
Appears to be linked to Firefox on Linux, works OK with Opera

A DESCRIPTION OF THE PROBLEM :
When the user presses on a button, the application calls a function on an Applet. The application then distributes the tesk to multiple threads and waits for them to finish.

One of the threads calls the JavaScript interface to update it. This works on other OSs and on Linux with Opera. With Firefox, the whole browser freezes.

We have simplified the code to bring everything into one class.

Our conclusion is that the current version of the plugin relies on the incoming thead (that transmits the event) to handle screen re-writing. As it is otherwise occupied inside the Applet, it can't do this. and the browser hangs.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the HTML page in Firefox 3
Press on the button

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
A text should appear next to the button.
ACTUAL -
The whole browser freezes

ERROR MESSAGES/STACK TRACES THAT OCCUR :
None, the browser freezes

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
<html>

<head>
        
<script language="javascript">
    function test() {
        document.getElementById("BugApplet").calledFromJavascript();
    }
    
    function calledFromJava() {
        var x = document.createTextNode(" some text ");
        document.getElementById("body").appendChild(x);
    }
</script>
        
</head>

<body id="body">

<div style="visibility:hidden;">
    <applet id="BugApplet" name="BugApplet" width="1" height="1" code="BugApplet.class" mayscript="true" scriptable="true">
    </applet>
</div>
     
<input type="button" value="test" onclick="javascript:test();" />
        
</body>
</html>

==========================================================

import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

import javax.swing.JApplet;

import netscape.javascript.JSException;
import netscape.javascript.JSObject;

/**
 * The aim of this class is to demonstrate a bug in the Java Plugin when using Firefox 3 on Linux.
 *
 * The code of the original application has been simplified to illustrate the bug. The code has no other use.
 * The original code is spred over many classes. We have concentrated the main points in one file.
 *
 * The problem that as far as we can see is that control is transfered to
 * another thread and the main thread is parked until the other thread(s) have finished.
 *
 * It is one of these "slave" threads that tries to call the JavaScript which immediately hangs.
 *
 */
public class BugApplet extends JApplet {

    /**
     * This will execute code that works on other systems and other browsers.
     * but causes Firefox 3 to hang.
     */
    private static final boolean LINUX_FIREFOX3_BUG = true;
    /**
     * Using this code works on Firefox 3. It is functionally equivalent but uses
     * a new Thread each time rather than reusing the previous one.
     */
    private static final boolean LINUX_FIREFOX3_WORKAROUND = !LINUX_FIREFOX3_BUG;
    
    /**
     * A JavaScript object
     */
    private JSObject parentObject = null;
    /**
     * A lock to simulate the locking process.
     */
    private ReentrantLock parentObjectLock = new ReentrantLock();
    
    private volatile boolean threadMode = false;
    
    @Override
    public void init() {
        JSObject window = null;
        try {
            window = JSObject.getWindow(this);
        } catch (JSException jse) {
            jse.printStackTrace();
            throw new Error();
        } catch (Throwable t) {
            t.printStackTrace();
            throw new Error();
        }
        this.parentObject = window;
    }
    
    /**
     * This method is called by the JavaScript when something happens.
     */
    public void calledFromJavascript() {
        if (!this.parentObjectLock.tryLock()) {
            throw new AssertionError("parentObjectLock Should not be locked");
        }
        try {
            callParentObject0();
        } catch (Exception e) {
            e.printStackTrace();
            throw new Error();
        } finally {
            this.parentObjectLock.unlock();
        }
    }
    
    /**
     * This method isolates the code that causes the bug to appear.
     * @throws Exception
     */
    private void callParentObject0() throws Exception {
        if (LINUX_FIREFOX3_BUG) {
            callParentObject0WithBug2();
        }
        if (LINUX_FIREFOX3_WORKAROUND) {
            callParentObject0WithWorkaround();
        }
    }
    
    private void callParentObject0WithBug2() throws Exception {
        TestThread testThread = new TestThread(Thread.currentThread());
        testThread.start();
        LockSupport.park();
    }

    private void callParentObject0WithWorkaround() throws Exception {
        new Thread() {
            @Override
            public void run() {
                try {
                    callParentObject00();
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new Error();
                }
            }
        }.start();
    }


    /**
     * This code doesn't affect the outcome.
     * @throws Exception
     */
    private void callParentObject00() {
        System.err.println("[BugApplet] callParentObject00 : BEFORE CALL TO JS");
        this.parentObject.call("calledFromJava", new Object[0]);
        System.err.println("[BugApplet] callParentObject00 : AFTER CALL TO JS");
    }
    
    class TestThread extends Thread {
        
        private Thread mainThread;
        
        TestThread(Thread mainThread) {
            this.mainThread = mainThread;
        }
        
        public void run() {
            
            System.err.println("[BugApplet] before sleep : ");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new Error("",e);
            }
            System.err.println("[BugApplet] callParentObject0.call : ");
            callParentObject00();
            LockSupport.unpark(this.mainThread);
        }
    }

}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Don't use multiple threads. But this defeats the whole reason for the Applet.

Release Regression From : 6u6
The above release value was the last known release where this 
bug was not reproducible. Since then there has been a regression.

Comments
EVALUATION tested with 6u12 nightly on Ubuntu Hardy. It does not hang. Also passed liveconnect conformance test without hang.
11-11-2008

EVALUATION I am 99% sure this is the same issue as 6760921 which was just fixed in the new Java Plug-In for 6u11. Assigning to engineer to try test case with latest 6u11 nightly build for confirmation.
28-10-2008