Skip to content

Commit

Permalink
[webview] Upgrade react native webview to 11.26.0
Browse files Browse the repository at this point in the history
  • Loading branch information
aleqsio committed Jan 24, 2023
1 parent 4aef3ca commit d1c7eb9
Show file tree
Hide file tree
Showing 11 changed files with 272 additions and 39 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -7,6 +7,7 @@ Package-specific changes not released in any SDK will be added here just before

### 📚 3rd party library updates

- Updated `react-native-webview` from `11.23.1` to `11.26.0`. ([#20933](https://github.com/expo/expo/pull/20933) by [@aleqsio](https://github.com/aleqsio))
- Updated `react-native-shared-element` from `0.8.4` to `0.8.7`. ([#20593](https://github.com/expo/expo/pull/20593) by [@ijzerenhein](https://github.com/ijzerenhein))
- Updated `@react-native-async-storage/async-storage` from `1.17.3' to `1.17.11`. ([#20780](https://github.com/expo/expo/pull/20780) by [@kudo](https://github.com/kudo))
- Updated `react-native-reanimated` from `2.12.0` to `2.14.0`. ([#20798](https://github.com/expo/expo/pull/20798) by [@kudo](https://github.com/kudo))
Expand Down
Expand Up @@ -33,7 +33,6 @@
import android.webkit.RenderProcessGoneDetail;
import android.webkit.SslErrorHandler;
import android.webkit.PermissionRequest;
import android.webkit.URLUtil;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
Expand Down Expand Up @@ -161,6 +160,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {

protected RNCWebChromeClient mWebChromeClient = null;
protected boolean mAllowsFullscreenVideo = false;
protected boolean mAllowsProtectedMedia = false;
protected @Nullable String mUserAgent = null;
protected @Nullable String mUserAgentWithApplicationName = null;
protected @Nullable String mDownloadingMessage = null;
Expand Down Expand Up @@ -313,14 +313,7 @@ public void setLackPermissionToDownlaodMessage(WebView view, String message) {

@ReactProp(name = "cacheEnabled")
public void setCacheEnabled(WebView view, boolean enabled) {
if (enabled) {
Context ctx = view.getContext();
if (ctx != null) {
view.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
}
} else {
view.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
}
view.getSettings().setCacheMode(enabled ? WebSettings.LOAD_DEFAULT : WebSettings.LOAD_NO_CACHE);
}

@ReactProp(name = "cacheMode")
Expand Down Expand Up @@ -677,6 +670,20 @@ public void setMinimumFontSize(WebView view, int fontSize) {
view.getSettings().setMinimumFontSize(fontSize);
}

@ReactProp(name = "allowsProtectedMedia")
public void setAllowsProtectedMedia(WebView view, boolean enabled) {
// This variable is used to keep consistency
// in case a new WebChromeClient is created
// (eg. when mAllowsFullScreenVideo changes)
mAllowsProtectedMedia = enabled;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WebChromeClient client = view.getWebChromeClient();
if (client != null && client instanceof RNCWebChromeClient) {
((RNCWebChromeClient) client).setAllowsProtectedMedia(enabled);
}
}
}

@Override
protected void addEventEmitters(ThemedReactContext reactContext, WebView view) {
// Do not register default touch emitter and let WebView implementation handle touches
Expand Down Expand Up @@ -882,8 +889,6 @@ public void onHideCustomView() {
mReactContext.removeLifecycleEventListener(this);
}
};

webView.setWebChromeClient(mWebChromeClient);
} else {
if (mWebChromeClient != null) {
mWebChromeClient.onHideCustomView();
Expand All @@ -895,9 +900,9 @@ public Bitmap getDefaultVideoPoster() {
return Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888);
}
};

webView.setWebChromeClient(mWebChromeClient);
}
mWebChromeClient.setAllowsProtectedMedia(mAllowsProtectedMedia);
webView.setWebChromeClient(mWebChromeClient);
}

protected static class RNCWebViewClient extends WebViewClient {
Expand Down Expand Up @@ -1234,6 +1239,9 @@ protected static class RNCWebChromeClient extends WebChromeClient implements Lif

protected RNCWebView.ProgressChangedFilter progressChangedFilter = null;

// True if protected media should be allowed, false otherwise
protected boolean mAllowsProtectedMedia = false;

public RNCWebChromeClient(ReactContext reactContext, WebView webView) {
this.mReactContext = reactContext;
this.mWebView = webView;
Expand Down Expand Up @@ -1295,9 +1303,20 @@ public void onPermissionRequest(final PermissionRequest request) {
} else if (requestedResource.equals(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) {
androidPermission = Manifest.permission.CAMERA;
} else if(requestedResource.equals(PermissionRequest.RESOURCE_PROTECTED_MEDIA_ID)) {
androidPermission = PermissionRequest.RESOURCE_PROTECTED_MEDIA_ID;
if (mAllowsProtectedMedia) {
grantedPermissions.add(requestedResource);
} else {
/**
* Legacy handling (Kept in case it was working under some conditions (given Android version or something))
*
* Try to ask user to grant permission using Activity.requestPermissions
*
* Find more details here: https://github.com/react-native-webview/react-native-webview/pull/2732
*/
androidPermission = PermissionRequest.RESOURCE_PROTECTED_MEDIA_ID;
}
}
// TODO: RESOURCE_MIDI_SYSEX, RESOURCE_PROTECTED_MEDIA_ID.
// TODO: RESOURCE_MIDI_SYSEX.

if (androidPermission != null) {
if (ContextCompat.checkSelfPermission(mReactContext, androidPermission) == PackageManager.PERMISSION_GRANTED) {
Expand Down Expand Up @@ -1487,6 +1506,15 @@ protected ViewGroup getRootView() {
public void setProgressChangedFilter(RNCWebView.ProgressChangedFilter filter) {
progressChangedFilter = filter;
}

/**
* Set whether or not protected media should be allowed
* /!\ Setting this to false won't revoke permission already granted to the current webpage.
* In order to do so, you'd need to reload the page /!\
*/
public void setAllowsProtectedMedia(boolean enabled) {
mAllowsProtectedMedia = enabled;
}
}

/**
Expand Down
@@ -0,0 +1,179 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/*
* The source code is obtained from the Android SDK Sources (API level 31),
* and modified by UNIDY2002 <UNIDY2002@outlook.com>.
*
* Change list:
* - Remove all unused class members except guessFileName,
* CONTENT_DISPOSITION_PATTERN and parseContentDisposition
* - Improve CONTENT_DISPOSITION_PATTERN and parseContentDisposition to add
* support for the "filename*" parameter in content disposition
*/

package versioned.host.exp.exponent.modules.api.components.webview;

import android.net.Uri;
import android.webkit.MimeTypeMap;
import androidx.annotation.Nullable;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class URLUtil {
/**
* Guesses canonical filename that a download would have, using
* the URL and contentDisposition. File extension, if not defined,
* is added based on the mimetype
* @param url Url to the content
* @param contentDisposition Content-Disposition HTTP header or {@code null}
* @param mimeType Mime-type of the content or {@code null}
*
* @return suggested filename
*/
public static final String guessFileName(
String url,
@Nullable String contentDisposition,
@Nullable String mimeType) {
String filename = null;
String extension = null;

// If we couldn't do anything with the hint, move toward the content disposition
if (filename == null && contentDisposition != null) {
filename = parseContentDisposition(contentDisposition);
if (filename != null) {
int index = filename.lastIndexOf('/') + 1;
if (index > 0) {
filename = filename.substring(index);
}
}
}

// If all the other http-related approaches failed, use the plain uri
if (filename == null) {
String decodedUrl = Uri.decode(url);
if (decodedUrl != null) {
int queryIndex = decodedUrl.indexOf('?');
// If there is a query string strip it, same as desktop browsers
if (queryIndex > 0) {
decodedUrl = decodedUrl.substring(0, queryIndex);
}
if (!decodedUrl.endsWith("/")) {
int index = decodedUrl.lastIndexOf('/') + 1;
if (index > 0) {
filename = decodedUrl.substring(index);
}
}
}
}

// Finally, if couldn't get filename from URI, get a generic filename
if (filename == null) {
filename = "downloadfile";
}

// Split filename between base and extension
// Add an extension if filename does not have one
int dotIndex = filename.indexOf('.');
if (dotIndex < 0) {
if (mimeType != null) {
extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
if (extension != null) {
extension = "." + extension;
}
}
if (extension == null) {
if (mimeType != null && mimeType.toLowerCase(Locale.ROOT).startsWith("text/")) {
if (mimeType.equalsIgnoreCase("text/html")) {
extension = ".html";
} else {
extension = ".txt";
}
} else {
extension = ".bin";
}
}
} else {
if (mimeType != null) {
// Compare the last segment of the extension against the mime type.
// If there's a mismatch, discard the entire extension.
int lastDotIndex = filename.lastIndexOf('.');
String typeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
filename.substring(lastDotIndex + 1));
if (typeFromExt != null && !typeFromExt.equalsIgnoreCase(mimeType)) {
extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
if (extension != null) {
extension = "." + extension;
}
}
}
if (extension == null) {
extension = filename.substring(dotIndex);
}
filename = filename.substring(0, dotIndex);
}

return filename + extension;
}

/** Regex used to parse content-disposition headers */
private static final Pattern CONTENT_DISPOSITION_PATTERN =
Pattern.compile("attachment(?:;\\s*filename\\s*=\\s*(\"?)([^\"]*)\\1)?(?:;\\s*filename\\s*\\*\\s*=\\s*([^']*)'[^']*'([^']*))?\\s*$",
Pattern.CASE_INSENSITIVE);

/**
* Parse the Content-Disposition HTTP Header. The format of the header
* is defined here: <a href="https://www.rfc-editor.org/rfc/rfc6266">RFC 6266</a>
* This header provides a filename for content that is going to be
* downloaded to the file system. We only support the attachment type.
*/
static String parseContentDisposition(String contentDisposition) {
try {
// The regex attempts to match the following pattern:
// attachment; filename="(Group 2)"; filename*=(Group 3)'(lang)'(Group 4)
// Group 4 refers to the percent-encoded filename, and the charset
// is specified in Group 3.
// Group 2 is the fallback filename.
// Group 1 refers to the quotation marks around Group 2.
//
// Test cases can be found at http://test.greenbytes.de/tech/tc2231/
// Examples can be found at https://www.rfc-editor.org/rfc/rfc6266#section-5
// There are a few known limitations:
// - any Content Disposition value that does not have parameters
// arranged in the order of "attachment...filename...filename*"
// or contains extra parameters shall fail to be parsed
// - any filename that contains " shall fail to be parsed
Matcher m = CONTENT_DISPOSITION_PATTERN.matcher(contentDisposition);
if (m.find()) {
if (m.group(3) != null && m.group(4) != null) {
try {
return URLDecoder.decode(m.group(4), m.group(3).isEmpty() ? "UTF-8" : m.group(3));
} catch (UnsupportedEncodingException e) {
// Skip the ext-parameter as the encoding is unsupported
}
}
return m.group(2);
}
} catch (IllegalStateException ex) {
// This function is defined as returning null when it can't parse the header
}
return null;
}
}
4 changes: 2 additions & 2 deletions apps/bare-expo/ios/Podfile.lock
Expand Up @@ -564,7 +564,7 @@ PODS:
- React-Core
- react-native-view-shot (3.4.0):
- React-Core
- react-native-webview (11.23.1):
- react-native-webview (11.26.0):
- React-Core
- React-perflogger (0.71.0)
- React-RCTActionSheet (0.71.0):
Expand Down Expand Up @@ -1298,7 +1298,7 @@ SPEC CHECKSUMS:
react-native-segmented-control: 06607462630512ff8eef652ec560e6235a30cc3e
react-native-slider: cecabb58ecffad671d2ad3ccc58c7f4d2d029e95
react-native-view-shot: a60a98a18c72bcaaaf2138f9aab960ae9b0d96c7
react-native-webview: d33e2db8925d090871ffeb232dfa50cb3a727581
react-native-webview: 994b9f8fbb504d6314dc40d83f94f27c6831b3bf
React-perflogger: e5fc4149e9bbb972b8520277f3b23141faa47a36
React-RCTActionSheet: 991de88216bf03ab9bb1d213d73c62ecbe64ade7
React-RCTAnimation: b74e3d1bf5280891a573e447b487fa1db0713b5b
Expand Down
2 changes: 1 addition & 1 deletion apps/bare-expo/package.json
Expand Up @@ -90,7 +90,7 @@
"react-native-shared-element": "0.8.7",
"react-native-svg": "13.4.0",
"react-native-view-shot": "3.5.0",
"react-native-webview": "11.23.1",
"react-native-webview": "11.26.0",
"test-suite": "*"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion apps/native-component-list/package.json
Expand Up @@ -153,7 +153,7 @@
"react-native-svg": "13.4.0",
"react-native-view-shot": "3.5.0",
"react-native-web": "~0.18.10",
"react-native-webview": "11.23.1",
"react-native-webview": "11.26.0",
"react-navigation": "^4.4.0",
"react-navigation-shared-element": "^3.1.2",
"regl": "^1.3.0",
Expand Down
4 changes: 2 additions & 2 deletions ios/Podfile.lock
Expand Up @@ -2009,7 +2009,7 @@ PODS:
- React-Core
- react-native-slider (4.2.4):
- React-Core
- react-native-webview (11.23.1):
- react-native-webview (11.26.0):
- React-Core
- React-perflogger (0.71.0)
- React-RCTActionSheet (0.71.0):
Expand Down Expand Up @@ -3624,7 +3624,7 @@ SPEC CHECKSUMS:
react-native-segmented-control: 06607462630512ff8eef652ec560e6235a30cc3e
react-native-skia: 571e20d8f275a305d5d30f17d4329862b852d65e
react-native-slider: cecabb58ecffad671d2ad3ccc58c7f4d2d029e95
react-native-webview: d33e2db8925d090871ffeb232dfa50cb3a727581
react-native-webview: 994b9f8fbb504d6314dc40d83f94f27c6831b3bf
React-perflogger: e5fc4149e9bbb972b8520277f3b23141faa47a36
React-RCTActionSheet: 991de88216bf03ab9bb1d213d73c62ecbe64ade7
React-RCTAnimation: b74e3d1bf5280891a573e447b487fa1db0713b5b
Expand Down

0 comments on commit d1c7eb9

Please sign in to comment.