react-native-webview / react-native-webview

React Native Cross-Platform WebView
https://github.com/react-native-community/discussions-and-proposals/pull/3
MIT License
6.31k stars 2.96k forks source link

[Android] Issue with injectedJavaScriptBeforeContentLoaded #3377

Closed ivankdev closed 2 weeks ago

ivankdev commented 2 months ago

Integrating webview in my RN app. I need to handle Publitas API by injected javascript code. This is minimal code snippet for repro.

"react-native-webview": "^13.8.4",
import { Alert } from 'react-native'
import WebView from 'react-native-webview'

const publitasInjectedJS = `
  console.log('publitasInjectedJS'); // called always

  window.viewerReady = function(api) {
    console.log('viewerReady'); // called sometimes
    // ... doing logic here
  };

  true;
`

export const App = () => {
  return (
    <WebView
      injectedJavaScript={publitasInjectedJS} // works well for iOS, never for Android
      injectedJavaScriptBeforeContentLoaded={publitasInjectedJS} // works sometimes for Android
      source={{ uri: 'https://view.publitas.com/delhaize-belgium/2023-w42-fr' }}
      onMessage={() => Alert.alert('postMessage')}
      cacheEnabled={false}
    />
  )
}

So on iOS default injectedJavaScript prop works well and method viewerReady is always called in time.

But on Android side, injectedJavaScript doesn't work at all and injectedJavaScriptBeforeContentLoaded works like in 1 case of 20 page reloads. You can see actually that viewerReady not called at all most of the reloads.

Screenshot 2024-04-03 at 02 05 26 Screenshot 2024-04-03 at 01 35 09


What I also tried is

  onLoad={() => webViewRef.current?.injectJavaScript(publitasInjectedJS)}

but no any visible changes with that approach

Question: Any clues how to make it work for Android properly?

gronxb commented 2 months ago

you can see this issue: https://github.com/react-native-webview/react-native-webview/pull/1099

injectedJavaScriptBeforeContentLoaded is sometimes weird on Android.

injectedJavaScriptObject is an alternate prop for this issue. https://github.com/react-native-webview/react-native-webview/blob/master/docs/Reference.md#injectedjavascriptobject

see https://github.com/react-native-webview/react-native-webview/pull/2960#issuecomment-1715027083

ivankdev commented 2 months ago

you can see this issue: #1099

injectedJavaScriptBeforeContentLoaded is sometimes weird on Android.

injectedJavaScriptObject is an alternate prop for this issue. https://github.com/react-native-webview/react-native-webview/blob/master/docs/Reference.md#injectedjavascriptobject

see #2960 (comment)

@gronxb thanks for answer, but honestly I'm not sure how can passed object help here, when exactly function window.viewerReady not being called. All what I need is get viewerReady called in correct moment, no any data from js needed.

gronxb commented 2 months ago

Please don't pass the callback of window.viewerReady in React Native. Instead, call window.viewerReady directly in the web, not in React Native, and pass the necessary data to window.viewerReady using injectedJavaScriptObject.

ivankdev commented 2 months ago

@gronxb I cannot change the web part, it's dynamic url shared with mobile team. Anyway seems like I found out some valid workaround, will share in next comment

ivankdev commented 2 months ago

Resolved by adding new prop on native level which handled in onLoadResource callback

   <WebView
      ....
      source={{ uri: params?.url }}
      injectedJavaScript={publitasInjectedJS}
      injectedJavaScriptOnLoadResources={publitasInjectedJS} // android only
    />
diff --git a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebView.java b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebView.java
index 6664b6f..6cb9a28 100644
--- a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebView.java
+++ b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebView.java
@@ -46,6 +46,8 @@ public class RNCWebView extends WebView implements LifecycleEventListener {
     String injectedJS;
     protected @Nullable
     String injectedJSBeforeContentLoaded;
+    protected @Nullable
+    String injectedJSOnLoadResources;
     protected static final String JAVASCRIPT_INTERFACE = "ReactNativeWebView";
     protected @Nullable
     RNCWebViewBridge bridge;
@@ -283,6 +285,14 @@ public class RNCWebView extends WebView implements LifecycleEventListener {
         }
     }

