javaFX webview window.onload is fired before loadworker succeeds - webview

I use a JavaFX webview in my application. With the following code I set a member after the page has been loaded
webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() {
#Override
public void changed(ObservableValue ov, Worker.State oldState, Worker.State newState) {
if (newState == Worker.State.SUCCEEDED) {
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("mymember", new JavaScriptBridge(this));
}
}
});
Now in javascript I can invoke mymember.doSomething() e.g. called when I press the button and it's executed successfully, but if I add the following code to the html
<script>
function startup() {
mymember.doSomething();
}
window.onload=startup;
</script>
It's not executed automatically when the page is loaded. It seems like window.onload is executed before the LoadWorker gets notified. So mymember is not set yet. But on the other hand, I cannot set mymember before the html has been loaded, right?
Any idea when I need to set mymember to have it ready when window.onload is executed?
Thanks!

Maybe it's too late for an answer to this problem, but after answering this question, I've been trying to find a reason why executeScript has to be called after the complete load of the webpage.
So I've done this test:
public class EarlyWebEngineTest extends Application {
#Override
public void start(Stage stage) {
final WebView webView = new WebView();
final WebEngine webEngine = webView.getEngine();
// Early call of executeScript to get a JavaScript object, a proxy for the
// Java object to be accessed on the JavaScript environment
JSObject window = (JSObject) webEngine.executeScript("window");
window.setMember("app", new JavaApplication());
webEngine.getLoadWorker().stateProperty().addListener((ov,oldState,newState)->{
if(newState==State.SCHEDULED){
System.out.println("state: scheduled");
} else if(newState==State.RUNNING){
System.out.println("state: running");
} else if(newState==State.SUCCEEDED){
System.out.println("state: succeeded");
}
});
Button button=new Button("Load Content");
button.setOnAction(e->webEngine.loadContent("<html>"
+ " <script>function initialize() {"
+ " var nameVar = \"This is a JS var\"; "
+ " app.callJavascript(nameVar);"
+ "} </script>"
+ " <body onLoad=\"initialize()\">Hi, this is a test!</body>"
+ "</html>"));
VBox vbox = new VBox(10,button,webView);
Scene scene = new Scene(vbox,400,300);
stage.setScene(scene);
stage.show();
}
public class JavaApplication {
public void callJavascript(String msg){
System.out.println("JS>> "+msg);
}
}
public static void main(String[] args) {
launch(args);
}
}
The content is not loaded until the button is clicked, but we've already created the JavaScript object on the browser.
Before clicking the button, there's nothing on the output console. But if we click the button... this is the output:
state: scheduled
state: running
JS>> This is a JS var
state: succeeded
As we can see, the Java object is effectively passed to the script before the latter is executed, and app.callJavascript is successfully called while the content is being loaded.
Note that for the common purpose of accessing the loaded DOM, the usual approach of calling executeScript after State.SUCCEEDED is still the recommended way.

Woks for all (including subsequent) pages:
Where "java" is set in JAVA code:
window.setMember("java", new JavaApplication());
HTML (subsequent) page, keep waiting for 100ms if var "java" is not set (externally by JAVA):
<script>
function init(){
if (typeof java !== 'undefined') {
java.doSomething();
}else{
setTimeout(function(){ init() }, 100 );
}
}
$(document).ready(function(){
init();
});
</script>

Yes, loadworker always execute after window.onload or document.onload.
The workaround you can try, you can create new listener in javascript, for example so-called:
document.addEventListener("deviceready", function(){
MyJavaBridge.executeJavaMethod();
});
And then in your loadworker, you can do this:
webview.getEngine().getLoadWorker().stateProperty().addListener((ov, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
JSObject window = (JSObject) webview.getEngine().executeScript("window");
System.out.println("window : " + window);
window.setMember("MyJavaBridge", javaBridge);
webview.getEngine().executeScript("const event = new Event('deviceready');document.dispatchEvent(event);");
}
});
As you can see, you execute this webview.getEngine().executeScript("const event = new Event('deviceready');document.dispatchEvent(event);"); after setMember, so instead of initialise your work in window.onload, you can do it in your custom event listener deviceready, so you can have better control on the sequence of page load and java side loadworker.
This is exactly how cordova doing, this idea is coming from it.
JQuery document.ready vs Phonegap deviceready

