diff --git a/SBrowser/Backend/ResourcePolicyManager.swift b/SBrowser/Backend/ResourcePolicyManager.swift index ac22133..2f69a23 100644 --- a/SBrowser/Backend/ResourcePolicyManager.swift +++ b/SBrowser/Backend/ResourcePolicyManager.swift @@ -10,28 +10,31 @@ import Foundation class ResourcePolicyManager: NSObject, SBRResourceOriginPolicyDataSource { static let AllowedOriginsDefaultsKey = "allowedOrigins" - private var allowedOriginSet: Set = { - if let allowedOrigins = UserDefaults.standard.array(forKey: AllowedOriginsDefaultsKey) as? [String] { - return Set(allowedOrigins) + static let EnabledOriginsDefaultsKey = "enabledOrigins" + + private static func stringSetForKey(_ key: String) -> Set { + if let set = UserDefaults.standard.array(forKey: key) as? [String] { + return Set(set) } return Set() - }() - - func allowedOriginsForScriptResources() -> Set - { - allowedOriginSet } - func allowOriginToLoadScriptResources(_ origin: String) - { + private static func saveStringSet(_ set: Set, forKey key: String) { + UserDefaults.standard.set(Array(set), forKey: key) + } + + private var allowedOriginSet: Set = stringSetForKey(AllowedOriginsDefaultsKey) { + didSet { Self.saveStringSet(allowedOriginSet, forKey: Self.AllowedOriginsDefaultsKey) } + } + + func allowedOriginsForScriptResources() -> Set { allowedOriginSet } + + func allowOriginToLoadScriptResources(_ origin: String) { allowedOriginSet.formUnion([ origin ]) - UserDefaults.standard.set(Array(allowedOriginSet), forKey: Self.AllowedOriginsDefaultsKey) } - func disallowOriginToLoadScriptResources(_ origin: String) - { + func disallowOriginToLoadScriptResources(_ origin: String) { allowedOriginSet.remove(origin) - UserDefaults.standard.set(Array(allowedOriginSet), forKey: Self.AllowedOriginsDefaultsKey) } } diff --git a/SBrowser/Browser View/BrowserView.swift b/SBrowser/Browser View/BrowserView.swift index 402bdcc..4b092aa 100644 --- a/SBrowser/Browser View/BrowserView.swift +++ b/SBrowser/Browser View/BrowserView.swift @@ -37,31 +37,48 @@ class BrowserView: UIView addSubview(titlebarView) keyboardWillShowObserver = NotificationCenter.default.publisher(for: UIWindow.keyboardWillShowNotification).sink { notification in - if let keyboardFrame = notification.userInfo?[UIWindow.keyboardFrameEndUserInfoKey] as? CGRect { - self.keyboardLayoutOffset = self.bounds.height - keyboardFrame.minY - } + self.adjustOffsetForKeyboardNotification(userInfo: notification.userInfo!) } keyboardWillHideObserver = NotificationCenter.default.publisher(for: UIWindow.keyboardWillHideNotification).sink { notification in - if let keyboardFrame = notification.userInfo?[UIWindow.keyboardFrameEndUserInfoKey] as? CGRect { - self.keyboardLayoutOffset = self.bounds.height - keyboardFrame.minY - } + self.adjustOffsetForKeyboardNotification(userInfo: notification.userInfo!) } } - override func layoutSubviews() - { + private func adjustOffsetForKeyboardNotification(userInfo: [AnyHashable : Any]) { + guard let keyboardEndFrame = userInfo[UIWindow.keyboardFrameEndUserInfoKey] as? CGRect else { return } + guard let animationDuration = userInfo[UIWindow.keyboardAnimationDurationUserInfoKey] as? TimeInterval else { return } + guard let animationCurve = userInfo[UIWindow.keyboardAnimationCurveUserInfoKey] as? Int else { return } + + let animationOptions: UIView.AnimationOptions = { curve -> UIView.AnimationOptions in + switch UIView.AnimationCurve(rawValue: curve) { + case .easeIn: return .curveEaseIn + case .easeOut: return .curveEaseOut + case .easeInOut: return .curveEaseInOut + default: return .init() + } + }(animationCurve) + + self.keyboardLayoutOffset = bounds.height - keyboardEndFrame.minY + UIView.animate(withDuration: animationDuration, delay: 0.0, options: animationOptions, animations: { self.layoutIfNeeded() }, completion: nil) + } + + override func layoutSubviews() { super.layoutSubviews() webView?.frame = bounds if let toolbarView = toolbarView { var toolbarSize = toolbarView.sizeThatFits(bounds.size) + + var bottomOffset: CGFloat = 0.0 if keyboardLayoutOffset == 0 { toolbarSize.height += safeAreaInsets.bottom + } else if toolbarView.urlBar?.textField.isFirstResponder ?? false { + bottomOffset = keyboardLayoutOffset } toolbarView.bounds = CGRect(origin: .zero, size: toolbarSize) - toolbarView.center = CGPoint(x: bounds.center.x, y: bounds.maxY - (toolbarView.bounds.height / 2) - keyboardLayoutOffset) + toolbarView.center = CGPoint(x: bounds.center.x, y: bounds.maxY - (toolbarView.bounds.height / 2) - bottomOffset) } bringSubviewToFront(titlebarView) @@ -77,7 +94,7 @@ class BrowserView: UIView var webViewContentInset = UIEdgeInsets() webViewContentInset.top = titlebarView.frame.height webViewContentInset.bottom = toolbarView?.frame.height ?? 0 - webView?.scrollView.contentInset = webViewContentInset + webView?.scrollView.contentInset = webViewContentInset.subtracting(safeAreaInsets) webView?.scrollView.scrollIndicatorInsets = webViewContentInset } } diff --git a/SBrowser/Browser View/BrowserViewController.swift b/SBrowser/Browser View/BrowserViewController.swift index ecb5866..a27d566 100644 --- a/SBrowser/Browser View/BrowserViewController.swift +++ b/SBrowser/Browser View/BrowserViewController.swift @@ -14,8 +14,11 @@ class BrowserViewController: UIViewController, let bridge = SBRProcessBundleBridge() let browserView = BrowserView() + var javaScriptEnabledForTab: Bool = false + private let policyManager = ResourcePolicyManager() private let toolbarController = ToolbarViewController() + private var allowedScriptOrigins = Set() private var blockedScriptOrigins = Set() override var canBecomeFirstResponder: Bool { true } @@ -52,8 +55,11 @@ class BrowserViewController: UIViewController, // Script button toolbarController.scriptControllerIconView.addAction(UIAction(handler: { action in - let scriptViewController = ScriptPolicyViewController(policyManager: self.policyManager, blockedScripts: self.blockedScriptOrigins) + let hostOrigin = webView.url?.host ?? "" + let loadedScripts = self.allowedScriptOrigins.union(self.blockedScriptOrigins) + let scriptViewController = ScriptPolicyViewController(policyManager: self.policyManager, hostOrigin: hostOrigin, loadedScripts: loadedScripts) scriptViewController.delegate = self + scriptViewController.allowScriptsForTab = self.javaScriptEnabledForTab let navController = UINavigationController(rootViewController: scriptViewController) self.present(navController, animated: true, completion: nil) @@ -64,7 +70,11 @@ class BrowserViewController: UIViewController, // Load progress loadingObservation = webView.observe(\.estimatedProgress) { (webView, observedChange) in - self.toolbarController.urlBar.loadProgress = .loading(progress: webView.estimatedProgress) + if webView.estimatedProgress == 1.0 { + self.toolbarController.urlBar.loadProgress = .complete + } else { + self.toolbarController.urlBar.loadProgress = .loading(progress: webView.estimatedProgress) + } } // Title observer @@ -76,7 +86,7 @@ class BrowserViewController: UIViewController, } override func viewDidLoad() { - beginLoadingURL(URL(string: "https://reddit.com")!) + beginLoadingURL(URL(string: "https://google.com")!) } override func viewWillAppear(_ animated: Bool) { @@ -95,9 +105,14 @@ class BrowserViewController: UIViewController, // MARK: SBRProcessBundleBridgeDelegate + func webProcess(_ bridge: SBRProcessBundleBridge, didAllowScriptResourceFromOrigin origin: String) { + print("Allowed script resource from origin: \(origin)") + allowedScriptOrigins.formUnion([ origin ]) + updateScriptBlockerButton() + } + func webProcess(_ bridge: SBRProcessBundleBridge, didBlockScriptResourceFromOrigin origin: String) { print("Blocked script resource from origin: \(origin)") - blockedScriptOrigins.formUnion([ origin ]) updateScriptBlockerButton() } @@ -117,6 +132,18 @@ class BrowserViewController: UIViewController, toolbarController.urlBar.loadProgress = .complete } + func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: @escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void) + { + var allowJavaScript = javaScriptEnabledForTab + if !allowJavaScript, let host = navigationAction.request.url?.host { + // Check origin policy + allowJavaScript = policyManager.allowedOriginsForScriptResources().contains(host) + } + + preferences.allowsContentJavaScript = allowJavaScript + decisionHandler(.allow, preferences) + } + // MARK: UITextField Delegate func textFieldShouldReturn(_ textField: UITextField) -> Bool { @@ -140,4 +167,9 @@ class BrowserViewController: UIViewController, bridge.webView.reload() } + func setScriptsEnabledForTab(_ enabled: Bool) { + javaScriptEnabledForTab = enabled + bridge.allowAllScripts = enabled + } + } diff --git a/SBrowser/Browser View/ToolbarViewController.swift b/SBrowser/Browser View/ToolbarViewController.swift index 34a9b90..37e3543 100644 --- a/SBrowser/Browser View/ToolbarViewController.swift +++ b/SBrowser/Browser View/ToolbarViewController.swift @@ -9,7 +9,7 @@ import UIKit class ToolbarButtonView: UIView { - private var buttonPadding = CGFloat(8.0) + private var buttonPadding = CGFloat(24.0) private var buttonViews: [UIView] = [] func addButtonView(_ button: UIView) { @@ -28,7 +28,7 @@ class ToolbarButtonView: UIView override func layoutSubviews() { var buttonRect = CGRect(origin: .zero, size: CGSize(width: 0, height: bounds.height)) - buttonRect.origin.x = buttonPadding + buttonRect.origin.x = layoutMargins.left for button in buttonViews { let buttonSize = button.sizeThatFits(bounds.size) @@ -44,9 +44,12 @@ class ToolbarView: UIView { var urlBar: URLBar? { didSet { containerView.addSubview(urlBar!) } } + var cancelButtonVisible: Bool = false { didSet { layoutSubviews() } } + let containerView = UIView(frame: .zero) let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemThickMaterial)) let buttonsView = ToolbarButtonView(frame: .zero) + let cancelButton = UIButton(type: .system) convenience init() { @@ -55,6 +58,9 @@ class ToolbarView: UIView addSubview(containerView) containerView.addSubview(buttonsView) + + cancelButton.setTitle("Cancel", for: .normal) + containerView.addSubview(cancelButton) } override func sizeThatFits(_ size: CGSize) -> CGSize @@ -73,12 +79,32 @@ class ToolbarView: UIView containerView.frame = containerBounds containerView.frame = containerView.frame.insetBy(dx: 8.0, dy: 4.0) + // Cancel button + let urlBarPadding: CGFloat = 8.0 + let cancelButtonSize = cancelButton.sizeThatFits(containerView.bounds.size) + cancelButton.frame = CGRect(origin: CGPoint(x: (urlBar?.frame.maxX ?? 0) + urlBarPadding, y: 0), + size: CGSize(width: cancelButtonSize.width, height: containerView.bounds.height)) + + // Toolbar buttons let toolbarSize = buttonsView.sizeThatFits(containerView.bounds.size) - if let urlBar = urlBar { - urlBar.frame = CGRect(origin: .zero, size: CGSize(width: containerView.bounds.width - toolbarSize.width, height: toolbarSize.height)) + buttonsView.frame = CGRect(origin: CGPoint(x: (urlBar?.frame.maxX ?? 0) + urlBarPadding, y: 0), size: toolbarSize) + + var avoidingSize: CGSize = .zero + if cancelButtonVisible { + cancelButton.alpha = 1.0 + buttonsView.alpha = 0.0 + + avoidingSize = cancelButtonSize + } else { + cancelButton.alpha = 0.0 + buttonsView.alpha = 1.0 + + avoidingSize = toolbarSize } - buttonsView.frame = CGRect(origin: CGPoint(x: urlBar?.frame.maxX ?? 0 + 8.0, y: 0), size: toolbarSize) + if let urlBar = urlBar { + urlBar.frame = CGRect(origin: .zero, size: CGSize(width: containerView.bounds.width - avoidingSize.width - urlBarPadding, height: containerView.bounds.height)) + } } } @@ -88,15 +114,30 @@ class ToolbarViewController: UIViewController let toolbarView = ToolbarView() let scriptControllerIconView = ScriptControllerIconView() let shareButton = UIButton(frame: .zero) + let darkModeButton = UIButton(frame: .zero) init() { super.init(nibName: nil, bundle: nil) toolbarView.urlBar = urlBar + darkModeButton.setImage(UIImage(systemName: "moon.circle"), for: .normal) + toolbarView.buttonsView.addButtonView(darkModeButton) + shareButton.setImage(UIImage(systemName: "square.and.arrow.up"), for: .normal) toolbarView.buttonsView.addButtonView(shareButton) toolbarView.buttonsView.addButtonView(scriptControllerIconView) + + let toolbarAnimationDuration: TimeInterval = 0.3 + urlBar.textField.addAction(.init(handler: { _ in + UIView.animate(withDuration: toolbarAnimationDuration) { + self.toolbarView.cancelButtonVisible = self.urlBar.textField.isFirstResponder + } + }), for: [ .editingDidBegin, .editingDidEnd ]) + + toolbarView.cancelButton.addAction(.init(handler: { action in + self.urlBar.textField.resignFirstResponder() + }), for: .touchUpInside) } override func loadView() { diff --git a/SBrowser/Browser View/URLBar.swift b/SBrowser/Browser View/URLBar.swift index cd912a0..8da12dd 100644 --- a/SBrowser/Browser View/URLBar.swift +++ b/SBrowser/Browser View/URLBar.swift @@ -48,8 +48,13 @@ class URLBar: UIView textField.autocapitalizationType = .none textField.font = .systemFont(ofSize: 14.0) textField.clearingBehavior = .clearOnInsertionAndShowSelectionTint + textField.clearButtonMode = .whileEditing addSubview(textField) + textField.addAction(.init(handler: { _ in + self.refreshButton.isHidden = self.textField.isFirstResponder + }), for: [ .editingDidBegin, .editingDidEnd ]) + refreshButton.tintColor = .secondaryLabel refreshButton.setImage(refreshImage, for: .normal) addSubview(refreshButton) diff --git a/SBrowser/Script Policy UI/ScriptControllerIconView.swift b/SBrowser/Script Policy UI/ScriptControllerIconView.swift index beaf7fc..f64799a 100644 --- a/SBrowser/Script Policy UI/ScriptControllerIconView.swift +++ b/SBrowser/Script Policy UI/ScriptControllerIconView.swift @@ -41,10 +41,6 @@ class ScriptControllerIconView: UIButton setNeedsLayout() } - override func sizeThatFits(_ size: CGSize) -> CGSize { - return CGSize(width: 44.0, height: 44.0) - } - override func layoutSubviews() { super.layoutSubviews() diff --git a/SBrowser/Script Policy UI/ScriptPolicyControl.swift b/SBrowser/Script Policy UI/ScriptPolicyControl.swift index 183b8a7..30f730d 100644 --- a/SBrowser/Script Policy UI/ScriptPolicyControl.swift +++ b/SBrowser/Script Policy UI/ScriptPolicyControl.swift @@ -15,10 +15,7 @@ class ScriptPolicyControl: UIControl } var policyStatus: PolicyStatus = .blocked { - didSet { - sendActions(for: .valueChanged) - setNeedsLayout() - } + didSet { setNeedsLayout() } } private class PolicyButton: UIButton { @@ -35,12 +32,14 @@ class ScriptPolicyControl: UIControl allowButton.addAction(UIAction(handler: { _ in self.policyStatus = .allowed + self.sendActions(for: .valueChanged) }), for: .touchUpInside) allowButton.imageView?.contentMode = .scaleAspectFit addSubview(allowButton) denyButton.addAction(UIAction(handler: { _ in self.policyStatus = .blocked + self.sendActions(for: .valueChanged) }), for: .touchUpInside) denyButton.imageView?.contentMode = .scaleAspectFit addSubview(denyButton) diff --git a/SBrowser/Script Policy UI/ScriptPolicyViewController.swift b/SBrowser/Script Policy UI/ScriptPolicyViewController.swift index bff3380..90efd42 100644 --- a/SBrowser/Script Policy UI/ScriptPolicyViewController.swift +++ b/SBrowser/Script Policy UI/ScriptPolicyViewController.swift @@ -9,10 +9,18 @@ import UIKit protocol ScriptPolicyViewControllerDelegate { func didChangeScriptPolicy() + func setScriptsEnabledForTab(_ enabled: Bool) } class ScriptPolicyControlListCell: UICollectionViewListCell { + var enabled: Bool = true { + didSet { + if enabled != oldValue { + setNeedsLayout() + } + } + } let policyControl = ScriptPolicyControl() override init(frame: CGRect) { super.init(frame: frame) @@ -23,14 +31,46 @@ class ScriptPolicyControlListCell: UICollectionViewListCell fatalError("init(coder:) has not been implemented") } + + override func layoutSubviews() { + let policyControlWidth = CGFloat(80.0) + policyControl.frame = CGRect(x: bounds.maxX - policyControlWidth - layoutMargins.right, y: 0, width: policyControlWidth, height: bounds.height) + bringSubviewToFront(policyControl) + + super.layoutSubviews() + + if enabled { + contentView.alpha = 1.0 + policyControl.alpha = 1.0 + policyControl.isUserInteractionEnabled = true + } else { + contentView.alpha = 0.5 + policyControl.alpha = 0.5 + policyControl.isUserInteractionEnabled = false + } + } +} + +class SwitchListCell: UICollectionViewListCell +{ + let switchView = UISwitch(frame: .zero) + + override init(frame: CGRect) { + super.init(frame: frame) + addSubview(switchView) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func layoutSubviews() { super.layoutSubviews() - let policyControlWidth = CGFloat(100.0) - policyControl.frame = CGRect(x: bounds.maxX - policyControlWidth, y: 0, width: policyControlWidth, height: bounds.height) - bringSubviewToFront(policyControl) - - contentView.frame = CGRect(origin: contentView.frame.origin, size: CGSize(width: bounds.width - policyControl.frame.width, height: contentView.frame.height)) + let switchWidth: CGFloat = switchView.sizeThatFits(bounds.size).width + switchView.frame = CGRect(x: bounds.maxX - switchWidth - layoutMargins.right, y: 0, width: switchWidth, height: bounds.height) + switchView.frame = switchView.frame.centeredY(inRect: bounds) + contentView.frame = CGRect(origin: contentView.frame.origin, size: CGSize(width: switchView.frame.minX, height: contentView.frame.height)) } } @@ -38,18 +78,58 @@ class ScriptPolicyViewController: UIViewController, UICollectionViewDelegate { var collectionView: UICollectionView? var delegate: ScriptPolicyViewControllerDelegate? = nil - var dataSource: UICollectionViewDiffableDataSource? + var allowScriptsForTab = false + private var dataSource: UICollectionViewDiffableDataSource? private var didChangeScriptPolicy = false - convenience init(policyManager: ResourcePolicyManager, blockedScripts: Set) { + private enum Section: Int { + case tabOptions + case origins + } + + private static let enableScriptsForTabItem: String = "enableScriptsForTab" + + convenience init(policyManager: ResourcePolicyManager, hostOrigin: String, loadedScripts: Set) { self.init(nibName: nil, bundle: nil) let listConfig = UICollectionLayoutListConfiguration(appearance: .grouped) let listLayout = UICollectionViewCompositionalLayout.list(using: listConfig) let collectionView = UICollectionView(frame: .zero, collectionViewLayout: listLayout) - let registry = UICollectionView.CellRegistration { (listCell, indexPath, item) in + // Make sure host origin goes first in the list. + let otherOriginScripts = loadedScripts.subtracting([ hostOrigin ]) + let originItems = [ hostOrigin ] + otherOriginScripts + + let switchCellRegistry = UICollectionView.CellRegistration { (listCell, indexPath, item) in + var config = listCell.defaultContentConfiguration() + if item == Self.enableScriptsForTabItem { + config.text = "Allow for Tab" + listCell.switchView.isOn = self.allowScriptsForTab + listCell.switchView.addAction(.init(handler: { _ in + let enabled = listCell.switchView.isOn + + self.allowScriptsForTab = enabled + self.didChangeScriptPolicy = true + + if var snapshot = self.dataSource?.snapshot() { + if enabled { + // Hide script origins + snapshot.deleteSections([ .origins ]) + } else { + snapshot.appendSections([ .origins ]) + snapshot.appendItems(originItems, toSection: .origins) + } + + self.dataSource?.apply(snapshot, animatingDifferences: true) + } + }), for: .valueChanged) + } + + listCell.contentConfiguration = config + } + + let scriptPolicyRegistry = UICollectionView.CellRegistration { (listCell, indexPath, item) in var config = listCell.defaultContentConfiguration() config.text = item @@ -62,26 +142,44 @@ class ScriptPolicyViewController: UIViewController, UICollectionViewDelegate } listCell.policyControl.addAction(UIAction(handler: { _ in - if listCell.policyControl.policyStatus == .allowed { + let allowed: Bool = listCell.policyControl.policyStatus == .allowed + + if allowed { policyManager.allowOriginToLoadScriptResources(item) } else { policyManager.disallowOriginToLoadScriptResources(item) } + if item == hostOrigin { + if var snapshot = self.dataSource?.snapshot() { + snapshot.reloadItems(Array(otherOriginScripts)) + self.dataSource?.apply(snapshot, animatingDifferences: true) + } + } + self.didChangeScriptPolicy = true }), for: .valueChanged) + + if item != hostOrigin { + listCell.enabled = policyManager.allowedOriginsForScriptResources().contains(hostOrigin) + } } - let dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) { (collectionView, indexPath, item) -> UICollectionViewCell? in - collectionView.dequeueConfiguredReusableCell(using: registry, for: indexPath, item: item) + let dataSource = UICollectionViewDiffableDataSource(collectionView: collectionView) { (collectionView, indexPath, item) -> UICollectionViewCell? in + if item == Self.enableScriptsForTabItem { + return collectionView.dequeueConfiguredReusableCell(using: switchCellRegistry, for: indexPath, item: item) + } + + return collectionView.dequeueConfiguredReusableCell(using: scriptPolicyRegistry, for: indexPath, item: item) } collectionView.dataSource = dataSource collectionView.delegate = self var snapshot = dataSource.snapshot() - snapshot.appendSections([ 0 ]) - snapshot.appendItems(Array(blockedScripts)) + snapshot.appendSections([ .tabOptions, .origins ]) + snapshot.appendItems([ Self.enableScriptsForTabItem ], toSection: .tabOptions) + snapshot.appendItems(originItems, toSection: .origins) dataSource.apply(snapshot) self.dataSource = dataSource @@ -91,6 +189,7 @@ class ScriptPolicyViewController: UIViewController, UICollectionViewDelegate navigationItem.rightBarButtonItem = UIBarButtonItem(systemItem: .done, primaryAction: UIAction(handler: { action in if self.didChangeScriptPolicy { self.delegate?.didChangeScriptPolicy() + self.delegate?.setScriptsEnabledForTab(self.allowScriptsForTab) } self.dismiss(animated: true, completion: nil) diff --git a/SBrowser/Utilities/CGPoint+Utils.swift b/SBrowser/Utilities/CGPoint+Utils.swift index 9d38d73..7252388 100644 --- a/SBrowser/Utilities/CGPoint+Utils.swift +++ b/SBrowser/Utilities/CGPoint+Utils.swift @@ -22,4 +22,11 @@ extension CGRect return rect } + + public func centeredY(inRect: CGRect) -> CGRect { + var rect = self + rect.origin.y = CGRound((inRect.height - rect.height) / 2.0) + + return rect + } } diff --git a/SBrowser/Utilities/UIEdgeInsets+Layout.swift b/SBrowser/Utilities/UIEdgeInsets+Layout.swift index 7712c95..2d9d01f 100644 --- a/SBrowser/Utilities/UIEdgeInsets+Layout.swift +++ b/SBrowser/Utilities/UIEdgeInsets+Layout.swift @@ -14,4 +14,11 @@ extension UIEdgeInsets return UIEdgeInsets(top: -top, left: -left, bottom: -bottom, right: -right) } } + + func subtracting(_ other: UIEdgeInsets) -> UIEdgeInsets { + return UIEdgeInsets(top: self.top - other.top, + left: self.left - other.left, + bottom: self.bottom - other.bottom, + right: self.right - other.right) + } } diff --git a/SBrowser/Web Process Bundle Bridge/SBRProcessBundleBridge.h b/SBrowser/Web Process Bundle Bridge/SBRProcessBundleBridge.h index 51b3d05..27371f3 100644 --- a/SBrowser/Web Process Bundle Bridge/SBRProcessBundleBridge.h +++ b/SBrowser/Web Process Bundle Bridge/SBRProcessBundleBridge.h @@ -19,8 +19,8 @@ NS_ASSUME_NONNULL_BEGIN @class SBRProcessBundleBridge; @protocol SBRProcessBundleBridgeDelegate +- (void)webProcess:(SBRProcessBundleBridge *)bridge didAllowScriptResourceFromOrigin:(NSString *)origin; - (void)webProcess:(SBRProcessBundleBridge *)bridge didBlockScriptResourceFromOrigin:(NSString *)origin; - @end @interface SBRProcessBundleBridge : NSObject @@ -28,7 +28,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly) WKWebView *webView; @property (nonatomic, weak) id delegate; -@property (nonatomic, weak) id policyDataSource; +@property (nonatomic, strong) id policyDataSource; +@property (nonatomic, assign) BOOL allowAllScripts; // default is NO - (void)policyDataSourceDidChange; diff --git a/SBrowser/Web Process Bundle Bridge/SBRProcessBundleBridge.m b/SBrowser/Web Process Bundle Bridge/SBRProcessBundleBridge.m index 718a58c..191961a 100644 --- a/SBrowser/Web Process Bundle Bridge/SBRProcessBundleBridge.m +++ b/SBrowser/Web Process Bundle Bridge/SBRProcessBundleBridge.m @@ -37,6 +37,12 @@ // Set up process pool WKProcessPool *processPool = [[WKProcessPool alloc] _initWithConfiguration:poolConfiguration]; + + // Initialize allowed origins now + NSArray *allowedOrigins = [[_policyDataSource allowedOriginsForScriptResources] allObjects]; + [processPool _setObject:allowedOrigins forBundleParameter:SBRGetAllowedOriginsKey()]; + [processPool _setObject:@(_allowAllScripts) forBundleParameter:SBRGetAllScriptsAllowedKey()]; + WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init]; configuration.processPool = processPool; @@ -79,4 +85,9 @@ [_webProcessProxy syncAllowedResourceOrigins:allowedOrigins]; } +- (void)setAllowAllScripts:(BOOL)allowAllScripts +{ + [_webProcessProxy setAllScriptsAllowed:allowAllScripts]; +} + @end diff --git a/SBrowserProcessBundle/SBRProcessPlugin.m b/SBrowserProcessBundle/SBRProcessPlugin.m index 994e601..16a059f 100644 --- a/SBrowserProcessBundle/SBRProcessPlugin.m +++ b/SBrowserProcessBundle/SBRProcessPlugin.m @@ -17,8 +17,10 @@ #import @interface SBRProcessPlugin () + @property (nonatomic, strong) id processDelegate; @property (nonatomic, strong) NSMutableSet *allowedResourceOrigins; +@property (nonatomic, assign) BOOL allScriptsAllowed; @end @implementation SBRProcessPlugin @@ -41,8 +43,20 @@ _allowedResourceOrigins = [NSMutableSet setWithArray:allowedOrigins]; } +- (void)setAllScriptsAllowed:(BOOL)allScriptsAllowed +{ + _allScriptsAllowed = allScriptsAllowed; +} + #pragma mark +- (void)webProcessPlugIn:(WKWebProcessPlugInController *)plugInController initializeWithObject:(id)initializationObject +{ + _allowedResourceOrigins = [[plugInController parameters] valueForKey:SBRGetAllowedOriginsKey()]; + _allScriptsAllowed = [[[plugInController parameters] valueForKey:SBRGetAllScriptsAllowedKey()] boolValue]; + NSLog(@"Got %lu allowed script origins at initialization", (unsigned long)_allowedResourceOrigins.count); +} + - (void)webProcessPlugIn:(WKWebProcessPlugInController *)plugInController didCreateBrowserContextController:(WKWebProcessPlugInBrowserContextController *)browserContextController { _WKRemoteObjectInterface *proxyInterface = [_WKRemoteObjectInterface remoteObjectInterfaceWithProtocol:@protocol(SBRWebProcessProxy)]; @@ -61,6 +75,9 @@ - (NSURLRequest *)webProcessPlugInBrowserContextController:(WKWebProcessPlugInBrowserContextController *)controller frame:(WKWebProcessPlugInFrame *)frame willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse { NSLog(@"SBRProcessPlugin: Sending request: %@", request); + if (_allowedResourceOrigins == nil) { + NSLog(@"Allowed resource origins should not be nil!!!!"); + } NSURL *requestURL = [request URL]; NSString *originString = [requestURL host]; @@ -68,7 +85,7 @@ if (requestExtension.length > 0 && [requestExtension isEqualToString:@"js"]) { [[self processDelegate] webProcessDidLoadScriptWithOrigin:originString]; - if ([_allowedResourceOrigins containsObject:originString]) { + if ([self allScriptsAllowed] || [_allowedResourceOrigins containsObject:originString]) { NSLog(@"SBRProcessPlugin: Allowing whitelisted requestURL: %@", requestURL); } else { NSLog(@"SBRProcessPlugin: Blocking requestURL: %@", requestURL); diff --git a/SBrowserProcessBundle/SBRWebProcessProxy.h b/SBrowserProcessBundle/SBRWebProcessProxy.h index c689044..f39bf8a 100644 --- a/SBrowserProcessBundle/SBRWebProcessProxy.h +++ b/SBrowserProcessBundle/SBRWebProcessProxy.h @@ -7,9 +7,18 @@ #import +static inline NSString* SBRGetAllowedOriginsKey() { + return @"allowedOrigins"; +} + +static inline NSString* SBRGetAllScriptsAllowedKey() { + return @"allScriptsAllowed"; +} + @protocol SBRWebProcessProxy - (void)hello; - (void)syncAllowedResourceOrigins:(NSArray *)allowedOrigins; +- (void)setAllScriptsAllowed:(BOOL)allowed; @end