[webview_flutter_android] Updates the Dart InstanceManager to take a listener for when an object is garbage collected (#6148)
diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
index 5706d65..dc9850e 100644
--- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
+++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 2.9.3
+
+* Updates the Dart InstanceManager to take a listener for when an object is garbage collected.
+ See https://github.com/flutter/flutter/issues/107199.
+
## 2.9.2
* Updates the Java InstanceManager to take a listener for when an object is garbage collected.
diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart
index 005e281..f2e305f 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart
@@ -28,6 +28,11 @@
/// This should only be used by subclasses created by this library or to
/// create copies.
JavaObject.detached();
+
+ /// Global instance of [InstanceManager].
+ static final InstanceManager globalInstanceManager = InstanceManager(
+ onWeakReferenceRemoved: (_) {},
+ );
}
/// An Android View that displays web pages.
diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart
index b1268f4..c3e2096 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart
@@ -88,28 +88,27 @@
WebViewHostApiImpl({
BinaryMessenger? binaryMessenger,
InstanceManager? instanceManager,
- }) : super(binaryMessenger: binaryMessenger) {
- this.instanceManager = instanceManager ?? InstanceManager.instance;
- }
+ }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
+ super(binaryMessenger: binaryMessenger);
/// Maintains instances stored to communicate with java objects.
- late final InstanceManager instanceManager;
+ final InstanceManager instanceManager;
/// Helper method to convert instances ids to objects.
- Future<void> createFromInstance(WebView instance) async {
- final int? instanceId = instanceManager.tryAddInstance(instance);
- if (instanceId != null) {
- return create(instanceId, instance.useHybridComposition);
- }
+ Future<void> createFromInstance(WebView instance) {
+ return create(
+ instanceManager.addDartCreatedInstance(instance),
+ instance.useHybridComposition,
+ );
}
/// Helper method to convert instances ids to objects.
Future<void> disposeFromInstance(WebView instance) async {
- final int? instanceId = instanceManager.getInstanceId(instance);
+ final int? instanceId = instanceManager.getIdentifier(instance);
if (instanceId != null) {
+ instanceManager.remove(instanceId);
await dispose(instanceId);
}
- instanceManager.removeInstance(instance);
}
/// Helper method to convert the instances ids to objects.
@@ -120,7 +119,7 @@
String? encoding,
) {
return loadData(
- instanceManager.getInstanceId(instance)!,
+ instanceManager.getIdentifier(instance)!,
data,
mimeType,
encoding,
@@ -137,7 +136,7 @@
String? historyUrl,
) {
return loadDataWithBaseUrl(
- instanceManager.getInstanceId(instance)!,
+ instanceManager.getIdentifier(instance)!,
baseUrl,
data,
mimeType,
@@ -152,7 +151,7 @@
String url,
Map<String, String> headers,
) {
- return loadUrl(instanceManager.getInstanceId(instance)!, url, headers);
+ return loadUrl(instanceManager.getIdentifier(instance)!, url, headers);
}
/// Helper method to convert instances ids to objects.
@@ -161,43 +160,43 @@
String url,
Uint8List data,
) {
- return postUrl(instanceManager.getInstanceId(instance)!, url, data);
+ return postUrl(instanceManager.getIdentifier(instance)!, url, data);
}
/// Helper method to convert instances ids to objects.
Future<String?> getUrlFromInstance(WebView instance) {
- return getUrl(instanceManager.getInstanceId(instance)!);
+ return getUrl(instanceManager.getIdentifier(instance)!);
}
/// Helper method to convert instances ids to objects.
Future<bool> canGoBackFromInstance(WebView instance) {
- return canGoBack(instanceManager.getInstanceId(instance)!);
+ return canGoBack(instanceManager.getIdentifier(instance)!);
}
/// Helper method to convert instances ids to objects.
Future<bool> canGoForwardFromInstance(WebView instance) {
- return canGoForward(instanceManager.getInstanceId(instance)!);
+ return canGoForward(instanceManager.getIdentifier(instance)!);
}
/// Helper method to convert instances ids to objects.
Future<void> goBackFromInstance(WebView instance) {
- return goBack(instanceManager.getInstanceId(instance)!);
+ return goBack(instanceManager.getIdentifier(instance)!);
}
/// Helper method to convert instances ids to objects.
Future<void> goForwardFromInstance(WebView instance) {
- return goForward(instanceManager.getInstanceId(instance)!);
+ return goForward(instanceManager.getIdentifier(instance)!);
}
/// Helper method to convert instances ids to objects.
Future<void> reloadFromInstance(WebView instance) {
- return reload(instanceManager.getInstanceId(instance)!);
+ return reload(instanceManager.getIdentifier(instance)!);
}
/// Helper method to convert instances ids to objects.
Future<void> clearCacheFromInstance(WebView instance, bool includeDiskFiles) {
return clearCache(
- instanceManager.getInstanceId(instance)!,
+ instanceManager.getIdentifier(instance)!,
includeDiskFiles,
);
}
@@ -208,34 +207,34 @@
String javascriptString,
) {
return evaluateJavascript(
- instanceManager.getInstanceId(instance)!,
+ instanceManager.getIdentifier(instance)!,
javascriptString,
);
}
/// Helper method to convert instances ids to objects.
Future<String?> getTitleFromInstance(WebView instance) {
- return getTitle(instanceManager.getInstanceId(instance)!);
+ return getTitle(instanceManager.getIdentifier(instance)!);
}
/// Helper method to convert instances ids to objects.
Future<void> scrollToFromInstance(WebView instance, int x, int y) {
- return scrollTo(instanceManager.getInstanceId(instance)!, x, y);
+ return scrollTo(instanceManager.getIdentifier(instance)!, x, y);
}
/// Helper method to convert instances ids to objects.
Future<void> scrollByFromInstance(WebView instance, int x, int y) {
- return scrollBy(instanceManager.getInstanceId(instance)!, x, y);
+ return scrollBy(instanceManager.getIdentifier(instance)!, x, y);
}
/// Helper method to convert instances ids to objects.
Future<int> getScrollXFromInstance(WebView instance) {
- return getScrollX(instanceManager.getInstanceId(instance)!);
+ return getScrollX(instanceManager.getIdentifier(instance)!);
}
/// Helper method to convert instances ids to objects.
Future<int> getScrollYFromInstance(WebView instance) {
- return getScrollY(instanceManager.getInstanceId(instance)!);
+ return getScrollY(instanceManager.getIdentifier(instance)!);
}
/// Helper method to convert instances ids to objects.
@@ -244,8 +243,8 @@
WebViewClient webViewClient,
) {
return setWebViewClient(
- instanceManager.getInstanceId(instance)!,
- instanceManager.getInstanceId(webViewClient)!,
+ instanceManager.getIdentifier(instance)!,
+ instanceManager.getIdentifier(webViewClient)!,
);
}
@@ -255,8 +254,8 @@
JavaScriptChannel javaScriptChannel,
) {
return addJavaScriptChannel(
- instanceManager.getInstanceId(instance)!,
- instanceManager.getInstanceId(javaScriptChannel)!,
+ instanceManager.getIdentifier(instance)!,
+ instanceManager.getIdentifier(javaScriptChannel)!,
);
}
@@ -266,8 +265,8 @@
JavaScriptChannel javaScriptChannel,
) {
return removeJavaScriptChannel(
- instanceManager.getInstanceId(instance)!,
- instanceManager.getInstanceId(javaScriptChannel)!,
+ instanceManager.getIdentifier(instance)!,
+ instanceManager.getIdentifier(javaScriptChannel)!,
);
}
@@ -277,8 +276,8 @@
DownloadListener? listener,
) {
return setDownloadListener(
- instanceManager.getInstanceId(instance)!,
- listener != null ? instanceManager.getInstanceId(listener) : null,
+ instanceManager.getIdentifier(instance)!,
+ listener != null ? instanceManager.getIdentifier(listener) : null,
);
}
@@ -288,14 +287,14 @@
WebChromeClient? client,
) {
return setWebChromeClient(
- instanceManager.getInstanceId(instance)!,
- client != null ? instanceManager.getInstanceId(client) : null,
+ instanceManager.getIdentifier(instance)!,
+ client != null ? instanceManager.getIdentifier(client) : null,
);
}
/// Helper method to convert instances ids to objects.
Future<void> setBackgroundColorFromInstance(WebView instance, int color) {
- return setBackgroundColor(instanceManager.getInstanceId(instance)!, color);
+ return setBackgroundColor(instanceManager.getIdentifier(instance)!, color);
}
}
@@ -305,28 +304,25 @@
WebSettingsHostApiImpl({
BinaryMessenger? binaryMessenger,
InstanceManager? instanceManager,
- }) : super(binaryMessenger: binaryMessenger) {
- this.instanceManager = instanceManager ?? InstanceManager.instance;
- }
+ }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
+ super(binaryMessenger: binaryMessenger);
/// Maintains instances stored to communicate with java objects.
- late final InstanceManager instanceManager;
+ final InstanceManager instanceManager;
/// Helper method to convert instances ids to objects.
- Future<void> createFromInstance(WebSettings instance, WebView webView) async {
- final int? instanceId = instanceManager.tryAddInstance(instance);
- if (instanceId != null) {
- return create(
- instanceId,
- instanceManager.getInstanceId(webView)!,
- );
- }
+ Future<void> createFromInstance(WebSettings instance, WebView webView) {
+ return create(
+ instanceManager.addDartCreatedInstance(instance),
+ instanceManager.getIdentifier(webView)!,
+ );
}
/// Helper method to convert instances ids to objects.
Future<void> disposeFromInstance(WebSettings instance) async {
- final int? instanceId = instanceManager.removeInstance(instance);
+ final int? instanceId = instanceManager.getIdentifier(instance);
if (instanceId != null) {
+ instanceManager.remove(instanceId);
return dispose(instanceId);
}
}
@@ -336,7 +332,7 @@
WebSettings instance,
bool flag,
) {
- return setDomStorageEnabled(instanceManager.getInstanceId(instance)!, flag);
+ return setDomStorageEnabled(instanceManager.getIdentifier(instance)!, flag);
}
/// Helper method to convert instances ids to objects.
@@ -345,7 +341,7 @@
bool flag,
) {
return setJavaScriptCanOpenWindowsAutomatically(
- instanceManager.getInstanceId(instance)!,
+ instanceManager.getIdentifier(instance)!,
flag,
);
}
@@ -356,7 +352,7 @@
bool support,
) {
return setSupportMultipleWindows(
- instanceManager.getInstanceId(instance)!, support);
+ instanceManager.getIdentifier(instance)!, support);
}
/// Helper method to convert instances ids to objects.
@@ -365,7 +361,7 @@
bool flag,
) {
return setJavaScriptEnabled(
- instanceManager.getInstanceId(instance)!,
+ instanceManager.getIdentifier(instance)!,
flag,
);
}
@@ -376,7 +372,7 @@
String? userAgentString,
) {
return setUserAgentString(
- instanceManager.getInstanceId(instance)!,
+ instanceManager.getIdentifier(instance)!,
userAgentString,
);
}
@@ -387,7 +383,7 @@
bool require,
) {
return setMediaPlaybackRequiresUserGesture(
- instanceManager.getInstanceId(instance)!,
+ instanceManager.getIdentifier(instance)!,
require,
);
}
@@ -397,7 +393,7 @@
WebSettings instance,
bool support,
) {
- return setSupportZoom(instanceManager.getInstanceId(instance)!, support);
+ return setSupportZoom(instanceManager.getIdentifier(instance)!, support);
}
/// Helper method to convert instances ids to objects.
@@ -406,7 +402,7 @@
bool overview,
) {
return setLoadWithOverviewMode(
- instanceManager.getInstanceId(instance)!,
+ instanceManager.getIdentifier(instance)!,
overview,
);
}
@@ -416,7 +412,7 @@
WebSettings instance,
bool use,
) {
- return setUseWideViewPort(instanceManager.getInstanceId(instance)!, use);
+ return setUseWideViewPort(instanceManager.getIdentifier(instance)!, use);
}
/// Helper method to convert instances ids to objects.
@@ -425,7 +421,7 @@
bool enabled,
) {
return setDisplayZoomControls(
- instanceManager.getInstanceId(instance)!,
+ instanceManager.getIdentifier(instance)!,
enabled,
);
}
@@ -436,7 +432,7 @@
bool enabled,
) {
return setBuiltInZoomControls(
- instanceManager.getInstanceId(instance)!,
+ instanceManager.getIdentifier(instance)!,
enabled,
);
}
@@ -447,7 +443,7 @@
bool enabled,
) {
return setAllowFileAccess(
- instanceManager.getInstanceId(instance)!,
+ instanceManager.getIdentifier(instance)!,
enabled,
);
}
@@ -459,18 +455,20 @@
JavaScriptChannelHostApiImpl({
BinaryMessenger? binaryMessenger,
InstanceManager? instanceManager,
- }) : super(binaryMessenger: binaryMessenger) {
- this.instanceManager = instanceManager ?? InstanceManager.instance;
- }
+ }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
+ super(binaryMessenger: binaryMessenger);
/// Maintains instances stored to communicate with java objects.
- late final InstanceManager instanceManager;
+ final InstanceManager instanceManager;
/// Helper method to convert instances ids to objects.
Future<void> createFromInstance(JavaScriptChannel instance) async {
- final int? instanceId = instanceManager.tryAddInstance(instance);
- if (instanceId != null) {
- return create(instanceId, instance.channelName);
+ if (instanceManager.getIdentifier(instance) == null) {
+ final int identifier = instanceManager.addDartCreatedInstance(instance);
+ await create(
+ identifier,
+ instance.channelName,
+ );
}
}
}
@@ -478,22 +476,21 @@
/// Flutter api implementation for [JavaScriptChannel].
class JavaScriptChannelFlutterApiImpl extends JavaScriptChannelFlutterApi {
/// Constructs a [JavaScriptChannelFlutterApiImpl].
- JavaScriptChannelFlutterApiImpl({InstanceManager? instanceManager}) {
- this.instanceManager = instanceManager ?? InstanceManager.instance;
- }
+ JavaScriptChannelFlutterApiImpl({InstanceManager? instanceManager})
+ : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
/// Maintains instances stored to communicate with java objects.
- late final InstanceManager instanceManager;
+ final InstanceManager instanceManager;
@override
void dispose(int instanceId) {
- instanceManager.removeInstance(instanceId);
+ instanceManager.remove(instanceId);
}
@override
void postMessage(int instanceId, String message) {
- final JavaScriptChannel? instance =
- instanceManager.getInstance(instanceId) as JavaScriptChannel?;
+ final JavaScriptChannel? instance = instanceManager
+ .getInstanceWithWeakReference(instanceId) as JavaScriptChannel?;
assert(
instance != null,
'InstanceManager does not contain an JavaScriptChannel with instanceId: $instanceId',
@@ -508,18 +505,17 @@
WebViewClientHostApiImpl({
BinaryMessenger? binaryMessenger,
InstanceManager? instanceManager,
- }) : super(binaryMessenger: binaryMessenger) {
- this.instanceManager = instanceManager ?? InstanceManager.instance;
- }
+ }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
+ super(binaryMessenger: binaryMessenger);
/// Maintains instances stored to communicate with java objects.
- late final InstanceManager instanceManager;
+ final InstanceManager instanceManager;
/// Helper method to convert instances ids to objects.
Future<void> createFromInstance(WebViewClient instance) async {
- final int? instanceId = instanceManager.tryAddInstance(instance);
- if (instanceId != null) {
- return create(instanceId, instance.shouldOverrideUrlLoading);
+ if (instanceManager.getIdentifier(instance) == null) {
+ final int identifier = instanceManager.addDartCreatedInstance(instance);
+ return create(identifier, instance.shouldOverrideUrlLoading);
}
}
}
@@ -527,24 +523,23 @@
/// Flutter api implementation for [WebViewClient].
class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi {
/// Constructs a [WebViewClientFlutterApiImpl].
- WebViewClientFlutterApiImpl({InstanceManager? instanceManager}) {
- this.instanceManager = instanceManager ?? InstanceManager.instance;
- }
+ WebViewClientFlutterApiImpl({InstanceManager? instanceManager})
+ : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
/// Maintains instances stored to communicate with java objects.
- late final InstanceManager instanceManager;
+ final InstanceManager instanceManager;
@override
void dispose(int instanceId) {
- instanceManager.removeInstance(instanceId);
+ instanceManager.remove(instanceId);
}
@override
void onPageFinished(int instanceId, int webViewInstanceId, String url) {
- final WebViewClient? instance =
- instanceManager.getInstance(instanceId) as WebViewClient?;
- final WebView? webViewInstance =
- instanceManager.getInstance(webViewInstanceId) as WebView?;
+ final WebViewClient? instance = instanceManager
+ .getInstanceWithWeakReference(instanceId) as WebViewClient?;
+ final WebView? webViewInstance = instanceManager
+ .getInstanceWithWeakReference(webViewInstanceId) as WebView?;
assert(
instance != null,
'InstanceManager does not contain an WebViewClient with instanceId: $instanceId',
@@ -558,10 +553,10 @@
@override
void onPageStarted(int instanceId, int webViewInstanceId, String url) {
- final WebViewClient? instance =
- instanceManager.getInstance(instanceId) as WebViewClient?;
- final WebView? webViewInstance =
- instanceManager.getInstance(webViewInstanceId) as WebView?;
+ final WebViewClient? instance = instanceManager
+ .getInstanceWithWeakReference(instanceId) as WebViewClient?;
+ final WebView? webViewInstance = instanceManager
+ .getInstanceWithWeakReference(webViewInstanceId) as WebView?;
assert(
instance != null,
'InstanceManager does not contain an WebViewClient with instanceId: $instanceId',
@@ -581,10 +576,10 @@
String description,
String failingUrl,
) {
- final WebViewClient? instance =
- instanceManager.getInstance(instanceId) as WebViewClient?;
- final WebView? webViewInstance =
- instanceManager.getInstance(webViewInstanceId) as WebView?;
+ final WebViewClient? instance = instanceManager
+ .getInstanceWithWeakReference(instanceId) as WebViewClient?;
+ final WebView? webViewInstance = instanceManager
+ .getInstanceWithWeakReference(webViewInstanceId) as WebView?;
assert(
instance != null,
'InstanceManager does not contain an WebViewClient with instanceId: $instanceId',
@@ -609,10 +604,10 @@
WebResourceRequestData request,
WebResourceErrorData error,
) {
- final WebViewClient? instance =
- instanceManager.getInstance(instanceId) as WebViewClient?;
- final WebView? webViewInstance =
- instanceManager.getInstance(webViewInstanceId) as WebView?;
+ final WebViewClient? instance = instanceManager
+ .getInstanceWithWeakReference(instanceId) as WebViewClient?;
+ final WebView? webViewInstance = instanceManager
+ .getInstanceWithWeakReference(webViewInstanceId) as WebView?;
assert(
instance != null,
'InstanceManager does not contain an WebViewClient with instanceId: $instanceId',
@@ -634,10 +629,10 @@
int webViewInstanceId,
WebResourceRequestData request,
) {
- final WebViewClient? instance =
- instanceManager.getInstance(instanceId) as WebViewClient?;
- final WebView? webViewInstance =
- instanceManager.getInstance(webViewInstanceId) as WebView?;
+ final WebViewClient? instance = instanceManager
+ .getInstanceWithWeakReference(instanceId) as WebViewClient?;
+ final WebView? webViewInstance = instanceManager
+ .getInstanceWithWeakReference(webViewInstanceId) as WebView?;
assert(
instance != null,
'InstanceManager does not contain an WebViewClient with instanceId: $instanceId',
@@ -655,10 +650,10 @@
int webViewInstanceId,
String url,
) {
- final WebViewClient? instance =
- instanceManager.getInstance(instanceId) as WebViewClient?;
- final WebView? webViewInstance =
- instanceManager.getInstance(webViewInstanceId) as WebView?;
+ final WebViewClient? instance = instanceManager
+ .getInstanceWithWeakReference(instanceId) as WebViewClient?;
+ final WebView? webViewInstance = instanceManager
+ .getInstanceWithWeakReference(webViewInstanceId) as WebView?;
assert(
instance != null,
'InstanceManager does not contain an WebViewClient with instanceId: $instanceId',
@@ -677,18 +672,17 @@
DownloadListenerHostApiImpl({
BinaryMessenger? binaryMessenger,
InstanceManager? instanceManager,
- }) : super(binaryMessenger: binaryMessenger) {
- this.instanceManager = instanceManager ?? InstanceManager.instance;
- }
+ }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
+ super(binaryMessenger: binaryMessenger);
/// Maintains instances stored to communicate with java objects.
- late final InstanceManager instanceManager;
+ final InstanceManager instanceManager;
/// Helper method to convert instances ids to objects.
Future<void> createFromInstance(DownloadListener instance) async {
- final int? instanceId = instanceManager.tryAddInstance(instance);
- if (instanceId != null) {
- return create(instanceId);
+ if (instanceManager.getIdentifier(instance) == null) {
+ final int identifier = instanceManager.addDartCreatedInstance(instance);
+ return create(identifier);
}
}
}
@@ -696,16 +690,15 @@
/// Flutter api implementation for [DownloadListener].
class DownloadListenerFlutterApiImpl extends DownloadListenerFlutterApi {
/// Constructs a [DownloadListenerFlutterApiImpl].
- DownloadListenerFlutterApiImpl({InstanceManager? instanceManager}) {
- this.instanceManager = instanceManager ?? InstanceManager.instance;
- }
+ DownloadListenerFlutterApiImpl({InstanceManager? instanceManager})
+ : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
/// Maintains instances stored to communicate with java objects.
- late final InstanceManager instanceManager;
+ final InstanceManager instanceManager;
@override
void dispose(int instanceId) {
- instanceManager.removeInstance(instanceId);
+ instanceManager.remove(instanceId);
}
@override
@@ -717,8 +710,8 @@
String mimetype,
int contentLength,
) {
- final DownloadListener? instance =
- instanceManager.getInstance(instanceId) as DownloadListener?;
+ final DownloadListener? instance = instanceManager
+ .getInstanceWithWeakReference(instanceId) as DownloadListener?;
assert(
instance != null,
'InstanceManager does not contain an DownloadListener with instanceId: $instanceId',
@@ -739,21 +732,23 @@
WebChromeClientHostApiImpl({
BinaryMessenger? binaryMessenger,
InstanceManager? instanceManager,
- }) : super(binaryMessenger: binaryMessenger) {
- this.instanceManager = instanceManager ?? InstanceManager.instance;
- }
+ }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
+ super(binaryMessenger: binaryMessenger);
/// Maintains instances stored to communicate with java objects.
- late final InstanceManager instanceManager;
+ final InstanceManager instanceManager;
/// Helper method to convert instances ids to objects.
Future<void> createFromInstance(
WebChromeClient instance,
WebViewClient webViewClient,
) async {
- final int? instanceId = instanceManager.tryAddInstance(instance);
- if (instanceId != null) {
- return create(instanceId, instanceManager.getInstanceId(webViewClient)!);
+ if (instanceManager.getIdentifier(instance) == null) {
+ final int identifier = instanceManager.addDartCreatedInstance(instance);
+ return create(
+ identifier,
+ instanceManager.getIdentifier(webViewClient)!,
+ );
}
}
}
@@ -761,24 +756,23 @@
/// Flutter api implementation for [DownloadListener].
class WebChromeClientFlutterApiImpl extends WebChromeClientFlutterApi {
/// Constructs a [DownloadListenerFlutterApiImpl].
- WebChromeClientFlutterApiImpl({InstanceManager? instanceManager}) {
- this.instanceManager = instanceManager ?? InstanceManager.instance;
- }
+ WebChromeClientFlutterApiImpl({InstanceManager? instanceManager})
+ : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
/// Maintains instances stored to communicate with java objects.
- late final InstanceManager instanceManager;
+ final InstanceManager instanceManager;
@override
void dispose(int instanceId) {
- instanceManager.removeInstance(instanceId);
+ instanceManager.remove(instanceId);
}
@override
void onProgressChanged(int instanceId, int webViewInstanceId, int progress) {
- final WebChromeClient? instance =
- instanceManager.getInstance(instanceId) as WebChromeClient?;
- final WebView? webViewInstance =
- instanceManager.getInstance(webViewInstanceId) as WebView?;
+ final WebChromeClient? instance = instanceManager
+ .getInstanceWithWeakReference(instanceId) as WebChromeClient?;
+ final WebView? webViewInstance = instanceManager
+ .getInstanceWithWeakReference(webViewInstanceId) as WebView?;
assert(
instance != null,
'InstanceManager does not contain an WebChromeClient with instanceId: $instanceId',
@@ -797,23 +791,22 @@
WebStorageHostApiImpl({
BinaryMessenger? binaryMessenger,
InstanceManager? instanceManager,
- }) : super(binaryMessenger: binaryMessenger) {
- this.instanceManager = instanceManager ?? InstanceManager.instance;
- }
+ }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
+ super(binaryMessenger: binaryMessenger);
/// Maintains instances stored to communicate with java objects.
- late final InstanceManager instanceManager;
+ final InstanceManager instanceManager;
/// Helper method to convert instances ids to objects.
Future<void> createFromInstance(WebStorage instance) async {
- final int? instanceId = instanceManager.tryAddInstance(instance);
- if (instanceId != null) {
- return create(instanceId);
+ if (instanceManager.getIdentifier(instance) == null) {
+ final int identifier = instanceManager.addDartCreatedInstance(instance);
+ return create(identifier);
}
}
/// Helper method to convert instances ids to objects.
Future<void> deleteAllDataFromInstance(WebStorage instance) {
- return deleteAllData(instanceManager.getInstanceId(instance)!);
+ return deleteAllData(instanceManager.getIdentifier(instance)!);
}
}
diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart b/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart
index 531fffa..5892823 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/instance_manager.dart
@@ -23,51 +23,179 @@
Copyable copy();
}
-/// Maintains instances stored to communicate with java objects.
+/// Maintains instances used to communicate with the native objects they
+/// represent.
+///
+/// Added instances are stored as weak references and their copies are stored
+/// as strong references to maintain access to their variables and callback
+/// methods. Both are stored with the same identifier.
+///
+/// When a weak referenced instance becomes inaccessible,
+/// [onWeakReferenceRemoved] is called with its associated identifier.
+///
+/// If an instance is retrieved and has the possibility to be used,
+/// (e.g. calling [getInstanceWithWeakReference]) a copy of the strong reference
+/// is added as a weak reference with the same identifier. This prevents a
+/// scenario where the weak referenced instance was released and then later
+/// returned by the host platform.
class InstanceManager {
- final Map<int, Object> _instanceIdsToInstances = <int, Object>{};
- final Map<Object, int> _instancesToInstanceIds = <Object, int>{};
+ /// Constructs an [InstanceManager].
+ InstanceManager({required void Function(int) onWeakReferenceRemoved}) {
+ this.onWeakReferenceRemoved = (int identifier) {
+ _weakInstances.remove(identifier);
+ onWeakReferenceRemoved(identifier);
+ };
+ _finalizer = Finalizer<int>(this.onWeakReferenceRemoved);
+ }
- int _nextInstanceId = 0;
+ // Identifiers are locked to a specific range to avoid collisions with objects
+ // created simultaneously by the host platform.
+ // Host uses identifiers >= 2^16 and Dart is expected to use values n where,
+ // 0 <= n < 2^16.
+ static const int _maxDartCreatedIdentifier = 65536;
- /// Global instance of [InstanceManager].
- static final InstanceManager instance = InstanceManager();
+ // Expando is used because it doesn't prevent its keys from becoming
+ // inaccessible. This allows the manager to efficiently retrieve an identifier
+ // of an instance without holding a strong reference to that instance.
+ //
+ // It also doesn't use `==` to search for identifiers, which would lead to an
+ // infinite loop when comparing an object to its copy. (i.e. which was caused
+ // by calling instanceManager.getIdentifier() inside of `==` while this was a
+ // HashMap).
+ final Expando<int> _identifiers = Expando<int>();
+ final Map<int, WeakReference<Copyable>> _weakInstances =
+ <int, WeakReference<Copyable>>{};
+ final Map<int, Copyable> _strongInstances = <int, Copyable>{};
+ late final Finalizer<int> _finalizer;
+ int _nextIdentifier = 0;
- /// Attempt to add a new instance.
+ /// Called when a weak referenced instance is removed by [removeWeakReference]
+ /// or becomes inaccessible.
+ late final void Function(int) onWeakReferenceRemoved;
+
+ /// Adds a new instance that was instantiated by Dart.
///
- /// Returns new if [instance] has already been added. Otherwise, it is added
- /// with a new instance id.
- int? tryAddInstance(Object instance) {
- if (_instancesToInstanceIds.containsKey(instance)) {
+ /// In other words, Dart wants to add a new instance that will represent
+ /// an object that will be instantiated on the host platform.
+ ///
+ /// Throws assertion error if the instance has already been added.
+ ///
+ /// Returns the randomly generated id of the [instance] added.
+ int addDartCreatedInstance(Copyable instance) {
+ assert(getIdentifier(instance) == null);
+
+ final int identifier = _nextUniqueIdentifier();
+ _addInstanceWithIdentifier(instance, identifier);
+ return identifier;
+ }
+
+ /// Removes the instance, if present, and call [onWeakReferenceRemoved] with
+ /// its identifier.
+ ///
+ /// Returns the identifier associated with the removed instance. Otherwise,
+ /// `null` if the instance was not found in this manager.
+ ///
+ /// This does not remove the the strong referenced instance associated with
+ /// [instance]. This can be done with [remove].
+ int? removeWeakReference(Copyable instance) {
+ final int? identifier = getIdentifier(instance);
+ if (identifier == null) {
return null;
}
- final int instanceId = _nextInstanceId++;
- _instancesToInstanceIds[instance] = instanceId;
- _instanceIdsToInstances[instanceId] = instance;
- return instanceId;
+ _identifiers[instance] = null;
+ _finalizer.detach(instance);
+ onWeakReferenceRemoved(identifier);
+
+ return identifier;
}
- /// Remove the instance from the manager.
+ /// Removes [identifier] and its associated strongly referenced instance, if
+ /// present, from the manager.
///
- /// Returns null if the instance is removed. Otherwise, return the instanceId
- /// of the removed instance.
- int? removeInstance(Object instance) {
- final int? instanceId = _instancesToInstanceIds[instance];
- if (instanceId != null) {
- _instancesToInstanceIds.remove(instance);
- _instanceIdsToInstances.remove(instanceId);
+ /// Returns the strong referenced instance associated with [identifier] before
+ /// it was removed. Returns `null` if [identifier] was not associated with
+ /// any strong reference.
+ ///
+ /// This does not remove the the weak referenced instance associtated with
+ /// [identifier]. This can be done with [removeWeakReference].
+ T? remove<T extends Copyable>(int identifier) {
+ return _strongInstances.remove(identifier) as T?;
+ }
+
+ /// Retrieves the instance associated with identifier.
+ ///
+ /// The value returned is chosen from the following order:
+ ///
+ /// 1. A weakly referenced instance associated with identifier.
+ /// 2. If the only instance associated with identifier is a strongly
+ /// referenced instance, a copy of the instance is added as a weak reference
+ /// with the same identifier. Returning the newly created copy.
+ /// 3. If no instance is associated with identifier, returns null.
+ ///
+ /// This method also expects the host `InstanceManager` to have a strong
+ /// reference to the instance the identifier is associated with.
+ T? getInstanceWithWeakReference<T extends Copyable>(int identifier) {
+ final Copyable? weakInstance = _weakInstances[identifier]?.target;
+
+ if (weakInstance == null) {
+ final Copyable? strongInstance = _strongInstances[identifier];
+ if (strongInstance != null) {
+ final Copyable copy = strongInstance.copy();
+ _identifiers[copy] = identifier;
+ _weakInstances[identifier] = WeakReference<Copyable>(copy);
+ _finalizer.attach(copy, identifier, detach: copy);
+ return copy as T;
+ }
+ return strongInstance as T?;
}
- return instanceId;
+
+ return weakInstance as T;
}
- /// Retrieve the Object paired with instanceId.
- Object? getInstance(int instanceId) {
- return _instanceIdsToInstances[instanceId];
+ /// Retrieves the identifier associated with instance.
+ int? getIdentifier(Copyable instance) {
+ return _identifiers[instance];
}
- /// Retrieve the instanceId paired with instance.
- int? getInstanceId(Object instance) {
- return _instancesToInstanceIds[instance];
+ /// Adds a new instance that was instantiated by the host platform.
+ ///
+ /// In other words, the host platform wants to add a new instance that
+ /// represents an object on the host platform. Stored with [identifier].
+ ///
+ /// Throws assertion error if the instance or its identifier has already been
+ /// added.
+ ///
+ /// Returns unique identifier of the [instance] added.
+ void addHostCreatedInstance(Copyable instance, int identifier) {
+ assert(!containsIdentifier(identifier));
+ assert(getIdentifier(instance) == null);
+ assert(identifier >= 0);
+ _addInstanceWithIdentifier(instance, identifier);
+ }
+
+ void _addInstanceWithIdentifier(Copyable instance, int identifier) {
+ _identifiers[instance] = identifier;
+ _weakInstances[identifier] = WeakReference<Copyable>(instance);
+ _finalizer.attach(instance, identifier, detach: instance);
+
+ final Copyable copy = instance.copy();
+ _identifiers[copy] = identifier;
+ _strongInstances[identifier] = copy;
+ }
+
+ /// Whether this manager contains the given [identifier].
+ bool containsIdentifier(int identifier) {
+ return _weakInstances.containsKey(identifier) ||
+ _strongInstances.containsKey(identifier);
+ }
+
+ int _nextUniqueIdentifier() {
+ late int identifier;
+ do {
+ identifier = _nextIdentifier;
+ _nextIdentifier = (_nextIdentifier + 1) % _maxDartCreatedIdentifier;
+ } while (containsIdentifier(identifier));
+ return identifier;
}
}
diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart
index 1f0eb7b..4774024 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart
@@ -11,7 +11,6 @@
import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
import 'src/android_webview.dart';
-import 'src/instance_manager.dart';
import 'webview_android_widget.dart';
/// Builds an Android webview.
@@ -55,8 +54,8 @@
gestureRecognizers: gestureRecognizers,
layoutDirection:
Directionality.maybeOf(context) ?? TextDirection.rtl,
- creationParams:
- InstanceManager.instance.getInstanceId(controller.webView),
+ creationParams: JavaObject.globalInstanceManager
+ .getIdentifier(controller.webView),
creationParamsCodec: const StandardMessageCodec(),
),
);
diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart
index 61ec110..e89fb7d 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart
@@ -10,7 +10,6 @@
import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
import 'src/android_webview.dart';
-import 'src/instance_manager.dart';
import 'webview_android.dart';
import 'webview_android_widget.dart';
@@ -74,8 +73,8 @@
// directionality.
layoutDirection:
Directionality.maybeOf(context) ?? TextDirection.ltr,
- webViewIdentifier:
- InstanceManager.instance.getInstanceId(controller.webView)!,
+ webViewIdentifier: JavaObject.globalInstanceManager
+ .getIdentifier(controller.webView)!,
)
..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
..addOnPlatformViewCreatedListener((int id) {
diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml
index f765341..a60c4f9 100644
--- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml
@@ -2,7 +2,7 @@
description: A Flutter plugin that provides a WebView widget on Android.
repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_android
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22
-version: 2.9.2
+version: 2.9.3
environment:
sdk: ">=2.14.0 <3.0.0"
diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart
index cb2ea0f..9d479e6 100644
--- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart
@@ -45,11 +45,11 @@
mockPlatformHostApi = MockTestWebViewHostApi();
TestWebViewHostApi.setup(mockPlatformHostApi);
- instanceManager = InstanceManager();
+ instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {});
WebView.api = WebViewHostApiImpl(instanceManager: instanceManager);
webView = WebView();
- webViewInstanceId = instanceManager.getInstanceId(webView)!;
+ webViewInstanceId = instanceManager.getIdentifier(webView)!;
});
test('create', () {
@@ -206,11 +206,12 @@
);
final WebViewClient mockWebViewClient = MockWebViewClient();
+ when(mockWebViewClient.copy()).thenReturn(MockWebViewClient());
when(mockWebViewClient.shouldOverrideUrlLoading).thenReturn(false);
webView.setWebViewClient(mockWebViewClient);
final int webViewClientInstanceId =
- instanceManager.getInstanceId(mockWebViewClient)!;
+ instanceManager.getIdentifier(mockWebViewClient)!;
verify(mockPlatformHostApi.setWebViewClient(
webViewInstanceId,
webViewClientInstanceId,
@@ -224,12 +225,13 @@
);
final JavaScriptChannel mockJavaScriptChannel = MockJavaScriptChannel();
+ when(mockJavaScriptChannel.copy()).thenReturn(MockJavaScriptChannel());
when(mockJavaScriptChannel.channelName).thenReturn('aChannel');
webView.addJavaScriptChannel(mockJavaScriptChannel);
final int javaScriptChannelInstanceId =
- instanceManager.getInstanceId(mockJavaScriptChannel)!;
+ instanceManager.getIdentifier(mockJavaScriptChannel)!;
verify(mockPlatformHostApi.addJavaScriptChannel(
webViewInstanceId,
javaScriptChannelInstanceId,
@@ -243,6 +245,7 @@
);
final JavaScriptChannel mockJavaScriptChannel = MockJavaScriptChannel();
+ when(mockJavaScriptChannel.copy()).thenReturn(MockJavaScriptChannel());
when(mockJavaScriptChannel.channelName).thenReturn('aChannel');
expect(
@@ -254,7 +257,7 @@
webView.removeJavaScriptChannel(mockJavaScriptChannel);
final int javaScriptChannelInstanceId =
- instanceManager.getInstanceId(mockJavaScriptChannel)!;
+ instanceManager.getIdentifier(mockJavaScriptChannel)!;
verify(mockPlatformHostApi.removeJavaScriptChannel(
webViewInstanceId,
javaScriptChannelInstanceId,
@@ -268,10 +271,11 @@
);
final DownloadListener mockDownloadListener = MockDownloadListener();
+ when(mockDownloadListener.copy()).thenReturn(MockDownloadListener());
webView.setDownloadListener(mockDownloadListener);
final int downloadListenerInstanceId =
- instanceManager.getInstanceId(mockDownloadListener)!;
+ instanceManager.getIdentifier(mockDownloadListener)!;
verify(mockPlatformHostApi.setDownloadListener(
webViewInstanceId,
downloadListenerInstanceId,
@@ -285,6 +289,7 @@
instanceManager: instanceManager,
);
final WebViewClient mockWebViewClient = MockWebViewClient();
+ when(mockWebViewClient.copy()).thenReturn(MockWebViewClient());
when(mockWebViewClient.shouldOverrideUrlLoading).thenReturn(false);
webView.setWebViewClient(mockWebViewClient);
@@ -294,10 +299,11 @@
);
final WebChromeClient mockWebChromeClient = MockWebChromeClient();
+ when(mockWebChromeClient.copy()).thenReturn(MockWebChromeClient());
webView.setWebChromeClient(mockWebChromeClient);
final int webChromeClientInstanceId =
- instanceManager.getInstanceId(mockWebChromeClient)!;
+ instanceManager.getIdentifier(mockWebChromeClient)!;
verify(mockPlatformHostApi.setWebChromeClient(
webViewInstanceId,
webChromeClientInstanceId,
@@ -312,7 +318,7 @@
WebSettings.api =
WebSettingsHostApiImpl(instanceManager: instanceManager);
final int webSettingsInstanceId =
- instanceManager.getInstanceId(webView.settings)!;
+ instanceManager.getIdentifier(webView.settings)!;
webView.release();
verify(mockWebSettingsPlatformHostApi.dispose(webSettingsInstanceId));
@@ -333,7 +339,7 @@
late int webSettingsInstanceId;
setUp(() {
- instanceManager = InstanceManager();
+ instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {});
TestWebViewHostApi.setup(MockTestWebViewHostApi());
WebView.api = WebViewHostApiImpl(instanceManager: instanceManager);
@@ -346,7 +352,7 @@
);
webSettings = WebSettings(WebView());
- webSettingsInstanceId = instanceManager.getInstanceId(webSettings)!;
+ webSettingsInstanceId = instanceManager.getIdentifier(webSettings)!;
});
test('create', () {
@@ -463,14 +469,16 @@
late int mockJavaScriptChannelInstanceId;
setUp(() {
- instanceManager = InstanceManager();
+ instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {});
flutterApi = JavaScriptChannelFlutterApiImpl(
instanceManager: instanceManager,
);
mockJavaScriptChannel = MockJavaScriptChannel();
+ when(mockJavaScriptChannel.copy()).thenReturn(MockJavaScriptChannel());
+
mockJavaScriptChannelInstanceId =
- instanceManager.tryAddInstance(mockJavaScriptChannel)!;
+ instanceManager.addDartCreatedInstance(mockJavaScriptChannel);
});
test('postMessage', () {
@@ -501,17 +509,20 @@
late int mockWebViewInstanceId;
setUp(() {
- instanceManager = InstanceManager();
+ instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {});
flutterApi = WebViewClientFlutterApiImpl(
instanceManager: instanceManager,
);
mockWebViewClient = MockWebViewClient();
+ when(mockWebViewClient.copy()).thenReturn(MockWebViewClient());
mockWebViewClientInstanceId =
- instanceManager.tryAddInstance(mockWebViewClient)!;
+ instanceManager.addDartCreatedInstance(mockWebViewClient);
mockWebView = MockWebView();
- mockWebViewInstanceId = instanceManager.tryAddInstance(mockWebView)!;
+ when(mockWebView.copy()).thenReturn(MockWebView());
+ mockWebViewInstanceId =
+ instanceManager.addDartCreatedInstance(mockWebView);
});
test('onPageStarted', () {
@@ -621,14 +632,15 @@
late int mockDownloadListenerInstanceId;
setUp(() {
- instanceManager = InstanceManager();
+ instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {});
flutterApi = DownloadListenerFlutterApiImpl(
instanceManager: instanceManager,
);
mockDownloadListener = MockDownloadListener();
+ when(mockDownloadListener.copy()).thenReturn(MockDownloadListener());
mockDownloadListenerInstanceId =
- instanceManager.tryAddInstance(mockDownloadListener)!;
+ instanceManager.addDartCreatedInstance(mockDownloadListener);
});
test('onPageStarted', () {
@@ -666,17 +678,21 @@
late int mockWebViewInstanceId;
setUp(() {
- instanceManager = InstanceManager();
+ instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {});
flutterApi = WebChromeClientFlutterApiImpl(
instanceManager: instanceManager,
);
mockWebChromeClient = MockWebChromeClient();
+ when(mockWebChromeClient.copy()).thenReturn(MockWebChromeClient());
+
mockWebChromeClientInstanceId =
- instanceManager.tryAddInstance(mockWebChromeClient)!;
+ instanceManager.addDartCreatedInstance(mockWebChromeClient);
mockWebView = MockWebView();
- mockWebViewInstanceId = instanceManager.tryAddInstance(mockWebView)!;
+ when(mockWebView.copy()).thenReturn(MockWebView());
+ mockWebViewInstanceId =
+ instanceManager.addDartCreatedInstance(mockWebView);
});
test('onPageStarted', () {
@@ -722,7 +738,7 @@
webStorage = WebStorage();
webStorageInstanceId =
- WebStorage.api.instanceManager.getInstanceId(webStorage)!;
+ WebStorage.api.instanceManager.getIdentifier(webStorage)!;
});
test('create', () {
diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart
index 72d1d41..db877e5 100644
--- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart
@@ -59,7 +59,6 @@
/// A class which mocks [DownloadListener].
///
/// See the documentation for Mockito's code generation for more information.
-// ignore: must_be_immutable
class MockDownloadListener extends _i1.Mock implements _i2.DownloadListener {
MockDownloadListener() {
_i1.throwOnMissingStub(this);
@@ -81,7 +80,6 @@
/// A class which mocks [JavaScriptChannel].
///
/// See the documentation for Mockito's code generation for more information.
-// ignore: must_be_immutable
class MockJavaScriptChannel extends _i1.Mock implements _i2.JavaScriptChannel {
MockJavaScriptChannel() {
_i1.throwOnMissingStub(this);
@@ -417,7 +415,6 @@
/// A class which mocks [WebChromeClient].
///
/// See the documentation for Mockito's code generation for more information.
-// ignore: must_be_immutable
class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient {
MockWebChromeClient() {
_i1.throwOnMissingStub(this);
@@ -436,7 +433,6 @@
/// A class which mocks [WebView].
///
/// See the documentation for Mockito's code generation for more information.
-// ignore: must_be_immutable
class MockWebView extends _i1.Mock implements _i2.WebView {
MockWebView() {
_i1.throwOnMissingStub(this);
@@ -590,7 +586,6 @@
/// A class which mocks [WebViewClient].
///
/// See the documentation for Mockito's code generation for more information.
-// ignore: must_be_immutable
class MockWebViewClient extends _i1.Mock implements _i2.WebViewClient {
MockWebViewClient() {
_i1.throwOnMissingStub(this);
diff --git a/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart b/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart
index 3aeb005..dd3dcca 100644
--- a/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/instance_manager_test.dart
@@ -7,29 +7,137 @@
void main() {
group('InstanceManager', () {
- late InstanceManager testInstanceManager;
+ test('addHostCreatedInstance', () {
+ final CopyableObject object = CopyableObject();
- setUp(() {
- testInstanceManager = InstanceManager();
+ final InstanceManager instanceManager =
+ InstanceManager(onWeakReferenceRemoved: (_) {});
+
+ instanceManager.addHostCreatedInstance(object, 0);
+
+ expect(instanceManager.getIdentifier(object), 0);
+ expect(
+ instanceManager.getInstanceWithWeakReference(0),
+ object,
+ );
});
- test('tryAddInstance', () {
- final Object object = Object();
+ test('addHostCreatedInstance prevents already used objects and ids', () {
+ final CopyableObject object = CopyableObject();
- expect(testInstanceManager.tryAddInstance(object), 0);
- expect(testInstanceManager.getInstanceId(object), 0);
- expect(testInstanceManager.getInstance(0), object);
- expect(testInstanceManager.tryAddInstance(object), null);
+ final InstanceManager instanceManager =
+ InstanceManager(onWeakReferenceRemoved: (_) {});
+
+ instanceManager.addHostCreatedInstance(object, 0);
+
+ expect(
+ () => instanceManager.addHostCreatedInstance(object, 0),
+ throwsAssertionError,
+ );
+
+ expect(
+ () => instanceManager.addHostCreatedInstance(CopyableObject(), 0),
+ throwsAssertionError,
+ );
});
- test('removeInstance', () {
- final Object object = Object();
- testInstanceManager.tryAddInstance(object);
+ test('addFlutterCreatedInstance', () {
+ final CopyableObject object = CopyableObject();
- expect(testInstanceManager.removeInstance(object), 0);
- expect(testInstanceManager.getInstanceId(object), null);
- expect(testInstanceManager.getInstance(0), null);
- expect(testInstanceManager.removeInstance(object), null);
+ final InstanceManager instanceManager =
+ InstanceManager(onWeakReferenceRemoved: (_) {});
+
+ instanceManager.addDartCreatedInstance(object);
+
+ final int? instanceId = instanceManager.getIdentifier(object);
+ expect(instanceId, isNotNull);
+ expect(
+ instanceManager.getInstanceWithWeakReference(instanceId!),
+ object,
+ );
+ });
+
+ test('removeWeakReference', () {
+ final CopyableObject object = CopyableObject();
+
+ int? weakInstanceId;
+ final InstanceManager instanceManager =
+ InstanceManager(onWeakReferenceRemoved: (int instanceId) {
+ weakInstanceId = instanceId;
+ });
+
+ instanceManager.addHostCreatedInstance(object, 0);
+
+ expect(instanceManager.removeWeakReference(object), 0);
+ expect(
+ instanceManager.getInstanceWithWeakReference(0),
+ isA<CopyableObject>(),
+ );
+ expect(weakInstanceId, 0);
+ });
+
+ test('removeWeakReference removes only weak reference', () {
+ final CopyableObject object = CopyableObject();
+
+ final InstanceManager instanceManager =
+ InstanceManager(onWeakReferenceRemoved: (_) {});
+
+ instanceManager.addHostCreatedInstance(object, 0);
+
+ expect(instanceManager.removeWeakReference(object), 0);
+ final CopyableObject copy = instanceManager.getInstanceWithWeakReference(
+ 0,
+ )!;
+ expect(identical(object, copy), isFalse);
+ });
+
+ test('removeStrongReference', () {
+ final CopyableObject object = CopyableObject();
+
+ final InstanceManager instanceManager =
+ InstanceManager(onWeakReferenceRemoved: (_) {});
+
+ instanceManager.addHostCreatedInstance(object, 0);
+ instanceManager.removeWeakReference(object);
+ expect(instanceManager.remove(0), isA<CopyableObject>());
+ expect(instanceManager.containsIdentifier(0), isFalse);
+ });
+
+ test('removeStrongReference removes only strong reference', () {
+ final CopyableObject object = CopyableObject();
+
+ final InstanceManager instanceManager =
+ InstanceManager(onWeakReferenceRemoved: (_) {});
+
+ instanceManager.addHostCreatedInstance(object, 0);
+ expect(instanceManager.remove(0), isA<CopyableObject>());
+ expect(
+ instanceManager.getInstanceWithWeakReference(0),
+ object,
+ );
+ });
+
+ test('getInstance can add a new weak reference', () {
+ final CopyableObject object = CopyableObject();
+
+ final InstanceManager instanceManager =
+ InstanceManager(onWeakReferenceRemoved: (_) {});
+
+ instanceManager.addHostCreatedInstance(object, 0);
+ instanceManager.removeWeakReference(object);
+
+ final CopyableObject newWeakCopy =
+ instanceManager.getInstanceWithWeakReference(
+ 0,
+ )!;
+ expect(identical(object, newWeakCopy), isFalse);
});
});
}
+
+class CopyableObject with Copyable {
+ @override
+ Copyable copy() {
+ return CopyableObject();
+ }
+}
diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart
index 2432b35..2fa75af 100644
--- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart
@@ -828,7 +828,7 @@
// of WebView itstelf.
mockPlatformHostApi = MockTestWebViewHostApi();
TestWebViewHostApi.setup(mockPlatformHostApi);
- instanceManager = InstanceManager();
+ instanceManager = InstanceManager(onWeakReferenceRemoved: (_) {});
android_webview.WebView.api =
WebViewHostApiImpl(instanceManager: instanceManager);
});
diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart
index f9e8838..04c2d77 100644
--- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart
@@ -64,7 +64,6 @@
/// A class which mocks [WebSettings].
///
/// See the documentation for Mockito's code generation for more information.
-// ignore: must_be_immutable
class MockWebSettings extends _i1.Mock implements _i2.WebSettings {
MockWebSettings() {
_i1.throwOnMissingStub(this);
@@ -140,7 +139,6 @@
/// A class which mocks [WebStorage].
///
/// See the documentation for Mockito's code generation for more information.
-// ignore: must_be_immutable
class MockWebStorage extends _i1.Mock implements _i2.WebStorage {
MockWebStorage() {
_i1.throwOnMissingStub(this);
@@ -159,7 +157,6 @@
/// A class which mocks [WebView].
///
/// See the documentation for Mockito's code generation for more information.
-// ignore: must_be_immutable
class MockWebView extends _i1.Mock implements _i2.WebView {
MockWebView() {
_i1.throwOnMissingStub(this);