Files
Attractor/App/Web Process Bundle Bridge/SBRProcessBundleBridge.m

194 lines
6.9 KiB
Mathematica
Raw Normal View History

//
// 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>
2020-07-29 18:17:22 -07:00
#import <WebKit/_WKUserStyleSheet.h>
#import <WebKit/WKProcessPoolPrivate.h>
#import <WebKit/WKWebViewPrivate.h>
#import <WebKit/WKWebViewConfigurationPrivate.h>
2020-07-29 18:17:22 -07:00
#import <WebKit/WKUserContentControllerPrivate.h>
@interface SBRProcessBundleBridge () <SBRWebProcessDelegate>
@end
@implementation SBRProcessBundleBridge {
2020-07-29 18:17:22 -07:00
WKWebView *_webView;
WKWebViewConfiguration *_webViewConfiguration;
WKProcessPool *_processPool;
id<SBRWebProcessProxy> _webProcessProxy;
2020-07-29 18:17:22 -07:00
_WKUserStyleSheet *_darkModeStyleSheet;
2021-02-15 22:34:05 -08:00
WKUserScript *_readabilityScript;
}
- (void)tearDown
{
[[_webView _remoteObjectRegistry] unregisterExportedObject:self interface:[self _webProcessDelegateInterface]];
}
- (_WKRemoteObjectInterface *)_webProcessDelegateInterface
{
static dispatch_once_t onceToken;
static _WKRemoteObjectInterface *interface = nil;
dispatch_once(&onceToken, ^{
interface = [_WKRemoteObjectInterface remoteObjectInterfaceWithProtocol:@protocol(SBRWebProcessDelegate)];
});
return interface;
}
- (_WKRemoteObjectInterface *)_webProcessProxyInterface
{
static dispatch_once_t onceToken;
static _WKRemoteObjectInterface *interface = nil;
dispatch_once(&onceToken, ^{
interface = [_WKRemoteObjectInterface remoteObjectInterfaceWithProtocol:@protocol(SBRWebProcessProxy)];
});
return interface;
}
2020-09-24 16:36:31 -07:00
- (instancetype)initWithWebViewConfiguration:(WKWebViewConfiguration *)webViewConfiguration
{
2020-09-24 16:36:31 -07:00
self = [super init];
if (self) {
if (!webViewConfiguration) {
webViewConfiguration = [[WKWebViewConfiguration alloc] init];
// Inject bundle
_WKProcessPoolConfiguration *poolConfiguration = [[_WKProcessPoolConfiguration alloc] init];
NSURL *bundleURL = [[[NSBundle mainBundle] builtInPlugInsURL] URLByAppendingPathComponent:@"SBrowserProcessBundle.bundle"];
// Make sure it exists. Bail if otherwise.
NSBundle *pluginBundle = [NSBundle bundleWithURL:bundleURL];
NSAssert(pluginBundle != nil, @"Attix process bundle not found at %@", bundleURL.path);
[poolConfiguration setInjectedBundleURL:bundleURL];
// Set up process pool
_processPool = [[WKProcessPool alloc] _initWithConfiguration:poolConfiguration];
// Initialize allowed origins now
NSArray<NSString *> *allowedOrigins = [[_policyDataSource allowedOriginsForScriptResources] allObjects];
[_processPool _setObject:allowedOrigins forBundleParameter:SBRGetAllowedOriginsKey()];
[_processPool _setObject:@(_allowAllScripts) forBundleParameter:SBRGetAllScriptsAllowedKey()];
webViewConfiguration.processPool = _processPool;
}
2020-08-14 17:40:01 -07:00
2020-09-24 16:36:31 -07:00
_webViewConfiguration = webViewConfiguration;
// Instantiate web view
2020-09-24 16:36:31 -07:00
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:webViewConfiguration];
// Configure proxy interface (interface to remote web process)
_webProcessProxy = [[webView _remoteObjectRegistry] remoteObjectProxyWithInterface:[self _webProcessProxyInterface]];
// Configure delegate interface (registering us as the web process delegate for the remote process)
[[webView _remoteObjectRegistry] registerExportedObject:self interface:[self _webProcessDelegateInterface]];
_webView = webView;
}
2020-09-24 16:36:31 -07:00
return self;
}
#pragma mark <SBRWebProcessDelegate>
- (void)webProcessDidConnect
{
NSLog(@"SBRProcessBundleBridge: did connect. Saying hello, syncing allowlist");
_webContentProcessConnected = YES;
[_webProcessProxy hello];
[self policyDataSourceDidChange];
}
- (void)webProcessDidAllowScriptWithOrigin:(NSString *)origin
{
[[self delegate] webProcess:self didAllowScriptResourceFromOrigin:origin];
}
- (void)webProcessDidBlockScriptWithOrigin:(NSString *)origin
{
[[self delegate] webProcess:self didBlockScriptResourceFromOrigin:origin];
}
#pragma mark Actions
- (void)policyDataSourceDidChange
{
2020-07-24 19:26:35 -07:00
NSArray<NSString *> *allowedOrigins = [[_policyDataSource allowedOriginsForScriptResources] allObjects];
[_processPool _setObject:allowedOrigins forBundleParameter:SBRGetAllowedOriginsKey()];
[_webProcessProxy syncAllowedResourceOrigins:allowedOrigins];
}
- (void)setAllowAllScripts:(BOOL)allowAllScripts
{
_allowAllScripts = allowAllScripts;
[_processPool _setObject:@(_allowAllScripts) forBundleParameter:SBRGetAllScriptsAllowedKey()];
[_webProcessProxy setAllScriptsAllowed:allowAllScripts];
}
2020-07-29 18:17:22 -07:00
- (void)setDarkModeEnabled:(BOOL)darkModeEnabled
{
_darkModeEnabled = darkModeEnabled;
WKUserContentController *userContentController = [_webViewConfiguration userContentController];
if (darkModeEnabled) {
if (!_darkModeStyleSheet) {
NSURL *styleSheetURL = [[NSBundle mainBundle] URLForResource:@"darkmode" withExtension:@"css"];
NSString *styleSheetSource = [NSString stringWithContentsOfURL:styleSheetURL encoding:NSUTF8StringEncoding error:nil];
_darkModeStyleSheet = [[_WKUserStyleSheet alloc] initWithSource:styleSheetSource forMainFrameOnly:NO];
}
[userContentController _addUserStyleSheet:_darkModeStyleSheet];
} else if (_darkModeStyleSheet) {
[userContentController _removeUserStyleSheet:_darkModeStyleSheet];
}
}
2021-02-15 22:34:05 -08:00
- (void)parseDocumentForReaderMode:(void (^)(NSString * _Nonnull))completionBlock
{
WKUserContentController *userContentController = [_webViewConfiguration userContentController];
if (!_readabilityScript) {
NSURL *readabilityJSURL = [[NSBundle mainBundle] URLForResource:@"Readability" withExtension:@"js"];
NSString *readabilityJSSource = [NSString stringWithContentsOfURL:readabilityJSURL encoding:NSUTF8StringEncoding error:nil];
_readabilityScript = [[WKUserScript alloc] initWithSource:readabilityJSSource injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
}
[userContentController _addUserScriptImmediately:_readabilityScript];
NSString *script = @""
"var documentClone = document.cloneNode(true);"
"var article = new Readability(documentClone).parse();"
"article.content";
[_webView evaluateJavaScript:script completionHandler:^(NSString *result, NSError * _Nullable error) {
if (error != nil) {
NSLog(@"Bridge: Readability error: %@", error.localizedDescription);
} else {
completionBlock(result);
}
}];
}
@end