Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Commit

Permalink
Isolate Tab to MainActor and Update all Script Injection to be Async-…
Browse files Browse the repository at this point in the history
…Await
  • Loading branch information
StephenHeaps authored and Brandon-T committed Feb 10, 2024
1 parent d9df510 commit abd926d
Show file tree
Hide file tree
Showing 70 changed files with 609 additions and 556 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
askForAppToLaunch = "Yes"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
Expand Down
4 changes: 3 additions & 1 deletion App/iOS/Delegates/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
146 changes: 71 additions & 75 deletions Sources/Brave/BraveSkus/BraveSkusManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import BraveCore
import BraveVPN
import os.log

@MainActor
public class BraveSkusManager {
private let sku: SkusSkusService

Expand All @@ -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 {
Expand All @@ -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
}
}

Expand Down
3 changes: 2 additions & 1 deletion Sources/Brave/Frontend/Browser/BraveGetUA.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -132,19 +132,21 @@ 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
if let readabilityResult = ReadabilityResult(object: object as AnyObject?) {
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)
}
}
}
}
Expand All @@ -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
Expand All @@ -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)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ extension BrowserViewController: TabManagerDelegate {
if !privateBrowsingManager.isPrivateBrowsing {
rewards.reportTabClosed(tabId: Int(tab.rewardsId))
}

tab.destroy()
}

func tabManagerDidAddTabs(_ tabManager: TabManager) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ extension BrowserViewController: TopToolbarDelegate {
}

func topToolbarDidPressReaderMode(_ topToolbar: TopToolbarView) {
toggleReaderMode()
Task { @MainActor in
await toggleReaderMode()
}
}

func topToolbarDidPressPlaylistButton(_ urlBar: TopToolbarView) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1161,6 +1161,7 @@ extension BrowserViewController: WKUIDelegate {
webView.evaluateSafeJavaScript(functionName: script, contentWorld: .defaultClient, asFunction: false)
}

@MainActor
func handleAlert<T: JSAlertInfo>(webView: WKWebView, alert: inout T, completionHandler: @escaping () -> Void) {
guard let promptingTab = tabManager[webView], !promptingTab.blockAllAlerts else {
suppressJSAlerts(webView: webView)
Expand All @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down
2 changes: 2 additions & 0 deletions Sources/Brave/Frontend/Browser/FaviconHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class FaviconHandler {
unregister(tabObservers)
}

@MainActor
func loadFaviconURL(
_ url: URL,
forTab tab: Tab
Expand All @@ -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) {
Expand Down
Loading

0 comments on commit abd926d

Please sign in to comment.