hermes is messing up with injectedJavaScript in react-native-webview in Android 6

Created on 9 Jul 2020  路  10Comments  路  Source: facebook/hermes

Bug Description

When using hermes in react-native it's not possible to use injectedJavaScript correctly on android 6.
Without hermes we just have to use:

function myFunction() {}

myFunction.toString();

And myFunction was used inside webview. With hermes activated, this is not possible.
We have to use the string instead of converting the function. That would be no problem if it worked on android 6 and below, but it doesn't work.

If I remove disable hermes, I can use myFunction.toString() even on Android 6 and below, and it works without problems.

  • [x] I have run gradle clean and confirmed this bug does not occur with JSC"

Hermes version:
React Native version (if any): 0.62.2
Android version (if any): 6
Platform (most likely one of arm64-v8a, armeabi-v7a, x86, x86_64):

Steps To Reproduce

1.
2.

code example:

The Expected Behavior

All 10 comments

Hermes doesn't currently support getting source code using Function.prototype.toString().

See #114 for a more detailed discussion.

@avp there is some WIP about this?
My application has webviews as a really important part of project, but I wouldn't like to remove Hermes. The gains are pretty expressives.

What problems arise when trying to use a plain string instead of myFunction.toString() in Android <= 6? Is there an error message?

It seems at first glance that using the expected output of myFunction.toString() as a string literal should be equivalent to using myFunction.toString() regardless of what version of Android you're running.

Using string literal on devices above 6 works without errors.
When I use the same string on marshmallow, it's like if he doesn't accept because I'm using some things like arrow functions, const, let.

I tried to convert to pure javascript without any new features, and it seems to run. The problem is that my webview is loading a content that uses those new features, and it endup not working correctly.

For example, my site is not able to click and redirect to a page.

And I can't debug my webview, because chrome debugger is messed in android 6.

But without hermes, the same literal string (or myFunction.toString) works in any device.

@avp sorry, I've converted the Js (es6) to pure javascript and the injection worked on marshmallow.

Looks like the use of myFunction.toString() in JSC will output lowered JS code (without ES6+ features) because the function has been run through Babel by Metro prior to being sent to JSC. Then it makes sense that myFunction.toString() works while the string literal encoding ES6+ JS doesn't.

Unfortunately, Hermes isn't really designed for this kind of use case. You're essentially using the RN build pipeline as a way to run Babel on JS code which is intended for a non-Hermes JS engine (that of the WebView). As such, the benefits of using Hermes bytecode as an encoding for these functions don't really exist. Also, as Hermes supports more and more ES6+ features and the Metro config for Babel starts eliminating transforms for those new features, this solution will become more and more fragile and inevitably break.

Even if we supported Function.prototype.toString as detailed in the linked issue above, it would likely be in a way that you'd have to annotate any functions for which you wanted to include the source with some directive like "use source". If we were to include the source for every function in addition to the bytecode, the bundle size would be huge (and that's probably not what you want).

It seems that the correct solution to your problem is to avoid using Hermes and the RN build pipeline for code that isn't intended for Hermes and RN by making a separate build step which lowers any code that you intend to execute in the WebView. Then, you can control transformation explicitly both for code that goes to Hermes and code that doesn't go to Hermes, and you avoid the entanglement of intent and syntactic lowering that you seem to have here.

@avp I've endup just converting my functions to pure javascript, and setting them manually as literal string.
It is less painful, since the code isn't that big. It's a very important part of our application, but the injection part is tiny, and the benefits of hermes are very big.

thanks a lot.

@witalobenicio can you provide an example how you covert your function to pure javascript string? I'm facing the same problem using webview, but I don't know where to start.
Thank you very much!

@chj-damon So in my case I previously had code like this:

const INJECTED_JS = function() {
  console.log('Hello from webview world');
};

<WebView ...
    injectedJavaScript={`${String(INJECTED_JS)}()`}
/>

This was "nice to have" because I could get editor hints for the JS code. Now I have to do it this way:

```

const INJECTED_JS = function() { console.log('Hello from webview world'); } ;

injectedJavaScript={${INJECTED_JS}()}
/>

@JakeStoeffler thank you very much!

Was this page helpful?
0 / 5 - 0 ratings