Skip to content

Commit 2063c31

Browse files
author
hunghd
committed
enable Javascript on iOS, support abort loading specific URL(s) by using regex
1 parent 065524a commit 2063c31

File tree

8 files changed

+128
-10
lines changed

8 files changed

+128
-10
lines changed

android/src/main/java/com/flutter_webview_plugin/BrowserClient.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,42 @@
11
package com.flutter_webview_plugin;
22

3+
import android.annotation.TargetApi;
34
import android.graphics.Bitmap;
5+
import android.os.Build;
46
import android.webkit.WebResourceRequest;
57
import android.webkit.WebResourceResponse;
68
import android.webkit.WebView;
79
import android.webkit.WebViewClient;
810

911
import java.util.HashMap;
1012
import java.util.Map;
13+
import java.util.regex.Matcher;
14+
import java.util.regex.Pattern;
1115

1216
/**
1317
* Created by lejard_h on 20/12/2017.
1418
*/
1519

1620
public class BrowserClient extends WebViewClient {
21+
private Pattern invalidUrlPattern = null;
22+
1723
public BrowserClient() {
24+
this(null);
25+
}
26+
27+
public BrowserClient(String invalidUrlRegex) {
1828
super();
29+
if (invalidUrlRegex != null) {
30+
invalidUrlPattern = Pattern.compile(invalidUrlRegex);
31+
}
32+
}
33+
34+
public void updateInvalidUrlRegex(String invalidUrlRegex) {
35+
if (invalidUrlRegex != null) {
36+
invalidUrlPattern = Pattern.compile(invalidUrlRegex);
37+
} else {
38+
invalidUrlPattern = null;
39+
}
1940
}
2041

2142
@Override
@@ -40,6 +61,35 @@ public void onPageFinished(WebView view, String url) {
4061

4162
}
4263

64+
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
65+
@Override
66+
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
67+
// returning true causes the current WebView to abort loading the URL,
68+
// while returning false causes the WebView to continue loading the URL as usual.
69+
String url = request.getUrl().toString();
70+
boolean isInvalid = checkInvalidUrl(url);
71+
Map<String, Object> data = new HashMap<>();
72+
data.put("url", url);
73+
data.put("type", isInvalid ? "abortLoad" : "shouldStart");
74+
75+
FlutterWebviewPlugin.channel.invokeMethod("onState", data);
76+
return isInvalid;
77+
}
78+
79+
@Override
80+
public boolean shouldOverrideUrlLoading(WebView view, String url) {
81+
// returning true causes the current WebView to abort loading the URL,
82+
// while returning false causes the WebView to continue loading the URL as usual.
83+
boolean isInvalid = checkInvalidUrl(url);
84+
Map<String, Object> data = new HashMap<>();
85+
data.put("url", url);
86+
data.put("type", isInvalid ? "abortLoad" : "shouldStart");
87+
88+
FlutterWebviewPlugin.channel.invokeMethod("onState", data);
89+
return isInvalid;
90+
}
91+
92+
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
4393
@Override
4494
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
4595
super.onReceivedHttpError(view, request, errorResponse);
@@ -48,4 +98,22 @@ public void onReceivedHttpError(WebView view, WebResourceRequest request, WebRes
4898
data.put("code", Integer.toString(errorResponse.getStatusCode()));
4999
FlutterWebviewPlugin.channel.invokeMethod("onHttpError", data);
50100
}
101+
102+
@Override
103+
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
104+
super.onReceivedError(view, errorCode, description, failingUrl);
105+
Map<String, Object> data = new HashMap<>();
106+
data.put("url", failingUrl);
107+
data.put("code", errorCode);
108+
FlutterWebviewPlugin.channel.invokeMethod("onHttpError", data);
109+
}
110+
111+
private boolean checkInvalidUrl(String url) {
112+
if (invalidUrlPattern == null) {
113+
return false;
114+
} else {
115+
Matcher matcher = invalidUrlPattern.matcher(url);
116+
return matcher.lookingAt();
117+
}
118+
}
51119
}

