tabris-js: WebView Change Events Not Triggering

Problem description

When navigating using a WebView, the ChangeEvents associated with certain properties are not firing, even when those properties are indeed changing.

Environment

  • Tabris.js version: 3.0.0-beta1
  • iOS 11.1.1

Here is some code I wrote to illustrate the problem

/* webview */
let wv = new WebView({
	top: 0,
	bottom: '#toolbar',
	left: 0,
	right: 0,
	url: "https://www.duckduckgo.com"
})
.on('canGoBackChanged', () => console.log('canGoBackChanged triggered'))
.on('canGoForwardChanged', () => console.log('canGoForwardChanged triggered'))
.on('urlChanged', () => console.log('urlChanged triggered'))
.on('htmlChanged', () => console.log('htmlChanged triggered'))
.on('load', () => console.log('load triggered'))
.on('navigate', () => console.log('navigate triggered'))
.on('download', () => console.log('download triggered'))
.on('message', () => console.log('message triggered'))
.appendTo(ui.contentView);

/* log properties */
new Button({
	id: 'toolbar',
	left: 0,
	right: '#goback',
	bottom: 0,
	top: '90%',
	text: 'Log Properties'
}).on('select', () => {
	console.debug("canGoBack: " + wv.canGoBack);
	console.debug("canGoForward: " + wv.canGoForward);
	console.debug("url: " + wv.url);
	console.debug("html: " + wv.html);
})
.appendTo(ui.contentView);

/* go back */
new Button({
	id: 'goback',
	left: '#toolbar',
	right: 0,
	bottom: 0,
	top: '90%',
	text: 'Go Back'
}).on('select', () => {
	wv.goBack();
}).appendTo(ui.contentView);

After running the code above, here is the console output, with comments to explain when which actions were performed:

<!-- initial appending to contentView -->
navigate triggered
navigate triggered
navigate triggered
load triggered

<!-- properties logged -->
canGoBack: false
canGoForward: false
url: https://duckduckgo.com/
html: undefined

<!-- searching for "test" -->
navigate triggered
navigate triggered
load triggered

<!-- properties logged -->
canGoBack: true
canGoForward: false
url: https://duckduckgo.com/?q=test&t=h_&ia=news
html: undefined

<!-- calling .goBack() -->
navigate triggered
load triggered

<!-- logging properties -->
canGoBack: false
canGoForward: true
url: https://duckduckgo.com/
html: undefined

As far as I can tell, canGoBackChanged, canGoForwardChanged, and urlChanged should be firing, as it is clear that their values are changing. I understand why download, htmlChanged, and message aren’t being triggered, but I figured I’d include them for completeness.

In the above example, navigate and load are triggered on every navigation, but I’ve also run into cases where they don’t fire when they’re supposed to as well. Here is another annotated console log, showing what happens when the URL is initially set as this tweet:

<!-- initial appending -->
navigate triggered
navigate triggered
load triggered

<!-- log properties -->
canGoBack: false
canGoForward: false
url: https://mobile.twitter.com/tabrisjs/status/1089827821487312897
html: undefined

<!-- navigating to Tabris.js user page -->

<!-- log properties -->
canGoBack: true
canGoForward: false
url: https://mobile.twitter.com/tabrisjs
html: undefined

<!-- navigating to a different tweet -->

<!-- log properties -->
canGoBack: true
canGoForward: false
url: https://mobile.twitter.com/tabrisjs/status/1075320982909407232
html: undefined

<!-- calling .goBack() -->

In my particular use case, the goal is to darken a back button if the WebView cannot navigate back. As of now, I initialize the button as dark, but there is no way to tell when the WebView becomes able to navigate back without a reliable event to listen to.

Thanks in advance!

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 16 (13 by maintainers)

Most upvoted comments

WebView has an undocumented initScript API. This allows to execute a JavaScript code before page is loaded. Maybe you can find helpful. We are going to make this API public with next release.

Hi @raphaelpreston. We have fixed canGoBack and canGoForward properties and theirs appropriate events. Those were the only events that we could reliably fix when a page is using history.pushState() for navigation. With current state of WebView we have decided to only fix these as other events were simply unreliable or duplicated. We hope this helps your issue.

You are right @cookieguru. I have found similar reports regarding WKWebView and history.pushState() on the Internet. However I might have found a workaround for several events. That still needs some fine-tuning on native side.

evaluate has option to execute some JS code. However Tabris JS VM and WebView JS VM do not share resources in any way. Therefore callbacks are no go but you can get a return value from such evaluation. Maybe doing it on a timer would provide some help to this issue.

I believe this has to do with history.pushState()

I used this slightly modified snippet:

let wv = new WebView({
	top: 0,
	bottom: '#toolbar',
	left: 0,
	right: 0,
	url: 'http://html5doctor.com/demos/history/',
})
.on('canGoBackChanged', ({value}) => console.log(`canGoBack changed to ${value}`))
.on('canGoForwardChanged', ({value}) => console.log(`canGoForward changed to ${value}`))
.on('urlChanged', ({value}) => console.log(`url changed to ${value}`))
.on('htmlChanged', () => console.log('htmlChanged triggered'))
.on('load', () => console.log('load triggered'))
.on('navigate', ({url}) => console.log(`navigate triggered to ${url}`))
.on('download', () => console.log('download triggered'))
.on('message', () => console.log('message triggered'))
.appendTo(contentView);

/* log properties */
new Button({
	id: 'toolbar',
	left: 0,
	right: '#goback',
	bottom: 0,
	top: '90%',
	text: 'Log Properties',
}).on('select', () => {
	console.debug('canGoBack: ' + wv.canGoBack);
	console.debug('canGoForward: ' + wv.canGoForward);
	console.debug('url: ' + wv.url);
	console.debug('html: ' + wv.html);
})
.appendTo(contentView);

/* go back */
new Button({
	id: 'goback',
	left: '#toolbar',
	right: '#url',
	bottom: 0,
	top: '90%',
	text: 'Go Back',
}).on('select', () => {
	wv.goBack();
}).appendTo(contentView);

new Button({
	id: 'url',
	left: '#goback',
	right: 0,
	bottom: 0,
	top: '90%',
	text: 'url',
}).on('select', () => {
	wv.url = 'http://html5doctor.com/demos/history/';
}).appendTo(contentView);

I tapped on cat names several times, and the console only outputs the following:

navigate triggered to http://html5doctor.com/demos/history/
url changed to undefined
load triggered

 (tap Log properties)
canGoBack: false
canGoForward: false
url: http://html5doctor.com/demos/history/
html: undefined

The Go Back button did work as expected.

Several unsubstantiated claims indicate that iOS has added its own security around the history API. I’m willing to bet Twitter is leveraging the pushState method for their SPA.

I don’t know if it would be possible to inject an onpopstate handler using the aforementioned evaluate method. This answer has a possible workaround and this answer also has some other suggestions. Having it on a third party site significantly complicates the matter.

@raphaelpreston WebView has a evaluate method. Maybe you could inject some JS code that could help you. This is just a suggestion. I do not know if this will help you.

Hello. I will investigate this again. Reopening.

(Feel free to reopen issues if you think they are still valid.)

Fixes will be available in next nightly. Closing.