Skip to content

Commit a4d3d16

Browse files
[webview_flutter_android] Adds a WebViewFlutterApi (flutter#3324)
[webview_flutter_android] Adds a WebViewFlutterApi
1 parent 9bfef95 commit a4d3d16

File tree

14 files changed

+271
-56
lines changed

14 files changed

+271
-56
lines changed

packages/webview_flutter/webview_flutter_android/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 3.4.1
2+
3+
* Fixes a potential bug where a `WebView` that was not added to the `InstanceManager` could be
4+
returned by a `WebViewClient` or `WebChromeClient`.
5+
16
## 3.4.0
27

38
* Adds support to set text zoom of a page. See `AndroidWebViewController.setTextZoom`.

packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/FileChooserParamsFlutterApiImpl.java

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,16 @@ private static GeneratedAndroidWebView.FileChooserModeEnumData toFileChooserEnum
5757
/**
5858
* Stores the FileChooserParams instance and notifies Dart to create a new FileChooserParams
5959
* instance that is attached to this one.
60-
*
61-
* @return the instanceId of the stored instance
6260
*/
63-
public long create(WebChromeClient.FileChooserParams instance, Reply<Void> callback) {
64-
final long instanceId = instanceManager.addHostCreatedInstance(instance);
65-
create(
66-
instanceId,
67-
instance.isCaptureEnabled(),
68-
Arrays.asList(instance.getAcceptTypes()),
69-
toFileChooserEnumData(instance.getMode()),
70-
instance.getFilenameHint(),
71-
callback);
72-
return instanceId;
61+
public void create(WebChromeClient.FileChooserParams instance, Reply<Void> callback) {
62+
if (!instanceManager.containsInstance(instance)) {
63+
create(
64+
instanceManager.addHostCreatedInstance(instance),
65+
instance.isCaptureEnabled(),
66+
Arrays.asList(instance.getAcceptTypes()),
67+
toFileChooserEnumData(instance.getMode()),
68+
instance.getFilenameHint(),
69+
callback);
70+
}
7371
}
7472
}

packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1574,6 +1574,42 @@ public void error(Throwable error) {
15741574
}
15751575
}
15761576
}
1577+
/**
1578+
* Flutter API for `WebView`.
1579+
*
1580+
* <p>This class may handle instantiating and adding Dart instances that are attached to a native
1581+
* instance or receiving callback methods from an overridden native class.
1582+
*
1583+
* <p>See https://developer.android.com/reference/android/webkit/WebView.
1584+
*
1585+
* <p>Generated class from Pigeon that represents Flutter messages that can be called from Java.
1586+
*/
1587+
public static class WebViewFlutterApi {
1588+
private final BinaryMessenger binaryMessenger;
1589+
1590+
public WebViewFlutterApi(BinaryMessenger argBinaryMessenger) {
1591+
this.binaryMessenger = argBinaryMessenger;
1592+
}
1593+
1594+
public interface Reply<T> {
1595+
void reply(T reply);
1596+
}
1597+
/** The codec used by WebViewFlutterApi. */
1598+
static MessageCodec<Object> getCodec() {
1599+
return new StandardMessageCodec();
1600+
}
1601+
/** Create a new Dart instance and add it to the `InstanceManager`. */
1602+
public void create(@NonNull Long identifierArg, Reply<Void> callback) {
1603+
BasicMessageChannel<Object> channel =
1604+
new BasicMessageChannel<>(
1605+
binaryMessenger, "dev.flutter.pigeon.WebViewFlutterApi.create", getCodec());
1606+
channel.send(
1607+
new ArrayList<Object>(Collections.singletonList(identifierArg)),
1608+
channelReply -> {
1609+
callback.reply(null);
1610+
});
1611+
}
1612+
}
15771613
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
15781614
public interface WebSettingsHostApi {
15791615

packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientFlutterApiImpl.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
public class WebChromeClientFlutterApiImpl extends WebChromeClientFlutterApi {
2222
private final BinaryMessenger binaryMessenger;
2323
private final InstanceManager instanceManager;
24+
private final WebViewFlutterApiImpl webViewFlutterApi;
2425

2526
/**
2627
* Creates a Flutter api that sends messages to Dart.
@@ -33,15 +34,16 @@ public WebChromeClientFlutterApiImpl(
3334
super(binaryMessenger);
3435
this.binaryMessenger = binaryMessenger;
3536
this.instanceManager = instanceManager;
37+
webViewFlutterApi = new WebViewFlutterApiImpl(binaryMessenger, instanceManager);
3638
}
3739

3840
/** Passes arguments from {@link WebChromeClient#onProgressChanged} to Dart. */
3941
public void onProgressChanged(
4042
WebChromeClient webChromeClient, WebView webView, Long progress, Reply<Void> callback) {
41-
final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView);
42-
if (webViewIdentifier == null) {
43-
throw new IllegalStateException("Could not find identifier for WebView.");
44-
}
43+
webViewFlutterApi.create(webView, reply -> {});
44+
45+
final Long webViewIdentifier =
46+
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView));
4547
super.onProgressChanged(
4648
getIdentifierForClient(webChromeClient), webViewIdentifier, progress, callback);
4749
}
@@ -53,17 +55,15 @@ public void onShowFileChooser(
5355
WebView webView,
5456
WebChromeClient.FileChooserParams fileChooserParams,
5557
Reply<List<String>> callback) {
56-
Long paramsInstanceId = instanceManager.getIdentifierForStrongReference(fileChooserParams);
57-
if (paramsInstanceId == null) {
58-
final FileChooserParamsFlutterApiImpl flutterApi =
59-
new FileChooserParamsFlutterApiImpl(binaryMessenger, instanceManager);
60-
paramsInstanceId = flutterApi.create(fileChooserParams, reply -> {});
61-
}
58+
webViewFlutterApi.create(webView, reply -> {});
59+
60+
new FileChooserParamsFlutterApiImpl(binaryMessenger, instanceManager)
61+
.create(fileChooserParams, reply -> {});
6262

6363
onShowFileChooser(
6464
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webChromeClient)),
6565
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView)),
66-
paramsInstanceId,
66+
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(fileChooserParams)),
6767
callback);
6868
}
6969

packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientFlutterApiImpl.java

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,17 @@
1515
import io.flutter.plugin.common.BinaryMessenger;
1616
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebViewClientFlutterApi;
1717
import java.util.HashMap;
18+
import java.util.Objects;
1819

1920
/**
2021
* Flutter Api implementation for {@link WebViewClient}.
2122
*
2223
* <p>Passes arguments of callbacks methods from a {@link WebViewClient} to Dart.
2324
*/
2425
public class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi {
26+
private final BinaryMessenger binaryMessenger;
2527
private final InstanceManager instanceManager;
28+
private final WebViewFlutterApiImpl webViewFlutterApi;
2629

2730
@RequiresApi(api = Build.VERSION_CODES.M)
2831
static GeneratedAndroidWebView.WebResourceErrorData createWebResourceErrorData(
@@ -71,26 +74,28 @@ static GeneratedAndroidWebView.WebResourceRequestData createWebResourceRequestDa
7174
public WebViewClientFlutterApiImpl(
7275
BinaryMessenger binaryMessenger, InstanceManager instanceManager) {
7376
super(binaryMessenger);
77+
this.binaryMessenger = binaryMessenger;
7478
this.instanceManager = instanceManager;
79+
webViewFlutterApi = new WebViewFlutterApiImpl(binaryMessenger, instanceManager);
7580
}
7681

7782
/** Passes arguments from {@link WebViewClient#onPageStarted} to Dart. */
7883
public void onPageStarted(
7984
WebViewClient webViewClient, WebView webView, String urlArg, Reply<Void> callback) {
80-
final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView);
81-
if (webViewIdentifier == null) {
82-
throw new IllegalStateException("Could not find identifier for WebView.");
83-
}
85+
webViewFlutterApi.create(webView, reply -> {});
86+
87+
final Long webViewIdentifier =
88+
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView));
8489
onPageStarted(getIdentifierForClient(webViewClient), webViewIdentifier, urlArg, callback);
8590
}
8691

