Skip to content

Commit a92298e

Browse files
committed
Merge branch 'main' into merge-master-into-fork
2 parents 1ddfe70 + d8d4091 commit a92298e

28 files changed

+4162
-5494
lines changed

.github/workflows/android-ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
build:
99
strategy:
1010
matrix:
11-
os: [ubuntu-latest, windows-latest]
11+
os: [ubuntu-latest]
1212
newArchEnabled: [false, true]
1313
runs-on: ${{ matrix.os }}
1414
steps:
@@ -28,7 +28,7 @@ jobs:
2828
run: yarn --frozen-lockfile
2929
shell: bash
3030
- name: Build Android test app
31-
uses: gradle/gradle-build-action@v2
31+
uses: gradle/actions/setup-gradle@db19848a5fa7950289d3668fb053140cf3028d43 #v3.3.2
3232
with:
3333
gradle-version: wrapper
3434
arguments: -PnewArchEnabled=${{matrix.newArchEnabled}} --no-daemon clean build check test
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: 'MetaMask Security Code Scanner'
2+
3+
on:
4+
push:
5+
branches: ['main']
6+
pull_request:
7+
branches: ['main']
8+
9+
jobs:
10+
run-security-scan:
11+
runs-on: ubuntu-latest
12+
permissions:
13+
actions: read
14+
contents: read
15+
security-events: write
16+
steps:
17+
- name: MetaMask Security Code Scanner
18+
uses: MetaMask/Security-Code-Scanner@main
19+
with:
20+
repo: ${{ github.repository }}
21+
paths_ignored: |
22+
.storybook/
23+
'**/__snapshots__/'
24+
'**/*.snap'
25+
'**/*.stories.js'
26+
'**/*.stories.tsx'
27+
'**/*.test.browser.ts*'
28+
'**/*.test.js*'
29+
'**/*.test.ts*'
30+
'**/fixtures/'
31+
'**/jest.config.js'
32+
'**/jest.environment.js'
33+
'**/mocks/'
34+
'**/test*/'
35+
docs/
36+
e2e/
37+
merged-packages/
38+
node_modules
39+
storybook/
40+
test*/
41+
rules_excluded: example
42+
project_metrics_token: ${{ secrets.SECURITY_SCAN_METRICS_TOKEN }}
43+
slack_webhook: ${{ secrets.APPSEC_BOT_SLACK_WEBHOOK }}

.github/workflows/windows-ci.yml

Lines changed: 0 additions & 55 deletions
This file was deleted.

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
# React Native WebView
22