Related

How hide items when print current page?

I create my web project by Vaadin 7.3.6
When I want to print current page I use this:
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickListener;
private ClickListener printListener;
printListener = new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
JavaScript.getCurrent().execute("print();");
}
};
As result it's print current page.
The page content from text and two buttons (Print, OK) on the bottom of page. Nice.
But I want to print only text. Without this 2 buttons.
I want to hide buttons ONLY when printing current page.
When return to page I want to see again this two buttons.
How I can do this?
P.S. I try this:
final Button okButton = new Button(MessageService.getMessage("ok"));
final Button printButton = new Button(MessageService.getMessage("print"));
printButton.setStyleName("small-top-margin");
final JavaScript js = JavaScript.getCurrent();
final UI ui = UI.getCurrent();
printButton.addClickListener(event -> {
logger.debug("click_print");
Thread thread = new Thread(() -> {
ui.access(() -> {
logger.debug("hide_all_buttons");
printButton.setVisible(false);
okButton.setVisible(false);
js.execute("print();");
});
try {
logger.debug("wating_n_seconds");
Thread.sleep(3000);
} catch (InterruptedException e) {
logger.error(e.getMessage(), e);
}
ui.access(() -> {
logger.debug("show_all_buttons");
printButton.setVisible(true);
okButton.setVisible(true);
});
});
thread.start();
}); // click listener
First click on printButton - nothing happened
Second click on printButton - print all buttons. It's not correct.
You can hide the button easily with button.setVisible(false). The true trick is to get the button back. One one is to do this in thread and have sufficient delay before switching the button back visible. Here is an example (Java 8 syntax to make it more compact) This works both with Vaadin 7 & 8.
final Button print = new Button("Print");
final UI ui = this; // or UI.getCurrent() or getUI() depending where you are
final JavaScript js = JavaScript.getCurrent();
print.addClickListener(event -> {
Thread t = new Thread(() -> {
ui.access(() -> {
print.setVisible(false);
js.execute("print();");
});
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ui.access(() -> print.setVisible(true));
});
t.start();
});
layout.addComponent(print);
Alternative approach is to use theming, I.e.
Button print = new Button("Print");
print.addStyleName("no-print");
And the following into your mytheme.scss file, before #mixin mytheme
#media print {
.no-print {
display:none;
}
}

converting a javascript object returned from a javascript API to a dart class

