Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
ReactNativeWebView_kotlinVersion=1.6.0
ReactNativeWebView_webkitVersion=1.4.0
ReactNativeWebView_compileSdkVersion=29
ReactNativeWebView_webkitVersion=1.5.0
ReactNativeWebView_compileSdkVersion=32
ReactNativeWebView_buildToolsVersion=29.0.3
ReactNativeWebView_targetSdkVersion=28
ReactNativeWebView_minSdkVersion=21
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@
import androidx.core.content.ContextCompat;
import androidx.core.util.Pair;
import androidx.webkit.WebSettingsCompat;
import androidx.webkit.ScriptHandler;
import androidx.webkit.WebViewAssetLoader;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewFeature;

import com.facebook.common.logging.FLog;
Expand Down Expand Up @@ -629,6 +631,19 @@ public void setInjectedJavaScriptBeforeContentLoadedForMainFrameOnly(RNCWebViewC
view.ifHasRNCWebView(webView -> webView.setInjectedJavaScriptBeforeContentLoadedForMainFrameOnly(enabled));
}

/**
* Registers a script that runs at document start in every frame (main frame and subframes) whose
* origin matches one of allowedOriginRules, before any of the page's own scripts. Unlike
* injectedJavaScriptBeforeContentLoaded, which uses evaluateJavascript and so only ever reaches the
* main frame, this can reach cross-origin iframes. Takes effect on the next navigation of each
* matching frame; documents that are already loaded are not affected.
* See https://developer.android.com/reference/androidx/webkit/WebViewCompat#addDocumentStartJavaScript(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E)
*/
@ReactProp(name = "androidInjectedJavaScriptForOrigins")
public void setInjectedJavaScriptForOrigins(RNCWebViewContainer view, @Nullable ReadableMap config) {
view.ifHasRNCWebView(webView -> webView.setInjectedJavaScriptForOrigins(config));
}