3-
![star this repo](https://img.shields.io/github/stars/react-native-webview/react-native-webview?style=flat-square)
3+
![star this repo](https://img.shields.io/github/stars/MetaMask/react-native-webview-mm?style=flat-square)
44
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
5-
[![NPM Version](https://img.shields.io/npm/v/react-native-webview.svg?style=flat-square)](https://www.npmjs.com/package/react-native-webview)
6-
![Npm Downloads](https://img.shields.io/npm/dm/react-native-webview.svg)
5+
[![NPM Version](https://img.shields.io/npm/v/@metamask/react-native-webview.svg?style=flat-square)](https://www.npmjs.com/package/@metamask/react-native-webview)
6+
![Npm Downloads](https://img.shields.io/npm/dm/@metamask/react-native-webview.svg)
77

88
**React Native WebView** is a community-maintained WebView component for React Native. It is intended to be a replacement for the built-in WebView (which was [removed from core](https://github.com/react-native-community/discussions-and-proposals/pull/3)).
99

1010
### Maintainers
1111

12-
**Many thanks to these companies** for providing us with time to work on open source.
12+
**Many thanks to these companies** for providing us with time to work on open source.
1313
Please note that maintainers spend a lot of free time working on this too so feel free to sponsor them, **it really makes a difference.**
1414

15-
- [Thibault Malbranche](https://github.com/Titozzz) ([Twitter @titozzz](https://twitter.com/titozzz)) from [Brigad](https://www.brigad.co/en-gb/about-us)
15+
- [Thibault Malbranche](https://github.com/Titozzz) ([Twitter @titozzz](https://twitter.com/titozzz)) from [Brigad](https://www.brigad.co/en-gb/about-us)
1616
[*Sponsor me* ❤️ !](https://github.com/sponsors/Titozzz)
1717

1818

@@ -50,7 +50,7 @@ Import the `WebView` component from `react-native-webview` and use it like so:
5050
```tsx
5151
import React, { Component } from 'react';
5252
import { StyleSheet, Text, View } from 'react-native';
53-
import { WebView } from 'react-native-webview';
53+
import { WebView } from '@metamask/react-native-webview';
5454

5555
// ...
5656
const MyWebComponent = () => {
@@ -63,11 +63,11 @@ For more, read the [API Reference](./docs/Reference.md) and [Guide](./docs/Guide
6363
### Common issues
6464

6565
- If you're getting `Invariant Violation: Native component for "RNCWebView does not exist"` it likely means you forgot to run `react-native link` or there was some error with the linking process
66-
- If you encounter a build error during the task `:app:mergeDexRelease`, you need to enable multidex support in `android/app/build.gradle` as discussed in [this issue](https://github.com/react-native-webview/react-native-webview/issues/1344#issuecomment-650544648)
66+
- If you encounter a build error during the task `:app:mergeDexRelease`, you need to enable multidex support in `android/app/build.gradle` as discussed in [this issue](https://github.com/MetaMask/react-native-webview-mm/issues/1344#issuecomment-650544648)
6767

6868
#### Contributing
6969

70-
Contributions are welcome, see [Contributing.md](https://github.com/react-native-webview/react-native-webview/blob/master/docs/Contributing.md)
70+
Contributions are welcome, see [Contributing.md](https://github.com/MetaMask/react-native-webview-mm/blob/master/docs/Contributing.md)
7171

7272
### License
7373

android/src/main/java/com/reactnativecommunity/webview/RNCWebChromeClient.java

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,25 @@
33
import android.Manifest;
44
import android.annotation.TargetApi;
55
import android.app.Activity;
6+
import android.app.AlertDialog;
7+
import android.content.DialogInterface;
68
import android.content.pm.PackageManager;
79
import android.net.Uri;
810
import android.os.Build;
11+
import android.os.Handler;
912
import android.os.Message;
1013
import android.view.Gravity;
1114
import android.view.View;
1215
import android.view.ViewGroup;
1316
import android.webkit.ConsoleMessage;
1417
import android.webkit.GeolocationPermissions;
18+
import android.webkit.JsPromptResult;
1519
import android.webkit.PermissionRequest;
1620
import android.webkit.ValueCallback;
1721
import android.webkit.WebChromeClient;
1822
import android.webkit.WebView;
1923
import android.webkit.WebViewClient;
24+
import android.widget.Button;
2025
import android.widget.FrameLayout;
2126

2227
import androidx.annotation.RequiresApi;
@@ -55,6 +60,9 @@ public class RNCWebChromeClient extends WebChromeClient implements LifecycleEven
5560
protected View mVideoView;
5661
protected WebChromeClient.CustomViewCallback mCustomViewCallback;
5762

63+
// This boolean block JS prompts and alerts from displaying during loading
64+
protected boolean blockJsDuringLoading = true;
65+
5866
/*
5967
* - Permissions -
6068
* As native permissions are asynchronously handled by the PermissionListener, many fields have
@@ -150,11 +158,14 @@ public void onPermissionRequest(final PermissionRequest request) {
150158
ArrayList<String> requestedAndroidPermissions = new ArrayList<>();
151159
for (String requestedResource : request.getResources()) {
152160
String androidPermission = null;
161+
String requestPermissionIdentifier = null;
153162

154163
if (requestedResource.equals(PermissionRequest.RESOURCE_AUDIO_CAPTURE)) {
155164
androidPermission = Manifest.permission.RECORD_AUDIO;
165+
requestPermissionIdentifier = "microphone";
156166
} else if (requestedResource.equals(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) {
157167
androidPermission = Manifest.permission.CAMERA;
168+
requestPermissionIdentifier = "camera";
158169
} else if(requestedResource.equals(PermissionRequest.RESOURCE_PROTECTED_MEDIA_ID)) {
159170
if (mAllowsProtectedMedia) {
160171
grantedPermissions.add(requestedResource);
@@ -168,10 +179,30 @@ public void onPermissionRequest(final PermissionRequest request) {
168179
*/
169180
androidPermission = PermissionRequest.RESOURCE_PROTECTED_MEDIA_ID;
170181
} }
182+
Uri originUri = request.getOrigin();
183+
String host = originUri.getHost();
171184
// TODO: RESOURCE_MIDI_SYSEX, RESOURCE_PROTECTED_MEDIA_ID.
185+
String alertMessage = String.format("Allow " + host + " to use your " + requestPermissionIdentifier + "?");
172186
if (androidPermission != null) {
173187
if (ContextCompat.checkSelfPermission(this.mWebView.getThemedReactContext(), androidPermission) == PackageManager.PERMISSION_GRANTED) {
174-
grantedPermissions.add(requestedResource);
188+
AlertDialog.Builder builder = new AlertDialog.Builder(this.mWebView.getContext());
189+
builder.setMessage(alertMessage);
190+
builder.setCancelable(false);
191+
String finalAndroidPermission = androidPermission;
192+
builder.setPositiveButton("Allow", (dialog, which) -> {
193+
permissionRequest = request;
194+
grantedPermissions.add(finalAndroidPermission);
195+
requestPermissions(grantedPermissions);
196+
});
197+
builder.setNegativeButton("Don't allow", (dialog, which) -> {
198+
request.deny();
199+
});
200+
AlertDialog alertDialog = builder.create();
201+
alertDialog.show();
202+
//Delay making `allow` clickable for 500ms to avoid unwanted presses.
203+
Button posButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
204+
posButton.setEnabled(false);
205+
this.runDelayed(() -> posButton.setEnabled(true), 500);
175206
} else {
176207
requestedAndroidPermissions.add(androidPermission);
177208
}
@@ -180,8 +211,10 @@ public void onPermissionRequest(final PermissionRequest request) {
180211

181212
// If all the permissions are already granted, send the response to the WebView synchronously
182213
if (requestedAndroidPermissions.isEmpty()) {
183-
request.grant(grantedPermissions.toArray(new String[0]));
184-
grantedPermissions = null;
214+
if (!grantedPermissions.isEmpty()) {
215+
request.grant(grantedPermissions.toArray(new String[0]));
216+
grantedPermissions = null;
217+
}
185218
return;
186219
}
187220

@@ -192,6 +225,10 @@ public void onPermissionRequest(final PermissionRequest request) {
192225
requestPermissions(requestedAndroidPermissions);
193226
}
194227

228+
private void runDelayed(Runnable function, long delayMillis) {
229+
Handler handler = new Handler();
230+
handler.postDelayed(function, delayMillis);
231+
}
195232

196233
@Override
197234
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
@@ -208,7 +245,23 @@ public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermiss
208245
requestPermissions(Collections.singletonList(Manifest.permission.ACCESS_FINE_LOCATION));
209246

210247
} else {
211-
callback.invoke(origin, true, false);
248+
String alertMessage = String.format("Allow %s to use your location?", origin);
249+
AlertDialog.Builder builder = new AlertDialog.Builder(this.mWebView.getContext());
250+
builder.setMessage(alertMessage);
251+
builder.setCancelable(false);
252+
builder.setPositiveButton("Allow", (dialog, which) -> {
253+
callback.invoke(origin, true, false);
254+
});
255+
builder.setNegativeButton("Don't allow", (dialog, which) -> {
256+
callback.invoke(origin, false, false);
257+
});
258+
AlertDialog alertDialog = builder.create();
259+
alertDialog.show();
260+
//Delay making `allow` clickable for 500ms to avoid unwanted presses.
261+
Button posButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
262+
posButton.setEnabled(false);
263+
this.runDelayed(() -> posButton.setEnabled(true), 500);
264+
212265
}
213266
}
214267

@@ -336,6 +389,16 @@ public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathC
336389
return this.mWebView.getThemedReactContext().getNativeModule(RNCWebViewModule.class).startPhotoPickerIntent(filePathCallback, acceptTypes, allowMultiple, fileChooserParams.isCaptureEnabled());
337390
}
338391

392+
@Override
393+
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
394+
if (blockJsDuringLoading) {
395+
result.cancel();
396+
return true;
397+
} else {
398+
return super.onJsPrompt(view, url, message, defaultValue, result);
399+
}
400+
}
401+
339402
@Override
340403
public void onHostResume() {
341404
if (mVideoView != null && mVideoView.getSystemUiVisibility() != FULLSCREEN_SYSTEM_UI_VISIBILITY) {

0 commit comments

Comments
 (0)