diff --git a/App/Client.xcodeproj/xcshareddata/xcschemes/ActionExtension.xcscheme b/App/Client.xcodeproj/xcshareddata/xcschemes/ActionExtension.xcscheme index 380f4146390..1ade4d922e9 100644 --- a/App/Client.xcodeproj/xcshareddata/xcschemes/ActionExtension.xcscheme +++ b/App/Client.xcodeproj/xcshareddata/xcschemes/ActionExtension.xcscheme @@ -73,7 +73,6 @@ savedToolIdentifier = "" useCustomWorkingDirectory = "NO" debugDocumentVersioning = "YES" - askForAppToLaunch = "Yes" launchAutomaticallySubstyle = "2"> diff --git a/App/iOS/Delegates/SceneDelegate.swift b/App/iOS/Delegates/SceneDelegate.swift index 755824de6d5..1e2f97fc0a0 100644 --- a/App/iOS/Delegates/SceneDelegate.swift +++ b/App/iOS/Delegates/SceneDelegate.swift @@ -243,7 +243,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { AppState.shared.dau.sendPingToServer() } - BraveSkusManager.refreshSKUCredential(isPrivate: scene.browserViewController?.privateBrowsingManager.isPrivateBrowsing == true) + Task { @MainActor in + await BraveSkusManager.refreshSKUCredential(isPrivate: scene.browserViewController?.privateBrowsingManager.isPrivateBrowsing == true) + } } func sceneWillResignActive(_ scene: UIScene) { diff --git a/Sources/Brave/BraveSkus/BraveSkusManager.swift b/Sources/Brave/BraveSkus/BraveSkusManager.swift index 9349b20cb38..a924d02fef4 100644 --- a/Sources/Brave/BraveSkus/BraveSkusManager.swift +++ b/Sources/Brave/BraveSkus/BraveSkusManager.swift @@ -10,6 +10,7 @@ import BraveCore import BraveVPN import os.log +@MainActor public class BraveSkusManager { private let sku: SkusSkusService @@ -22,7 +23,7 @@ public class BraveSkusManager { self.sku = skusService } - public static func refreshSKUCredential(isPrivate: Bool) { + public static func refreshSKUCredential(isPrivate: Bool) async { guard let _ = Preferences.VPN.skusCredential.value, let domain = Preferences.VPN.skusCredentialDomain.value, let expirationDate = Preferences.VPN.expirationDate.value else { @@ -39,98 +40,93 @@ public class BraveSkusManager { return } - manager.credentialSummary(for: domain) { completion in - Logger.module.debug("credentialSummary response") - } + _ = await manager.credentialSummary(for: domain) + Logger.module.debug("credentialSummary response") } // MARK: - Handling SKU methods. - func refreshOrder(for orderId: String, domain: String, resultJSON: @escaping (Any?) -> Void) { - sku.refreshOrder(domain, orderId: orderId) { completion in - do { - guard let data = completion.data(using: .utf8) else { return } - let json = try JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed) - Logger.module.debug("refreshOrder json parsed successfully") - resultJSON(json) - } catch { - resultJSON(nil) - Logger.module.error("refrshOrder: Failed to decode json: \(error.localizedDescription)") - } + func refreshOrder(for orderId: String, domain: String) async -> Any? { + let response = await sku.refreshOrder(domain, orderId: orderId) + + do { + guard let data = response.data(using: .utf8) else { return nil } + let json = try JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed) + Logger.module.debug("refreshOrder json parsed successfully") + return json + } catch { + Logger.module.error("refrshOrder: Failed to decode json: \(error.localizedDescription)") + return nil } } - func fetchOrderCredentials(for orderId: String, domain: String, resultCredential: @escaping (String) -> Void) { - sku.fetchOrderCredentials(domain, orderId: orderId) { completion in - Logger.module.debug("skus fetchOrderCredentials") - resultCredential(completion) - } + func fetchOrderCredentials(for orderId: String, domain: String) async -> String { + let credential = await sku.fetchOrderCredentials(domain, orderId: orderId) + Logger.module.debug("skus fetchOrderCredentials") + return credential } - func prepareCredentialsPresentation(for domain: String, path: String, - resultCredential: ((String) -> Void)?) { + func prepareCredentialsPresentation(for domain: String, path: String) async -> String { Logger.module.debug("skus prepareCredentialsPresentation") - sku.prepareCredentialsPresentation(domain, path: path) { credential in - if !credential.isEmpty { - if let vpnCredential = BraveSkusWebHelper.fetchVPNCredential(credential, domain: domain) { - Preferences.VPN.skusCredential.value = credential - Preferences.VPN.skusCredentialDomain.value = domain - Preferences.VPN.expirationDate.value = vpnCredential.expirationDate - - BraveVPN.setCustomVPNCredential(vpnCredential) - } - } else { - Logger.module.debug("skus empty credential from prepareCredentialsPresentation call") + + let credential = await sku.prepareCredentialsPresentation(domain, path: path) + + if !credential.isEmpty { + if let vpnCredential = BraveSkusWebHelper.fetchVPNCredential(credential, domain: domain) { + Preferences.VPN.skusCredential.value = credential + Preferences.VPN.skusCredentialDomain.value = domain + Preferences.VPN.expirationDate.value = vpnCredential.expirationDate + + BraveVPN.setCustomVPNCredential(vpnCredential) } - - resultCredential?(credential) + } else { + Logger.module.debug("skus empty credential from prepareCredentialsPresentation call") } + + return credential } - func credentialSummary(for domain: String, resultJSON: @escaping (Any?) -> Void) { - sku.credentialSummary(domain) { [self] completion in - do { - Logger.module.debug("skus credentialSummary") - - guard let data = completion.data(using: .utf8) else { - resultJSON(nil) - return + func credentialSummary(for domain: String) async -> Any? { + let credential = await sku.credentialSummary(domain) + + do { + Logger.module.debug("skus credentialSummary") + + guard let data = credential.data(using: .utf8) else { + return nil + } + + let json = try JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed) + + let jsonDecoder = JSONDecoder() + jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase + let credentialSummaryJson = try jsonDecoder.decode(CredentialSummary.self, from: data) + + switch credentialSummaryJson.state { + case .valid: + if Preferences.VPN.skusCredential.value == nil { + Logger.module.debug("The credential does NOT exists, calling prepareCredentialsPresentation") + let _ = await prepareCredentialsPresentation(for: domain, path: "*") + } else { + Logger.module.debug("The credential exists, NOT calling prepareCredentialsPresentation") } - let json = try JSONSerialization.jsonObject(with: data, options: .fragmentsAllowed) - - let jsonDecoder = JSONDecoder() - jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase - let credentialSummaryJson = try jsonDecoder.decode(CredentialSummary.self, from: data) - - switch credentialSummaryJson.state { - case .valid: - if Preferences.VPN.skusCredential.value == nil { - Logger.module.debug("The credential does NOT exists, calling prepareCredentialsPresentation") - self.prepareCredentialsPresentation(for: domain, path: "*") { _ in - // Keep the skus manager alive until preparing credential presentation finishes. - _ = self - } - } else { - Logger.module.debug("The credential exists, NOT calling prepareCredentialsPresentation") - } - case .sessionExpired: - Logger.module.debug("This credential session has expired") - Self.keepShowingSessionExpiredState = true - case .invalid: - if !credentialSummaryJson.active { - Logger.module.debug("The credential summary is not active") - } - - if credentialSummaryJson.remainingCredentialCount <= 0 { - Logger.module.debug("The credential summary does not have any remaining credentials") - } + case .sessionExpired: + Logger.module.debug("This credential session has expired") + Self.keepShowingSessionExpiredState = true + case .invalid: + if !credentialSummaryJson.active { + Logger.module.debug("The credential summary is not active") } - resultJSON(json) - } catch { - resultJSON(nil) - Logger.module.error("refrshOrder: Failed to decode json: \(error.localizedDescription)") + if credentialSummaryJson.remainingCredentialCount <= 0 { + Logger.module.debug("The credential summary does not have any remaining credentials") + } } + + return json + } catch { + Logger.module.error("refrshOrder: Failed to decode json: \(error.localizedDescription)") + return nil } } diff --git a/Sources/Brave/Frontend/Browser/BraveGetUA.swift b/Sources/Brave/Frontend/Browser/BraveGetUA.swift index 3961b0dcb89..c8f9c7693eb 100644 --- a/Sources/Brave/Frontend/Browser/BraveGetUA.swift +++ b/Sources/Brave/Frontend/Browser/BraveGetUA.swift @@ -30,8 +30,9 @@ class BraveGetUA: TabContentScript { in: scriptSandbox) }() - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { // 🙀 😭 🏃‍♀️💨 + return (nil, nil) } static var isActivated: Bool { diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+ReaderMode.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+ReaderMode.swift index dc649604492..d505770fb19 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+ReaderMode.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+ReaderMode.swift @@ -118,8 +118,8 @@ extension BrowserViewController { /// and be done with it. In the more complicated case, reader mode was already open for this page and we simply /// navigated away from it. So we look to the left and right in the BackForwardList to see if a readerized version /// of the current page is there. And if so, we go there. - - func enableReaderMode() { + @MainActor + func enableReaderMode() async { guard let tab = tabManager.selectedTab, let webView = tab.webView else { return } let backList = webView.backForwardList.backList @@ -132,11 +132,11 @@ extension BrowserViewController { if backList.count > 1 && backList.last?.url == readerModeURL { let playlistItem = tab.playlistItem webView.go(to: backList.last!) - PlaylistScriptHandler.updatePlaylistTab(tab: tab, item: playlistItem) + await PlaylistScriptHandler.updatePlaylistTab(tab: tab, item: playlistItem) } else if !forwardList.isEmpty && forwardList.first?.url == readerModeURL { let playlistItem = tab.playlistItem webView.go(to: forwardList.first!) - PlaylistScriptHandler.updatePlaylistTab(tab: tab, item: playlistItem) + await PlaylistScriptHandler.updatePlaylistTab(tab: tab, item: playlistItem) } else { // Store the readability result in the cache and load it. This will later move to the ReadabilityHelper. webView.evaluateSafeJavaScript(functionName: "\(ReaderModeNamespace).readerize", contentWorld: ReaderModeScriptHandler.scriptSandbox) { (object, error) -> Void in @@ -144,7 +144,9 @@ extension BrowserViewController { let playlistItem = tab.playlistItem try? self.readerModeCache.put(currentURL, readabilityResult) if webView.load(PrivilegedRequest(url: readerModeURL) as URLRequest) != nil { - PlaylistScriptHandler.updatePlaylistTab(tab: tab, item: playlistItem) + Task { @MainActor in + await PlaylistScriptHandler.updatePlaylistTab(tab: tab, item: playlistItem) + } } } } @@ -155,8 +157,8 @@ extension BrowserViewController { /// means that there is nothing in the BackForwardList except the internal url for the reader mode page. In that /// case we simply open a new page with the original url. In the more complicated page, the non-readerized version /// of the page is either to the left or right in the BackForwardList. If that is the case, we navigate there. - - func disableReaderMode() { + @MainActor + func disableReaderMode() async { if let tab = tabManager.selectedTab, let webView = tab.webView { let backList = webView.backForwardList.backList @@ -167,15 +169,15 @@ extension BrowserViewController { if backList.count > 1 && backList.last?.url == originalURL { let playlistItem = tab.playlistItem webView.go(to: backList.last!) - PlaylistScriptHandler.updatePlaylistTab(tab: tab, item: playlistItem) + await PlaylistScriptHandler.updatePlaylistTab(tab: tab, item: playlistItem) } else if !forwardList.isEmpty && forwardList.first?.url == originalURL { let playlistItem = tab.playlistItem webView.go(to: forwardList.first!) - PlaylistScriptHandler.updatePlaylistTab(tab: tab, item: playlistItem) + await PlaylistScriptHandler.updatePlaylistTab(tab: tab, item: playlistItem) } else { let playlistItem = tab.playlistItem if webView.load(URLRequest(url: originalURL)) != nil { - PlaylistScriptHandler.updatePlaylistTab(tab: tab, item: playlistItem) + await PlaylistScriptHandler.updatePlaylistTab(tab: tab, item: playlistItem) } } } diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+ShareActivity.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+ShareActivity.swift index 256fc0c5618..4029f0d35db 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+ShareActivity.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+ShareActivity.swift @@ -73,7 +73,9 @@ extension BrowserViewController { title: Strings.toggleReaderMode, braveSystemImage: "leo.product.speedreader", callback: { [weak self] in - self?.toggleReaderMode() + Task { @MainActor in + await self?.toggleReaderMode() + } } ) ) diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+TabManagerDelegate.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+TabManagerDelegate.swift index 73d2657894b..69cca5e7a06 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+TabManagerDelegate.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+TabManagerDelegate.swift @@ -187,6 +187,8 @@ extension BrowserViewController: TabManagerDelegate { if !privateBrowsingManager.isPrivateBrowsing { rewards.reportTabClosed(tabId: Int(tab.rewardsId)) } + + tab.destroy() } func tabManagerDidAddTabs(_ tabManager: TabManager) { diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+ToolbarDelegate.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+ToolbarDelegate.swift index 3dd001f37a8..bb68627c8db 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+ToolbarDelegate.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+ToolbarDelegate.swift @@ -127,7 +127,9 @@ extension BrowserViewController: TopToolbarDelegate { } func topToolbarDidPressReaderMode(_ topToolbar: TopToolbarView) { - toggleReaderMode() + Task { @MainActor in + await toggleReaderMode() + } } func topToolbarDidPressPlaylistButton(_ urlBar: TopToolbarView) { diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+WKNavigationDelegate.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+WKNavigationDelegate.swift index 0a58c67f48e..b4efbdd1484 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+WKNavigationDelegate.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+WKNavigationDelegate.swift @@ -1161,6 +1161,7 @@ extension BrowserViewController: WKUIDelegate { webView.evaluateSafeJavaScript(functionName: script, contentWorld: .defaultClient, asFunction: false) } + @MainActor func handleAlert(webView: WKWebView, alert: inout T, completionHandler: @escaping () -> Void) { guard let promptingTab = tabManager[webView], !promptingTab.blockAllAlerts else { suppressJSAlerts(webView: webView) @@ -1169,9 +1170,10 @@ extension BrowserViewController: WKUIDelegate { return } promptingTab.alertShownCount += 1 + let suppressBlock: JSAlertInfo.SuppressHandler = { [unowned self] suppress in if suppress { - func suppressDialogues(_: UIAlertAction) { + @MainActor func suppressDialogues(_: UIAlertAction) { self.suppressJSAlerts(webView: webView) promptingTab.blockAllAlerts = true self.tabManager[webView]?.cancelQueuedAlerts() diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController.swift index 4c9f461078b..4cd550b1e35 100644 --- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController.swift +++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController.swift @@ -1713,6 +1713,7 @@ public class BrowserViewController: UIViewController { // to report internal page load to Rewards lib var rewardsXHRLoadURL: URL? + @MainActor override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) { guard let webView = object as? WKWebView else { @@ -1945,8 +1946,10 @@ public class BrowserViewController: UIViewController { Task { @MainActor in do { - let result = await BraveCertificateUtils.verifyTrust(serverTrust, host: host, port: port) - + let result = BraveCertificateUtility.verifyTrust(serverTrust, + host: host, + port: port) + // Cert is valid! if result == 0 { tab.secureContentState = .secure @@ -2331,14 +2334,15 @@ public class BrowserViewController: UIViewController { } } - func toggleReaderMode() { + @MainActor + func toggleReaderMode() async { guard let tab = tabManager.selectedTab else { return } if let readerMode = tab.getContentScript(name: ReaderModeScriptHandler.scriptName) as? ReaderModeScriptHandler { switch readerMode.state { case .available: - enableReaderMode() + await enableReaderMode() case .active: - disableReaderMode() + await disableReaderMode() case .unavailable: break } diff --git a/Sources/Brave/Frontend/Browser/FaviconHandler.swift b/Sources/Brave/Frontend/Browser/FaviconHandler.swift index 72b69ea362a..3dcd06ede90 100644 --- a/Sources/Brave/Frontend/Browser/FaviconHandler.swift +++ b/Sources/Brave/Frontend/Browser/FaviconHandler.swift @@ -23,6 +23,7 @@ class FaviconHandler { unregister(tabObservers) } + @MainActor func loadFaviconURL( _ url: URL, forTab tab: Tab @@ -34,6 +35,7 @@ class FaviconHandler { } extension FaviconHandler: TabEventHandler { + @MainActor func tab(_ tab: Tab, didLoadPageMetadata metadata: PageMetadata) { if let currentURL = tab.url { if let favicon = FaviconFetcher.getIconFromCache(for: currentURL) { diff --git a/Sources/Brave/Frontend/Browser/FingerprintingProtection.swift b/Sources/Brave/Frontend/Browser/FingerprintingProtection.swift index 1f794f1de62..93852b4c561 100644 --- a/Sources/Brave/Frontend/Browser/FingerprintingProtection.swift +++ b/Sources/Brave/Frontend/Browser/FingerprintingProtection.swift @@ -30,11 +30,12 @@ class FingerprintingProtection: TabContentScript { in: scriptSandbox) }() - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } + @MainActor + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if let stats = self.tab?.contentBlocker.stats { self.tab?.contentBlocker.stats = stats.adding(fingerprintingCount: 1) BraveGlobalShieldStats.shared.fpProtection += 1 } + return (nil, nil) } } diff --git a/Sources/Brave/Frontend/Browser/FrequencyQuery.swift b/Sources/Brave/Frontend/Browser/FrequencyQuery.swift index 024aa17e974..5cc832ef67e 100644 --- a/Sources/Brave/Frontend/Browser/FrequencyQuery.swift +++ b/Sources/Brave/Frontend/Browser/FrequencyQuery.swift @@ -75,6 +75,7 @@ class FrequencyQuery { } } + @MainActor private func fetchSitesFromTabs(_ tabs: [Tab]) -> [Site] { var tabList = [Site]() diff --git a/Sources/Brave/Frontend/Browser/Helpers/BrowserNavigationHelper.swift b/Sources/Brave/Frontend/Browser/Helpers/BrowserNavigationHelper.swift index 5b58563ff6b..e87e166286f 100644 --- a/Sources/Brave/Frontend/Browser/Helpers/BrowserNavigationHelper.swift +++ b/Sources/Brave/Frontend/Browser/Helpers/BrowserNavigationHelper.swift @@ -74,6 +74,7 @@ class BrowserNavigationHelper { open(vc, doneButton: DoneButton(style: .done, position: .left)) } + @MainActor func openShareSheet() { guard let bvc = bvc else { return } dismissView() diff --git a/Sources/Brave/Frontend/Browser/Helpers/MetadataParserHelper.swift b/Sources/Brave/Frontend/Browser/Helpers/MetadataParserHelper.swift index b895e934496..f637708749b 100644 --- a/Sources/Brave/Frontend/Browser/Helpers/MetadataParserHelper.swift +++ b/Sources/Brave/Frontend/Browser/Helpers/MetadataParserHelper.swift @@ -21,6 +21,7 @@ class MetadataParserHelper: TabEventHandler { unregister(tabObservers) } + @MainActor func tab(_ tab: Tab, didChangeURL url: URL) { // Get the metadata out of the page-metadata-parser, and into a type safe struct as soon // as possible. diff --git a/Sources/Brave/Frontend/Browser/Helpers/ScreenshotHelper.swift b/Sources/Brave/Frontend/Browser/Helpers/ScreenshotHelper.swift index a89c01b8186..6695cb0312f 100644 --- a/Sources/Brave/Frontend/Browser/Helpers/ScreenshotHelper.swift +++ b/Sources/Brave/Frontend/Browser/Helpers/ScreenshotHelper.swift @@ -20,6 +20,7 @@ class ScreenshotHelper { self.tabManager = tabManager } + @MainActor func takeScreenshot(_ tab: Tab) { guard let webView = tab.webView, let url = tab.url else { Logger.module.error("Tab webView or url is nil") @@ -70,6 +71,7 @@ class ScreenshotHelper { } } + @MainActor func takePendingScreenshots(_ tabs: [Tab]) { for tab in tabs where tab.pendingScreenshot { tab.pendingScreenshot = false diff --git a/Sources/Brave/Frontend/Browser/LinkPreviewViewController.swift b/Sources/Brave/Frontend/Browser/LinkPreviewViewController.swift index aaa1afb1798..1336eab6348 100644 --- a/Sources/Brave/Frontend/Browser/LinkPreviewViewController.swift +++ b/Sources/Brave/Frontend/Browser/LinkPreviewViewController.swift @@ -61,7 +61,9 @@ class LinkPreviewViewController: UIViewController { } deinit { - self.currentTab?.navigationDelegate = nil - self.currentTab = nil + Task { @MainActor in + self.currentTab?.navigationDelegate = nil + self.currentTab = nil + } } } diff --git a/Sources/Brave/Frontend/Browser/NavigationRouter.swift b/Sources/Brave/Frontend/Browser/NavigationRouter.swift index 29706cdf34a..273b58a241a 100644 --- a/Sources/Brave/Frontend/Browser/NavigationRouter.swift +++ b/Sources/Brave/Frontend/Browser/NavigationRouter.swift @@ -15,6 +15,7 @@ public enum DeepLink: String { } // The root navigation for the Router. Look at the tests to see a complete URL +@MainActor public enum NavigationPath: Equatable { case url(webURL: URL?, isPrivate: Bool) case deepLink(DeepLink) diff --git a/Sources/Brave/Frontend/Browser/PageZoom/PageZoomHandler.swift b/Sources/Brave/Frontend/Browser/PageZoom/PageZoomHandler.swift index f727685131a..102c5ee9e30 100644 --- a/Sources/Brave/Frontend/Browser/PageZoom/PageZoomHandler.swift +++ b/Sources/Brave/Frontend/Browser/PageZoom/PageZoomHandler.swift @@ -8,6 +8,7 @@ import Shared import Data import Preferences +@MainActor class PageZoomHandler: ObservableObject { enum ChangeStatus { diff --git a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistCarplayController.swift b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistCarplayController.swift index 6a6db325390..09184c5feaf 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistCarplayController.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistCarplayController.swift @@ -27,6 +27,7 @@ enum PlaylistCarplayError: Error { case itemExpired(id: String) } +@MainActor class PlaylistCarplayController: NSObject { private let player: MediaPlayer private let mediaStreamer: PlaylistMediaStreamer diff --git a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistViewController.swift b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistViewController.swift index 3f33415fb24..c78bd3beced 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistViewController.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/Controllers/PlaylistViewController.swift @@ -99,11 +99,11 @@ class PlaylistViewController: UIViewController { if let item = PlaylistCarplayManager.shared.currentPlaylistItem { updateLastPlayedItem(item: item) } - + // Stop picture in picture player.pictureInPictureController?.delegate = nil player.pictureInPictureController?.stopPictureInPicture() - + // Simulator cannot "detect" if Car-Play is enabled, therefore we need to STOP playback // When this controller deallocates. The user can still manually resume playback in CarPlay. if !PlaylistCarplayManager.shared.isCarPlayAvailable { @@ -111,16 +111,19 @@ class PlaylistViewController: UIViewController { stop(playerView) PlaylistCarplayManager.shared.currentPlaylistItem = nil PlaylistCarplayManager.shared.currentlyPlayingItemIndex = -1 - + // Destroy folder observers folderObserver = nil PlaylistManager.shared.currentFolder = nil } - + // Cancel all loading. listController.stopLoadingSharedPlaylist() PlaylistManager.shared.playbackTask = nil - PlaylistCarplayManager.shared.playlistController = nil + + Task { @MainActor in + PlaylistCarplayManager.shared.playlistController = nil + } } override func viewDidLoad() { diff --git a/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCacheLoader.swift b/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCacheLoader.swift index 97cb1b376f4..01edc3e63f4 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCacheLoader.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCacheLoader.swift @@ -17,11 +17,13 @@ import os.log import Playlist class LivePlaylistWebLoaderFactory: PlaylistWebLoaderFactory { + @MainActor func makeWebLoader() -> PlaylistWebLoader { LivePlaylistWebLoader() } } +@MainActor class LivePlaylistWebLoader: UIView, PlaylistWebLoader { fileprivate static var pageLoadTimeout = 300.0 private var pendingRequests = [String: URLRequest]() @@ -60,11 +62,6 @@ class LivePlaylistWebLoader: UIView, PlaylistWebLoader { fatalError("init(coder:) has not been implemented") } - deinit { - self.removeFromSuperview() - } - - @MainActor func load(url: URL) async -> PlaylistInfo? { return await withCheckedContinuation { continuation in self.handler = { [weak self] in @@ -110,24 +107,26 @@ class LivePlaylistWebLoader: UIView, PlaylistWebLoader { webView.loadHTMLString("PlayList", baseURL: nil) } + @MainActor private class PlaylistWebLoaderContentHelper: TabContentScript { private weak var webLoader: LivePlaylistWebLoader? private var playlistItems = Set() private var isPageLoaded = false - private var timeout: DispatchWorkItem? + private var timeout: Task? init(_ webLoader: LivePlaylistWebLoader) { self.webLoader = webLoader - timeout = DispatchWorkItem(block: { [weak self] in - guard let self = self else { return } + timeout = Task.delayed(bySeconds: LivePlaylistWebLoader.pageLoadTimeout) { @MainActor in self.webLoader?.handler?(nil) self.webLoader?.tab.webView?.loadHTMLString("PlayList", baseURL: nil) self.webLoader = nil - }) - + } + if let timeout = timeout { - DispatchQueue.main.asyncAfter(deadline: .now() + LivePlaylistWebLoader.pageLoadTimeout, execute: timeout) + Task { + _ = try? await timeout.value + } } } @@ -138,14 +137,13 @@ class LivePlaylistWebLoader: UIView, PlaylistWebLoader { static let userScript: WKUserScript? = nil static let playlistProcessDocumentLoad = PlaylistScriptHandler.playlistProcessDocumentLoad - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { + @MainActor + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message) { assertionFailure("Missing required security token.") - return + return (nil, nil) } - replyHandler(nil, nil) - let cancelRequest = { self.timeout?.cancel() self.timeout = nil @@ -159,54 +157,56 @@ class LivePlaylistWebLoader: UIView, PlaylistWebLoader { if readyState.state == "cancel" { cancelRequest() - return + return (nil, nil) } if isPageLoaded { timeout?.cancel() - timeout = DispatchWorkItem(block: { [weak self] in - guard let self = self else { return } + timeout = Task.delayed(bySeconds: LivePlaylistWebLoader.pageLoadTimeout) { @MainActor in self.webLoader?.handler?(nil) self.webLoader?.tab.webView?.loadHTMLString("PlayList", baseURL: nil) self.webLoader = nil - }) - + } + if let timeout = timeout { - DispatchQueue.main.asyncAfter(deadline: .now() + LivePlaylistWebLoader.pageLoadTimeout, execute: timeout) + Task { + _ = try? await timeout.value + } } } - return + return (nil, nil) } guard let item = PlaylistInfo.from(message: message), item.detected else { cancelRequest() - return + return (nil, nil) } if item.isInvisible { timeout?.cancel() - timeout = DispatchWorkItem(block: { [weak self] in - guard let self = self else { return } + timeout = Task.delayed(bySeconds: LivePlaylistWebLoader.pageLoadTimeout) { @MainActor in self.webLoader?.handler?(nil) self.webLoader?.tab.webView?.loadHTMLString("PlayList", baseURL: nil) self.webLoader = nil - }) + } if let timeout = timeout { - DispatchQueue.main.asyncAfter(deadline: .now() + LivePlaylistWebLoader.pageLoadTimeout, execute: timeout) + Task { + _ = try? await timeout.value + } } - return + return (nil, nil) } // For now, we ignore base64 video mime-types loaded via the `data:` scheme. if item.duration <= 0.0 && !item.detected || item.src.isEmpty || item.src.hasPrefix("data:") || item.src.hasPrefix("blob:") { cancelRequest() - return + return (nil, nil) } - DispatchQueue.main.async { + await MainActor.run { if !self.playlistItems.contains(item.src) { self.playlistItems.insert(item.src) @@ -224,6 +224,8 @@ class LivePlaylistWebLoader: UIView, PlaylistWebLoader { self.webLoader?.tab.webView?.loadHTMLString("PlayList", baseURL: nil) self.webLoader = nil } + + return (nil, nil) } } } diff --git a/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCarplayManager.swift b/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCarplayManager.swift index 54aa844419c..a779720076d 100644 --- a/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCarplayManager.swift +++ b/Sources/Brave/Frontend/Browser/Playlist/Managers & Cache/PlaylistCarplayManager.swift @@ -32,6 +32,7 @@ public class PlaylistCarplayManager: NSObject { var isPlaylistControllerPresented = false // When Picture-In-Picture is enabled, we need to store a reference to the controller to keep it alive, otherwise if it deallocates, the system automatically kills Picture-In-Picture. + @MainActor var playlistController: PlaylistViewController? { didSet { // TODO: REFACTOR and Decide what happens to Playlist in multiple windows in the future @@ -54,6 +55,7 @@ public class PlaylistCarplayManager: NSObject { } } + @MainActor public func destroyPiP() { // This is the only way to have the system kill picture in picture as the restoration controller is deallocated // And that means the video is deallocated, its AudioSession is stopped, and the Picture-In-Picture controller is deallocated. @@ -69,6 +71,7 @@ public class PlaylistCarplayManager: NSObject { // in use at any given moment public static let shared = PlaylistCarplayManager() + @MainActor func getCarPlayController() -> PlaylistCarplayController? { // On iOS 14, we use CPTemplate (Custom UI) // We control what gets displayed @@ -106,6 +109,7 @@ public class PlaylistCarplayManager: NSObject { return carPlayController } + @MainActor func getPlaylistController(tab: Tab?, initialItem: PlaylistInfo?, initialItemPlaybackOffset: Double) -> PlaylistViewController { // If background playback is enabled (on iPhone), tabs will continue to play media @@ -132,6 +136,7 @@ public class PlaylistCarplayManager: NSObject { return playlistController } + @MainActor func getPlaylistController(tab: Tab?, completion: @escaping (PlaylistViewController) -> Void) { if let playlistController = self.playlistController { return completion(playlistController) @@ -157,6 +162,7 @@ public class PlaylistCarplayManager: NSObject { } } + @MainActor private func attemptInterfaceConnection(isCarPlayAvailable: Bool) { self.isCarPlayAvailable = isCarPlayAvailable diff --git a/Sources/Brave/Frontend/Browser/Tab.swift b/Sources/Brave/Frontend/Browser/Tab.swift index 23e0e385035..8a7088e21ed 100644 --- a/Sources/Brave/Frontend/Browser/Tab.swift +++ b/Sources/Brave/Frontend/Browser/Tab.swift @@ -30,8 +30,9 @@ protocol TabContentScript: TabContentScriptLoader { func verifyMessage(message: WKScriptMessage) -> Bool func verifyMessage(message: WKScriptMessage, securityToken: String) -> Bool - - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: @escaping (Any?, String?) -> Void) + + @MainActor + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) } protocol TabDelegate { @@ -75,6 +76,7 @@ enum TabSecureContentState { } } +@MainActor class Tab: NSObject { let id: UUID let rewardsId: UInt32 @@ -493,27 +495,19 @@ class Tab: NSObject { } webView = nil } - - deinit { + + func destroy() { deleteWebView() deleteNewTabPageController() contentScriptManager.helpers.removeAll() - - // A number of mojo-powered core objects have to be deconstructed on the same - // thread they were constructed - var mojoObjects: [Any?] = [ - _faviconDriver, - _syncTab, - _walletEthProvider, - _walletSolProvider, - _walletKeyringService - ] - - DispatchQueue.main.async { - // Reference inside to retain it, supress warnings by reading/writing - _ = mojoObjects - mojoObjects = [] - } + } + + deinit { +// Task { @MainActor in +// deleteWebView() +// deleteNewTabPageController() +// contentScriptManager.helpers.removeAll() +// } } var loading: Bool { @@ -902,23 +896,26 @@ extension Tab: TabWebViewDelegate { private class TabContentScriptManager: NSObject, WKScriptMessageHandlerWithReply { fileprivate var helpers = [String: TabContentScript]() + @MainActor func uninstall(from tab: Tab) { helpers.forEach { let name = type(of: $0.value).messageHandlerName tab.webView?.configuration.userContentController.removeScriptMessageHandler(forName: name) } } - - @objc func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage, replyHandler: @escaping (Any?, String?) -> Void) { + + @MainActor + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { for helper in helpers.values { let scriptMessageHandlerName = type(of: helper).messageHandlerName if scriptMessageHandlerName == message.name { - helper.userContentController(userContentController, didReceiveScriptMessage: message, replyHandler: replyHandler) - return + return await helper.userContentController(userContentController, didReceive: message) } } + return (nil, nil) } + @MainActor func addContentScript(_ helper: TabContentScript, name: String, forTab tab: Tab, contentWorld: WKContentWorld) { if let _ = helpers[name] { assertionFailure("Duplicate helper added: \(name)") @@ -932,6 +929,7 @@ private class TabContentScriptManager: NSObject, WKScriptMessageHandlerWithReply tab.webView?.configuration.userContentController.addScriptMessageHandler(self, contentWorld: contentWorld, name: scriptMessageHandlerName) } + @MainActor func removeContentScript(name: String, forTab tab: Tab, contentWorld: WKContentWorld) { if let helper = helpers[name] { let scriptMessageHandlerName = type(of: helper).messageHandlerName diff --git a/Sources/Brave/Frontend/Browser/TabManager.swift b/Sources/Brave/Frontend/Browser/TabManager.swift index df2cd8c781c..d442009140d 100644 --- a/Sources/Brave/Frontend/Browser/TabManager.swift +++ b/Sources/Brave/Frontend/Browser/TabManager.swift @@ -45,6 +45,7 @@ class WeakTabManagerDelegate { } // TabManager must extend NSObjectProtocol in order to implement WKNavigationDelegate +@MainActor class TabManager: NSObject { fileprivate var delegates = [WeakTabManagerDelegate]() fileprivate let tabEventHandlers: [TabEventHandler] diff --git a/Sources/Brave/Frontend/Browser/TabType.swift b/Sources/Brave/Frontend/Browser/TabType.swift index de6c6d5bf5a..8d103183994 100644 --- a/Sources/Brave/Frontend/Browser/TabType.swift +++ b/Sources/Brave/Frontend/Browser/TabType.swift @@ -36,8 +36,8 @@ enum TabType: Int, CustomDebugStringConvertible { /// /// - parameter tab: An object representing a Tab. /// - returns: A Tab type. + @MainActor static func of(_ tab: Tab?) -> TabType { return tab?.type ?? .regular } - } diff --git a/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift b/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift index de17ff6f165..16e6f997b3d 100644 --- a/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift +++ b/Sources/Brave/Frontend/Browser/Tabs/TabTray/TabTrayController.swift @@ -222,12 +222,12 @@ class TabTrayController: AuthenticationController { required init?(coder: NSCoder) { fatalError() } deinit { - tabManager.removeDelegate(self) - - // Remove the open tabs service observer - if let observer = openTabsSessionServiceListener { - braveCore.openTabsAPI.removeObserver(observer) - } +// tabManager.removeDelegate(self) +// +// // Remove the open tabs service observer +// if let observer = openTabsSessionServiceListener { +// braveCore.openTabsAPI.removeObserver(observer) +// } } override func viewDidLoad() { @@ -332,6 +332,17 @@ class TabTrayController: AuthenticationController { } } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + tabManager.removeDelegate(self) + + // Remove the open tabs service observer + if let observer = openTabsSessionServiceListener { + braveCore.openTabsAPI.removeObserver(observer) + } + } + override func loadView() { createTypeSelectorItems() layoutTabTray() diff --git a/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/VPNMenuButton.swift b/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/VPNMenuButton.swift index 0a6e668ba22..a72f3fdf15a 100644 --- a/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/VPNMenuButton.swift +++ b/Sources/Brave/Frontend/Browser/Toolbars/BottomToolbar/Menu/VPNMenuButton.swift @@ -35,6 +35,7 @@ struct VPNMenuButton: View { @ScaledMetric private var iconSize: CGFloat = 32.0 + @MainActor private var isVPNEnabledBinding: Binding { Binding( get: { isVPNEnabled }, @@ -42,6 +43,7 @@ struct VPNMenuButton: View { ) } + @MainActor private func toggleVPN(_ enabled: Bool) { if BraveSkusManager.keepShowingSessionExpiredState { let alert = BraveSkusManager.sessionExpiredStateAlert(loginCallback: { _ in @@ -80,6 +82,7 @@ struct VPNMenuButton: View { } } + @MainActor private var vpnToggle: some View { Toggle("Brave VPN", isOn: isVPNEnabledBinding) .toggleStyle(SwitchToggleStyle(tint: retryStateActive ? Color(.braveErrorBorder) : .accentColor)) diff --git a/Sources/Brave/Frontend/Browser/UserScriptManager.swift b/Sources/Brave/Frontend/Browser/UserScriptManager.swift index 3ed659a598c..b838774dca6 100644 --- a/Sources/Brave/Frontend/Browser/UserScriptManager.swift +++ b/Sources/Brave/Frontend/Browser/UserScriptManager.swift @@ -12,6 +12,7 @@ import os.log private class ScriptLoader: TabContentScriptLoader { } +@MainActor class UserScriptManager { static let shared = UserScriptManager() diff --git a/Sources/Brave/Frontend/Rewards/BraveRewards.swift b/Sources/Brave/Frontend/Rewards/BraveRewards.swift index b43df547a9e..434b9193af5 100644 --- a/Sources/Brave/Frontend/Rewards/BraveRewards.swift +++ b/Sources/Brave/Frontend/Rewards/BraveRewards.swift @@ -176,6 +176,7 @@ public class BraveRewards: NSObject { // MARK: - Reporting /// Report that a tab with a given id was updated + @MainActor func reportTabUpdated( tab: Tab, url: URL, diff --git a/Sources/Brave/Frontend/Share/ShareExtensionHelper.swift b/Sources/Brave/Frontend/Share/ShareExtensionHelper.swift index 8f7f8938af5..c4e7e65ebb1 100644 --- a/Sources/Brave/Frontend/Share/ShareExtensionHelper.swift +++ b/Sources/Brave/Frontend/Share/ShareExtensionHelper.swift @@ -7,6 +7,7 @@ import Shared import UIKit /// A helper class that aids in the creation of share sheets +@MainActor class ShareExtensionHelper { /// Create a activity view controller with the given elements. /// - Parameters: diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/BlockedDomainScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/BlockedDomainScriptHandler.swift index 95dcce2d86c..7bca1d752c4 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/BlockedDomainScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/BlockedDomainScriptHandler.swift @@ -20,17 +20,16 @@ class BlockedDomainScriptHandler: TabContentScript { static let scriptSandbox: WKContentWorld = .page static let userScript: WKUserScript? = nil - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } - + @MainActor + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message, securityToken: UserScriptManager.securityToken) { assertionFailure("Missing required security token.") - return + return (nil, nil) } guard let params = message.body as? [String: AnyObject], let action = params["action"] as? String else { assertionFailure("Missing required params.") - return + return (nil, nil) } switch action { @@ -41,8 +40,11 @@ class BlockedDomainScriptHandler: TabContentScript { default: assertionFailure("Unhandled action `\(action)`") } + + return (nil, nil) } + @MainActor private func blockedDomainDidProceed() { guard let url = tab?.url?.stippedInternalURL, let etldP1 = url.baseDomain else { assertionFailure("There should be no way this method can be triggered if the tab is not on an internal url") @@ -54,6 +56,7 @@ class BlockedDomainScriptHandler: TabContentScript { tab?.loadRequest(request) } + @MainActor private func blockedDomainDidGoBack() { guard let url = tab?.url?.stippedInternalURL else { assertionFailure("There should be no way this method can be triggered if the tab is not on an internal url") diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/ErrorPageScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/ErrorPageScriptHandler.swift index 4ae25393bf8..493d7784500 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/ErrorPageScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/ErrorPageScriptHandler.swift @@ -17,12 +17,10 @@ extension ErrorPageHelper: TabContentScript { static let scriptSandbox: WKContentWorld = .page static let userScript: WKUserScript? = nil - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } - + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message) { assertionFailure("Missing required security token.") - return + return (nil, nil) } guard let errorURL = message.frameInfo.request.url, @@ -31,21 +29,24 @@ extension ErrorPageHelper: TabContentScript { let originalURL = internalUrl.originalURLFromErrorPage, let res = message.body as? [String: String], let type = res["type"] - else { return } + else { + return (nil, nil) + } switch type { case MessageOpenInSafari: - UIApplication.shared.open(originalURL, options: [:]) + await UIApplication.shared.open(originalURL, options: [:]) case MessageCertVisitOnce: if let cert = CertificateErrorPageHandler.certsFromErrorURL(errorURL)?.first, let host = originalURL.host { let origin = "\(host):\(originalURL.port ?? 443)" addCertificate(cert, forOrigin: origin) - message.webView?.replaceLocation(with: originalURL) + await message.webView?.replaceLocation(with: originalURL) // webview.reload will not change the error URL back to the original URL } default: assertionFailure("Unknown error message") } + return (nil, nil) } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/SessionRestoreScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/SessionRestoreScriptHandler.swift index f0f8a2d3916..58a0be74dde 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/SessionRestoreScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/SessionRestoreScriptHandler.swift @@ -34,20 +34,20 @@ class SessionRestoreScriptHandler: TabContentScript { in: scriptSandbox) }() - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } - + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message, securityToken: UserScriptManager.securityToken) { assertionFailure("Missing required security token.") - return + return (nil, nil) } if let tab = tab, let params = message.body as? [String: AnyObject] { if params["name"] as? String == "didRestoreSession" { - DispatchQueue.main.async { + await MainActor.run { self.delegate?.sessionRestore(self, didRestoreSessionForTab: tab) } } } + + return (nil, nil) } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/Web3IPFSScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/Web3IPFSScriptHandler.swift index b47fa8023a0..e520afb8d13 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/Web3IPFSScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/Web3IPFSScriptHandler.swift @@ -27,11 +27,9 @@ class Web3IPFSScriptHandler: TabContentScript { static let scriptSandbox: WKContentWorld = .page static let userScript: WKUserScript? = nil - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } - + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { guard let params = message.body as? [String: String], let originalURL = originalURL else { - return + return (nil, nil) } if params["type"] == "IPFSDisable" { @@ -41,5 +39,7 @@ class Web3IPFSScriptHandler: TabContentScript { } else { assertionFailure("Invalid message: \(message.body)") } + + return (nil, nil) } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/Web3NameServiceScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/Web3NameServiceScriptHandler.swift index 0bc53b28538..6dc3407ef25 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/Web3NameServiceScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/Web3NameServiceScriptHandler.swift @@ -38,11 +38,9 @@ class Web3NameServiceScriptHandler: TabContentScript { static let scriptSandbox: WKContentWorld = .page static let userScript: WKUserScript? = nil - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } - + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { guard let params = message.body as? [String: String], let originalURL = originalURL else { - return + return (nil, nil) } if params[ParamKey.buttonType.rawValue] == ParamValue.disable.rawValue, @@ -56,5 +54,7 @@ class Web3NameServiceScriptHandler: TabContentScript { } else { assertionFailure("Invalid message: \(message.body)") } + + return (nil, nil) } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveSearchScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveSearchScriptHandler.swift index 7803e334741..8ebe4d7e012 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveSearchScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveSearchScriptHandler.swift @@ -58,14 +58,10 @@ class BraveSearchScriptHandler: TabContentScript { let methodId: Int } - func userContentController( - _ userContentController: WKUserContentController, - didReceiveScriptMessage message: WKScriptMessage, - replyHandler: (Any?, String?) -> Void - ) { + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message) { assertionFailure("Missing required security token.") - return + return (nil, nil) } let allowedHosts = DomainUserScript.braveSearchHelper.associatedDomains @@ -75,40 +71,37 @@ class BraveSearchScriptHandler: TabContentScript { message.frameInfo.isMainFrame else { Logger.module.error("Backup search request called from disallowed host") - replyHandler(nil, nil) - return + return (nil, nil) } guard let data = try? JSONSerialization.data(withJSONObject: message.body, options: []), let method = try? JSONDecoder().decode(MethodModel.self, from: data).methodId else { Logger.module.error("Failed to retrieve method id") - replyHandler(nil, nil) - return + return (nil, nil) } switch method { case Method.canSetBraveSearchAsDefault.rawValue: - handleCanSetBraveSearchAsDefault(replyHandler: replyHandler) + return await handleCanSetBraveSearchAsDefault() case Method.setBraveSearchDefault.rawValue: - handleSetBraveSearchDefault(replyHandler: replyHandler) + return await handleSetBraveSearchDefault() default: - break + return (nil, nil) } } - private func handleCanSetBraveSearchAsDefault(replyHandler: (Any?, String?) -> Void) { + @MainActor + private func handleCanSetBraveSearchAsDefault() async -> (Any?, String?) { if tab?.isPrivate == true { Logger.module.debug("Private mode detected, skipping setting Brave Search as a default") - replyHandler(false, nil) - return + return (false, nil) } let maximumPromptCount = Preferences.Search.braveSearchDefaultBrowserPromptCount if Self.canSetAsDefaultCounter >= maxCountOfDefaultBrowserPromptsPerSession || maximumPromptCount.value >= maxCountOfDefaultBrowserPromptsTotal { Logger.module.debug("Maximum number of tries of Brave Search website prompts reached") - replyHandler(false, nil) - return + return (false, nil) } Self.canSetAsDefaultCounter += 1 @@ -116,11 +109,11 @@ class BraveSearchScriptHandler: TabContentScript { let defaultEngine = profile.searchEngines.defaultEngine(forType: .standard).shortName let canSetAsDefault = defaultEngine != OpenSearchEngine.EngineNames.brave - replyHandler(canSetAsDefault, nil) + return (canSetAsDefault, nil) } - private func handleSetBraveSearchDefault(replyHandler: (Any?, String?) -> Void) { + private func handleSetBraveSearchDefault() async -> (Any?, String?) { profile.searchEngines.updateDefaultEngine(OpenSearchEngine.EngineNames.brave, forType: .standard) - replyHandler(nil, nil) + return (nil, nil) } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveSkusScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveSkusScriptHandler.swift index 6d14930d6a0..01019704c0b 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveSkusScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveSkusScriptHandler.swift @@ -16,6 +16,7 @@ class BraveSkusScriptHandler: TabContentScript { private let braveSkusManager: BraveSkusManager + @MainActor required init?(tab: Tab) { guard let manager = BraveSkusManager(isPrivateMode: tab.isPrivate) else { return nil @@ -47,12 +48,10 @@ class BraveSkusScriptHandler: TabContentScript { case credentialsSummary = 4 } - func userContentController(_ userContentController: WKUserContentController, - didReceiveScriptMessage message: WKScriptMessage, - replyHandler: @escaping (Any?, String?) -> Void) { + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message) { assertionFailure("Missing required security token.") - return + return (nil, nil) } let allowedHosts = DomainUserScript.braveSkus.associatedDomains @@ -61,40 +60,38 @@ class BraveSkusScriptHandler: TabContentScript { allowedHosts.contains(requestHost), message.frameInfo.isMainFrame else { Logger.module.error("Brave skus request called from disallowed host") - return + return (nil, nil) } guard let response = message.body as? [String: Any], let methodId = response["method_id"] as? Int, let data = response["data"] as? [String: Any] else { Logger.module.error("Failed to retrieve method id") - return + return (nil, nil) } switch methodId { case Method.refreshOrder.rawValue: if let orderId = data["orderId"] as? String { - braveSkusManager.refreshOrder(for: orderId, domain: requestHost) { result in - replyHandler(result, nil) - } + let result = await braveSkusManager.refreshOrder(for: orderId, domain: requestHost) + return (result, nil) } case Method.fetchOrderCredentials.rawValue: if let orderId = data["orderId"] as? String { - braveSkusManager.fetchOrderCredentials(for: orderId, domain: requestHost) { result in - replyHandler(result, nil) - } + let result = await braveSkusManager.fetchOrderCredentials(for: orderId, domain: requestHost) + return (result, nil) } case Method.prepareCredentialsPresentation.rawValue: assertionFailure("The website should never call the credentialsPresentation.") case Method.credentialsSummary.rawValue: if let domain = data["domain"] as? String { - braveSkusManager.credentialSummary(for: domain) { result in - replyHandler(result, nil) - } + let result = await braveSkusManager.credentialSummary(for: domain) + return (result, nil) } default: assertionFailure("Failure, the website called unhandled method with id: \(methodId)") } + return (nil, nil) } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveTalkScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveTalkScriptHandler.swift index faa37a3a60e..8d764a3bce8 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveTalkScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/BraveTalkScriptHandler.swift @@ -19,6 +19,7 @@ class BraveTalkScriptHandler: TabContentScript { private var rewardsEnabledReplyHandler: ((Any?, String?) -> Void)? private let launchNativeBraveTalk: (_ tab: Tab?, _ room: String, _ token: String) -> Void + @MainActor required init( tab: Tab, rewards: BraveRewards, @@ -81,14 +82,10 @@ class BraveTalkScriptHandler: TabContentScript { } } - func userContentController( - _ userContentController: WKUserContentController, - didReceiveScriptMessage message: WKScriptMessage, - replyHandler: @escaping (Any?, String?) -> Void - ) { + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message) { assertionFailure("Missing required security token.") - return + return (nil, nil) } let allowedHosts = DomainUserScript.braveTalkHelper.associatedDomains @@ -98,46 +95,51 @@ class BraveTalkScriptHandler: TabContentScript { message.frameInfo.isMainFrame else { Logger.module.error("Backup search request called from disallowed host") - replyHandler(nil, nil) - return + return (nil, nil) } guard let json = try? JSONSerialization.data(withJSONObject: message.body, options: []), let payload = try? JSONDecoder().decode(Payload.self, from: json) else { - return + return (nil, nil) } switch payload.kind { case .braveRequestAdsEnabled: - handleBraveRequestAdsEnabled(replyHandler) + return await withCheckedContinuation { continuation in + handleBraveRequestAdsEnabled { result, error in + continuation.resume(returning: (result, error)) + } + } case .launchNativeBraveTalk(let url): guard let components = URLComponents(string: url), case let room = String(components.path.dropFirst(1)), let jwt = components.queryItems?.first(where: { $0.name == "jwt" })?.value else { - return + return (nil, nil) } launchNativeBraveTalk(tab, room, jwt) - replyHandler(nil, nil) + return (nil, nil) } } private func handleBraveRequestAdsEnabled(_ replyHandler: @escaping (Any?, String?) -> Void) { - guard let rewards = rewards, tab?.isPrivate != true else { - replyHandler(false, nil) - return - } - - if rewards.isEnabled { - replyHandler(true, nil) - return - } - - // If rewards are disabled we show a Rewards panel, - // The `rewardsEnabledReplyHandler` will be called from other place. - if let tab = tab { - rewardsEnabledReplyHandler = replyHandler - tab.tabDelegate?.showRequestRewardsPanel(tab) + Task { @MainActor in + guard let rewards = rewards, tab?.isPrivate != true else { + replyHandler(false, nil) + return + } + + if rewards.isEnabled { + replyHandler(true, nil) + return + } + + // If rewards are disabled we show a Rewards panel, + // The `rewardsEnabledReplyHandler` will be called from other place. + if let tab = tab { + rewardsEnabledReplyHandler = replyHandler + tab.tabDelegate?.showRequestRewardsPanel(tab) + } } } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/ContentBlockerScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/ContentBlockerScriptHandler.swift index 0782a967096..d436c0d7cf2 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/ContentBlockerScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/ContentBlockerScriptHandler.swift @@ -47,17 +47,15 @@ extension ContentBlockerHelper: TabContentScript { blockedRequests.removeAll() } - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } - - guard let currentTabURL = tab?.webView?.url else { + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { + guard let currentTabURL = await tab?.webView?.url else { assertionFailure("Missing tab or webView") - return + return (nil, nil) } if !verifyMessage(message: message, securityToken: UserScriptManager.securityToken) { assertionFailure("Missing required security token.") - return + return (nil, nil) } do { @@ -134,5 +132,7 @@ extension ContentBlockerHelper: TabContentScript { } catch { Logger.module.error("\(error.localizedDescription)") } + + return (nil, nil) } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/CosmeticFiltersScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/CosmeticFiltersScriptHandler.swift index ac5c74e5ebd..d9c21c8c28b 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/CosmeticFiltersScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/CosmeticFiltersScriptHandler.swift @@ -36,11 +36,10 @@ class CosmeticFiltersScriptHandler: TabContentScript { self.tab = tab } - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: @escaping (Any?, String?) -> Void) { + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message) { assertionFailure("Invalid security token. Fix the `RequestBlocking.js` script") - replyHandler(nil, nil) - return + return (nil, nil) } do { @@ -48,11 +47,10 @@ class CosmeticFiltersScriptHandler: TabContentScript { let dto = try JSONDecoder().decode(CosmeticFiltersDTO.self, from: data) guard let frameURL = URL(string: dto.data.sourceURL) else { - replyHandler(nil, nil) - return + return (nil, nil) } - Task { @MainActor in + return await Task { @MainActor in let domain = Domain.getOrCreate(forUrl: frameURL, persistent: self.tab?.isPrivate == true ? false : true) let cachedEngines = await AdBlockStats.shared.cachedEngines(for: domain) @@ -83,14 +81,14 @@ class CosmeticFiltersScriptHandler: TabContentScript { } } - replyHandler([ + return ([ "aggressiveSelectors": Array(aggressiveSelectors), "standardSelectors": Array(standardSelectors) ], nil) - } + }.value } catch { assertionFailure("Invalid type of message. Fix the `RequestBlocking.js` script") - replyHandler(nil, nil) + return (nil, nil) } } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/EthereumProviderScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/EthereumProviderScriptHandler.swift index 71979f4a24e..25f5d9ec693 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/EthereumProviderScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/EthereumProviderScriptHandler.swift @@ -60,14 +60,11 @@ class EthereumProviderScriptHandler: TabContentScript { } } - @MainActor func userContentController( - _ userContentController: WKUserContentController, - didReceiveScriptMessage message: WKScriptMessage, - replyHandler: @escaping (Any?, String?) -> Void - ) { + @MainActor + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message) { assertionFailure("Missing required security token.") - return + return (nil, nil) } guard let tab = tab, @@ -80,55 +77,53 @@ class EthereumProviderScriptHandler: TabContentScript { let body = try? JSONDecoder().decode(MessageBody.self, from: messageData) else { Logger.module.error("Failed to handle ethereum provider communication") - return + return (nil, nil) } if message.webView?.url?.isLocal == false, message.webView?.hasOnlySecureContent == false { // prevent communication in mixed-content scenarios Logger.module.error("Failed ethereum provider communication security test") - return + return (nil, nil) } // The web page has communicated with `window.ethereum`, so we should show the wallet icon tab.isWalletIconVisible = true + @MainActor func handleResponse( - id: MojoBase.Value, + _ response: (id: MojoBase.Value, formedResponse: MojoBase.Value, reject: Bool, firstAllowedAccount: String, - updateJSProperties: Bool - ) { - Task { @MainActor in - if updateJSProperties { - await tab.updateEthereumProperties() - } - - if reject { - replyHandler(nil, formedResponse.jsonString) - } else { - replyHandler(formedResponse.jsonObject, nil) - } + updateJSProperties: Bool) + ) async -> (Any?, String?) { + if response.updateJSProperties { + await tab.updateEthereumProperties() + } + + if response.reject { + return (nil, response.formedResponse.jsonString) + } else { + return (response.formedResponse.jsonObject, nil) } } switch body.method { case .request: guard let requestPayload = MojoBase.Value(jsonString: body.args) else { - replyHandler(nil, "Invalid args") - return + return (nil, "Invalid args") } - provider.request(requestPayload, completion: handleResponse) + + return await handleResponse(provider.request(requestPayload)) case .isConnected: - replyHandler(nil, nil) + return (nil, nil) case .enable: - provider.enable(handleResponse) + return await handleResponse(provider.enable()) case .sendAsync: guard let requestPayload = MojoBase.Value(jsonString: body.args) else { - replyHandler(nil, "Invalid args") - return + return (nil, "Invalid args") } - provider.sendAsync(requestPayload, completion: handleResponse) + return await handleResponse(provider.sendAsync(requestPayload)) case .send: struct SendPayload { var method: String @@ -142,37 +137,28 @@ class EthereumProviderScriptHandler: TabContentScript { } } guard let sendPayload = SendPayload(payload: body.args) else { - replyHandler(nil, "Invalid args") - return + return (nil, "Invalid args") } if sendPayload.method.isEmpty { if let params = sendPayload.params, params.tag != .null { - provider.sendAsync(params, completion: handleResponse) - } else { - // Empty method with no params is not valid - replyHandler(nil, "Invalid args") + return await handleResponse(provider.sendAsync(params)) } - return + + // Empty method with no params is not valid + return (nil, "Invalid args") } if !Self.supportedSingleArgMethods.contains(sendPayload.method), (sendPayload.params == nil || sendPayload.params?.tag == .null) { // If its not a single arg supported method and there are no parameters then its not a valid // call - replyHandler(nil, "Invalid args") - return + return (nil, "Invalid args") } - provider.send( - sendPayload.method, - params: sendPayload.params ?? .init(listValue: []), - completion: handleResponse - ) + return await handleResponse(provider.send(sendPayload.method, params: sendPayload.params ?? .init(listValue: []))) case .isUnlocked: - provider.isLocked { isLocked in - replyHandler(!isLocked, nil) - } + return await (!provider.isLocked(), nil) } } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/PlaylistFolderSharingScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/PlaylistFolderSharingScriptHandler.swift index 0613745d2c0..9994c3b4e9e 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/PlaylistFolderSharingScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/PlaylistFolderSharingScriptHandler.swift @@ -39,12 +39,10 @@ class PlaylistFolderSharingScriptHandler: NSObject, TabContentScript { in: scriptSandbox) }() - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } - + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message) { assertionFailure("Missing required security token.") - return + return (nil, nil) } if let sharingInfo = PlaylistFolderSharingInfo.from(message: message) { @@ -56,6 +54,7 @@ class PlaylistFolderSharingScriptHandler: NSObject, TabContentScript { delegate?.openPlaylistSharingFolder(with: sharedFolderPageUrl) } + return (nil, nil) } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/PlaylistScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/PlaylistScriptHandler.swift index fdc05c24aa0..11b2659dd3c 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/PlaylistScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/PlaylistScriptHandler.swift @@ -34,6 +34,7 @@ class PlaylistScriptHandler: NSObject, TabContentScript { private var asset: AVURLAsset? private static let queue = DispatchQueue(label: "com.playlisthelper.queue", qos: .userInitiated) + @MainActor init(tab: Tab) { self.tab = tab self.url = tab.url @@ -90,38 +91,39 @@ class PlaylistScriptHandler: NSObject, TabContentScript { in: scriptSandbox) }() - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } - + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message) { assertionFailure("Missing required security token.") - return + return (nil, nil) } // If this URL is blocked from Playlist support, do nothing if url?.isPlaylistBlockedSiteURL == true { - return + return (nil, nil) } if ReadyState.from(message: message) != nil { - return + return (nil, nil) } - Self.processPlaylistInfo( + await Self.processPlaylistInfo( handler: self, item: PlaylistInfo.from(message: message)) + + return (nil, nil) } - private class func processPlaylistInfo(handler: PlaylistScriptHandler, item: PlaylistInfo?) { + @MainActor + private class func processPlaylistInfo(handler: PlaylistScriptHandler, item: PlaylistInfo?) async { guard var item = item, !item.src.isEmpty else { - DispatchQueue.main.async { + await MainActor.run { handler.delegate?.updatePlaylistURLBar(tab: handler.tab, state: .none, item: nil) } return } if handler.url?.baseDomain != "soundcloud.com", item.isInvisible { - DispatchQueue.main.async { + await MainActor.run { handler.delegate?.updatePlaylistURLBar(tab: handler.tab, state: .none, item: nil) } return @@ -213,6 +215,7 @@ class PlaylistScriptHandler: NSObject, TabContentScript { extension PlaylistScriptHandler: UIGestureRecognizerDelegate { @objc + @MainActor func onLongPressedWebView(_ gestureRecognizer: UILongPressGestureRecognizer) { if gestureRecognizer.state == .began, let webView = tab?.webView, @@ -275,6 +278,7 @@ extension PlaylistScriptHandler { } } + @MainActor static func stopPlayback(tab: Tab?) { guard let tab = tab else { return } @@ -290,9 +294,10 @@ extension PlaylistScriptHandler { } extension PlaylistScriptHandler { - static func updatePlaylistTab(tab: Tab, item: PlaylistInfo?) { + @MainActor + static func updatePlaylistTab(tab: Tab, item: PlaylistInfo?) async { if let handler = tab.getContentScript(name: Self.scriptName) as? PlaylistScriptHandler { - Self.processPlaylistInfo(handler: handler, item: item) + await Self.processPlaylistInfo(handler: handler, item: item) } } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/PrintScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/PrintScriptHandler.swift index 855a8e79c90..147f16131ad 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/PrintScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/PrintScriptHandler.swift @@ -36,12 +36,11 @@ class PrintScriptHandler: TabContentScript { in: scriptSandbox) }() - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } - + @MainActor + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message, securityToken: UserScriptManager.securityToken) { assertionFailure("Missing required security token.") - return + return (nil, nil) } if let tab = tab, let webView = tab.webView, let url = webView.url { @@ -54,7 +53,7 @@ class PrintScriptHandler: TabContentScript { currentDomain = url.baseDomain if isPresentingController || isBlocking { - return + return (nil, nil) } let showPrintSheet = { [weak self] in @@ -105,5 +104,7 @@ class PrintScriptHandler: TabContentScript { showPrintSheet() } } + + return (nil, nil) } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/ReadyStateScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/ReadyStateScriptHandler.swift index 8360fa8af0a..92e6a171526 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/ReadyStateScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/ReadyStateScriptHandler.swift @@ -68,20 +68,19 @@ class ReadyStateScriptHandler: TabContentScript { in: scriptSandbox) }() - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - - defer { replyHandler(nil, nil) } - + @MainActor + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message) { assertionFailure("Missing required security token.") - return + return (nil, nil) } guard let readyState = ReadyState.from(message: message) else { Logger.module.error("Invalid Ready State") - return + return (nil, nil) } tab?.onPageReadyStateChanged?(readyState.state) + return (nil, nil) } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/RequestBlockingContentScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/RequestBlockingContentScriptHandler.swift index d5fac2ab186..eeed899252d 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/RequestBlockingContentScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/RequestBlockingContentScriptHandler.swift @@ -46,16 +46,16 @@ class RequestBlockingContentScriptHandler: TabContentScript { self.tab = tab } - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: @escaping (Any?, String?) -> Void) { + @MainActor + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { guard let tab = tab, let currentTabURL = tab.webView?.url else { assertionFailure("Should have a tab set") - return + return (nil, nil) } if !verifyMessage(message: message) { assertionFailure("Invalid security token. Fix the `RequestBlocking.js` script") - replyHandler(false, nil) - return + return (false, nil) } do { @@ -64,13 +64,13 @@ class RequestBlockingContentScriptHandler: TabContentScript { // Because javascript urls allow some characters that `URL` does not, // we use `NSURL(idnString: String)` to parse them - guard let requestURL = NSURL(idnString: dto.data.resourceURL) as URL? else { return } - guard let sourceURL = NSURL(idnString: dto.data.sourceURL) as URL? else { return } + guard let requestURL = NSURL(idnString: dto.data.resourceURL) as URL? else { return (nil, nil) } + guard let sourceURL = NSURL(idnString: dto.data.sourceURL) as URL? else { return (nil, nil) } let isPrivateBrowsing = tab.isPrivate - Task { @MainActor in + return await Task { @MainActor in let domain = Domain.getOrCreate(forUrl: currentTabURL, persistent: !isPrivateBrowsing) - guard let domainURLString = domain.url else { return } + guard let domainURLString = domain.url else { return (nil, nil) } let shouldBlock = await AdBlockStats.shared.shouldBlock( requestURL: requestURL, sourceURL: sourceURL, resourceType: dto.data.resourceType, isAggressiveMode: domain.blockAdsAndTrackingLevel.isAggressive @@ -82,9 +82,8 @@ class RequestBlockingContentScriptHandler: TabContentScript { // For subframes which may use different etld+1 than the main frame (example `reddit.com` and `redditmedia.com`) // We simply check the known subframeURLs on this page. guard tab.url?.baseDomain == sourceURL.baseDomain || - self.tab?.currentPageData?.allSubframeURLs.contains(sourceURL) == true else { - replyHandler(shouldBlock, nil) - return + self.tab?.currentPageData?.allSubframeURLs.contains(sourceURL) == true else { + return (shouldBlock, nil) } if shouldBlock, Preferences.PrivacyReports.captureShieldsData.value, @@ -93,7 +92,7 @@ class RequestBlockingContentScriptHandler: TabContentScript { !isPrivateBrowsing { PrivacyReportsManager.pendingBlockedRequests.append((blockedResourceHost, domainURL, Date())) } - + if shouldBlock && !tab.contentBlocker.blockedRequests.contains(requestURL) { BraveGlobalShieldStats.shared.adblock += 1 let stats = tab.contentBlocker.stats @@ -101,11 +100,11 @@ class RequestBlockingContentScriptHandler: TabContentScript { tab.contentBlocker.blockedRequests.insert(requestURL) } - replyHandler(shouldBlock, nil) - } + return (shouldBlock, nil) + }.value } catch { assertionFailure("Invalid type of message. Fix the `RequestBlocking.js` script") - replyHandler(false, nil) + return (false, nil) } } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/RewardsReportingScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/RewardsReportingScriptHandler.swift index acdd7f3dba3..5dcaa48847f 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/RewardsReportingScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/RewardsReportingScriptHandler.swift @@ -34,45 +34,48 @@ class RewardsReportingScriptHandler: TabContentScript { in: scriptSandbox) }() - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { struct Content: Decodable { var method: String var url: String var data: String? var referrerUrl: String? } - - if tab?.isPrivate == true || !rewards.isEnabled { - return - } if !verifyMessage(message: message) { assertionFailure("Missing required security token.") - return + return (nil, nil) } - - do { + + await MainActor.run { + guard let tab = tab, !tab.isPrivate, rewards.isEnabled, let tabURL = tab.url else { + return + } + guard let body = message.body as? [String: AnyObject] else { return } - - if let body = body["data"] as? [String: AnyObject] { - let json = try JSONSerialization.data(withJSONObject: body, options: []) - var content = try JSONDecoder().decode(Content.self, from: json) - - guard let tab = tab, let tabURL = tab.url else { return } - - if content.url.hasPrefix("//") { - content.url = "\(tabURL.scheme ?? "http"):\(content.url)" + + Task { + do { + if let body = body["data"] as? [String: AnyObject] { + let json = try JSONSerialization.data(withJSONObject: body, options: []) + var content = try JSONDecoder().decode(Content.self, from: json) + + if content.url.hasPrefix("//") { + content.url = "\(tabURL.scheme ?? "http"):\(content.url)" + } + + guard let url = URL(string: content.url) else { return } + let refURL = URL(string: content.referrerUrl ?? "") + rewards.reportXHRLoad(url: url, tabId: Int(tab.rewardsId), firstPartyURL: tabURL, referrerURL: refURL) + } + } catch { + adsRewardsLog.error("Failed to parse message from rewards reporting JS: \(error.localizedDescription)") } - - guard let url = URL(string: content.url) else { return } - let refURL = URL(string: content.referrerUrl ?? "") - rewards.reportXHRLoad(url: url, tabId: Int(tab.rewardsId), firstPartyURL: tabURL, referrerURL: refURL) } - } catch { - adsRewardsLog.error("Failed to parse message from rewards reporting JS: \(error.localizedDescription)") } + + return (nil, nil) } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/SolanaProviderScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/SolanaProviderScriptHandler.swift index 8bf664b7501..9d8c1bac832 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/SolanaProviderScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/SolanaProviderScriptHandler.swift @@ -71,10 +71,11 @@ class SolanaProviderScriptHandler: TabContentScript { } } - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: @escaping (Any?, String?) -> Void) { + @MainActor + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message) { assertionFailure("Missing required security token.") - return + return (nil, nil) } guard let tab = tab, @@ -87,57 +88,56 @@ class SolanaProviderScriptHandler: TabContentScript { let body = try? JSONDecoder().decode(MessageBody.self, from: messageData) else { Logger.module.error("Failed to handle solana provider communication") - return + return (nil, nil) } if message.webView?.url?.isLocal == false, message.webView?.hasOnlySecureContent == false { // prevent communication in mixed-content scenarios Logger.module.error("Failed solana provider communication security test") - return + return (nil, nil) } // The web page has communicated with `window.solana`, so we should show the wallet icon tab.isWalletIconVisible = true - Task { @MainActor in + return await Task { @MainActor in switch body.method { case .connect: let (publicKey, error) = await connect(args: body.args) - replyHandler(publicKey, error) if let publicKey = publicKey as? String { await emitConnectEvent(publicKey: publicKey) } + return (publicKey, error) case .disconnect: provider.disconnect() - replyHandler("{:}", nil) + return ("{:}", nil) case .signAndSendTransaction: let (result, error) = await signAndSendTransaction(args: body.args) - replyHandler(result, error) + return (result, error) case .signMessage: let (result, error) = await signMessage(args: body.args) - replyHandler(result, error) + return (result, error) case .request: guard let args = body.args, let argDict = MojoBase.Value(jsonString: args)?.dictionaryValue, let method = argDict[Keys.method.rawValue]?.stringValue else { - replyHandler(nil, buildErrorJson(status: .invalidParams, errorMessage: "Invalid args")) - return + return (nil, buildErrorJson(status: .invalidParams, errorMessage: "Invalid args")) } let (result, error) = await request(args: body.args) - replyHandler(result, error) if method == Keys.connect.rawValue, let publicKey = result as? String { await emitConnectEvent(publicKey: publicKey) } else if method == Keys.disconnect.rawValue { tab.emitSolanaEvent(.disconnect) } + return (result, error) case .signTransaction: let (result, error) = await signTransaction(args: body.args) - replyHandler(result, error) + return (result, error) case .signAllTransactions: let (result, error) = await signAllTransactions(args: body.args) - replyHandler(result, error) + return (result, error) } - } + }.value } /// Given optional args `{onlyIfTrusted: Bool}`, will return the base 58 encoded public key for success or the error dictionary for failures. diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/URLPartinessScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/URLPartinessScriptHandler.swift index f4d84d22043..2b9294a9046 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/URLPartinessScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/URLPartinessScriptHandler.swift @@ -35,11 +35,10 @@ class URLPartinessScriptHandler: TabContentScript { self.tab = tab } - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: @escaping (Any?, String?) -> Void) { + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message) { assertionFailure("Invalid security token. Fix the `SelectorsPollerScript.js` script") - replyHandler(nil, nil) - return + return (nil, nil) } do { @@ -54,8 +53,7 @@ class URLPartinessScriptHandler: TabContentScript { results[urlString] = false } - replyHandler(results, nil) - return + return (results, nil) } let frameETLD1 = frameURL.baseDomain @@ -71,10 +69,10 @@ class URLPartinessScriptHandler: TabContentScript { results[urlString] = frameETLD1 == etld1 } - replyHandler(results, nil) + return (results, nil) } catch { assertionFailure("Invalid type of message. Fix the `RequestBlocking.js` script") - replyHandler(nil, nil) + return (nil, nil) } } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/YoutubeQualityScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/YoutubeQualityScriptHandler.swift index 2f95cdad199..a40b613eaa8 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/YoutubeQualityScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Paged/YoutubeQualityScriptHandler.swift @@ -14,6 +14,7 @@ class YoutubeQualityScriptHandler: NSObject, TabContentScript { private var url: URL? private var urlObserver: NSObjectProtocol? + @MainActor init(tab: Tab) { self.tab = tab self.url = tab.url @@ -21,12 +22,12 @@ class YoutubeQualityScriptHandler: NSObject, TabContentScript { urlObserver = tab.webView?.observe( \.url, options: [.new], - changeHandler: { [weak self] object, change in + changeHandler: { [weak self] webView, change in guard let self = self, let url = change.newValue else { return } if self.url?.withoutFragment != url?.withoutFragment { self.url = url - object.evaluateSafeJavaScript(functionName: "window.__firefox__.\(Self.refreshQuality)", + webView.evaluateSafeJavaScript(functionName: "window.__firefox__.\(Self.refreshQuality)", contentWorld: Self.scriptSandbox, asFunction: true) } @@ -56,19 +57,20 @@ class YoutubeQualityScriptHandler: NSObject, TabContentScript { in: scriptSandbox) }() + @MainActor static func setEnabled(option: Preferences.Option, for tab: Tab) { let enabled = canEnableHighQuality(option: option) tab.webView?.evaluateSafeJavaScript(functionName: "window.__firefox__.\(Self.setQuality)", args: [enabled ? Self.highestQuality: "''"], contentWorld: Self.scriptSandbox, escapeArgs: false, asFunction: true) } - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message) { assertionFailure("Missing required security token.") - return + return (nil, nil) } - replyHandler(Self.canEnableHighQuality(option: Preferences.General.youtubeHighQuality) ? + return (Self.canEnableHighQuality(option: Preferences.General.youtubeHighQuality) ? Self.highestQuality : "", nil) } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/AdsMediaReportingScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/AdsMediaReportingScriptHandler.swift index 8a7b0f7a89f..2896377a610 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/AdsMediaReportingScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/AdsMediaReportingScriptHandler.swift @@ -23,25 +23,27 @@ class AdsMediaReportingScriptHandler: TabContentScript { static let scriptSandbox: WKContentWorld = .defaultClient static let userScript: WKUserScript? = nil - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } - + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message, securityToken: UserScriptManager.securityToken) { assertionFailure("Missing required security token.") - return + return (nil, nil) } guard let body = message.body as? [String: AnyObject] else { - return + return (nil, nil) } - if let isPlaying = body["data"] as? Bool, rewards.isEnabled { + await MainActor.run { guard let tab = tab else { return } - if isPlaying { - rewards.reportMediaStarted(tabId: Int(tab.rewardsId)) - } else { - rewards.reportMediaStopped(tabId: Int(tab.rewardsId)) + + if let isPlaying = body["data"] as? Bool, rewards.isEnabled { + if isPlaying { + rewards.reportMediaStarted(tabId: Int(tab.rewardsId)) + } else { + rewards.reportMediaStopped(tabId: Int(tab.rewardsId)) + } } } + return (nil, nil) } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/CustomSearchScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/CustomSearchScriptHandler.swift index e325ac0cc98..5a46d655168 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/CustomSearchScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/CustomSearchScriptHandler.swift @@ -28,8 +28,8 @@ class CustomSearchScriptHandler: TabContentScript { in: scriptSandbox) }() - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - replyHandler(nil, nil) + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { // We don't listen to messages because the BVC calls the searchHelper script by itself. + return (nil, nil) } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/DeAmpScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/DeAmpScriptHandler.swift index 37d5d84cd8d..11b3775eef8 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/DeAmpScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/DeAmpScriptHandler.swift @@ -35,11 +35,10 @@ public class DeAmpScriptHandler: TabContentScript { in: scriptSandbox) }() - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: @escaping (Any?, String?) -> Void) { + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message) { assertionFailure("Missing required security token.") - replyHandler(false, nil) - return + return (nil, nil) } do { @@ -50,11 +49,13 @@ public class DeAmpScriptHandler: TabContentScript { // or that previousURL is nil which indicates circular loop cause by a client side redirect // Also check that our window url does not match the previously committed url // or that previousURL is nil which indicates as circular loop caused by a server side redirect - let shouldRedirect = dto.destURL != tab?.previousComittedURL && tab?.committedURL != tab?.previousComittedURL - replyHandler(shouldRedirect, nil) + return await MainActor.run { + let shouldRedirect = dto.destURL != tab?.previousComittedURL && tab?.committedURL != tab?.previousComittedURL + return (shouldRedirect, nil) + } } catch { assertionFailure("Invalid type of message. Fix the `RequestBlocking.js` script") - replyHandler(false, nil) + return (false, nil) } } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/DownloadContentScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/DownloadContentScriptHandler.swift index 422f23519dd..fd49439426c 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/DownloadContentScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/DownloadContentScriptHandler.swift @@ -33,6 +33,7 @@ class DownloadContentScriptHandler: TabContentScript { static let scriptSandbox: WKContentWorld = .defaultClient static let userScript: WKUserScript? = nil + @MainActor static func downloadBlob(url: URL, tab: Tab) -> Bool { let safeUrl = url.absoluteString.replacingOccurrences(of: "'", with: "%27") guard url.scheme == "blob" else { @@ -45,37 +46,37 @@ class DownloadContentScriptHandler: TabContentScript { return true } - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: @escaping (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } - + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message, securityToken: UserScriptManager.securityToken) { assertionFailure("Missing required security token.") - return + return (nil, nil) } do { guard let body = message.body as? [String: Any?] else { - return + return (nil, nil) } let info = try JSONDecoder().decode(BlobDownloadInfo.self, from: JSONSerialization.data(withJSONObject: body)) guard let _ = Bytes.decodeBase64(info.base64String) else { - return + return (nil, nil) } defer { - browserViewController?.pendingDownloadWebView = nil - Self.blobUrlForDownload = nil + Task { @MainActor in + browserViewController?.pendingDownloadWebView = nil + Self.blobUrlForDownload = nil + } } guard let requestedUrl = Self.blobUrlForDownload else { Logger.module.error("\(Self.scriptName): no url was requested") - return + return (nil, nil) } guard requestedUrl == info.url else { Logger.module.error("\(Self.scriptName): URL mismatch") - return + return (nil, nil) } var filename = info.url.absoluteString.components(separatedBy: "/").last ?? "data" @@ -97,5 +98,7 @@ class DownloadContentScriptHandler: TabContentScript { } catch { Logger.module.error("\(error.localizedDescription)") } + + return (nil, nil) } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/FaviconScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/FaviconScriptHandler.swift index a014183d22a..b7e51437c44 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/FaviconScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/FaviconScriptHandler.swift @@ -35,39 +35,40 @@ class FaviconScriptHandler: NSObject, TabContentScript { in: scriptSandbox) }() - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } - guard let tab = tab else { return } + @MainActor + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { + guard let tab = tab else { + return (nil, nil) + } - Task { @MainActor in - // Assign default favicon - tab.favicon = Favicon.default - - guard let webView = message.webView, - let url = webView.url else { - return - } - - // The WebView has a valid URL - // Attempt to fetch the favicon from cache - let isPrivate = tab.isPrivate - tab.favicon = FaviconFetcher.getIconFromCache(for: url) ?? Favicon.default - - // If this is an internal page, we don't fetch favicons for such pages from Brave-Core - guard !InternalURL.isValid(url: url), - !(InternalURL(url)?.isSessionRestore ?? false) else { - return - } - - // Update the favicon for this tab, from Brave-Core - tab.faviconDriver?.webView(webView, scriptMessage: message) { [weak tab] iconUrl, icon in - FaviconScriptHandler.updateFavicon(tab: tab, - url: url, - isPrivate: isPrivate, - icon: icon, - iconUrl: iconUrl) - } + // Assign default favicon + tab.favicon = Favicon.default + + guard let webView = message.webView, + let url = webView.url else { + return (nil, nil) + } + + // The WebView has a valid URL + // Attempt to fetch the favicon from cache + let isPrivate = tab.isPrivate + tab.favicon = FaviconFetcher.getIconFromCache(for: url) ?? Favicon.default + + // If this is an internal page, we don't fetch favicons for such pages from Brave-Core + guard !InternalURL.isValid(url: url), + !(InternalURL(url)?.isSessionRestore ?? false) else { + return (nil, nil) + } + + // Update the favicon for this tab, from Brave-Core + tab.faviconDriver?.webView(webView, scriptMessage: message) { [weak tab] iconUrl, icon in + FaviconScriptHandler.updateFavicon(tab: tab, + url: url, + isPrivate: isPrivate, + icon: icon, + iconUrl: iconUrl) } + return (nil, nil) } private static func updateFavicon(tab: Tab?, url: URL, isPrivate: Bool, icon: UIImage?, iconUrl: URL?) { diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/FindInPageScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/FindInPageScriptHandler.swift index c2260e21d3e..e00ce5bf4a6 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/FindInPageScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/FindInPageScriptHandler.swift @@ -28,16 +28,14 @@ class FindInPageScriptHandler: TabContentScript { static let scriptSandbox: WKContentWorld = .defaultClient static let userScript: WKUserScript? = nil - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } - + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message, securityToken: UserScriptManager.securityToken) { assertionFailure("Missing required security token.") - return + return (nil, nil) } guard let body = message.body as? [String: AnyObject] else { - return + return (nil, nil) } guard let data = body["data"] as? [String: Int] else { @@ -45,7 +43,7 @@ class FindInPageScriptHandler: TabContentScript { Logger.module.error("Could not find a message body or the data did not meet expectations: \(body))") } - return + return (nil, nil) } if let currentResult = data["currentResult"] { @@ -55,5 +53,7 @@ class FindInPageScriptHandler: TabContentScript { if let totalResults = data["totalResults"] { delegate?.findInPageHelper(self, didUpdateTotalResults: totalResults) } + + return (nil, nil) } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/FocusScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/FocusScriptHandler.swift index 46969a82d5b..c5c3a3fd1fe 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/FocusScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/FocusScriptHandler.swift @@ -20,35 +20,40 @@ class FocusScriptHandler: TabContentScript { static let scriptSandbox: WKContentWorld = .defaultClient static let userScript: WKUserScript? = nil - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } - + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message, securityToken: UserScriptManager.securityToken) { assertionFailure("Missing required security token.") - return + return (nil, nil) } guard let body = message.body as? [String: AnyObject] else { - return Logger.module.error("FocusHelper.js sent wrong type of message") + Logger.module.error("FocusHelper.js sent wrong type of message") + return (nil, nil) } guard let data = body["data"] as? [String: String] else { - return Logger.module.error("FocusHelper.js sent wrong type of message") + Logger.module.error("FocusHelper.js sent wrong type of message") + return (nil, nil) } guard let _ = data["elementType"], let eventType = data["eventType"] else { - return Logger.module.error("FocusHelper.js sent wrong keys for message") + Logger.module.error("FocusHelper.js sent wrong keys for message") + return (nil, nil) } - switch eventType { - case "focus": - tab?.isEditing = true - case "blur": - tab?.isEditing = false - default: - return Logger.module.error("FocusHelper.js sent unhandled eventType") + await MainActor.run { + switch eventType { + case "focus": + tab?.isEditing = true + case "blur": + tab?.isEditing = false + default: + return Logger.module.error("FocusHelper.js sent unhandled eventType") + } } + + return (nil, nil) } } diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/LoginsScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/LoginsScriptHandler.swift index c033d4f8b8a..1e02fb93a58 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/LoginsScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/LoginsScriptHandler.swift @@ -30,27 +30,26 @@ class LoginsScriptHandler: TabContentScript { static let messageHandlerName = "loginsScriptMessageHandler" static let userScript: WKUserScript? = nil - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message, securityToken: UserScriptManager.securityToken) { assertionFailure("Missing required security token.") - return + return (nil, nil) } guard let body = message.body as? [String: AnyObject] else { - return + return (nil, nil) } - guard let res = body["data"] as? [String: AnyObject] else { return } - guard let type = res["type"] as? String else { return } + guard let res = body["data"] as? [String: AnyObject], + let type = res["type"] as? String else { return (nil, nil) } // Check to see that we're in the foreground before trying to check the logins. We want to // make sure we don't try accessing the logins database while we're backgrounded to avoid // the system from terminating our app due to background disk access. // // See https://bugzilla.mozilla.org/show_bug.cgi?id=1307822 for details. - guard UIApplication.shared.applicationState == .active && !profile.isShutdown else { - return + guard await UIApplication.shared.applicationState == .active && !profile.isShutdown else { + return (nil, nil) } // We don't use the WKWebView's URL since the page can spoof the URL by using document.location @@ -59,26 +58,24 @@ class LoginsScriptHandler: TabContentScript { // Since responses go to the main frame, make sure we only listen for main frame requests // to avoid XSS attacks. if type == "request" { - passwordAPI.getSavedLogins(for: url, formScheme: .typeHtml) { [weak self] logins in - guard let self = self else { return } - - if let requestId = res["requestId"] as? String { - self.autoFillRequestedCredentials( - formSubmitURL: res["formSubmitURL"] as? String ?? "", - logins: logins, - requestId: requestId, - frameInfo: message.frameInfo) - } + let logins = await passwordAPI.savedLogins(for: url, formScheme: .typeHtml) + if let requestId = res["requestId"] as? String { + await self.autoFillRequestedCredentials( + formSubmitURL: res["formSubmitURL"] as? String ?? "", + logins: logins, + requestId: requestId, + frameInfo: message.frameInfo) } } else if type == "submit" { if Preferences.General.saveLogins.value { - updateORSaveCredentials(for: url, script: res) + await updateORSaveCredentials(for: url, script: res) } } } + return (nil, nil) } - private func updateORSaveCredentials(for url: URL, script: [String: Any]) { + private func updateORSaveCredentials(for url: URL, script: [String: Any]) async { guard let scriptCredentials = passwordAPI.fetchFromScript(url, script: script), let username = scriptCredentials.usernameValue, scriptCredentials.usernameElement != nil, @@ -94,41 +91,34 @@ class LoginsScriptHandler: TabContentScript { return } - passwordAPI.getSavedLogins(for: url, formScheme: .typeHtml) { [weak self] logins in - guard let self = self else { return } - - for login in logins { - guard let usernameLogin = login.usernameValue else { - continue - } - - if usernameLogin.caseInsensitivelyEqual(to: username) { - if password == login.passwordValue { - return - } + let logins = await passwordAPI.savedLogins(for: url, formScheme: .typeHtml) + for login in logins { + guard let usernameLogin = login.usernameValue else { + continue + } - self.showUpdatePrompt(from: login, to: scriptCredentials) - return - } else { - self.showAddPrompt(for: scriptCredentials) + if usernameLogin.caseInsensitivelyEqual(to: username) { + if password == login.passwordValue { return } - } - self.showAddPrompt(for: scriptCredentials) + await self.showUpdatePrompt(from: login, to: scriptCredentials) + return + } } + + await self.showAddPrompt(for: scriptCredentials) } + @MainActor private func showAddPrompt(for login: PasswordForm) { addSnackBarForPrompt(for: login, isUpdating: false) { [weak self] in guard let self = self else { return } - - DispatchQueue.main.async { - self.passwordAPI.addLogin(login) - } + self.passwordAPI.addLogin(login) } } + @MainActor private func showUpdatePrompt(from old: PasswordForm, to new: PasswordForm) { addSnackBarForPrompt(for: new, isUpdating: true) { [weak self] in guard let self = self else { return } @@ -137,6 +127,7 @@ class LoginsScriptHandler: TabContentScript { } } + @MainActor private func addSnackBarForPrompt(for login: PasswordForm, isUpdating: Bool, _ completion: @escaping () -> Void) { guard let username = login.usernameValue else { return @@ -182,6 +173,7 @@ class LoginsScriptHandler: TabContentScript { } } + @MainActor private func autoFillRequestedCredentials(formSubmitURL: String, logins: [PasswordForm], requestId: String, frameInfo: WKFrameInfo) { let securityOrigin = frameInfo.securityOrigin diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/NightModeScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/NightModeScriptHandler.swift index 70daf6ce820..9ff24b7583d 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/NightModeScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/NightModeScriptHandler.swift @@ -32,10 +32,12 @@ class NightModeScriptHandler: TabContentScript { in: scriptSandbox) }() - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: @escaping (Any?, String?) -> Void) { + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { // Do nothing. + return (nil, nil) } + @MainActor static func setNightMode(tabManager: TabManager, enabled: Bool) { Preferences.General.nightModeEnabled.value = enabled diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/ReaderModeScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/ReaderModeScriptHandler.swift index 52ad743ada6..1d46ab0ca39 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/ReaderModeScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/ReaderModeScriptHandler.swift @@ -281,16 +281,14 @@ class ReaderModeScriptHandler: TabContentScript { delegate?.readerMode(self, didParseReadabilityResult: readabilityResult, forTab: tab) } - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } - + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message, securityToken: UserScriptManager.securityToken) { assertionFailure("Missing required security token.") - return + return (nil, nil) } guard let body = message.body as? [String: AnyObject] else { - return + return (nil, nil) } if let msg = body["data"] as? Dictionary { @@ -313,8 +311,11 @@ class ReaderModeScriptHandler: TabContentScript { } } } + + return (nil, nil) } + @MainActor var style: ReaderModeStyle = DefaultReaderModeStyle { didSet { if state == ReaderModeState.active { @@ -325,6 +326,7 @@ class ReaderModeScriptHandler: TabContentScript { } } + @MainActor static func cache(for tab: Tab?) -> ReaderModeCache { switch TabType.of(tab) { case .regular: diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/ResourceDownloadScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/ResourceDownloadScriptHandler.swift index 4ab5f22d92e..b6226a67739 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/ResourceDownloadScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/ResourceDownloadScriptHandler.swift @@ -57,12 +57,11 @@ class ResourceDownloadScriptHandler: TabContentScript { in: scriptSandbox) }() - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } - + @MainActor + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message) { assertionFailure("Missing required security token.") - return + return (nil, nil) } do { @@ -71,8 +70,10 @@ class ResourceDownloadScriptHandler: TabContentScript { } catch { tab?.temporaryDocument?.onDocumentDownloaded(document: nil, error: error) } + return (nil, nil) } + @MainActor static func downloadResource(for tab: Tab, url: URL) { tab.webView?.evaluateSafeJavaScript(functionName: "window.__firefox__.downloadManager.download", args: [url.absoluteString], contentWorld: self.scriptSandbox) { _, error in if let error = error { diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/SiteStateListenerScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/SiteStateListenerScriptHandler.swift index 4947e73d0f5..326a7eb6f26 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/SiteStateListenerScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/SiteStateListenerScriptHandler.swift @@ -41,17 +41,15 @@ class SiteStateListenerScriptHandler: TabContentScript { in: scriptSandbox) }() - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: @escaping (Any?, String?) -> Void) { - defer { replyHandler(nil, nil) } - + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { if !verifyMessage(message: message) { assertionFailure("Missing required security token.") - return + return (nil, nil) } - guard let tab = tab, let webView = tab.webView else { + guard let tab = tab, let webView = await tab.webView else { assertionFailure("Should have a tab set") - return + return (nil, nil) } do { @@ -59,10 +57,10 @@ class SiteStateListenerScriptHandler: TabContentScript { let dto = try JSONDecoder().decode(MessageDTO.self, from: data) guard let frameURL = URL(string: dto.data.windowURL) else { - return + return (nil, nil) } - if let pageData = tab.currentPageData { + if let pageData = await tab.currentPageData { Task { @MainActor in let domain = pageData.domain(persistent: !tab.isPrivate) guard domain.isShieldExpected(.AdblockAndTp, considerAllShieldsOption: true) else { return } @@ -83,6 +81,8 @@ class SiteStateListenerScriptHandler: TabContentScript { assertionFailure("Invalid type of message. Fix the `SiteStateListenerScript.js` script") Logger.module.error("\(error.localizedDescription)") } + + return (nil, nil) } @MainActor private func makeSetup(from modelTuples: [AdBlockStats.CosmeticFilterModelTuple], isAggressive: Bool) throws -> UserScriptType.SelectorsPollerSetup { diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/WindowRenderScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/WindowRenderScriptHandler.swift index 45856b1911b..be7588e8b2e 100644 --- a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/WindowRenderScriptHandler.swift +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Sandboxed/WindowRenderScriptHandler.swift @@ -31,11 +31,14 @@ class WindowRenderScriptHandler: TabContentScript { in: scriptSandbox) }() - func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) { + func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) async -> (Any?, String?) { // Do nothing with the messages received. // For now.. It's useful for debugging though. + + return (nil, nil) } + @MainActor static func executeScript(for tab: Tab) { tab.webView?.evaluateSafeJavaScript(functionName: "window.__firefox__.\(resizeWindowFunction).resizeWindow", contentWorld: scriptSandbox) } diff --git a/Sources/Brave/Helpers/TabEventHandlers.swift b/Sources/Brave/Helpers/TabEventHandlers.swift index eea078542a5..701ca272c9a 100644 --- a/Sources/Brave/Helpers/TabEventHandlers.swift +++ b/Sources/Brave/Helpers/TabEventHandlers.swift @@ -5,6 +5,7 @@ import Foundation import Shared +@MainActor class TabEventHandlers { static func create(with prefs: Prefs) -> [TabEventHandler] { return [ diff --git a/Sources/Brave/Helpers/UserActivityHandler.swift b/Sources/Brave/Helpers/UserActivityHandler.swift index 832f6fb6af6..3d79ca3c18c 100644 --- a/Sources/Brave/Helpers/UserActivityHandler.swift +++ b/Sources/Brave/Helpers/UserActivityHandler.swift @@ -13,6 +13,7 @@ private let browsingActivityType: String = "com.brave.ios.browsing" private let searchableIndex = CSSearchableIndex(name: "firefox") +@MainActor class UserActivityHandler { private var tabObservers: TabObservers! diff --git a/Sources/Brave/Shortcuts/ActivityShortcutManager.swift b/Sources/Brave/Shortcuts/ActivityShortcutManager.swift index 6c60d37af0d..d47377f44be 100644 --- a/Sources/Brave/Shortcuts/ActivityShortcutManager.swift +++ b/Sources/Brave/Shortcuts/ActivityShortcutManager.swift @@ -147,6 +147,7 @@ public class ActivityShortcutManager: NSObject { } } + @MainActor private func handleActivityDetails(type: ActivityType, using bvc: BrowserViewController) { switch type { case .newTab: diff --git a/Sources/Favicon/FaviconFetcher.swift b/Sources/Favicon/FaviconFetcher.swift index 2332ac9b8d5..c86f4667cb8 100644 --- a/Sources/Favicon/FaviconFetcher.swift +++ b/Sources/Favicon/FaviconFetcher.swift @@ -44,6 +44,7 @@ public class FaviconFetcher { /// 4. Fetch Monogram Icons /// Notes: Does NOT make a request to fetch icons from the page. /// Requests are only made in FaviconScriptHandler, when the user visits the page. + nonisolated public static func loadIcon(url: URL, kind: FaviconFetcher.Kind = .smallIcon, persistent: Bool) async throws -> Favicon { try Task.checkCancellation() @@ -80,7 +81,7 @@ public class FaviconFetcher { /// Creates a monogram Favicon with the following conditions /// 1. If `monogramString` is not null, it is used to render the Favicon image. /// 2. If `monogramString` is null, the first character of the URL's domain is used to render the Favicon image. - public static func monogramIcon(url: URL, monogramString: Character? = nil, persistent: Bool) async throws -> Favicon { + nonisolated public static func monogramIcon(url: URL, monogramString: Character? = nil, persistent: Bool) async throws -> Favicon { try Task.checkCancellation() if let favicon = getFromCache(for: url) { diff --git a/Sources/Playlist/PlaylistMediaStreamer.swift b/Sources/Playlist/PlaylistMediaStreamer.swift index 50eb11c9373..b7f83a1e662 100644 --- a/Sources/Playlist/PlaylistMediaStreamer.swift +++ b/Sources/Playlist/PlaylistMediaStreamer.swift @@ -110,6 +110,7 @@ public class PlaylistMediaStreamer { } let newItem = await webLoader.load(url: url) + webLoader.stop() webLoader.removeFromSuperview() self.webLoader = nil diff --git a/Sources/Playlist/PlaylistSharedFolder.swift b/Sources/Playlist/PlaylistSharedFolder.swift index 12642a1ba13..e206a17f4df 100644 --- a/Sources/Playlist/PlaylistSharedFolder.swift +++ b/Sources/Playlist/PlaylistSharedFolder.swift @@ -159,10 +159,8 @@ public struct PlaylistSharedFolderNetwork { } let webLoader = webLoaderFactory.makeWebLoader() -// let webLoader = PlaylistWebLoader().then { viewForInvisibleWebView.insertSubview(webLoader, at: 0) -// } - + guard let newItem = await webLoader.load(url: url) else { // Destroy the web loader. webLoader.stop() @@ -170,6 +168,10 @@ public struct PlaylistSharedFolderNetwork { throw PlaylistMediaStreamer.PlaybackError.cannotLoadMedia } + // Destroy the web loader. + webLoader.stop() + webLoader.removeFromSuperview() + return await withCheckedContinuation { continuation in PlaylistManager.shared.getAssetDuration(item: newItem) { duration in let item = PlaylistInfo(name: item.name, @@ -184,10 +186,6 @@ public struct PlaylistSharedFolderNetwork { tagId: item.tagId, order: item.order, isInvisible: newItem.isInvisible) - - // Destroy the web loader when the callback is complete. - webLoader.stop() - webLoader.removeFromSuperview() continuation.resume(returning: item) } }