8792
/** Passes arguments from {@link WebViewClient#onPageFinished} to Dart. */
8893
public void onPageFinished(
8994
WebViewClient webViewClient, WebView webView, String urlArg, Reply<Void> callback) {
90-
final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView);
91-
if (webViewIdentifier == null) {
92-
throw new IllegalStateException("Could not find identifier for WebView.");
93-
}
95+
webViewFlutterApi.create(webView, reply -> {});
96+
97+
final Long webViewIdentifier =
98+
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView));
9499
onPageFinished(getIdentifierForClient(webViewClient), webViewIdentifier, urlArg, callback);
95100
}
96101

@@ -105,10 +110,10 @@ public void onReceivedRequestError(
105110
WebResourceRequest request,
106111
WebResourceError error,
107112
Reply<Void> callback) {
108-
final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView);
109-
if (webViewIdentifier == null) {
110-
throw new IllegalStateException("Could not find identifier for WebView.");
111-
}
113+
webViewFlutterApi.create(webView, reply -> {});
114+
115+
final Long webViewIdentifier =
116+
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView));
112117
onReceivedRequestError(
113118
getIdentifierForClient(webViewClient),
114119
webViewIdentifier,
@@ -128,10 +133,10 @@ public void onReceivedRequestError(
128133
WebResourceRequest request,
129134
WebResourceErrorCompat error,
130135
Reply<Void> callback) {
131-
final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView);
132-
if (webViewIdentifier == null) {
133-
throw new IllegalStateException("Could not find identifier for WebView.");
134-
}
136+
webViewFlutterApi.create(webView, reply -> {});
137+
138+
final Long webViewIdentifier =
139+
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView));
135140
onReceivedRequestError(
136141
getIdentifierForClient(webViewClient),
137142
webViewIdentifier,
@@ -151,10 +156,10 @@ public void onReceivedError(
151156
String descriptionArg,
152157
String failingUrlArg,
153158
Reply<Void> callback) {
154-
final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView);
155-
if (webViewIdentifier == null) {
156-
throw new IllegalStateException("Could not find identifier for WebView.");
157-
}
159+
webViewFlutterApi.create(webView, reply -> {});
160+
161+
final Long webViewIdentifier =
162+
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView));
158163
onReceivedError(
159164
getIdentifierForClient(webViewClient),
160165
webViewIdentifier,
@@ -174,10 +179,10 @@ public void requestLoading(
174179
WebView webView,
175180
WebResourceRequest request,
176181
Reply<Void> callback) {
177-
final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView);
178-
if (webViewIdentifier == null) {
179-
throw new IllegalStateException("Could not find identifier for WebView.");
180-
}
182+
webViewFlutterApi.create(webView, reply -> {});
183+
184+
final Long webViewIdentifier =
185+
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView));
181186
requestLoading(
182187
getIdentifierForClient(webViewClient),
183188
webViewIdentifier,
@@ -190,10 +195,10 @@ public void requestLoading(
190195
*/
191196
public void urlLoading(
192197
WebViewClient webViewClient, WebView webView, String urlArg, Reply<Void> callback) {
193-
final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView);
194-
if (webViewIdentifier == null) {
195-
throw new IllegalStateException("Could not find identifier for WebView.");
196-
}
198+
webViewFlutterApi.create(webView, reply -> {});
199+
200+
final Long webViewIdentifier =
201+
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView));
197202
urlLoading(getIdentifierForClient(webViewClient), webViewIdentifier, urlArg, callback);
198203
}
199204

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
package io.flutter.plugins.webviewflutter;
6+
7+
import android.webkit.WebView;
8+
import androidx.annotation.NonNull;
9+
import androidx.annotation.VisibleForTesting;
10+
import io.flutter.plugin.common.BinaryMessenger;
11+
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebViewFlutterApi;
12+
13+
/**
14+
* Flutter API implementation for `WebView`.
15+
*
16+
* <p>This class may handle adding native instances that are attached to a Dart instance or passing
17+
* arguments of callbacks methods to a Dart instance.
18+
*/
19+
public class WebViewFlutterApiImpl {
20+
// To ease adding additional methods, this value is added prematurely.
21+
@SuppressWarnings({"unused", "FieldCanBeLocal"})
22+
private final BinaryMessenger binaryMessenger;
23+
24+
private final InstanceManager instanceManager;
25+
private WebViewFlutterApi api;
26+
27+
/**
28+
* Constructs a {@link WebViewFlutterApiImpl}.
29+
*
30+
* @param binaryMessenger used to communicate with Dart over asynchronous messages
31+
* @param instanceManager maintains instances stored to communicate with attached Dart objects
32+
*/
33+
public WebViewFlutterApiImpl(
34+
@NonNull BinaryMessenger binaryMessenger, @NonNull InstanceManager instanceManager) {
35+
this.binaryMessenger = binaryMessenger;
36+
this.instanceManager = instanceManager;
37+
api = new WebViewFlutterApi(binaryMessenger);
38+
}
39+
40+
/**
41+
* Stores the `WebView` instance and notifies Dart to create and store a new `WebView` instance
42+
* that is attached to this one. If `instance` has already been added, this method does nothing.
43+
*/
44+
public void create(@NonNull WebView instance, @NonNull WebViewFlutterApi.Reply<Void> callback) {
45+
if (!instanceManager.containsInstance(instance)) {
46+
api.create(instanceManager.addHostCreatedInstance(instance), callback);
47+
}
48+
}
49+
50+
/**
51+
* Sets the Flutter API used to send messages to Dart.
52+
*
53+
* <p>This is only visible for testing.
54+
*/
55+
@VisibleForTesting
56+
void setApi(@NonNull WebViewFlutterApi api) {
57+
this.api = api;
58+
}
59+
}

packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebChromeClientTest.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ public class WebChromeClientTest {
4747
public void setUp() {
4848
instanceManager = InstanceManager.open(identifier -> {});
4949

50-
instanceManager.addDartCreatedInstance(mockWebView, 0L);
51-
5250
final WebChromeClientCreator webChromeClientCreator =
5351
new WebChromeClientCreator() {
5452
@Override

packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ public class WebViewClientTest {
4343
public void setUp() {
4444
instanceManager = InstanceManager.open(identifier -> {});
4545

46-
instanceManager.addDartCreatedInstance(mockWebView, 0L);
47-
4846
final WebViewClientCreator webViewClientCreator =
4947
new WebViewClientCreator() {
5048
@Override

packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import static org.junit.Assert.assertEquals;
88
import static org.junit.Assert.assertFalse;
99
import static org.junit.Assert.assertTrue;
10+
import static org.mockito.ArgumentMatchers.any;
1011
import static org.mockito.ArgumentMatchers.eq;
1112
import static org.mockito.Mockito.mock;
1213
import static org.mockito.Mockito.verify;
@@ -18,6 +19,7 @@
1819
import android.webkit.WebChromeClient;
1920
import android.webkit.WebViewClient;
2021
import io.flutter.plugin.common.BinaryMessenger;
22+
import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebViewFlutterApi;
2123
import io.flutter.plugins.webviewflutter.WebViewHostApiImpl.WebViewPlatformView;
2224
import java.util.HashMap;
2325
import java.util.Objects;
@@ -314,4 +316,23 @@ public void destroy() {
314316

315317
assertTrue(destroyCalled[0]);
316318
}
319+
320+
@Test
321+
public void flutterApiCreate() {
322+
final InstanceManager instanceManager = InstanceManager.open(identifier -> {});
323+
324+
final WebViewFlutterApiImpl flutterApiImpl =
325+
new WebViewFlutterApiImpl(mockBinaryMessenger, instanceManager);
326+
327+
final WebViewFlutterApi mockFlutterApi = mock(WebViewFlutterApi.class);
328+
flutterApiImpl.setApi(mockFlutterApi);
329+
330+
flutterApiImpl.create(mockWebView, reply -> {});
331+
332+
final long instanceIdentifier =
333+
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(mockWebView));
334+
verify(mockFlutterApi).create(eq(instanceIdentifier), any());
335+
336+
instanceManager.close();
337+
}
317338
}

0 commit comments

Comments
 (0)