fix(webview): release retained macOS views before profile removal

This commit is contained in:
Levi committed 2026-09-15 09:01:58 +08:00
1 parent 311e002e55
commit 7e49b678f2
4 files changed
+78

No files matched your search

+2
View File
@@ -18,6 +18,8 @@ Local native changes are limited to:
setting and attach the profile before navigation.
- `MyCookieManager`: route cookie operations to the requested profile, capturing
the store per operation so asynchronous calls cannot cross profiles.
- macOS `FlutterWebViewController`: weakly register profile views so uninstall
can release their WKWebViews even when Flutter retains a platform-view wrapper.
`lib/app/rpc/webview_profile.dart` uses the existing plugin method channel and
serializes these internal settings. No extension JavaScript API is added.
@@ -7,8 +7,25 @@ import FlutterMacOS
final class GopeedProfiles {
private static var proxyURLs: [String: String] = [:]
private static var stores: [String: WKWebsiteDataStore] = [:]
private static var views: [String: NSHashTable<FlutterWebViewController>] = [:]
static func store(_ identifier: String) -> WKWebsiteDataStore? { stores[identifier] }
static func register(_ view: FlutterWebViewController, identifier: String) {
let profileViews = views[identifier] ?? NSHashTable<FlutterWebViewController>.weakObjects()
profileViews.add(view)
views[identifier] = profileViews
}
private static func disposeViews(_ identifier: String) {
// Flutter may retain a removed platform-view wrapper beyond the frame
// boundary. Release its WKWebView now, before deleting the named store.
// The host has already closed all pages belonging to this profile.
let profileViews = views.removeValue(forKey: identifier)
for view in profileViews?.allObjects ?? [] {
view.dispose(removeFromSuperview: true)
}
}
static func prepare(arguments: NSDictionary?, result: @escaping FlutterResult) {
guard #available(macOS 14.0, iOS 17.0, *),
let identifier = arguments?["gopeedProfileId"] as? String,
@@ -45,6 +62,7 @@ final class GopeedProfiles {
}
// Deleting a named store alone can leave in-process cookie caches.
// Clear every website data type before releasing/removing the store.
disposeViews(identifier)
let store = stores[identifier] ?? WKWebsiteDataStore(forIdentifier: uuid)
store.removeData(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(), modifiedSince: Date.distantPast) {
stores.removeValue(forKey: identifier)
@@ -68,6 +68,9 @@ public class FlutterWebViewController: NSView, Disposable {
webView!.settings = settings
webView!.prepare()
webView!.windowCreated = true
if let identifier = initialSettings["gopeedProfileId"] as? String {
GopeedProfiles.register(self, identifier: identifier)
}
}
required init?(coder nsCoder: NSCoder) {