android/src/main/java/com/flutter_webview_plugin/FlutterWebviewPlugin.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ private void openUrl(MethodCall call, MethodChannel.Result result) {
9797
Map<String, String> headers = call.argument("headers");
9898
boolean scrollBar = call.argument("scrollBar");
9999
boolean allowFileURLs = call.argument("allowFileURLs");
100+
String invalidUrlRegex = call.argument("invalidUrlRegex");
100101

101102
if (webViewManager == null || webViewManager.closed == true) {
102103
webViewManager = new WebviewManager(activity);
@@ -118,7 +119,8 @@ private void openUrl(MethodCall call, MethodChannel.Result result) {
118119
scrollBar,
119120
supportMultipleWindows,
120121
appCacheEnabled,
121-
allowFileURLs
122+
allowFileURLs,
123+
invalidUrlRegex
122124
);
123125
result.success(null);
124126
}

android/src/main/java/com/flutter_webview_plugin/WebviewManager.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import android.content.Intent;
44
import android.net.Uri;
5-
import android.util.Log;
65
import android.annotation.TargetApi;
76
import android.app.Activity;
87
import android.os.Build;
@@ -14,7 +13,6 @@
1413
import android.webkit.WebChromeClient;
1514
import android.webkit.WebSettings;
1615
import android.webkit.WebView;
17-
import android.webkit.WebViewClient;
1816
import android.widget.FrameLayout;
1917

2018
import java.util.HashMap;
@@ -74,13 +72,14 @@ public boolean handleResult(int requestCode, int resultCode, Intent intent){
7472
boolean closed = false;
7573
WebView webView;
7674
Activity activity;
75+
BrowserClient webViewClient;
7776
ResultHandler resultHandler;
7877

7978
WebviewManager(final Activity activity) {
8079
this.webView = new ObservableWebView(activity);
8180
this.activity = activity;
8281
this.resultHandler = new ResultHandler();
83-
WebViewClient webViewClient = new BrowserClient();
82+
webViewClient = new BrowserClient();
8483
webView.setOnKeyListener(new View.OnKeyListener() {
8584
@Override
8685
public boolean onKey(View v, int keyCode, KeyEvent event) {
@@ -204,7 +203,8 @@ void openUrl(
204203
boolean scrollBar,
205204
boolean supportMultipleWindows,
206205
boolean appCacheEnabled,
207-
boolean allowFileURLs
206+
boolean allowFileURLs,
207+
String invalidUrlRegex
208208
) {
209209
webView.getSettings().setJavaScriptEnabled(withJavascript);
210210
webView.getSettings().setBuiltInZoomControls(withZoom);
@@ -219,6 +219,8 @@ void openUrl(
219219
webView.getSettings().setAllowFileAccessFromFileURLs(allowFileURLs);
220220
webView.getSettings().setAllowUniversalAccessFromFileURLs(allowFileURLs);
221221

222+
webViewClient.updateInvalidUrlRegex(invalidUrlRegex);
223+
222224
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
223225
webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
224226
}

example/android/app/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ android {
2525
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
2626
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
2727
applicationId "com.yourcompany.flutter_webview_plugin_example"
28+
minSdkVersion 16
2829
}
2930

3031
buildTypes {

example/lib/main.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ class _MyHomePageState extends State<MyHomePage> {
201201
selectedUrl,
202202
rect: Rect.fromLTWH(0.0, 0.0, MediaQuery.of(context).size.width, 300.0),
203203
userAgent: kAndroidUserAgent,
204+
invalidUrlRegex: r'^(https).+(twitter)',
204205
);
205206
},
206207
child: const Text('Open Webview (rect)'),

ios/Classes/FlutterWebviewPlugin.m

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
@interface FlutterWebviewPlugin() <WKNavigationDelegate, UIScrollViewDelegate> {
77
BOOL _enableAppScheme;
88
BOOL _enableZoom;
9+
NSString* _invalidUrlRegex;
910
}
1011
@end
1112

@@ -83,6 +84,8 @@ - (void)initWebview:(FlutterMethodCall*)call {
8384
NSString *userAgent = call.arguments[@"userAgent"];
8485
NSNumber *withZoom = call.arguments[@"withZoom"];
8586
NSNumber *scrollBar = call.arguments[@"scrollBar"];
87+
NSNumber *withJavascript = call.arguments[@"withJavascript"];
88+
_invalidUrlRegex = call.arguments[@"invalidUrlRegex"];
8689

8790
if (clearCache != (id)[NSNull null] && [clearCache boolValue]) {
8891
[[NSURLCache sharedURLCache] removeAllCachedResponses];
@@ -111,6 +114,13 @@ - (void)initWebview:(FlutterMethodCall*)call {
111114
self.webview.scrollView.showsHorizontalScrollIndicator = [scrollBar boolValue];
112115
self.webview.scrollView.showsVerticalScrollIndicator = [scrollBar boolValue];
113116

117+
WKPreferences* preferences = [[self.webview configuration] preferences];
118+
if ([withJavascript boolValue]) {
119+
[preferences setJavaScriptEnabled:YES];
120+
} else {
121+
[preferences setJavaScriptEnabled:NO];
122+
}
123+
114124
_enableZoom = [withZoom boolValue];
115125

116126
[self.viewController.view addSubview:self.webview];
@@ -234,18 +244,37 @@ - (void)cleanCookies {
234244
}];
235245
}
236246

247+
- (bool)checkInvalidUrl:(NSURL*)url {
248+
NSString* urlString = url != nil ? [url absoluteString] : nil;
249+
if (_invalidUrlRegex && urlString) {
250+
NSError* error = NULL;
251+
NSRegularExpression* regex =
252+
[NSRegularExpression regularExpressionWithPattern:_invalidUrlRegex
253+
options:NSRegularExpressionCaseInsensitive
254+
error:&error];
255+
NSTextCheckingResult* match = [regex firstMatchInString:urlString
256+
options:0
257+
range:NSMakeRange(0, [urlString length])];
258+
return match != nil;
259+
} else {
260+
return false;
261+
}
262+
}
263+
237264
#pragma mark -- WkWebView Delegate
238265
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
239266
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
240267

268+
BOOL isInvalid = [self checkInvalidUrl: navigationAction.request.URL];
269+
241270
id data = @{@"url": navigationAction.request.URL.absoluteString,
242-
@"type": @"shouldStart",
271+
@"type": isInvalid ? @"abortLoad" : @"shouldStart",
243272
@"navigationType": [NSNumber numberWithInt:navigationAction.navigationType]};
244273
[channel invokeMethod:@"onState" arguments:data];
245274

246275
if (navigationAction.navigationType == WKNavigationTypeBackForward) {
247276
[channel invokeMethod:@"onBackPressed" arguments:nil];
248-
} else {
277+
} else if (!isInvalid) {
249278
id data = @{@"url": navigationAction.request.URL.absoluteString};
250279
[channel invokeMethod:@"onUrlChanged" arguments:data];
251280
}
@@ -254,7 +283,11 @@ - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigati
254283
([webView.URL.scheme isEqualToString:@"http"] ||
255284
[webView.URL.scheme isEqualToString:@"https"] ||
256285
[webView.URL.scheme isEqualToString:@"about"])) {
257-
decisionHandler(WKNavigationActionPolicyAllow);
286+
if (isInvalid) {
287+
decisionHandler(WKNavigationActionPolicyCancel);
288+
} else {
289+
decisionHandler(WKNavigationActionPolicyAllow);
290+
}
258291
} else {
259292
decisionHandler(WKNavigationActionPolicyCancel);
260293
}

lib/src/base.dart

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import 'package:flutter/services.dart';
77
const _kChannel = 'flutter_webview_plugin';
88

99
// TODO: more general state for iOS/android
10-
enum WebViewState { shouldStart, startLoad, finishLoad }
10+
enum WebViewState { shouldStart, startLoad, finishLoad, abortLoad }
1111

1212
// TODO: use an id by webview to be able to manage multiple webview
1313

@@ -79,7 +79,6 @@ class FlutterWebviewPlugin {
7979
/// Start the Webview with [url]
8080
/// - [headers] specify additional HTTP headers
8181
/// - [withJavascript] enable Javascript or not for the Webview
82-
/// iOS WebView: Not implemented yet
8382
/// - [clearCache] clear the cache of the Webview
8483
/// - [clearCookies] clear all cookies of the Webview
8584
/// - [hidden] not show
@@ -94,6 +93,10 @@ class FlutterWebviewPlugin {
9493
/// - [withLocalUrl]: allow url as a local path
9594
/// Allow local files on iOs > 9.0
9695
/// - [scrollBar]: enable or disable scrollbar
96+
/// - [supportMultipleWindows] enable multiple windows support in Android
97+
/// - [invalidUrlRegex] is the regular expression of URLs that web view shouldn't load.
98+
/// For example, when web view is redirected to a specific URL, you want to intercept
99+
/// this process by stopping loading this URL and replacing web view by another screen.
97100
Future<Null> launch(String url, {
98101
Map<String, String> headers,
99102
bool withJavascript,
@@ -110,6 +113,7 @@ class FlutterWebviewPlugin {
110113
bool supportMultipleWindows,
111114
bool appCacheEnabled,
112115
bool allowFileURLs,
116+
String invalidUrlRegex,
113117
}) async {
114118
final args = <String, dynamic>{
115119
'url': url,
@@ -126,6 +130,7 @@ class FlutterWebviewPlugin {
126130
'supportMultipleWindows': supportMultipleWindows ?? false,
127131
'appCacheEnabled': appCacheEnabled ?? false,
128132
'allowFileURLs': allowFileURLs ?? false,
133+
'invalidUrlRegex': invalidUrlRegex,
129134
};
130135

131136
if (headers != null) {
@@ -233,6 +238,9 @@ class WebViewStateChanged {
233238
case 'finishLoad':
234239
t = WebViewState.finishLoad;
235240
break;
241+
case 'abortLoad':
242+
t = WebViewState.abortLoad;
243+
break;
236244
}
237245
return WebViewStateChanged(t, map['url'], map['navigationType']);
238246
}

lib/src/webview_scaffold.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class WebviewScaffold extends StatefulWidget {
3030
this.hidden = false,
3131
this.initialChild,
3232
this.allowFileURLs,
33+
this.invalidUrlRegex,
3334
}) : super(key: key);
3435

3536
final PreferredSizeWidget appBar;
@@ -52,6 +53,7 @@ class WebviewScaffold extends StatefulWidget {
5253
final bool hidden;
5354
final Widget initialChild;
5455
final bool allowFileURLs;
56+
final String invalidUrlRegex;
5557

5658
@override
5759
_WebviewScaffoldState createState() => _WebviewScaffoldState();
@@ -115,6 +117,7 @@ class _WebviewScaffoldState extends State<WebviewScaffold> {
115117
supportMultipleWindows: widget.supportMultipleWindows,
116118
appCacheEnabled: widget.appCacheEnabled,
117119
allowFileURLs: widget.allowFileURLs,
120+
invalidUrlRegex: widget.invalidUrlRegex,
118121
);
119122
} else {
120123
if (_rect != value) {

0 commit comments

Comments
 (0)