Call java applet method from JavaScript = is not a function - javascript

I have defined the following Java Applet:
import java.applet.Applet;
import java.awt.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class CombineData extends Applet{
private File destinationFile;
private FileOutputStream fileOutputStream;
public boolean setUpFile(String filePath) {
// This is just to check if it is a new file we write to or not
// We could return false at once saying file already exists
boolean result;
if ((destinationFile = new File(filePath)).exists()) {
result = false;
} else {
result = true;
}
try {
fileOutputStream = new FileOutputStream(destinationFile, true);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return result;
}
public boolean write_line(byte[] data) {
if (fileOutputStream == null) {
return false;
} else {
try {
fileOutputStream.write(data);
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
}
public void finished() {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
And the following JavaScript
function start_button() {
combineDataApplet = document.getElementById('combineDataApplet');
combineDataApplet.setUpFile("blabl");
var filename = "~/Downloads/" + prompt("Please enter file name (remember file extenstion)", "combined_data.txt");
console.log(filename);
}
And finally the HTML
<!DOCTYPE HTML>
<html lang="en">
<head>
<script src="js/page.js"></script>
</head>
<body>
<div id="content">
<div>
<applet id="combineDataApplet" code="CombineData.class" width="350" height="350">
APPLET
</applet>
<input id="start_button" type="button" onClick="start_button()" value="start"/>
</div>
</div>
</body>
</html>
And I get the following error: TypeError: combineDataApplet.setUpFile is not a function. (In 'combineDataApplet.setUpFile("blabl")', 'combineDataApplet.setUpFile' is undefined)
I have found a few post on stackoverflow which states that I need to put it into a div block without a display:none but there are no styles on any of my divs and by such there should be no display:none.
I hope someone can help me figure out what is wrong

Programming Applets is riding a dead horse.
untested: you should access the applet this way:
document.combineDataApplet.setUpFile("blabl");
and not by document.getElementById(). The latter only gets you the DOM element containing the applet.
Edit:
here is a link from January 2016 about Oracle deprecating Applets. This article gives the further link to Oracle's whitepaper for migrating applets. I haven't read it, but it might show you some alternatives.

Related

Webview - How to trigger Java Function from Javascript Button

So I got a problem, where there is a button that will call a webview function
The button is the one with id play in the index.html that will call the javascript playVideo function in src.js, where the playVideo function will notify the webview that the button has been pressed to check the condition in the Java Function.
How do I achieve this?
The codes are below to help getting context
index.html
<!DOCTYPE html>
<html>
<head>
<title> Video</title>
<script type="text/javascript" src="src.js"></script>
</head>
<body bgcolor="#333">
<center>
<video preload="auto" width="480" height="270"
#Embed-video
</video>
<br>
<br>
<div>
<a class="first" href="#" id="play">Play</a>
<a class="second" href="#" id="pause">Pause</a>
</div>
</center>
Javascript code
window.onload = function(){
var video = document.getElementById('my-video');
var play = document.getElementById('play');
var pause = document.getElementById('pause');
// associate functions with the 'onclick' events
play.onclick = playVideo;
pause.onclick = pauseVideo;
function playVideo(e) {
//call java function
}
function pauseVideo(e) {
}
}
Java Function
webView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(x,y) {
if (js.playVideo()) {
//do something
return true;
}else{
return false;
}
}
});
You should use annotation for Javascript like the following example
// Java File example
#JavascriptInterface
public void showToast(String toast) {
Toast.makeText(mContext, "show toast from web: " + toast, Toast.LENGTH_SHORT).show();
}
// Js file example
function showAndroidToast(msg) {
Android.showToast(msg);
}
Thus, you should try like the following code.
function playVideo(e) {
//call java function
Android.doSomething();
}
webView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(x,y) {
// implment your code with doSomething func here
if (doSomething()) {
//do something
return true;
}else{
return false;
}
}
});
#JavascriptInterface
public boolean doSomething() {
// do something
if(isPlaying) return true;
return false;
}

Capture a custom Javascript event in C# WPF mshtml WebBrowser control

I'm unable to capture a custom Javascript event in a C# WPF mshtml WebBrowser control. I've created the example code below. A button click raises a custom event. I realise that there are easy ways to capture a button click event. I need to use a custom event. I've used a button just for this example and testing. What am I doing wrong?
XAML file:
<Window x:Class="WebEvent2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<WebBrowser Name="webBrowser1" Loaded="webBrowser1_Loaded" LoadCompleted="webBrowser1_LoadCompleted"></WebBrowser>
</Grid>
</Window>
C# file:
namespace WebEvent2{
public partial class MainWindow : Window{
public MainWindow(){
InitializeComponent();
}
private void webBrowser1_Loaded(object sender, RoutedEventArgs e{
webBrowser1.Navigate(#"file:///D:/test.html");
}
private void webBrowser1_LoadCompleted(object sender, NavigationEventArgs e){
try{
var evtListener = new EventListener();
var window = ((IHTMLDocument2)webBrowser1.Document).parentWindow as IHTMLWindow3;
window.attachEvent("MyCustomEvent", evtListener);
// Also not working:
// ((HTMLDocument)webBrowser1.Document).attachEvent("MyCustomEvent", evtListener);
}catch (UnauthorizedAccessException err){
Console.WriteLine("OOPS: " + err);
}
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class EventListener{
[DispId(0)]
public void handler(IHTMLEventObj evt){
MessageBox.Show("message received");
}
}
}
}
HTML file:
<html>
<head>
<title>Test page</title>
<script>
function sendCustomEvent() {
var event;
if (typeof(Event) === 'function') {
event = new Event('MyCustomEvent');
} else {
event = document.createEvent('HTMLEvents');
event.initEvent('MyCustomEvent', true, false);
}
document.dispatchEvent(event);
}
</script>
</head>
<body>
<button onclick="sendCustomEvent()">Send custom event</button>
</body>
</html>
I was able to achieve the required functionality by using the WebBrowser controls 'ObjectForScripting' property. Nice and simple.
XAML remains the same as above.
WebInterface.cs:
namespace WebEvent2
{
[System.Runtime.InteropServices.ComVisibleAttribute(true)]
public class WebInterface
{
public void callMe()
{
MessageBox.Show("hello");
}
}
}
MainWindow.xaml.cs:
namespace WebEvent2
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
WebInterface wi = new WebInterface();
private void webBrowser1_Loaded(object sender, RoutedEventArgs e)
{
webBrowser1.ObjectForScripting = wi;
webBrowser1.Navigate(#"file:///D:/test.html");
}
}
}
test.html:
<html>
<head>
<title>Test page</title>
</head>
<body>
<button onclick="window.external.callMe()">Send custom event</button>
</body>
</html>

Load a new page in JavaFX webview

I am currently working on a Java application that uses a JavaFX webview to display its UI (with HTML/CSS).
Everything is working fine but I have trouble loading a new page in the system. When I do, the communication between the Java and the new page's JavaScript seems to be broken.
Here is my code :
** Broser **
public class Browser extends Region {
final WebView browser = new WebView();
final WebEngine webEngine = browser.getEngine();
public Browser() {
//apply the styles
getStyleClass().add("browser");
// load the web page
webEngine.load(some_url);
JSObject jsobj = (JSObject) webEngine.executeScript("window");
Bridge bridge = Bridge.getInstance();
bridge.init(webEngine);
jsobj.setMember("java", bridge);
//add the web view to the scene
getChildren().add(browser);
}
}
** Bridge **
public class Bridge {
private static Bridge instance = null;
private WebEngine webEngine;
public Bridge () {
}
public static Bridge getInstance() {
if(instance == null){
instance = new Bridge();
}
return instance;
}
public void init(WebEngine webEngine) {
if(this.webEngine == null) {
this.webEngine = webEngine;
}
}
public void btnStartSessionOnClick(String sessionName, String speakerNickname) {
// Load the new page
webEngine.load(some_other_url);
}
}
Whenever the web engine loads a new page, it replaces the DOM, so there is a different window object. The jsobj you define is only set once, so when a new page is loaded it will be pointing to the wrong object. You need to reset this object every time the page loads, which you can do by observing the engine's load state.
Your design doesn't make a whole lot of sense to me: it makes more sense to me to have the window (jsobj) object as part of the Bridge class, rather than the application class. And since the Browser is not a singleton, it doesn't make sense to make the Bridge a singleton (what if you had multiple web views in your application, for example?).
Here's an SSCCE:
package application;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class WebViewTest extends Application {
#Override
public void start(Stage primaryStage) {
WebView webView = new WebView();
WebEngine engine = webView.getEngine();
Label output = new Label();
Bridge bridge = new Bridge(engine);
engine.load(getClass().getResource("/resources/First.html").toExternalForm());
Button first = new Button("Load First");
first.setOnAction(e -> engine.load(getClass().getResource("/resources/First.html").toExternalForm()));
Button second = new Button("Load Second");
second.setOnAction(e -> engine.load(getClass().getResource("/resources/Second.html").toExternalForm()));
TextField textField = new TextField();
Button button = new Button("Send");
EventHandler<ActionEvent> handler = e -> {
bridge.execute(result -> output.setText(result.toString()),
"showText", textField.getText());
textField.setText("");
};
button.setOnAction(handler);
textField.setOnAction(handler);
HBox controls = new HBox(5, first, second, textField, button, new Label("Web page says: "), output);
controls.setPadding(new Insets(10));
BorderPane root = new BorderPane(webView, null, null, controls, null);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The Bridge class:
package application;
import java.util.function.Consumer;
import javafx.concurrent.Worker.State;
import javafx.scene.web.WebEngine;
import netscape.javascript.JSObject;
public class Bridge {
private JSObject window ;
public Bridge(WebEngine engine) {
engine.getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {
if (newState == State.SUCCEEDED) {
window = (JSObject) engine.executeScript("window");
window.setMember("application", this);
}
});
}
public void execute(Consumer<Object> callback, String function, Object... args) {
callback.accept(window.call(function, args));
}
}
And some simple test HTML files, which I have in a resources folder in the root of the classpath.
First.HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>First</title>
<script>
function showText(text) {
document.getElementById("text").innerHTML = text;
return text;
}
</script>
</head>
<body>
<p>This is the first page</p>
Go to the second page
<div id="text"></div>
</body>
</html>
and Second.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Second</title>
<script>
function showText(text) {
document.getElementById("text").innerHTML = text;
return text;
}
</script>
</head>
<body>
<p>This is the second page</p>
Go back to the first page
<div id="text"></div>
</body>
</html>

ActiveX object custom event does not show results

tl/dr
The managed C# COM visible object that I created has an event. In the "OnCardRead" method (which fires the actual event) I check to see if the event is subscribed to and it is. The event callback code in javascript appears to never be called.
The story
The project is simple. Create a webpage that "opens" a rfid device and returns the serial number of the card. That portion of has its own set of unit tests and works just fine. To help my self debug if the Serial Ports address is set to 0 then it uses my MockRfidSDK. But for the sake of being thorough here is the code surrounding that (minus the actual RFID code).
IRfidDevice
[Guid("E86A9038-368D-4e8f-B389-FDEF38935B2F"), InterfaceType(ComInterfaceType.InterfaceIsDual), ComVisible(true)]
public interface IRfidDevice
{
[DispId(1)]
int Open();
[DispId(2)]
int Close();
[DispId(3)]
void SetComPort(int comport);
[DispId(4)]
void FlashLed(int led);
}
IRfidEvent
[ComVisible(true), GuidAttribute("0422D916-C11A-474e-947D-45A107038D12"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
public interface IRfidEvent
{
[DispIdAttribute(0x60020000)]
void CardUidReceived(byte sak, ushort atq, string uid);
}
RfidSdk/EmptyRfidSdk
internal interface RfidSdk
{
void Open();
int GetCard();
byte[] SelectCard();
int Close();
void FlashLed(int led);
}
class EmptyRfidSdk : RfidSdk
{
public void Open()
{
}
public int GetCard()
{
System.Threading.Thread.Sleep(10);
return 0x0344;
}
public byte[] SelectCard()
{
System.Threading.Thread.Sleep(2000);
return new byte[] { 0x20, 0x44, 0x03, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37 };
}
public int Close()
{
return 0;
}
public void FlashLed(int led)
{
}
}
RfidDevice
[Guid("873355E1-2D0D-476f-9BEF-C7E645024C32"), ProgId("MasterRDActiveX.RfidDevice"), ClassInterface(ClassInterfaceType.None), ComDefaultInterface(typeof(IRfidDevice)), ComVisible(true), ComSourceInterfaces(typeof(IRfidEvent))]
public class RfidDevice : InternetSafeObject, IRfidDevice
{
private RfidSdk sdk;
private bool _searchForCard = false;
[ComVisible(false)]
public delegate void RfidEventHandler(byte sak, ushort atq, string uid);
public event RfidEventHandler CardUidReceived;
public int Open()
{
try
{
sdk.Open();
_searchForCard = true;
Task.Factory.StartNew(SearchForCard);
return 0;
}
catch (Exception ex)
{
Trace.TraceError(ex.Message);
return -1;
}
}
public int Close()
{
try
{
_searchForCard = false;
sdk.Close();
sdk = null;
return 0;
}
catch (Exception ex)
{
Trace.TraceError(ex.Message);
return -1;
}
}
public void SetComPort(int comport)
{
if (sdk != null) Close();
if (comport < 1)
{
sdk = new EmptyRfidSdk();
}
else
{
sdk = new MasterRDWrapper.MasterRD(comport);
}
}
public void FlashLed(int led)
{
sdk.FlashLed(led);
}
private void SearchForCard()
{
while (_searchForCard)
{
int temp = 0;
while ((temp = sdk.GetCard()) == 0)
{
}
if (temp == sdk.GetCard())
{
var card = sdk.SelectCard();
FireCardUidReceived(card[0], BitConverter.ToUInt16(card, 1), card.Skip(3).ToArray());
}
}
}
private void FireCardUidReceived(byte sak, ushort atq, byte[] uid)
{
if (CardUidReceived != null)
{
Console.Beep(1000,100);
CardUidReceived(sak, atq, BitConverter.ToString(uid));
}
else
{
Console.Beep(2000, 2000);
}
}
So that code is working just fine. Remember the beeps. Short beep means the Event is subscribed to, long beep means it's not being subscribed too.
Now the webpage is where things get a little murky for me. I've looked at a lot of ActiveX tutorials here are some rules about the HTML code. It can't use deprecated HTML tags (IE... OBJECT). Here is my solution thus far
rfidScript.js
function onRfidCard(a, b, c) {
console.log('I am in your event');
document.getElementById('result').innerHTML = c;
console.log('all done in your event');
}
function tryRfid(rfidDevice) {
var uidTag = document.getElementById('result');
var comPort = parseInt(document.getElementById('comPortBox').value);
if (rfidDevice != null) {
rfidDevice.SetComPort(comPort);
if (rfidDevice.Open() === 0) {
uidTag.innerHTML = "Please Tap Card";
}
else {
document.getElementById('error').innerHTML = "Failed to Open";
}
}
}
function closeRfid(rfidDevice) {
rfidDevice.Close();
}
and last bit the html code
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>RFID ActiveX Test</title>
<!--//C:\Users\snyder\Source\Repos\xPD\MasterRDActiveX\MasterRDActiveX\bin\Debug\testRfid.html-->
<script type="text/javascript">
var ctrl = new ActiveXObject('MasterRDActiveX.RfidDevice');
eval('function ctrl::CardUidReceived(a,b,c){ onRfidCard(a,b,c); }');
</script>
<script src="rfidScript.js"></script>
</head>
<body>
<h1>This is our Active X Test Page for RFID</h1>
<input type="text" id="comPortBox" /> <br />
<button type="button" onclick="tryRfid(ctrl);">Open</button>
<button type="button" onclick="closeRfid(ctrl);">Close</button>
<h2>
<output id="error" style="color: red;"></output>
<output id="result"></output>
</h2>
</body>
</html>
I fire up IE, put 0 in the textbox, click open and I hear 1 short beep(and only one.. not sure why on that one as I would expect a beep every 2 seconds see note1), but I don't see any text show up in the result output tag (and I've tried using alert to no avail.) So why?????? what am I missing?
Note 1 - Earlier when I was getting long beeps it would long beep over and over again as I would expect.

JavascriptInterface in Android's WebView: multiple calls to JS cause deadlock

This is the entire Java code I've used. I will explain in more detail below...
public class Test7 extends Activity {
//debug
private final static String TAG = "JSInterface";
private WebView wv;
private class JSInterface {
private WebView wv;
// Variables to manage interfacing with JS
private String returnValue;
private boolean canReadReturnValue;
private Lock lockOnJS;
private Condition condVarOnJS;
public JSInterface (WebView wv) {
this.wv = wv;
this.canReadReturnValue = false;
this.lockOnJS = new ReentrantLock();
this.condVarOnJS = lockOnJS.newCondition();
}
public void setReturnValue(String ret) {
lockOnJS.lock();
returnValue = ret;
canReadReturnValue = true;
condVarOnJS.signal();
lockOnJS.unlock();
Log.d(TAG, "returnValue = " + returnValue);
}
public String getReturnValue() {
Log.d(TAG, "enter in getReturnValue");
lockOnJS.lock();
while (!canReadReturnValue) {
try {
Log.d(TAG, "get wait...");
condVarOnJS.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lockOnJS.unlock();
Log.d(TAG, "returnValue: " + returnValue);
return returnValue;
}
public String getNewString() {
wv.loadUrl("javascript:JSInterface.setReturnValue(createNewString())");
return getReturnValue();
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
wv = (WebView) findViewById(R.id.webView1);
wv.getSettings().setJavaScriptEnabled(true);
wv.addJavascriptInterface(new JSInterface(wv), "JSInterface");
wv.loadUrl("file:///android_asset/prova7.html");
}
public void button1(View v) {
wv.loadUrl("javascript:func('1')");
}
}
And it seems work fine.
You can see that I've got a button (that we can call button1), and clicking on it, it tries to execute a JS method, called func().
public void button1(View v) {
wv.loadUrl("javascript:func('1')");
}
Inside this JS method, I have to call another Java method. This is the code:
function func(id) {
document.getElementById(id).innerHTML = JSInterface.getNewString();
}
I need to return the result of JSInterface.getNewString() to the innerHTML variable.
The code of JSInterface.getNewString() is this:
public String getNewString() {
wv.loadUrl("javascript:JSInterface.setReturnValue(createNewString())");
return getReturnValue();
}
You can see that I use the method setReturnValue and getReturnValue to return the value returned by another JS method. This is the code:
function createNewString() {
return "my New String";
}
The problem is that when I try to set the returnValue, the function createNewString is never executed! If I add a console.log() line, my logCat display nothing!
I cannot understand why this happens.
All the javascript and your JSInterface methods called from javascript are running on the single thread in Android WebView. So while you are waiting in condVarOnJS.await() no javascript can be executed, just because it is executed on the same thread.
Moreover, all the webview instances in your application share the same javascript thread.
In Internet Explorer I found the same problem. You can use setTimeout like this:
function func(id) {
setTimeout(
function(){
document.getElementById(id).innerHTML = JSInterface.getNewString();
},
500);
}
I did the functionality what you intend in that code,for me createNewString() is called,
I will show up the code i used,
In java
,
public String videoPlay(){
System.out.println("videoPlay");
mWebView.loadUrl("javascript:window.demo.setReturnValue(createNewString())");
return getReturnValue();}
public void setReturnValue(String test){
rotValue=test;
System.out.println(test);
}
public String getReturnValue(){
System.out.println("get"+rotValue);
return rotValue;
}
in HTML,
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script>
function inform(){
alert('et');
document.getElementById('myText').value=window.demo.videoPlay();
alert('et');
}
function createNewString() {
return "my New String";
}
</script>
</head>
<body onload="init()">
<form>
<input type='text' id='myText' />
<input type="button" name="test" value="Click me" onclick="inform()">
</form>
</body>
</html>
The function getter and setter called and values also set, but i have the output log as..
11-08 19:18:17.155: INFO/System.out(847): videoPlay
11-08 19:18:17.165: INFO/System.out(847): getnull
11-08 19:18:17.875: INFO/System.out(847): my New String
videoPlay called from JS and createnewString() also called from java to JS , but it returns the value before it set , because i don`t what is the purpose to use lock , even i tried using lock as you did for that it will print
11-08 19:18:17.155: INFO/System.out(847): videoPlay
11-08 19:18:17.165: INFO/System.out(847): getnull
using lock also the function callback works in wrong manner, you need work on locks.

Categories