mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-15 21:03:41 +00:00
fix(macos): resign WebView input focus before disposal
This commit is contained in:
1 parent
fe64a328ac
commit
3ed54ba93a
4 files changed
+8
-23
No files matched your search
Vendored
+2
-2
@@ -18,8 +18,8 @@ Local native changes are limited to:
|
|||||||
setting and attach the profile before navigation.
|
setting and attach the profile before navigation.
|
||||||
- `MyCookieManager`: route cookie operations to the requested profile, capturing
|
- `MyCookieManager`: route cookie operations to the requested profile, capturing
|
||||||
the store per operation so asynchronous calls cannot cross profiles.
|
the store per operation so asynchronous calls cannot cross profiles.
|
||||||
- macOS `FlutterWebViewController`: weakly register profile views so uninstall
|
- macOS `InAppWebView`: resign input focus before disposing a view so AppKit
|
||||||
can release their WKWebViews even when Flutter retains a platform-view wrapper.
|
cannot retain its profile through the responder chain after closing the page.
|
||||||
|
|
||||||
`lib/app/rpc/webview_profile.dart` uses the existing plugin method channel and
|
`lib/app/rpc/webview_profile.dart` uses the existing plugin method channel and
|
||||||
serializes these internal settings. No extension JavaScript API is added.
|
serializes these internal settings. No extension JavaScript API is added.
|
||||||
|
|||||||
-18
@@ -7,25 +7,8 @@ import FlutterMacOS
|
|||||||
final class GopeedProfiles {
|
final class GopeedProfiles {
|
||||||
private static var proxyURLs: [String: String] = [:]
|
private static var proxyURLs: [String: String] = [:]
|
||||||
private static var stores: [String: WKWebsiteDataStore] = [:]
|
private static var stores: [String: WKWebsiteDataStore] = [:]
|
||||||
private static var views: [String: NSHashTable<FlutterWebViewController>] = [:]
|
|
||||||
static func store(_ identifier: String) -> WKWebsiteDataStore? { stores[identifier] }
|
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) {
|
static func prepare(arguments: NSDictionary?, result: @escaping FlutterResult) {
|
||||||
guard #available(macOS 14.0, iOS 17.0, *),
|
guard #available(macOS 14.0, iOS 17.0, *),
|
||||||
let identifier = arguments?["gopeedProfileId"] as? String,
|
let identifier = arguments?["gopeedProfileId"] as? String,
|
||||||
@@ -62,7 +45,6 @@ final class GopeedProfiles {
|
|||||||
}
|
}
|
||||||
// Deleting a named store alone can leave in-process cookie caches.
|
// Deleting a named store alone can leave in-process cookie caches.
|
||||||
// Clear every website data type before releasing/removing the store.
|
// Clear every website data type before releasing/removing the store.
|
||||||
disposeViews(identifier)
|
|
||||||
let store = stores[identifier] ?? WKWebsiteDataStore(forIdentifier: uuid)
|
let store = stores[identifier] ?? WKWebsiteDataStore(forIdentifier: uuid)
|
||||||
store.removeData(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(), modifiedSince: Date.distantPast) {
|
store.removeData(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(), modifiedSince: Date.distantPast) {
|
||||||
stores.removeValue(forKey: identifier)
|
stores.removeValue(forKey: identifier)
|
||||||
|
|||||||
-3
@@ -68,9 +68,6 @@ public class FlutterWebViewController: NSView, Disposable {
|
|||||||
webView!.settings = settings
|
webView!.settings = settings
|
||||||
webView!.prepare()
|
webView!.prepare()
|
||||||
webView!.windowCreated = true
|
webView!.windowCreated = true
|
||||||
if let identifier = initialSettings["gopeedProfileId"] as? String {
|
|
||||||
GopeedProfiles.register(self, identifier: identifier)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder nsCoder: NSCoder) {
|
required init?(coder nsCoder: NSCoder) {
|
||||||
|
|||||||
Vendored
+6
@@ -2603,6 +2603,12 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public func dispose() {
|
public func dispose() {
|
||||||
|
// An edited web page can remain in AppKit's responder chain even after
|
||||||
|
// its headless wrapper is removed. Resign it while still in the window.
|
||||||
|
if let responder = window?.firstResponder as? NSView,
|
||||||
|
responder === self || responder.isDescendant(of: self) {
|
||||||
|
window?.makeFirstResponder(nil)
|
||||||
|
}
|
||||||
channelDelegate?.dispose()
|
channelDelegate?.dispose()
|
||||||
channelDelegate = nil
|
channelDelegate = nil
|
||||||
runWindowBeforeCreatedCallbacks()
|
runWindowBeforeCreatedCallbacks()
|
||||||
|
|||||||
Reference in new issue
Block a user