@ReactProp(name = "messagingEnabled")
public void setMessagingEnabled(RNCWebViewContainer view, boolean enabled) {
view.ifHasRNCWebView(webView -> webView.setMessagingEnabled(enabled));
Expand Down Expand Up @@ -1749,6 +1764,11 @@ protected static class RNCWebView extends WebView implements LifecycleEventListe

protected ReadableMap source;
protected @Nullable ReadableMap assetLoaderConfig;
protected @Nullable ReadableMap injectedJavaScriptForOriginsConfig;
// ScriptHandler is @RestrictTo(LIBRARY_GROUP) until androidx.webkit 1.9.0, which needs compileSdk 34. The
// signatures we use are unchanged from 1.5.0 through 1.13.0, so the restriction is lint-only for us.
@SuppressLint("RestrictedApi")
protected @Nullable ScriptHandler injectedJavaScriptForOriginsHandler;
protected boolean loadScheduled;
private static int idCounter = 0;

Expand Down Expand Up @@ -1845,6 +1865,47 @@ public boolean isNewAssetLoaderConfig(@Nullable ReadableMap newConfig) {
return !assetLoaderConfig.toHashMap().equals(newConfig.toHashMap());
}

@SuppressLint("RestrictedApi") // See injectedJavaScriptForOriginsHandler.
public void setInjectedJavaScriptForOrigins(@Nullable ReadableMap config) {
// Stored on the RNCWebView rather than the container so a keyed WebView that is reparented into a
// new container with an equal config keeps its existing registration.
if (injectedJavaScriptForOriginsConfig == null && config == null) return;
if (injectedJavaScriptForOriginsConfig != null && config != null
&& injectedJavaScriptForOriginsConfig.toHashMap().equals(config.toHashMap())) return;
injectedJavaScriptForOriginsConfig = config;

if (injectedJavaScriptForOriginsHandler != null) {
injectedJavaScriptForOriginsHandler.remove();
injectedJavaScriptForOriginsHandler = null;
}

if (config == null) return;

if (!WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) {
FLog.w(TAG, "androidInjectedJavaScriptForOrigins ignored: DOCUMENT_START_SCRIPT is not supported by this WebView.");
return;
}

String script = config.hasKey("script") ? config.getString("script") : null;
ReadableArray rules = config.hasKey("allowedOriginRules") ? config.getArray("allowedOriginRules") : null;
if (TextUtils.isEmpty(script) || rules == null || rules.size() == 0) {
FLog.w(TAG, "androidInjectedJavaScriptForOrigins ignored: script and a non-empty allowedOriginRules are required.");
return;
}

Set<String> allowedOriginRules = new HashSet<>();
for (int i = 0; i < rules.size(); i++) {
allowedOriginRules.add(rules.getString(i));
}

try {
injectedJavaScriptForOriginsHandler = WebViewCompat.addDocumentStartJavaScript(this, script, allowedOriginRules);
} catch (IllegalArgumentException e) {
// Thrown for malformed origin rules.
FLog.e(TAG, "androidInjectedJavaScriptForOrigins ignored: invalid allowedOriginRules " + allowedOriginRules, e);
}
}

@Override
public void onHostResume() {
// do nothing
Expand Down
23 changes: 23 additions & 0 deletions docs/Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ This document lays out the current public properties and methods for the React N
- [`javaScriptCanOpenWindowsAutomatically`](Reference.md#javascriptcanopenwindowsautomatically)
- [`androidHardwareAccelerationDisabled`](Reference.md#androidHardwareAccelerationDisabled)
- [`androidLayerType`](Reference.md#androidLayerType)
- [`androidInjectedJavaScriptForOrigins`](Reference.md#androidInjectedJavaScriptForOrigins)
- [`mixedContentMode`](Reference.md#mixedcontentmode)
- [`thirdPartyCookiesEnabled`](Reference.md#thirdpartycookiesenabled)
- [`userAgent`](Reference.md#useragent)
Expand Down Expand Up @@ -878,6 +879,28 @@ Avoid setting both `androidLayerType` and `androidHardwareAccelerationDisabled`

---

### `androidInjectedJavaScriptForOrigins`[⬆](#props-index)<!-- Link generated with jump2header -->

Registers a script with [`WebViewCompat.addDocumentStartJavaScript`](https://developer.android.com/reference/androidx/webkit/WebViewCompat#addDocumentStartJavaScript(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E)). The script runs at document start, before any of the page's own scripts, in every frame whose origin matches one of `allowedOriginRules`, including cross-origin iframes. This differs from `injectedJavaScriptBeforeContentLoaded`, which on Android only ever reaches the main frame.

The script is applied on the next navigation of each matching frame; documents that are already loaded are not affected. Changing the prop replaces the previous registration, and passing `undefined` removes it. If the installed WebView does not support `WebViewFeature.DOCUMENT_START_SCRIPT`, or the origin rules are malformed, the prop is ignored and a native warning is logged.

```jsx
<WebView
source={{ uri: 'https://shell.example.net/index.html' }}
androidInjectedJavaScriptForOrigins={{
script: "window.__injected = true;",
allowedOriginRules: ['https://*.example.com'],
}}
/>
```

| Type | Required | Platform |
| ------------------------------------------------------ | -------- | -------- |
| `{ script: string, allowedOriginRules: string[] }` | No | Android |

---

### `mixedContentMode`[⬆](#props-index)<!-- Link generated with jump2header -->

Specifies the mixed content mode. i.e WebView will allow a secure origin to load content from any other origin.
Expand Down
25 changes: 25 additions & 0 deletions lib/WebViewTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,21 @@ export interface AndroidAssetLoaderConfig {
*/
pathHandlers: AndroidAssetLoaderPathHandler[];
}
/**
* A script registered via androidx.webkit.WebViewCompat.addDocumentStartJavaScript.
*/
export interface AndroidInjectedJavaScriptForOrigins {
/**
* JavaScript source to run at document start, before any of the page's own scripts.
*/
script: string;
/**
* Origin rules a frame must match for the script to run in it, e.g. `https://*.example.com`.
* Matching applies to every frame, including cross-origin iframes.
* See https://developer.android.com/reference/androidx/webkit/WebViewCompat#addDocumentStartJavaScript(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E)
*/
allowedOriginRules: string[];
}
export interface WebViewSourceUri {
/**
* The URI to load in the `WebView`. Can be a local or remote file.
Expand Down Expand Up @@ -261,6 +276,7 @@ export interface AndroidNativeWebViewProps extends CommonNativeWebViewProps {
allowsFullscreenVideo?: boolean;
allowUniversalAccessFromFileURLs?: boolean;
androidAssetLoaderConfig?: AndroidAssetLoaderConfig;
androidInjectedJavaScriptForOrigins?: AndroidInjectedJavaScriptForOrigins;
androidHardwareAccelerationDisabled?: boolean;
androidLayerType?: AndroidLayerType;
domStorageEnabled?: boolean;
Expand Down Expand Up @@ -906,6 +922,15 @@ export interface AndroidWebViewProps extends WebViewSharedProps {
* assets and resources using http(s):// URLS inside a WebView.
*/
androidAssetLoaderConfig?: AndroidAssetLoaderConfig;
/**
* Injects `script` at document start into every frame (main frame and iframes) whose origin
* matches `allowedOriginRules`. Unlike `injectedJavaScriptBeforeContentLoaded`, this reaches
* cross-origin iframes. Changes take effect on the next navigation of each matching frame.
* No-op (with a native warning) when the installed WebView does not support
* `WebViewFeature.DOCUMENT_START_SCRIPT`.
* @platform android
*/
androidInjectedJavaScriptForOrigins?: AndroidInjectedJavaScriptForOrigins;
/**
* Boolean value to enable third party cookies in the `WebView`. Used on
* Android Lollipop and above only as third party cookies are enabled by
Expand Down
28 changes: 28 additions & 0 deletions src/WebViewTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,23 @@ export interface AndroidAssetLoaderConfig {
pathHandlers: AndroidAssetLoaderPathHandler[]
}

/**
* A script registered via androidx.webkit.WebViewCompat.addDocumentStartJavaScript.
*/
export interface AndroidInjectedJavaScriptForOrigins {
/**
* JavaScript source to run at document start, before any of the page's own scripts.
*/
script: string;

/**
* Origin rules a frame must match for the script to run in it, e.g. `https://*.example.com`.
* Matching applies to every frame, including cross-origin iframes.
* See https://developer.android.com/reference/androidx/webkit/WebViewCompat#addDocumentStartJavaScript(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E)
*/
allowedOriginRules: string[];
}

export interface WebViewSourceUri {
/**
* The URI to load in the `WebView`. Can be a local or remote file.
Expand Down Expand Up @@ -371,6 +388,7 @@ export interface AndroidNativeWebViewProps extends CommonNativeWebViewProps {
allowsFullscreenVideo?: boolean;
allowUniversalAccessFromFileURLs?: boolean;
androidAssetLoaderConfig?: AndroidAssetLoaderConfig
androidInjectedJavaScriptForOrigins?: AndroidInjectedJavaScriptForOrigins;
androidHardwareAccelerationDisabled?: boolean;
androidLayerType?: AndroidLayerType;
domStorageEnabled?: boolean;
Expand Down Expand Up @@ -1100,6 +1118,16 @@ export interface AndroidWebViewProps extends WebViewSharedProps {
*/
androidAssetLoaderConfig?: AndroidAssetLoaderConfig;

/**
* Injects `script` at document start into every frame (main frame and iframes) whose origin
* matches `allowedOriginRules`. Unlike `injectedJavaScriptBeforeContentLoaded`, this reaches
* cross-origin iframes. Changes take effect on the next navigation of each matching frame.
* No-op (with a native warning) when the installed WebView does not support
* `WebViewFeature.DOCUMENT_START_SCRIPT`.
* @platform android
*/
androidInjectedJavaScriptForOrigins?: AndroidInjectedJavaScriptForOrigins;

/**
* Boolean value to enable third party cookies in the `WebView`. Used on
* Android Lollipop and above only as third party cookies are enabled by
Expand Down
Loading