I'm trying to create a dart implementation of the auth0 javascript API using the js package from https://pub.dartlang.org/packages/js.
so I'm loading https://cdn.auth0.com/js/lock/10.0.0/lock.min.js in the index.html and I created the following auth0lock dart class:
#JS()
library auth0lock;
import 'package:js/js.dart';
#JS('Auth0Lock')
class Auth0LockJS {
external factory Auth0LockJS(String token, String domain, Object options);
external on(String event, Function func);
external show();
}
class Auth0Lock {
Auth0LockJS auth0;
Auth0Lock(String token, String domain, Object options) {
this.auth0 = new Auth0LockJS(token,domain,options);
}
on(String event, Function func) {
this.auth0.on(event,allowInterop(func));
}
show() {
this.auth0.show();
}
}
#anonymous
#JS()
class AuthResult {
external factory AuthResult({
String accessToken,
String idToken,
String idTokenPayload,
String state
});
external String get accessToken;
external set accessToken(String v);
external String get idToken;
external set idToken(String v);
external Object get idTokenPayload;
external set idTokenPayload(Object v);
external String get state;
external set state(String v);
}
I'm writing an angular2-dart application so I created an authentication service that uses the auth0lock.dart file that I showed previously using the following code:
#Injectable()
class Auth {
Auth0Lock lock;
authenticatedEvent(AuthResult authResult) {
print(authResult);
}
Auth() {
this.lock = new Auth0Lock(configObj.auth0.apiKey, configObj.auth0.domain,{});
this.lock.on("authenticated", authenticatedEvent);
}
updateProfileName(data) {
var profile = data['profile'] != null ? data['profile'] : data;
print(profile['name']);
}
authenticated() {
return false;
}
login() {
this.lock.show();
}
}
now... everything actually works! I created in the auth_service.dart file a variable typed AuthLock named lock. I instance is with parameters, run the show() function, connect to the authenticated event and the function is fired with the authentication is completed.
I created a class AuthResult but it ignores it and I get a javascript object returned from the function handler.
I program the code using Intellij and execute with dartium. so when I place a breakpoint on the print(authResult);line in the auth_service.dart file, the debugger shows that the authResult Object contains 2 properties, JavaScript View of type Object and class of type JSObjectimpl. the Javascript View contains all the properties that are returned by this function handler.
how do I convert it to a regular class in dart ? obviously I created the AuthResult class wrong cause the function handler doesn't return this type. so what am I missing?
thanks!
update
inside the authenticatedEvent function I tried printing authResult.values, that produced the error that a class of type JSObjectImpl doesn't have that function. so the return object is type JSObjectImpl. the weird thing is that I have no idea where JSObjectImpl is defined so I removed the variable type of authResult and tried to use the variables inside directory with the following code:
authenticatedEvent(authResult) {
var a = authResult.accessToken;
print(a);
}
this works. why I can't find JSObjectImpl type in dart? any quick way to convert this to a dart class?
thanks
Perhaps you just need to remove #anonymous from AuthResult
A simplified but complete and similar example
index.html
<!doctype html>
<html>
<head>
<script>
var Apple = function(type) {
this.type = type;
this.color = "red";
this.getInfo2 = function() {
return this.color + ' ' + this.type + ' apple';
};
};
Apple.prototype.getInfo = function() {
return this.color + ' ' + this.type + ' apple';
};
function callback(f) {
console.log(f);
f(new Apple('Golden Delicious'));
}
</script>
</head>
<body>
<script type="application/dart" src="index.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
index.dart
#JS()
library typed_callback.web;
import 'package:js/js.dart';
#JS()
class Apple {
external String get type;
external set type(String type);
external String get color;
external set color(String color);
external String getInfo();
external String getInfo2();
external Apple(String type);
}
typedef void Callback(Apple a);
#JS('callback')
external void callback(Callback /* or just Function */ f);
f(Apple apple) {
print(apple.getInfo());
print(apple); // prints the stringified JS object [object Object]
}
main() async {
callback(allowInterop(f));
}

onCatchError does not come if instantiated via HTML (in combination with bind toValue)

I'm a bit confused about how AngularDart / DI instantiates new objects.
If I instantiate 'appcontroller' via HTML it never gets the catchError (it fails in the console with 'Uncaught Error...'). The odd thing is - this happens ONLY if I bind(Proxy, toValue: new Proxy()); if I bind(Proxy); it works!
Another way to make it work is to make the completer in Proxy static.Then bind(SyncProxy,toValue: new SyncProxy()); is no problem and catchError comes.
If I run Controller, Proxy and Module in a unit-test it works too...
#angular.Controller(selector: '[appcontroller]', publishAs: "ctrl")
class Controller {
final Logger _logger = new Logger("integration.test.Angular.Controller");
final Proxy _proxy;
bool _loadedWithError = false;
Controller(this._proxy) {
_proxy.load().catchError((final String error) {
_logger.severe("Error X: $error");
_loadedWithError = true;
});
}
}
#angular.Injectable()
class Proxy {
final Completer<bool> _completer = new Completer<bool>();
Future<bool> load() {
//_completer.completeError("Error");
new Timer(new Duration(seconds: 1),() {
_completer.completeError("Error");
});
return _completer.future;
}
}
class SampleModule extends angular.Module {
SampleModule() {
bind(Controller);
// works for UNIT-Test but fails with HTML instantiation
bind(Proxy, toValue: new Proxy());
}
}
main() {
applicationFactory().addModule(new SampleModule()).run();
}
HTML-Code:
<body appcontroller>
...
[ Update ]
According to Günters answer below and my own tests the approach bind(Proxy,toValue: new Proxy()); to create a Singleton is useless and counter-productive.
The normal bind is enough and avoids the Zone-Problem!

