Control of the WebView from an independent javascript instance.
Opened this issue · 6 comments
Feature Request
Motivation Behind Feature
This feature would enable:
- Modifying html before loading it (i.e. rendering it with some templating engine).
- Smooth development using multi-page app architecture.
Feature Description
An option to run Cordova apps with a "background" javascript instance that could control and communicate with the WebView.
Alternatives or Workarounds
As an alternative you can use a single html file containing an iframe and scripts. Instead of loading desired pages in WebView the app would read the html file, render it adding mutation observers, write contents to a different file and then load that file in an iframe.
Drawbacks of the workaround:
- Apps become harder to debug.
- Unnecessary file reads/writes.
- Mutation observers have to be rendered into html in order to override link behavior. So that the iframe would send a message back to WebView making it read the next html file, render, write and load it in the iframe.
- On android platform
applicationDirectory
is read only, so you have to write the rendered html file to a different directory. In this case you have to either: copy all resources that are loaded using relative paths into the same directory or modify relative path when rendering html.
Modifying html before loading it (i.e. rendering it with some templating engine).
I don't think this is feasible. The native APIs expects an actual file URL of some sort, you can't just simply pass through HTML data for it to load. So this in itself, won't solve some of the mentioned workaround drawbacks. (#303 (comment))
Smooth development using multi-page app architecture.
Multi-page architecture is really not recommended in Cordova environments. If you have multiple pages, you constantly have to unload/re-initialise Cordova plugins, in addition to any of your app assets. Single-Page Apps is far more efficient, even in a general sense. I'd recommend taking a read over our Best Practices.
On android platform applicationDirectory is read only, so you have to write the rendered html file to a different directory. In this case you have to either: copy all resources that are loaded using relative paths into the same directory or modify relative path when rendering html.
On some other platforms, namely iOS, the WKWebView will refuse to load anything outside of the iOS equivalent of applicationDirectory
.
I don't think this is feasible. The native APIs expects an actual file URL of some sort, you can't just simply pass through HTML data for it to load.
Are you sure about that? What exact native APIs are you talking about?
I've found these methods for UIWebView, WKWebView and Android WebView. With them you can load an HTML string into WebView and also set the base url for relative paths.
Looks like I stand corrected.
If you have multiple pages, you constantly have to unload/re-initialise Cordova plugins, in addition to any of your app assets.
Yeah, but in some cases I think it might be saving up some memory i.e. if different pages use different plugins and assets. Multi-page architecture would be very useful for big projects.
I don't think this is feasible. The native APIs expects an actual file URL of some sort, you can't just simply pass through HTML data for it to load.
Are you sure about that? What exact native APIs are you talking about? I've found these methods for UIWebView, WKWebView and Android WebView. With them you can load an HTML string into WebView and also set the base url for relative paths.
I could load HTML string to Android WebView. I have tried a Base64 of an image as well to embed in Web View.
I'm not certain how feasible this is due to the architecture of Cordova but Android has an experimental JavaScriptEngine package, which allows you to build a javascript environment without a webview.
In theory this could be used for background processing as it can be done in a service/WorkManager task.
There are a few limitations of this however, one is that the JavaScriptSandbox may be only supported on API 26 or newer devices, and only if the installed android webview has support for it.
Note: Applications must check JavaScriptSandbox availability by calling JavaScriptSandbox.isSupported() API. Make sure that the application verifies that the sandbox is supported on the device before calling any other JavaScriptSandbox methods. The sandbox is supported on API 26 and above if the WebView implementation supports it.
JavaScriptEngine is not production ready so it's not something Cordova can use right now, and even if it was, we would need to bump our min SDK to at least API 26. So it's not something I can see being implemented in the foreseeable future, but it would be an interesting experiment, which potentially be a cordova plugin.
iOS also has a similar system called JavaScriptCore which is available as of iOS 16.0, so similar story to Android... in order to use this feature, it will need to bump our min version to iOS 16 which is too restrictive at this time. But likely implementable in the form of a plugin as an experiment.
I'm not certain how feasible this is due to the architecture of Cordova
Coming back to this, the main concern I have is Cordova makes a lot of assumptions and I don't think plugins will necessary have a guaranteed opportunity to control the webview instance just after the webview instance is created, short of implementing your own webview plugin. And if the JS is running in a background/service thread, it may not have safe access to the webview instance either. But these details may become more clear once these features are actually experimented with.
Update:
I've spent some time experimenting with Android's JavaScriptEngine and I can say it's still very primitive. It only seems to only work on API 33+ devices (testing simulators). It's possible that the older simulators just simply doesn't have the proper webview versoin installed. Even with API 33, not all features are support, including a way to exchange data from native to the webview. The WebView API has an API to exchange data, and the JavaScript engine doesn't have the equivalent API. It does have an Array Buffer API which seems to be significantly better over the WebView counterpart, but it also appears to be one direction... native -> JS only. As far as I can tell based on the documentation transferring data from JS to native is not supported. Additionally I was an unable to experiment far with data buffer exchanges because the API 33 simulator doesn't appear to support the feature (probably requires me using a beta or alpha release of the webview).
Another note is the JavaScriptEngine
has no APIs for debugging support as far as I can tell.