+    public void callInjectedJavaScriptOnLoadResources() {
+        if (getSettings().getJavaScriptEnabled() &&
+                injectedJSOnLoadResources != null &&
+                !TextUtils.isEmpty(injectedJSOnLoadResources)) {
+            evaluateJavascriptWithFallback("(function() {\n" + injectedJSOnLoadResources + ";\n})();");
+        }
+    }
+
     public void setInjectedJavaScriptObject(String obj) {
         if (getSettings().getJavaScriptEnabled()) {
             RNCWebViewBridge b = createRNCWebViewBridge(this);
diff --git a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewClient.java b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewClient.java
index d59e19c..6e0978b 100644
--- a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewClient.java
+++ b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewClient.java
@@ -64,6 +64,14 @@ public class RNCWebViewClient extends WebViewClient {
         }
     }

+    @Override
+    public void onLoadResource(WebView webView, String url) {
+        RNCWebView reactWebView = (RNCWebView) webView;
+        reactWebView.callInjectedJavaScriptOnLoadResources();
+
+        super.onLoadResource(webView, url);
+    }
+
     @Override
     public void doUpdateVisitedHistory (WebView webView, String url, boolean isReload) {
       super.doUpdateVisitedHistory(webView, url, isReload);
diff --git a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManagerImpl.kt b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManagerImpl.kt
index 6a48a81..a52dd98 100644
--- a/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManagerImpl.kt
+++ b/node_modules/react-native-webview/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManagerImpl.kt
@@ -488,6 +488,11 @@ class RNCWebViewManagerImpl {
         view.injectedJSBeforeContentLoaded = value
     }

+    fun setInjectedJavaScriptOnLoadResources(viewWrapper: RNCWebViewWrapper, injectedJavaScript: String?) {
+        val view = viewWrapper.webView
+        view.injectedJSOnLoadResources = injectedJavaScript
+    }
+
     fun setInjectedJavaScriptForMainFrameOnly(viewWrapper: RNCWebViewWrapper, value: Boolean) {
         val view = viewWrapper.webView
         view.injectedJavaScriptForMainFrameOnly = value
diff --git a/node_modules/react-native-webview/android/src/newarch/com/reactnativecommunity/webview/RNCWebViewManager.java b/node_modules/react-native-webview/android/src/newarch/com/reactnativecommunity/webview/RNCWebViewManager.java
index 5bae4aa..c55fd3c 100644
--- a/node_modules/react-native-webview/android/src/newarch/com/reactnativecommunity/webview/RNCWebViewManager.java
+++ b/node_modules/react-native-webview/android/src/newarch/com/reactnativecommunity/webview/RNCWebViewManager.java
@@ -171,6 +171,12 @@ public class RNCWebViewManager extends ViewGroupManager<RNCWebViewWrapper>
         mRNCWebViewManagerImpl.setInjectedJavaScriptBeforeContentLoaded(view, value);
     }

+    @Override
+    @ReactProp(name = "injectedJavaScriptOnLoadResources")
+    public void setInjectedJavaScriptOnLoadResources(RNCWebViewWrapper view, @Nullable String value) {
+        mRNCWebViewManagerImpl.setInjectedJavaScriptOnLoadResources(view, value);
+    }
+
     @Override
     @ReactProp(name = "injectedJavaScriptForMainFrameOnly")
     public void setInjectedJavaScriptForMainFrameOnly(RNCWebViewWrapper view, boolean value) {
diff --git a/node_modules/react-native-webview/android/src/oldarch/com/reactnativecommunity/webview/RNCWebViewManager.java b/node_modules/react-native-webview/android/src/oldarch/com/reactnativecommunity/webview/RNCWebViewManager.java
index 709117a..1b2c17b 100644
--- a/node_modules/react-native-webview/android/src/oldarch/com/reactnativecommunity/webview/RNCWebViewManager.java
+++ b/node_modules/react-native-webview/android/src/oldarch/com/reactnativecommunity/webview/RNCWebViewManager.java
@@ -136,6 +136,11 @@ public class RNCWebViewManager extends ViewGroupManager<RNCWebViewWrapper> {
         mRNCWebViewManagerImpl.setInjectedJavaScriptBeforeContentLoaded(view, value);
     }

+    @ReactProp(name = "injectedJavaScriptOnLoadResources")
+    public void setInjectedJavaScriptOnLoadResources(RNCWebViewWrapper view, @Nullable String value) {
+        mRNCWebViewManagerImpl.setInjectedJavaScriptOnLoadResources(view, value);
+    }
+
     @ReactProp(name = "injectedJavaScriptForMainFrameOnly")
     public void setInjectedJavaScriptForMainFrameOnly(RNCWebViewWrapper view, boolean value) {
         mRNCWebViewManagerImpl.setInjectedJavaScriptForMainFrameOnly(view, value);
diff --git a/node_modules/react-native-webview/src/WebViewTypes.ts b/node_modules/react-native-webview/src/WebViewTypes.ts
index 1bc55cb..54e4aa8 100644
--- a/node_modules/react-native-webview/src/WebViewTypes.ts
+++ b/node_modules/react-native-webview/src/WebViewTypes.ts
@@ -1121,6 +1121,13 @@ export interface AndroidWebViewProps extends WebViewSharedProps {
    * @platform android
    */
   allowsProtectedMedia?: boolean;
+
+  /**
+   * Set this to provide JavaScript that will be injected into the web page
+   * when the page resources loaded.
+   * @platform android
+   */
+  injectedJavaScriptOnLoadResources?: string;
 }

 export interface WebViewSharedProps extends ViewProps {
github-actions[bot] commented 3 weeks ago

Hello 👋, this issue has been opened for more than 2 months with no activity on it. If the issue is still here, please keep in mind that we need community support and help to fix it! Just comment something like still searching for solutions and if you found one, please open a pull request! You have 7 days until this gets closed automatically