Script blocking UI works now

This commit is contained in:
James Magahern
2020-07-24 19:26:35 -07:00
parent 125c7f8991
commit 37eeeacc85
16 changed files with 619 additions and 34 deletions

View File

@@ -18,6 +18,13 @@
1ADFF4A724C8C271006DC7AE /* SBrowserProcessBundle.bundle in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 1ADFF48124C8C12F006DC7AE /* SBrowserProcessBundle.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
1ADFF4AA24C8D477006DC7AE /* SBRProcessPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4A924C8D477006DC7AE /* SBRProcessPlugin.m */; };
1ADFF4AE24C8ED32006DC7AE /* ResourcePolicyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4AD24C8ED32006DC7AE /* ResourcePolicyManager.swift */; };
1ADFF4C024CA6964006DC7AE /* URLBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4BF24CA6964006DC7AE /* URLBar.swift */; };
1ADFF4C324CA6AF6006DC7AE /* CGPoint+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4C224CA6AF6006DC7AE /* CGPoint+Utils.swift */; };
1ADFF4C724CA6DEB006DC7AE /* UIEdgeInsets+Layout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4C624CA6DEB006DC7AE /* UIEdgeInsets+Layout.swift */; };
1ADFF4C924CA793E006DC7AE /* ToolbarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4C824CA793E006DC7AE /* ToolbarViewController.swift */; };
1ADFF4CB24CB8278006DC7AE /* ScriptControllerIconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4CA24CB8278006DC7AE /* ScriptControllerIconView.swift */; };
1ADFF4CD24CBB0C8006DC7AE /* ScriptPolicyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4CC24CBB0C8006DC7AE /* ScriptPolicyViewController.swift */; };
1ADFF4D024CBBCD1006DC7AE /* ScriptPolicyControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4CF24CBBCD1006DC7AE /* ScriptPolicyControl.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -65,6 +72,13 @@
1ADFF4AB24C8DF62006DC7AE /* SBRWebProcessDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SBRWebProcessDelegate.h; sourceTree = "<group>"; };
1ADFF4AC24C8DFEE006DC7AE /* SBRWebProcessProxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SBRWebProcessProxy.h; sourceTree = "<group>"; };
1ADFF4AD24C8ED32006DC7AE /* ResourcePolicyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResourcePolicyManager.swift; sourceTree = "<group>"; };
1ADFF4BF24CA6964006DC7AE /* URLBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLBar.swift; sourceTree = "<group>"; };
1ADFF4C224CA6AF6006DC7AE /* CGPoint+Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGPoint+Utils.swift"; sourceTree = "<group>"; };
1ADFF4C624CA6DEB006DC7AE /* UIEdgeInsets+Layout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIEdgeInsets+Layout.swift"; sourceTree = "<group>"; };
1ADFF4C824CA793E006DC7AE /* ToolbarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarViewController.swift; sourceTree = "<group>"; };
1ADFF4CA24CB8278006DC7AE /* ScriptControllerIconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptControllerIconView.swift; sourceTree = "<group>"; };
1ADFF4CC24CBB0C8006DC7AE /* ScriptPolicyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptPolicyViewController.swift; sourceTree = "<group>"; };
1ADFF4CF24CBBCD1006DC7AE /* ScriptPolicyControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScriptPolicyControl.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -112,6 +126,8 @@
1ADFF46124C7DE53006DC7AE /* SceneDelegate.swift */,
1ADFF47A24C7E176006DC7AE /* Backend */,
1ADFF47724C7DFE8006DC7AE /* Browser View */,
1ADFF4CE24CBBCBD006DC7AE /* Script Policy UI */,
1ADFF4C124CA6AE4006DC7AE /* Utilities */,
1ADFF4AF24C92E2F006DC7AE /* Web Process Bundle Bridge */,
1ADFF47624C7DF7F006DC7AE /* Supporting Files */,
);
@@ -135,6 +151,8 @@
children = (
1ADFF47324C7DE9C006DC7AE /* BrowserViewController.swift */,
1ADFF47824C7DFF8006DC7AE /* BrowserView.swift */,
1ADFF4C824CA793E006DC7AE /* ToolbarViewController.swift */,
1ADFF4BF24CA6964006DC7AE /* URLBar.swift */,
);
path = "Browser View";
sourceTree = "<group>";
@@ -176,6 +194,25 @@
path = "Web Process Bundle Bridge";
sourceTree = "<group>";
};
1ADFF4C124CA6AE4006DC7AE /* Utilities */ = {
isa = PBXGroup;
children = (
1ADFF4C224CA6AF6006DC7AE /* CGPoint+Utils.swift */,
1ADFF4C624CA6DEB006DC7AE /* UIEdgeInsets+Layout.swift */,
);
path = Utilities;
sourceTree = "<group>";
};
1ADFF4CE24CBBCBD006DC7AE /* Script Policy UI */ = {
isa = PBXGroup;
children = (
1ADFF4CA24CB8278006DC7AE /* ScriptControllerIconView.swift */,
1ADFF4CF24CBBCD1006DC7AE /* ScriptPolicyControl.swift */,
1ADFF4CC24CBB0C8006DC7AE /* ScriptPolicyViewController.swift */,
);
path = "Script Policy UI";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -277,10 +314,17 @@
buildActionMask = 2147483647;
files = (
1ADFF46024C7DE53006DC7AE /* AppDelegate.swift in Sources */,
1ADFF4C024CA6964006DC7AE /* URLBar.swift in Sources */,
1ADFF4C724CA6DEB006DC7AE /* UIEdgeInsets+Layout.swift in Sources */,
1ADFF4AE24C8ED32006DC7AE /* ResourcePolicyManager.swift in Sources */,
1ADFF47424C7DE9C006DC7AE /* BrowserViewController.swift in Sources */,
1ADFF4D024CBBCD1006DC7AE /* ScriptPolicyControl.swift in Sources */,
1ADFF48D24C8C176006DC7AE /* SBRProcessBundleBridge.m in Sources */,
1ADFF46224C7DE53006DC7AE /* SceneDelegate.swift in Sources */,
1ADFF4CB24CB8278006DC7AE /* ScriptControllerIconView.swift in Sources */,
1ADFF4C324CA6AF6006DC7AE /* CGPoint+Utils.swift in Sources */,
1ADFF4C924CA793E006DC7AE /* ToolbarViewController.swift in Sources */,
1ADFF4CD24CBB0C8006DC7AE /* ScriptPolicyViewController.swift in Sources */,
1ADFF47924C7DFF8006DC7AE /* BrowserView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;

View File

@@ -26,6 +26,12 @@ class ResourcePolicyManager: NSObject, SBRResourceOriginPolicyDataSource
func allowOriginToLoadScriptResources(_ origin: String)
{
allowedOriginSet.formUnion([ origin ])
UserDefaults.standard.set(allowedOriginSet, forKey: Self.AllowedOriginsDefaultsKey)
UserDefaults.standard.set(Array(allowedOriginSet), forKey: Self.AllowedOriginsDefaultsKey)
}
func disallowOriginToLoadScriptResources(_ origin: String)
{
allowedOriginSet.remove(origin)
UserDefaults.standard.set(Array(allowedOriginSet), forKey: Self.AllowedOriginsDefaultsKey)
}
}

View File

@@ -5,19 +5,59 @@
// Created by James Magahern on 7/21/20.
//
import Combine
import UIKit
import WebKit
class BrowserView: UIView
{
var toolbarView: ToolbarView? {
didSet { addSubview(toolbarView!) }
}
var webView: WKWebView? {
didSet { addSubview(webView!); setNeedsLayout() }
didSet {
if let toolbarView = toolbarView {
insertSubview(webView!, belowSubview: toolbarView)
} else {
addSubview(webView!)
}
}
}
var keyboardWillShowObserver: AnyCancellable?
var keyboardWillHideObserver: AnyCancellable?
var keyboardLayoutOffset: CGFloat = 0 { didSet { setNeedsLayout() } }
convenience init() {
self.init(frame: .zero)
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
}
}
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
}
}
}
override func layoutSubviews()
{
super.layoutSubviews()
webView?.frame = bounds
if let toolbarView = toolbarView {
var toolbarSize = toolbarView.sizeThatFits(bounds.size)
if keyboardLayoutOffset == 0 {
toolbarSize.height += safeAreaInsets.bottom
}
toolbarView.bounds = CGRect(origin: .zero, size: toolbarSize)
toolbarView.center = CGPoint(x: bounds.center.x, y: bounds.maxY - (toolbarView.bounds.height / 2) - keyboardLayoutOffset)
}
}
}

View File

@@ -7,59 +7,114 @@
import UIKit
class BrowserViewController: UIViewController, SBRProcessBundleBridgeDelegate
class BrowserViewController: UIViewController,
SBRProcessBundleBridgeDelegate, WKNavigationDelegate,
UITextFieldDelegate, ScriptPolicyViewControllerDelegate
{
let bridge = SBRProcessBundleBridge()
let browserView = BrowserView()
private let policyManager = ResourcePolicyManager()
private let toolbarController = ToolbarViewController()
private var blockedScriptOrigins = Set<String>()
private var scriptBlockerButtonItem: UIBarButtonItem
init()
{
scriptBlockerButtonItem = UIBarButtonItem(title: "0", image: nil, primaryAction: UIAction(handler: { action in
// present
}), menu: nil)
override var canBecomeFirstResponder: Bool { true }
init() {
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
override func loadView()
{
override func loadView() {
bridge.delegate = self
bridge.policyDataSource = policyManager
let webView = bridge.webView
webView.allowsBackForwardNavigationGestures = true
webView.navigationDelegate = self
browserView.webView = webView
browserView.toolbarView = toolbarController.toolbarView
// Refresh button
toolbarController.urlBar.refreshButton.addAction(UIAction(handler: { action in
webView.reload()
}), for: .touchUpInside)
// Script button
toolbarController.scriptControllerIconView.addAction(UIAction(handler: { action in
let scriptViewController = ScriptPolicyViewController(policyManager: self.policyManager, blockedScripts: self.blockedScriptOrigins)
scriptViewController.delegate = self
let navController = UINavigationController(rootViewController: scriptViewController)
self.present(navController, animated: true, completion: nil)
}), for: .touchUpInside)
// TextField delegate
toolbarController.urlBar.textField.delegate = self
self.view = browserView
}
override func viewDidLoad()
{
let request = URLRequest(url: URL(string: "https://yahoo.com")!)
browserView.webView?.load(request)
setToolbarItems([ scriptBlockerButtonItem ], animated: false)
override func viewDidLoad() {
beginLoadingURL(URL(string: "https://reddit.com")!)
}
private func updateScriptBlockerButton()
{
scriptBlockerButtonItem.title = "\(blockedScriptOrigins.count)"
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
becomeFirstResponder()
}
private func updateScriptBlockerButton() {
toolbarController.scriptControllerIconView.setBlockedScriptsNumber(blockedScriptOrigins.count)
}
func beginLoadingURL(_ url: URL) {
let request = URLRequest(url: url)
bridge.webView.load(request)
}
// MARK: SBRProcessBundleBridgeDelegate
func webProcess(_ bridge: SBRProcessBundleBridge, didBlockScriptResourceFromOrigin origin: String)
{
func webProcess(_ bridge: SBRProcessBundleBridge, didBlockScriptResourceFromOrigin origin: String) {
print("Blocked script resource from origin: \(origin)")
blockedScriptOrigins.formUnion([ origin ])
updateScriptBlockerButton()
}
// MARK: Navigation Delegate
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
// Reset tracking this
blockedScriptOrigins.removeAll()
if let urlString = webView.url?.absoluteString {
toolbarController.urlBar.textField.text = urlString
}
}
// MARK: UITextField Delegate
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
if let text = textField.text, let url = URL(string: text) {
if url.scheme == nil {
let urlString = "https://\(text)"
if let url = URL(string: urlString) {
beginLoadingURL(url)
}
}
}
textField.resignFirstResponder()
return false
}
// MARK: Script Policy View Controller Delegate
func didChangeScriptPolicy() {
bridge.policyDataSourceDidChange()
bridge.webView.reload()
}
}

View File

@@ -0,0 +1,109 @@
//
// ToolbarViewController.swift
// SBrowser
//
// Created by James Magahern on 7/23/20.
//
import UIKit
class ToolbarButtonView: UIView
{
private var buttonPadding = CGFloat(8.0)
private var buttonViews: [UIView] = []
func addButtonView(_ button: UIView) {
buttonViews.append(button)
addSubview(button)
setNeedsLayout()
}
override func sizeThatFits(_ size: CGSize) -> CGSize {
let width: CGFloat = buttonViews.reduce(0.0) { (result, button) -> CGFloat in
return result + button.sizeThatFits(size).width + buttonPadding
}
return CGSize(width: width, height: size.height)
}
override func layoutSubviews() {
var buttonRect = CGRect(origin: .zero, size: CGSize(width: 0, height: bounds.height))
buttonRect.origin.x = buttonPadding
for button in buttonViews {
let buttonSize = button.sizeThatFits(bounds.size)
buttonRect.size = CGSize(width: buttonSize.width, height: bounds.height)
button.frame = buttonRect
buttonRect.origin.x += buttonRect.width + buttonPadding
}
}
}
class ToolbarView: UIView
{
var urlBar: URLBar? { didSet { containerView.addSubview(urlBar!) } }
let containerView = UIView(frame: .zero)
let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemThickMaterial))
let buttonsView = ToolbarButtonView(frame: .zero)
convenience init()
{
self.init(frame: .zero)
addSubview(backgroundView)
addSubview(containerView)
containerView.addSubview(buttonsView)
}
override func sizeThatFits(_ size: CGSize) -> CGSize
{
return CGSize(width: size.width, height: 44.0)
}
override func layoutSubviews()
{
super.layoutSubviews()
backgroundView.frame = bounds
var containerBounds = bounds
containerBounds.size.height -= safeAreaInsets.bottom
containerView.frame = containerBounds
containerView.frame = containerView.frame.insetBy(dx: 8.0, dy: 4.0)
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 + 8.0, y: 0), size: toolbarSize)
}
}
class ToolbarViewController: UIViewController
{
let urlBar = URLBar()
let toolbarView = ToolbarView()
let scriptControllerIconView = ScriptControllerIconView()
let shareButton = UIButton(frame: .zero)
init() {
super.init(nibName: nil, bundle: nil)
toolbarView.urlBar = urlBar
shareButton.setImage(UIImage(systemName: "square.and.arrow.up"), for: .normal)
toolbarView.buttonsView.addButtonView(shareButton)
toolbarView.buttonsView.addButtonView(scriptControllerIconView)
}
override func loadView() {
self.view = toolbarView
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@@ -0,0 +1,58 @@
//
// URLBar.swift
// SBrowser
//
// Created by James Magahern on 7/23/20.
//
import UIKit
class URLBar: UIView
{
let textField = UITextField(frame: .zero)
let refreshButton = UIButton(frame: .zero)
private let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemThickMaterial))
convenience init() {
self.init(frame: .zero)
backgroundColor = .clear
backgroundView.layer.masksToBounds = true
backgroundView.layer.cornerRadius = 8
backgroundView.layer.borderWidth = 1
backgroundView.layer.borderColor = UIColor.systemFill.cgColor
backgroundView.isUserInteractionEnabled = false
addSubview(backgroundView)
textField.backgroundColor = .clear
textField.textContentType = .URL
textField.keyboardType = .webSearch
textField.autocorrectionType = .no
textField.autocapitalizationType = .none
textField.font = .preferredFont(forTextStyle: .body)
textField.clearingBehavior = .clearOnInsertionAndShowSelectionTint
addSubview(textField)
refreshButton.tintColor = .secondaryLabel
refreshButton.setImage(UIImage(systemName: "arrow.clockwise"), for: .normal)
addSubview(refreshButton)
}
override var intrinsicContentSize: CGSize
{
let preferredHeight = CGFloat(34)
return CGSize(width: 1000.0, height: preferredHeight)
}
override func layoutSubviews()
{
super.layoutSubviews()
backgroundView.frame = bounds
textField.frame = bounds.insetBy(dx: 6.0, dy: 0)
let refreshButtonSize = CGSize(width: textField.frame.height, height: textField.frame.height)
refreshButton.frame = CGRect(origin: CGPoint(x: bounds.width - refreshButtonSize.width, y: 0), size: refreshButtonSize)
}
}

View File

@@ -18,7 +18,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
navigationController.viewControllers = [ browserViewController ]
navigationController.setNavigationBarHidden(true, animated: false)
navigationController.setToolbarHidden(false, animated: false)
let window = UIWindow(windowScene: windowScene)
window.rootViewController = navigationController

View File

@@ -0,0 +1,55 @@
//
// ScriptControllerIconView.swift
// SBrowser
//
// Created by James Magahern on 7/24/20.
//
import UIKit
class ScriptControllerIconView: UIButton
{
private let labelView = UILabel(frame: .zero)
convenience init() {
self.init(frame: .zero)
addSubview(labelView)
let image = UIImage(systemName: "shield")
setImage(image, for: .normal)
imageView?.contentMode = .scaleAspectFit
labelView.backgroundColor = .systemRed
labelView.textAlignment = .center
labelView.layer.cornerRadius = 4.0
labelView.layer.masksToBounds = true
labelView.font = .boldSystemFont(ofSize: 8)
labelView.textColor = .white
setBlockedScriptsNumber(0)
}
public func setBlockedScriptsNumber(_ num: Int) {
if num > 0 {
labelView.isHidden = false
labelView.text = "\(num)"
} else {
labelView.isHidden = true
}
setNeedsLayout()
}
override func sizeThatFits(_ size: CGSize) -> CGSize {
return CGSize(width: 44.0, height: 44.0)
}
override func layoutSubviews() {
super.layoutSubviews()
labelView.sizeToFit()
labelView.center = CGPoint(x: bounds.center.x + 10, y: bounds.center.y + 10)
labelView.bounds = labelView.bounds.insetBy(dx: -2.0, dy: -2.0)
}
}

View File

@@ -0,0 +1,73 @@
//
// ScriptPolicyControl.swift
// SBrowser
//
// Created by James Magahern on 7/24/20.
//
import UIKit
class ScriptPolicyControl: UIControl
{
enum PolicyStatus {
case allowed
case blocked
}
var policyStatus: PolicyStatus = .blocked {
didSet {
sendActions(for: .valueChanged)
setNeedsLayout()
}
}
private class PolicyButton: UIButton {
override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
contentRect.insetBy(dx: 8.0, dy: 8.0)
}
}
private let allowButton = PolicyButton(frame: .zero)
private let denyButton = PolicyButton(frame: .zero)
convenience init() {
self.init(frame: .zero)
allowButton.addAction(UIAction(handler: { _ in
self.policyStatus = .allowed
}), for: .touchUpInside)
allowButton.imageView?.contentMode = .scaleAspectFit
addSubview(allowButton)
denyButton.addAction(UIAction(handler: { _ in
self.policyStatus = .blocked
}), for: .touchUpInside)
denyButton.imageView?.contentMode = .scaleAspectFit
addSubview(denyButton)
}
override var intrinsicContentSize: CGSize {
CGSize(width: 100.0, height: UIView.noIntrinsicMetric)
}
override func layoutSubviews() {
super.layoutSubviews()
allowButton.frame = CGRect(origin: .zero, size: CGSize(width: bounds.width / 2, height: bounds.height))
denyButton.frame = CGRect(origin: CGPoint(x: allowButton.frame.maxX, y: 0), size: allowButton.frame.size)
if policyStatus == .allowed {
allowButton.tintColor = .blue
allowButton.setImage(UIImage(systemName: "play.circle.fill"), for: .normal)
denyButton.tintColor = .darkGray
denyButton.setImage(UIImage(systemName: "stop.circle"), for: .normal)
} else {
allowButton.tintColor = .darkGray
allowButton.setImage(UIImage(systemName: "play.circle"), for: .normal)
denyButton.tintColor = .red
denyButton.setImage(UIImage(systemName: "stop.circle.fill"), for: .normal)
}
}
}

View File

@@ -0,0 +1,113 @@
//
// ScriptPolicyViewController.swift
// SBrowser
//
// Created by James Magahern on 7/24/20.
//
import UIKit
protocol ScriptPolicyViewControllerDelegate {
func didChangeScriptPolicy()
}
class ScriptPolicyControlListCell: UICollectionViewListCell
{
let policyControl = ScriptPolicyControl()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(policyControl)
}
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))
}
}
class ScriptPolicyViewController: UIViewController, UICollectionViewDelegate
{
var collectionView: UICollectionView?
var delegate: ScriptPolicyViewControllerDelegate? = nil
var dataSource: UICollectionViewDiffableDataSource<Int, String>?
private var didChangeScriptPolicy = false
convenience init(policyManager: ResourcePolicyManager, blockedScripts: Set<String>) {
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<ScriptPolicyControlListCell, String> { (listCell, indexPath, item) in
var config = listCell.defaultContentConfiguration()
config.text = item
listCell.contentConfiguration = config
if policyManager.allowedOriginsForScriptResources().contains(item) {
listCell.policyControl.policyStatus = .allowed
} else {
listCell.policyControl.policyStatus = .blocked
}
listCell.policyControl.addAction(UIAction(handler: { _ in
if listCell.policyControl.policyStatus == .allowed {
policyManager.allowOriginToLoadScriptResources(item)
} else {
policyManager.disallowOriginToLoadScriptResources(item)
}
self.didChangeScriptPolicy = true
}), for: .valueChanged)
}
let dataSource = UICollectionViewDiffableDataSource<Int, String>(collectionView: collectionView) { (collectionView, indexPath, item) -> UICollectionViewCell? in
collectionView.dequeueConfiguredReusableCell(using: registry, for: indexPath, item: item)
}
collectionView.dataSource = dataSource
collectionView.delegate = self
var snapshot = dataSource.snapshot()
snapshot.appendSections([ 0 ])
snapshot.appendItems(Array(blockedScripts))
dataSource.apply(snapshot)
self.dataSource = dataSource
self.collectionView = collectionView
title = "Script Origin Policy"
navigationItem.rightBarButtonItem = UIBarButtonItem(systemItem: .done, primaryAction: UIAction(handler: { action in
if self.didChangeScriptPolicy {
self.delegate?.didChangeScriptPolicy()
}
self.dismiss(animated: true, completion: nil)
}), menu: nil)
}
override func loadView() {
self.view = collectionView
}
// MARK: UICollectionViewDelegate
func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool {
false
}
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
false
}
}

View File

@@ -3,3 +3,6 @@
//
#import "SBRProcessBundleBridge.h"
// SPI
#import <UIKit/UITextField_Private.h>

View File

@@ -0,0 +1,17 @@
//
// CGPoint+Utils.swift
// SBrowser
//
// Created by James Magahern on 7/23/20.
//
import Foundation
extension CGRect
{
var center: CGPoint {
get {
return CGPoint(x: size.width / 2.0, y: size.height / 2.0)
}
}
}

View File

@@ -0,0 +1,17 @@
//
// UIEdgeInsets+Layout.swift
// SBrowser
//
// Created by James Magahern on 7/23/20.
//
import UIKit
extension UIEdgeInsets
{
var negative: UIEdgeInsets {
get {
return UIEdgeInsets(top: -top, left: -left, bottom: -bottom, right: -right)
}
}
}

View File

@@ -75,7 +75,7 @@
- (void)policyDataSourceDidChange
{
NSSet<NSString *> *allowedOrigins = [_policyDataSource allowedOriginsForScriptResources];
NSArray<NSString *> *allowedOrigins = [[_policyDataSource allowedOriginsForScriptResources] allObjects];
[_webProcessProxy syncAllowedResourceOrigins:allowedOrigins];
}

View File

@@ -36,13 +36,9 @@
NSLog(@"SBRProcessPlugin: Helloooooo");
}
- (void)syncAllowedResourceOrigins:(NSSet<NSString *> *)allowedOrigins
- (void)syncAllowedResourceOrigins:(NSArray<NSString *> *)allowedOrigins
{
if (!_allowedResourceOrigins) {
_allowedResourceOrigins = [allowedOrigins mutableCopy];
} else {
[_allowedResourceOrigins unionSet:allowedOrigins];
}
_allowedResourceOrigins = [NSMutableSet setWithArray:allowedOrigins];
}
#pragma mark <WKWebProcessPlugIn>

View File

@@ -10,6 +10,6 @@
@protocol SBRWebProcessProxy <NSObject>
- (void)hello;
- (void)syncAllowedResourceOrigins:(NSSet<NSString *> *)allowedOrigins;
- (void)syncAllowedResourceOrigins:(NSArray<NSString *> *)allowedOrigins;
@end