Groovy/Grails promises/futures. There is no .resolve(1,2,3) method. Strange?

I am developing in a Grails application. What I want to do is to lock the request/response, create a promise, and let someone else resolve it, that is somewhere else in the code, and then flush the response.
What I find really strange is that the Promise promise = task {} interface has no method that resembles resolve or similar.
I need to lock the response until someone resolves the promise, which is a global/static property set in development mode.
Promise interface:
http://grails.org/doc/latest/api/grails/async/Promise.html
I have looked at the GPars doc and can't find anything there that resembles a resolve method.
How can I create a promise, that locks the response or request, and then flushes the response when someone resolves it?
You can call get() on the promise which will block until whatever the task is doing completes, but I imagine what that is not what you want. What you want seems to be equivalent to a GPars DataflowVariable:
http://gpars.org/1.0.0/javadoc/groovyx/gpars/dataflow/DataflowVariable.html
Which allows using the left shift operator to resolve the value from another thread. Currently there is no way to use the left shift operator via Grails directly, but since Grails' promise API is just a layer over GPars this can probably be accomplished by using the GPars API directly with something like:
import org.grails.async.factory.gpars.*
import groovyx.gpars.dataflow.*
import static grails.async.Promise.*
def myAction() {
def dataflowVar = new DataflowVariable()
task {
// do some calculation and resolve data flow variable
def expensiveData = ...
dataflowVar << expensiveData
}
return new GParsPromise(dataflowVar)
}
It took me quite some time to get around this and have a working answer.
I must say that it appears as if Grails is quite a long way of making this work properly.
task { }
will always execute immediatly, so the call is not put on hold until dispatch() or whatever is invoked which is a problem.
Try this to see:
public def test() {
def dataflowVar = new groovyx.gpars.dataflow.DataflowVariable()
task {
// do some calculation and resolve data flow variable
println '1111111111111111111111111111111111111111111111111111'
//dataflowVar << expensiveData
}
return new org.grails.async.factory.gpars.GparsPromise(dataflowVar);
}
If you are wondering what this is for, it is to make the lesscss refresh automatically in grails, which is a problem when you are using import statements in less. When the file is touched, the lesscss compiler will trigger a recompilation, and only when it is done should it respond to the client.
On the client side I have some javascript that keeps replacing the last using the refresh action here:
In my controller:
/**
* Refreshes link resources. refresh?uri=/resource/in/web-app/such/as/empty.less
*/
public def refresh() {
return LessRefresh.stackRequest(request, params.uri);
}
A class written for this:
import grails.util.Environment
import grails.util.Holders
import javax.servlet.AsyncContext
import javax.servlet.AsyncEvent
import javax.servlet.AsyncListener
import javax.servlet.http.HttpServletRequest
/**
* #Author SecretService
*/
class LessRefresh {
static final Map<String, LessRefresh> FILES = new LinkedHashMap<String, LessRefresh>();
String file;
Boolean touched
List<AsyncContext> asyncContexts = new ArrayList<AsyncContext>();
String text;
public LessRefresh(String file) {
this.file = file;
}
/** Each request will be put on hold in a stack until dispatchAll below is called when the recompilation of the less file finished **/
public static AsyncContext stackRequest(HttpServletRequest request, String file) {
if ( !LessRefresh.FILES[file] ) {
LessRefresh.FILES[file] = new LessRefresh(file);
}
return LessRefresh.FILES[file].handleRequest(request);
}
public AsyncContext handleRequest(HttpServletRequest request) {
if ( Environment.current == Environment.DEVELOPMENT ) {
// We only touch it once since we are still waiting for the less compiler to finish from previous edits and recompilation
if ( !touched ) {
touched = true
touchFile(file);
}
AsyncContext asyncContext = request.startAsync();
asyncContext.setTimeout(10000)
asyncContexts.add (asyncContext);
asyncContext.addListener(new AsyncListener() {
#Override
void onComplete(AsyncEvent event) throws IOException {
event.getSuppliedResponse().writer << text;
}
#Override
void onTimeout(AsyncEvent event) throws IOException {
}
#Override
void onError(AsyncEvent event) throws IOException {
}
#Override
void onStartAsync(AsyncEvent event) throws IOException {
}
});
return asyncContext;
}
return null;
}
/** When recompilation is done, dispatchAll is called from LesscssResourceMapper.groovy **/
public void dispatchAll(String text) {
this.text = text;
if ( asyncContexts ) {
// Process all
while ( asyncContexts.size() ) {
AsyncContext asyncContext = asyncContexts.remove(0);
asyncContext.dispatch();
}
}
touched = false;
}
/** A touch of the lessfile will trigger a recompilation **/
int count = 0;
void touchFile(String uri) {
if ( Environment.current == Environment.DEVELOPMENT ) {
File file = getWebappFile(uri);
if (file && file.exists() ) {
++count;
if ( count < 5000 ) {
file << ' ';
}
else {
count = 0
file.write( file.getText().trim() )
}
}
}
}
static File getWebappFile(String uri) {
new File( Holders.getServletContext().getRealPath( uri ) )
}
}
In LesscssResourceMapper.groovy of the lesscsss-recources plugin:
...
try {
lessCompiler.compile input, target
// Update mapping entry
// We need to reference the new css file from now on
resource.processedFile = target
// Not sure if i really need these
resource.sourceUrlExtension = 'css'
resource.contentType = 'text/css'
resource.tagAttributes?.rel = 'stylesheet'
resource.updateActualUrlFromProcessedFile()
// ==========================================
// Call made here!
// ==========================================
LessRefresh.FILES[resource.sourceUrl.toString()]?.dispatchAll( target.getText() );
} catch (LessException e) {
log.error("error compiling less file: ${originalFile}", e)
}
...
In the index.gsp file:
<g:set var="uri" value="${"${App.files.root}App/styles/empty.less"}"/>
<link media="screen, projection" rel="stylesheet" type="text/css" href="${r.resource(uri:uri)}" refresh="${g.createLink(controller:'home', action:'refresh', params:[uri:uri])}" resource="true">
JavaScript method refreshResources to replace the previous link href=...
/**
* Should only be used in development mode
*/
function refreshResources(o) {
o || (o = {});
var timeoutBegin = o.timeoutBegin || 1000;
var intervalRefresh = o.intervalRefresh || 1000;
var timeoutBlinkAvoid = o.timeoutBlinkAvoid || 400 ;
var maxErrors = o.maxErrors || 200 ;
var xpath = 'link[resource][type="text/css"]';
// Find all link[resource]
$(xpath).each(function(i, element) {
refresh( $(element) );
});
function refresh(element) {
var parent = element.parent();
var next = element.next();
var outer = element.clone().attr('href', '').wrap('<p>').parent().html();
var uri = element.attr('refresh');
var errorCount = 0;
function replaceLink() {
var link = $(outer);
link.load(function () {
// The link has been successfully added! Now remove the other ones, then do again
errorCount = 0;
// setTimeout needed to avoid blinking, we allow duplicates for a few milliseconds
setTimeout(function() {
var links = parent.find(xpath + '[refresh="'+uri+'"]');
var i = 0;
// Remove all but this one
while ( i < links.length - 1 ) {
links[i++].remove();
}
replaceLinkTimeout();
}, timeoutBlinkAvoid );
});
link.error(function(event, handler) {
console.log('Error refreshing: ' + outer );
++errorCount;
if ( errorCount < maxErrors ) {
// Load error, it happens. Remove this & redo!
link.remove();
replaceLink();
}
else {
console.log('Refresh: Aborting!')
}
});
link.attr('href', urlRandom(uri)).get(0);
link.insertBefore(next); // Insert just after
}
function urlRandom(uri) {
return uri + "&rand=" + Math.random();
}
function replaceLinkTimeout() {
setTimeout(function() {
replaceLink();
}, intervalRefresh ) ;
}
// Waith 1s before triggering the interval
setTimeout(function() {
replaceLinkTimeout();
}, timeoutBegin);
}
};
Comments
I am unsure why Javascript style promises have not been added to the Grails stack.
You can not render or stuff like that in the onComplete. render, redirect and what not are not available.
Something tells me that Grails and Promises/Futures are not there yet. The design of the GPars libraries seems not take into account of the core features which is to resolve later. At least it is not simple to do so.
It would be great if the dispatch() method actually could be invoked with some paramaters to pass from the resolving context. I am able to go around this using static properties.
I might continue to write my own solution and possibly contribute with a more fitting solutions around the AsyncContext class, but for now, this is enough for me.
I just wanted to refresh my less resources automatically.
Phew...
EDIT:
I made it to support several number of files. It is complete now!

