Initial commit
Able to actually block scripts using a plugin, have XPC set up between plugin and host process. Limited UI support
This commit is contained in:
25
SBrowser/AppDelegate.swift
Normal file
25
SBrowser/AppDelegate.swift
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// AppDelegate.swift
|
||||
// SBrowser
|
||||
//
|
||||
// Created by James Magahern on 7/21/20.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@main
|
||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
|
||||
{
|
||||
return true
|
||||
}
|
||||
|
||||
// MARK: UISceneSession Lifecycle
|
||||
|
||||
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration
|
||||
{
|
||||
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
|
||||
}
|
||||
}
|
||||
|
||||
31
SBrowser/Backend/ResourcePolicyManager.swift
Normal file
31
SBrowser/Backend/ResourcePolicyManager.swift
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// ResourcePolicyManager.swift
|
||||
// SBrowser
|
||||
//
|
||||
// Created by James Magahern on 7/22/20.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class ResourcePolicyManager: NSObject, SBRResourceOriginPolicyDataSource
|
||||
{
|
||||
static let AllowedOriginsDefaultsKey = "allowedOrigins"
|
||||
private var allowedOriginSet: Set<String> = {
|
||||
if let allowedOrigins = UserDefaults.standard.array(forKey: AllowedOriginsDefaultsKey) as? [String] {
|
||||
return Set<String>(allowedOrigins)
|
||||
}
|
||||
|
||||
return Set<String>()
|
||||
}()
|
||||
|
||||
func allowedOriginsForScriptResources() -> Set<String>
|
||||
{
|
||||
allowedOriginSet
|
||||
}
|
||||
|
||||
func allowOriginToLoadScriptResources(_ origin: String)
|
||||
{
|
||||
allowedOriginSet.formUnion([ origin ])
|
||||
UserDefaults.standard.set(allowedOriginSet, forKey: Self.AllowedOriginsDefaultsKey)
|
||||
}
|
||||
}
|
||||
23
SBrowser/Browser View/BrowserView.swift
Normal file
23
SBrowser/Browser View/BrowserView.swift
Normal file
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// BrowserView.swift
|
||||
// SBrowser
|
||||
//
|
||||
// Created by James Magahern on 7/21/20.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import WebKit
|
||||
|
||||
class BrowserView: UIView
|
||||
{
|
||||
var webView: WKWebView? {
|
||||
didSet { addSubview(webView!); setNeedsLayout() }
|
||||
}
|
||||
|
||||
override func layoutSubviews()
|
||||
{
|
||||
super.layoutSubviews()
|
||||
webView?.frame = bounds
|
||||
}
|
||||
|
||||
}
|
||||
63
SBrowser/Browser View/BrowserViewController.swift
Normal file
63
SBrowser/Browser View/BrowserViewController.swift
Normal file
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// BrowserViewController.swift
|
||||
// SBrowser
|
||||
//
|
||||
// Created by James Magahern on 7/21/20.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class BrowserViewController: UIViewController, SBRProcessBundleBridgeDelegate
|
||||
{
|
||||
let bridge = SBRProcessBundleBridge()
|
||||
let browserView = BrowserView()
|
||||
|
||||
private let policyManager = ResourcePolicyManager()
|
||||
private var blockedScriptOrigins = Set<String>()
|
||||
private var scriptBlockerButtonItem: UIBarButtonItem
|
||||
|
||||
init()
|
||||
{
|
||||
scriptBlockerButtonItem = UIBarButtonItem(title: "0", image: nil, primaryAction: UIAction(handler: { action in
|
||||
// present
|
||||
}), menu: nil)
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
|
||||
|
||||
override func loadView()
|
||||
{
|
||||
bridge.delegate = self
|
||||
bridge.policyDataSource = policyManager
|
||||
|
||||
browserView.webView = bridge.webView
|
||||
browserView.webView?.allowsBackForwardNavigationGestures = true
|
||||
|
||||
self.view = browserView
|
||||
}
|
||||
|
||||
override func viewDidLoad()
|
||||
{
|
||||
let request = URLRequest(url: URL(string: "https://yahoo.com")!)
|
||||
browserView.webView?.load(request)
|
||||
|
||||
navigationItem.rightBarButtonItem = scriptBlockerButtonItem
|
||||
}
|
||||
|
||||
private func updateScriptBlockerButton()
|
||||
{
|
||||
scriptBlockerButtonItem.title = "\(blockedScriptOrigins.count)"
|
||||
}
|
||||
|
||||
// MARK: SBRProcessBundleBridgeDelegate
|
||||
|
||||
func webProcess(_ bridge: SBRProcessBundleBridge, didBlockScriptResourceFromOrigin origin: String)
|
||||
{
|
||||
print("Blocked script resource from origin: \(origin)")
|
||||
|
||||
blockedScriptOrigins.formUnion([ origin ])
|
||||
updateScriptBlockerButton()
|
||||
}
|
||||
}
|
||||
37
SBrowser/Browser View/SBRProcessBundleBridge.h
Normal file
37
SBrowser/Browser View/SBRProcessBundleBridge.h
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// SBRProcessBundleBridge.h
|
||||
// SBrowser
|
||||
//
|
||||
// Created by James Magahern on 7/22/20.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol SBRResourceOriginPolicyDataSource <NSObject>
|
||||
|
||||
/// Returns a list of origins (e.g., "buzzert.net") for which we are allowed to load script resources from
|
||||
- (NSSet<NSString *> *)allowedOriginsForScriptResources;
|
||||
|
||||
@end
|
||||
|
||||
@class SBRProcessBundleBridge;
|
||||
@protocol SBRProcessBundleBridgeDelegate <NSObject>
|
||||
- (void)webProcess:(SBRProcessBundleBridge *)bridge didBlockScriptResourceFromOrigin:(NSString *)origin;
|
||||
|
||||
@end
|
||||
|
||||
@interface SBRProcessBundleBridge : NSObject
|
||||
|
||||
@property (nonatomic, readonly) WKWebView *webView;
|
||||
|
||||
@property (nonatomic, weak) id<SBRProcessBundleBridgeDelegate> delegate;
|
||||
@property (nonatomic, weak) id<SBRResourceOriginPolicyDataSource> policyDataSource;
|
||||
|
||||
- (void)policyDataSourceDidChange;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
82
SBrowser/Browser View/SBRProcessBundleBridge.m
Normal file
82
SBrowser/Browser View/SBRProcessBundleBridge.m
Normal file
@@ -0,0 +1,82 @@
|
||||
//
|
||||
// SBRProcessBundleBridge.m
|
||||
// SBrowser
|
||||
//
|
||||
// Created by James Magahern on 7/22/20.
|
||||
//
|
||||
|
||||
#import "SBRProcessBundleBridge.h"
|
||||
|
||||
#import "SBRWebProcessDelegate.h"
|
||||
#import "SBRWebProcessProxy.h"
|
||||
|
||||
#import <WebKit/_WKRemoteObjectInterface.h>
|
||||
#import <WebKit/_WKRemoteObjectRegistry.h>
|
||||
#import <WebKit/_WKProcessPoolConfiguration.h>
|
||||
|
||||
#import <WebKit/WKProcessPoolPrivate.h>
|
||||
#import <WebKit/WKWebViewPrivate.h>
|
||||
#import <WebKit/WKWebViewConfigurationPrivate.h>
|
||||
|
||||
@interface SBRProcessBundleBridge () <SBRWebProcessDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation SBRProcessBundleBridge {
|
||||
WKWebView *_webView;
|
||||
id<SBRWebProcessProxy> _webProcessProxy;
|
||||
}
|
||||
|
||||
- (WKWebView *)webView
|
||||
{
|
||||
if (!_webView) {
|
||||
// Inject bundle
|
||||
_WKProcessPoolConfiguration *poolConfiguration = [[_WKProcessPoolConfiguration alloc] init];
|
||||
NSURL *bundleURL = [[[NSBundle mainBundle] builtInPlugInsURL] URLByAppendingPathComponent:@"SBrowserProcessBundle.bundle"];
|
||||
[poolConfiguration setInjectedBundleURL:bundleURL];
|
||||
|
||||
// Set up process pool
|
||||
WKProcessPool *processPool = [[WKProcessPool alloc] _initWithConfiguration:poolConfiguration];
|
||||
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
|
||||
configuration.processPool = processPool;
|
||||
|
||||
// Instantiate web view
|
||||
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration];
|
||||
|
||||
// Configure proxy interface (interface to remote web process)
|
||||
_WKRemoteObjectInterface *proxyInterface = [_WKRemoteObjectInterface remoteObjectInterfaceWithProtocol:@protocol(SBRWebProcessProxy)];
|
||||
_webProcessProxy = [[webView _remoteObjectRegistry] remoteObjectProxyWithInterface:proxyInterface];
|
||||
|
||||
// Configure delegate interface (registering us as the web process delegate for the remote process)
|
||||
_WKRemoteObjectInterface *delegateInterface = [_WKRemoteObjectInterface remoteObjectInterfaceWithProtocol:@protocol(SBRWebProcessDelegate)];
|
||||
[[webView _remoteObjectRegistry] registerExportedObject:self interface:delegateInterface];
|
||||
|
||||
_webView = webView;
|
||||
}
|
||||
|
||||
return _webView;
|
||||
}
|
||||
|
||||
#pragma mark <SBRWebProcessDelegate>
|
||||
|
||||
- (void)webProcessDidConnect
|
||||
{
|
||||
NSLog(@"SBRProcessBundleBridge: did connect. Saying hello, syncing allowlist");
|
||||
[_webProcessProxy hello];
|
||||
[self policyDataSourceDidChange];
|
||||
}
|
||||
|
||||
- (void)webProcessDidLoadScriptWithOrigin:(NSString *)origin
|
||||
{
|
||||
[_delegate webProcess:self didBlockScriptResourceFromOrigin:origin];
|
||||
}
|
||||
|
||||
#pragma mark Actions
|
||||
|
||||
- (void)policyDataSourceDidChange
|
||||
{
|
||||
NSSet<NSString *> *allowedOrigins = [_policyDataSource allowedOriginsForScriptResources];
|
||||
[_webProcessProxy syncAllowedResourceOrigins:allowedOrigins];
|
||||
}
|
||||
|
||||
@end
|
||||
27
SBrowser/SceneDelegate.swift
Normal file
27
SBrowser/SceneDelegate.swift
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// SceneDelegate.swift
|
||||
// SBrowser
|
||||
//
|
||||
// Created by James Magahern on 7/21/20.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||
var window: UIWindow?
|
||||
let navigationController = UINavigationController()
|
||||
let browserViewController = BrowserViewController()
|
||||
|
||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)
|
||||
{
|
||||
guard let windowScene = (scene as? UIWindowScene) else { return }
|
||||
|
||||
navigationController.viewControllers = [ browserViewController ]
|
||||
|
||||
let window = UIWindow(windowScene: windowScene)
|
||||
window.rootViewController = navigationController
|
||||
window.makeKeyAndVisible()
|
||||
self.window = window
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x60"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "20x20"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "40x40"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "1x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "76x76"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "83.5x83.5"
|
||||
},
|
||||
{
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
6
SBrowser/Supporting Files/Assets.xcassets/Contents.json
Normal file
6
SBrowser/Supporting Files/Assets.xcassets/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
25
SBrowser/Supporting Files/Base.lproj/LaunchScreen.storyboard
Normal file
25
SBrowser/Supporting Files/Base.lproj/LaunchScreen.storyboard
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
64
SBrowser/Supporting Files/Info.plist
Normal file
64
SBrowser/Supporting Files/Info.plist
Normal file
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<false/>
|
||||
<key>UISceneConfigurations</key>
|
||||
<dict>
|
||||
<key>UIWindowSceneSessionRoleApplication</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UISceneConfigurationName</key>
|
||||
<string>Default Configuration</string>
|
||||
<key>UISceneDelegateClassName</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<true/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
5
SBrowser/Supporting Files/SBrowser-Bridging-Header.h
Normal file
5
SBrowser/Supporting Files/SBrowser-Bridging-Header.h
Normal file
@@ -0,0 +1,5 @@
|
||||
//
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
|
||||
#import "SBRProcessBundleBridge.h"
|
||||
10
SBrowser/Supporting Files/SBrowser.entitlements
Normal file
10
SBrowser/Supporting Files/SBrowser.entitlements
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
Reference in New Issue
Block a user