// // Tab.swift // SBrowser // // Created by James Magahern on 7/29/20. // import UIKit import Combine protocol TabDelegate: class { func didBlockScriptOrigin(_ origin: String, forTab: Tab) } class Tab: NSObject, SBRProcessBundleBridgeDelegate { public weak var delegate: TabDelegate? public let homeURL: URL? public let bridge = SBRProcessBundleBridge() public var webView: WKWebView { if self.loadedWebView == nil { self.loadedWebView = bridge.webView if let homeURL = homeURL { beginLoadingURL(homeURL) } } return bridge.webView } public var policyManager: ResourcePolicyManager private var loadedWebView: WKWebView? = nil public var title: String? { loadedWebView?.title } public var url: URL? { loadedWebView?.url ?? self.homeURL } public var javaScriptEnabled: Bool = false { didSet { bridge.allowAllScripts = javaScriptEnabled } } public var identifier = UUID() public var favicon: UIImage? private var faviconHost: String? private var faviconRequest: AnyCancellable? public var allowedScriptOrigins = Set() public var blockedScriptOrigins = Set() private var titleObservation: NSKeyValueObservation? private var urlObservation: NSKeyValueObservation? convenience init(policyManager: ResourcePolicyManager) { self.init(url: nil, policyManager: policyManager) } convenience init(urlString: String, policyManager: ResourcePolicyManager) { self.init(url: URL(string: urlString), policyManager: policyManager) } init(url: URL?, policyManager: ResourcePolicyManager) { self.homeURL = url self.policyManager = policyManager bridge.policyDataSource = policyManager super.init() bridge.delegate = self } deinit { bridge.tearDown() } func beginLoadingURL(_ url: URL) { let request = URLRequest(url: url) webView.load(request) } // MARK: SBRProcessBundleBridgeDelegate func webProcess(_ bridge: SBRProcessBundleBridge, didAllowScriptResourceFromOrigin origin: String) { print("Allowed script resource from origin: \(origin)") allowedScriptOrigins.formUnion([ origin ]) } func webProcess(_ bridge: SBRProcessBundleBridge, didBlockScriptResourceFromOrigin origin: String) { print("Blocked script resource from origin: \(origin)") blockedScriptOrigins.formUnion([ origin ]) delegate?.didBlockScriptOrigin(origin, forTab: self) } func updateFaviconForURL(_ url: URL) { if let faviconHost = faviconHost, url.host == faviconHost {} else { guard var faviconURLComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return } faviconURLComponents.path = "/favicon.ico" let defaultImage = UIImage(systemName: "globe") guard let faviconURL = faviconURLComponents.url else { return } faviconRequest = URLSession.shared.dataTaskPublisher(for: faviconURL) .map { (data: Data, response: URLResponse) -> UIImage? in UIImage(data: data) } .replaceError(with: defaultImage) .replaceNil(with: defaultImage) .assign(to: \.favicon, on: self) } } }