Selenium2 WebDriver (Page factory) error after a postback (Element not found in the cache)

I'm using Selenium 2 tests (written in C#) that choose values from a "select" control.
Selection causes a post-back to the server, which updates the state of the page.
It s really frustrating because every PostBack occurs this exception
Element not found in the cache - perhaps the page has changed since it was looked up
Just to be precise i use Selenium 2 WebDriver (2.32.0.0)
And for my project i Use Pattern Page Factory
The code looks like that
class RegisterPersonelData
{
private IWebDriver driver;
[FindsBy(How = How.Id, Using = "ctl00_ContentMain_register1_txtName")]
private IWebElement txtLastname;
[FindsBy(How = How.Id, Using = "ctl00_ContentMain_register1_lstDrvLic")]
private IWebElement dlDrive;
private SelectElement selectDrive;
[FindsBy(How = How.Id, Using = "ctl00_ContentMain_register1_lstVeh")]
private IWebElement dlVehicule;
private SelectElement selectVehicule;
public RegisterPersonelData(IWebDriver driver)
{
this.driver = driver;
// initialize elements of the LoginPage class
PageFactory.InitElements(driver, this);
// all elements in the 'WebElements' region are now alive!
// FindElement or FindElements no longer required to locate elements
}
public void fillData(string lastname, string drive, string vehicule)
{
txtLastname.SendKeys(lastname);
this.selectDrive = new SelectElement(dlDrive);
selectDrive.SelectByText(drive);
selectVehicule = new SelectElement(dlVehicule);
IWait<IWebDriver> wait = new WebDriverWait(this.driver, TimeSpan.FromSeconds(Convert.ToInt32(ConfigurationManager.AppSettings["ExpliciteWait"])));
wait.Until(x => selectVehicule.Options.Count > 1);
selectVehicule.SelectByText(vehicule);
}
}
And here the code of main
class Program
{
static void Main()
{
IWebDriver driver = MyWebDriver.GetWebDriver(MyWebDriver.BrowserType.FIFREFOX);
driver.Navigate().GoToUrl("http://...");
...
registerPersonelData.fillData("lastname", "Permis B", "Auto");
}
}
This code doesn t work because one postback is triggered ...
I have try to use one explicite wait but it fails too !
Code use to retrieve one element with explicite wait
public static IWait<IWebDriver> GetWaitWebDriver(IWebDriver driver)
{
IWait<IWebDriver> wait = new WebDriverWait(driver, TimeSpan.FromSeconds(Convert.ToInt32(ConfigurationManager.AppSettings["ExpliciteWait"])));
return wait;
}
public static IWebElement GetElementAndWaitForIt(IWebDriver driver, By by)
{
return GetWaitWebDriver(driver).Until(x =>
{
return x.FindElement(by);
});
}
Someone has one idea to fix it ?
You can re-initialize the elements at the end of the fillData method.
PageFactory.InitElements(driver, this);
You could do it in the main as well with:
PageFactory.InitElements(driver, registerPersonelData);
You could also try adding the following to the field you need to reuse.
[CacheLookup]
I have done a lot of tries and finally i have found something usefull
public static void WaitForAnWebElementDisplayed(IWait<IWebDriver> wait, IWebElement webElement)
{
wait.Until(x => webElement.Displayed);
}
public static void WaitForAnWebElementEnabled(IWait<IWebDriver> wait, IWebElement webElement)
{
wait.Until(x => webElement.Enabled);
}
and Consequently i can wait that the load of page triggered after to have choosen one item in select option is completed !

Resources