Script blocking UI works now
This commit is contained in:
@@ -18,6 +18,13 @@
|
|||||||
1ADFF4A724C8C271006DC7AE /* SBrowserProcessBundle.bundle in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 1ADFF48124C8C12F006DC7AE /* SBrowserProcessBundle.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
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 */; };
|
1ADFF4AA24C8D477006DC7AE /* SBRProcessPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4A924C8D477006DC7AE /* SBRProcessPlugin.m */; };
|
||||||
1ADFF4AE24C8ED32006DC7AE /* ResourcePolicyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ADFF4AD24C8ED32006DC7AE /* ResourcePolicyManager.swift */; };
|
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 */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -65,6 +72,13 @@
|
|||||||
1ADFF4AB24C8DF62006DC7AE /* SBRWebProcessDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SBRWebProcessDelegate.h; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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 */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -112,6 +126,8 @@
|
|||||||
1ADFF46124C7DE53006DC7AE /* SceneDelegate.swift */,
|
1ADFF46124C7DE53006DC7AE /* SceneDelegate.swift */,
|
||||||
1ADFF47A24C7E176006DC7AE /* Backend */,
|
1ADFF47A24C7E176006DC7AE /* Backend */,
|
||||||
1ADFF47724C7DFE8006DC7AE /* Browser View */,
|
1ADFF47724C7DFE8006DC7AE /* Browser View */,
|
||||||
|
1ADFF4CE24CBBCBD006DC7AE /* Script Policy UI */,
|
||||||
|
1ADFF4C124CA6AE4006DC7AE /* Utilities */,
|
||||||
1ADFF4AF24C92E2F006DC7AE /* Web Process Bundle Bridge */,
|
1ADFF4AF24C92E2F006DC7AE /* Web Process Bundle Bridge */,
|
||||||
1ADFF47624C7DF7F006DC7AE /* Supporting Files */,
|
1ADFF47624C7DF7F006DC7AE /* Supporting Files */,
|
||||||
);
|
);
|
||||||
@@ -135,6 +151,8 @@
|
|||||||
children = (
|
children = (
|
||||||
1ADFF47324C7DE9C006DC7AE /* BrowserViewController.swift */,
|
1ADFF47324C7DE9C006DC7AE /* BrowserViewController.swift */,
|
||||||
1ADFF47824C7DFF8006DC7AE /* BrowserView.swift */,
|
1ADFF47824C7DFF8006DC7AE /* BrowserView.swift */,
|
||||||
|
1ADFF4C824CA793E006DC7AE /* ToolbarViewController.swift */,
|
||||||
|
1ADFF4BF24CA6964006DC7AE /* URLBar.swift */,
|
||||||
);
|
);
|
||||||
path = "Browser View";
|
path = "Browser View";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -176,6 +194,25 @@
|
|||||||
path = "Web Process Bundle Bridge";
|
path = "Web Process Bundle Bridge";
|
||||||
sourceTree = "<group>";
|
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 */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@@ -277,10 +314,17 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
1ADFF46024C7DE53006DC7AE /* AppDelegate.swift in Sources */,
|
1ADFF46024C7DE53006DC7AE /* AppDelegate.swift in Sources */,
|
||||||
|
1ADFF4C024CA6964006DC7AE /* URLBar.swift in Sources */,
|
||||||
|
1ADFF4C724CA6DEB006DC7AE /* UIEdgeInsets+Layout.swift in Sources */,
|
||||||
1ADFF4AE24C8ED32006DC7AE /* ResourcePolicyManager.swift in Sources */,
|
1ADFF4AE24C8ED32006DC7AE /* ResourcePolicyManager.swift in Sources */,
|
||||||
1ADFF47424C7DE9C006DC7AE /* BrowserViewController.swift in Sources */,
|
1ADFF47424C7DE9C006DC7AE /* BrowserViewController.swift in Sources */,
|
||||||
|
1ADFF4D024CBBCD1006DC7AE /* ScriptPolicyControl.swift in Sources */,
|
||||||
1ADFF48D24C8C176006DC7AE /* SBRProcessBundleBridge.m in Sources */,
|
1ADFF48D24C8C176006DC7AE /* SBRProcessBundleBridge.m in Sources */,
|
||||||
1ADFF46224C7DE53006DC7AE /* SceneDelegate.swift 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 */,
|
1ADFF47924C7DFF8006DC7AE /* BrowserView.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ class ResourcePolicyManager: NSObject, SBRResourceOriginPolicyDataSource
|
|||||||
func allowOriginToLoadScriptResources(_ origin: String)
|
func allowOriginToLoadScriptResources(_ origin: String)
|
||||||
{
|
{
|
||||||
allowedOriginSet.formUnion([ origin ])
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,19 +5,59 @@
|
|||||||
// Created by James Magahern on 7/21/20.
|
// Created by James Magahern on 7/21/20.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import Combine
|
||||||
import UIKit
|
import UIKit
|
||||||
import WebKit
|
import WebKit
|
||||||
|
|
||||||
class BrowserView: UIView
|
class BrowserView: UIView
|
||||||
{
|
{
|
||||||
var webView: WKWebView? {
|
var toolbarView: ToolbarView? {
|
||||||
didSet { addSubview(webView!); setNeedsLayout() }
|
didSet { addSubview(toolbarView!) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var webView: WKWebView? {
|
||||||
|
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()
|
override func layoutSubviews()
|
||||||
{
|
{
|
||||||
super.layoutSubviews()
|
super.layoutSubviews()
|
||||||
webView?.frame = bounds
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,59 +7,114 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class BrowserViewController: UIViewController, SBRProcessBundleBridgeDelegate
|
class BrowserViewController: UIViewController,
|
||||||
|
SBRProcessBundleBridgeDelegate, WKNavigationDelegate,
|
||||||
|
UITextFieldDelegate, ScriptPolicyViewControllerDelegate
|
||||||
{
|
{
|
||||||
let bridge = SBRProcessBundleBridge()
|
let bridge = SBRProcessBundleBridge()
|
||||||
let browserView = BrowserView()
|
let browserView = BrowserView()
|
||||||
|
|
||||||
private let policyManager = ResourcePolicyManager()
|
private let policyManager = ResourcePolicyManager()
|
||||||
|
private let toolbarController = ToolbarViewController()
|
||||||
private var blockedScriptOrigins = Set<String>()
|
private var blockedScriptOrigins = Set<String>()
|
||||||
private var scriptBlockerButtonItem: UIBarButtonItem
|
override var canBecomeFirstResponder: Bool { true }
|
||||||
|
|
||||||
init()
|
init() {
|
||||||
{
|
|
||||||
scriptBlockerButtonItem = UIBarButtonItem(title: "0", image: nil, primaryAction: UIAction(handler: { action in
|
|
||||||
// present
|
|
||||||
}), menu: nil)
|
|
||||||
|
|
||||||
super.init(nibName: nil, bundle: nil)
|
super.init(nibName: nil, bundle: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||||
|
|
||||||
override func loadView()
|
override func loadView() {
|
||||||
{
|
|
||||||
bridge.delegate = self
|
bridge.delegate = self
|
||||||
bridge.policyDataSource = policyManager
|
bridge.policyDataSource = policyManager
|
||||||
|
|
||||||
let webView = bridge.webView
|
let webView = bridge.webView
|
||||||
webView.allowsBackForwardNavigationGestures = true
|
webView.allowsBackForwardNavigationGestures = true
|
||||||
|
webView.navigationDelegate = self
|
||||||
|
|
||||||
browserView.webView = webView
|
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
|
self.view = browserView
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidLoad()
|
override func viewDidLoad() {
|
||||||
{
|
beginLoadingURL(URL(string: "https://reddit.com")!)
|
||||||
let request = URLRequest(url: URL(string: "https://yahoo.com")!)
|
|
||||||
browserView.webView?.load(request)
|
|
||||||
|
|
||||||
setToolbarItems([ scriptBlockerButtonItem ], animated: false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateScriptBlockerButton()
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
{
|
super.viewWillAppear(animated)
|
||||||
scriptBlockerButtonItem.title = "\(blockedScriptOrigins.count)"
|
becomeFirstResponder()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateScriptBlockerButton() {
|
||||||
|
toolbarController.scriptControllerIconView.setBlockedScriptsNumber(blockedScriptOrigins.count)
|
||||||
|
}
|
||||||
|
|
||||||
|
func beginLoadingURL(_ url: URL) {
|
||||||
|
let request = URLRequest(url: url)
|
||||||
|
bridge.webView.load(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: SBRProcessBundleBridgeDelegate
|
// MARK: SBRProcessBundleBridgeDelegate
|
||||||
|
|
||||||
func webProcess(_ bridge: SBRProcessBundleBridge, didBlockScriptResourceFromOrigin origin: String)
|
func webProcess(_ bridge: SBRProcessBundleBridge, didBlockScriptResourceFromOrigin origin: String) {
|
||||||
{
|
|
||||||
print("Blocked script resource from origin: \(origin)")
|
print("Blocked script resource from origin: \(origin)")
|
||||||
|
|
||||||
blockedScriptOrigins.formUnion([ origin ])
|
blockedScriptOrigins.formUnion([ origin ])
|
||||||
updateScriptBlockerButton()
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
109
SBrowser/Browser View/ToolbarViewController.swift
Normal file
109
SBrowser/Browser View/ToolbarViewController.swift
Normal 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
58
SBrowser/Browser View/URLBar.swift
Normal file
58
SBrowser/Browser View/URLBar.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,7 +18,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||||||
|
|
||||||
navigationController.viewControllers = [ browserViewController ]
|
navigationController.viewControllers = [ browserViewController ]
|
||||||
navigationController.setNavigationBarHidden(true, animated: false)
|
navigationController.setNavigationBarHidden(true, animated: false)
|
||||||
navigationController.setToolbarHidden(false, animated: false)
|
|
||||||
|
|
||||||
let window = UIWindow(windowScene: windowScene)
|
let window = UIWindow(windowScene: windowScene)
|
||||||
window.rootViewController = navigationController
|
window.rootViewController = navigationController
|
||||||
|
|||||||
55
SBrowser/Script Policy UI/ScriptControllerIconView.swift
Normal file
55
SBrowser/Script Policy UI/ScriptControllerIconView.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
73
SBrowser/Script Policy UI/ScriptPolicyControl.swift
Normal file
73
SBrowser/Script Policy UI/ScriptPolicyControl.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
113
SBrowser/Script Policy UI/ScriptPolicyViewController.swift
Normal file
113
SBrowser/Script Policy UI/ScriptPolicyViewController.swift
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,3 +3,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import "SBRProcessBundleBridge.h"
|
#import "SBRProcessBundleBridge.h"
|
||||||
|
|
||||||
|
// SPI
|
||||||
|
#import <UIKit/UITextField_Private.h>
|
||||||
|
|||||||
17
SBrowser/Utilities/CGPoint+Utils.swift
Normal file
17
SBrowser/Utilities/CGPoint+Utils.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
SBrowser/Utilities/UIEdgeInsets+Layout.swift
Normal file
17
SBrowser/Utilities/UIEdgeInsets+Layout.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
|
|
||||||
- (void)policyDataSourceDidChange
|
- (void)policyDataSourceDidChange
|
||||||
{
|
{
|
||||||
NSSet<NSString *> *allowedOrigins = [_policyDataSource allowedOriginsForScriptResources];
|
NSArray<NSString *> *allowedOrigins = [[_policyDataSource allowedOriginsForScriptResources] allObjects];
|
||||||
[_webProcessProxy syncAllowedResourceOrigins:allowedOrigins];
|
[_webProcessProxy syncAllowedResourceOrigins:allowedOrigins];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,13 +36,9 @@
|
|||||||
NSLog(@"SBRProcessPlugin: Helloooooo");
|
NSLog(@"SBRProcessPlugin: Helloooooo");
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)syncAllowedResourceOrigins:(NSSet<NSString *> *)allowedOrigins
|
- (void)syncAllowedResourceOrigins:(NSArray<NSString *> *)allowedOrigins
|
||||||
{
|
{
|
||||||
if (!_allowedResourceOrigins) {
|
_allowedResourceOrigins = [NSMutableSet setWithArray:allowedOrigins];
|
||||||
_allowedResourceOrigins = [allowedOrigins mutableCopy];
|
|
||||||
} else {
|
|
||||||
[_allowedResourceOrigins unionSet:allowedOrigins];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark <WKWebProcessPlugIn>
|
#pragma mark <WKWebProcessPlugIn>
|
||||||
|
|||||||
@@ -10,6 +10,6 @@
|
|||||||
@protocol SBRWebProcessProxy <NSObject>
|
@protocol SBRWebProcessProxy <NSObject>
|
||||||
|
|
||||||
- (void)hello;
|
- (void)hello;
|
||||||
- (void)syncAllowedResourceOrigins:(NSSet<NSString *> *)allowedOrigins;
|
- (void)syncAllowedResourceOrigins:(NSArray<NSString *> *)allowedOrigins;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
Reference in New Issue
Block a user