mirror of
https://github.com/osnr/TabFS.git
synced 2024-06-08 08:52:20 +02:00
safari: start migration to using out-of-band WebSocket to do extension<=>fs comm
This commit is contained in:
parent
930352dd42
commit
7211a5fdea
|
@ -713,24 +713,29 @@ async function onMessage(req) {
|
|||
};
|
||||
|
||||
function tryConnect() {
|
||||
console.log('start tryConnect');
|
||||
port = chrome.runtime.connectNative('com.rsnous.tabfs');
|
||||
console.log('start tryConnect - did connectNative');
|
||||
port.onMessage.addListener(onMessage);
|
||||
port.onDisconnect.addListener(p => {console.log('disconnect', p)});
|
||||
|
||||
console.log('tryConnect - about to sNM');
|
||||
// Safari is very weird -- it has this native app that we have to talk to,
|
||||
// so we poke that app to wake it up, get it to start the TabFS process,
|
||||
// and get it to start calling us whenever TabFS wants to do an FS call.
|
||||
// so we poke that app to wake it up, get it to start the TabFS process
|
||||
// and boot a WebSocket, then connect to it.
|
||||
// Is there a better way to do this?
|
||||
if (chrome.runtime.getURL('/').startsWith('safari-web-extension://')) { // Safari-only
|
||||
chrome.runtime.sendNativeMessage('com.rsnous.tabfs', {op: 'safari_did_connect'}, function(resp) {
|
||||
console.log('didConnect resp');
|
||||
chrome.runtime.sendNativeMessage('com.rsnous.tabfs', {op: 'safari_did_connect'}, resp => {
|
||||
console.log(resp);
|
||||
const socket = new WebSocket('ws://localhost:9991');
|
||||
|
||||
socket.addEventListener('message', event => {
|
||||
onMessage(JSON.parse(event.data));
|
||||
});
|
||||
|
||||
port = { postMessage(message) {
|
||||
socket.send(JSON.stringify(message));
|
||||
} };
|
||||
});
|
||||
return;
|
||||
}
|
||||
console.log('tryConnect - did sNM');
|
||||
|
||||
port = chrome.runtime.connectNative('com.rsnous.tabfs');
|
||||
port.onMessage.addListener(onMessage);
|
||||
port.onDisconnect.addListener(p => {console.log('disconnect', p)});
|
||||
}
|
||||
|
||||
if (!TESTING) {
|
||||
|
|
|
@ -6,48 +6,8 @@
|
|||
//
|
||||
|
||||
import SafariServices
|
||||
import SafariServices.SFSafariApplication
|
||||
import os.log
|
||||
|
||||
class TabFSServiceManager: TabFSServiceConsumerProtocol {
|
||||
static let shared = TabFSServiceManager()
|
||||
|
||||
var service: TabFSServiceProtocol!
|
||||
|
||||
func connect() {
|
||||
let connection = NSXPCConnection(serviceName: "com.rsnous.TabFSService")
|
||||
|
||||
connection.remoteObjectInterface = NSXPCInterface(with: TabFSServiceProtocol.self)
|
||||
|
||||
connection.exportedInterface = NSXPCInterface(with: TabFSServiceConsumerProtocol.self)
|
||||
connection.exportedObject = self
|
||||
|
||||
connection.resume()
|
||||
|
||||
service = connection.remoteObjectProxyWithErrorHandler { error in
|
||||
os_log(.default, "Received error: %{public}@", error as! CVarArg)
|
||||
} as? TabFSServiceProtocol
|
||||
|
||||
service?.upperCaseString("hello XPC") { response in
|
||||
os_log(.default, "Response from XPC service: %{public}@", response)
|
||||
}
|
||||
}
|
||||
|
||||
func request(_ req: Data) {
|
||||
SFSafariApplication.dispatchMessage(
|
||||
withName: "ToSafari",
|
||||
toExtensionWithIdentifier: "com.rsnous.TabFS-Extension",
|
||||
userInfo: try! JSONSerialization.jsonObject(with: req, options: []) as! [String : Any]
|
||||
) { error in
|
||||
debugPrint("Message attempted. Error info: \(String.init(describing: error))")
|
||||
}
|
||||
}
|
||||
|
||||
func response(_ resp: [AnyHashable: Any]) {
|
||||
try! service.response(JSONSerialization.data(withJSONObject: resp, options: []))
|
||||
}
|
||||
}
|
||||
|
||||
class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling {
|
||||
|
||||
func beginRequest(with context: NSExtensionContext) {
|
||||
|
@ -60,27 +20,35 @@ class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling {
|
|||
guard let message = item.userInfo?["message"] as? [AnyHashable: Any] else { return }
|
||||
|
||||
if message["op"] as! String == "safari_did_connect" {
|
||||
|
||||
os_log(.default, "TabFSmsg sdc")
|
||||
TabFSServiceManager.shared.connect()
|
||||
//
|
||||
// let response = NSExtensionItem()
|
||||
// response.userInfo = [ "message": [ "aResponse to": "moop" ] ]
|
||||
// context.completeRequest(returningItems: [response], completionHandler: nil)
|
||||
|
||||
// The XPC service is a subprocess that lives outside the macOS App Sandbox.
|
||||
// It can do forbidden things like spawn tabfs filesystem and set up WebSocket server.
|
||||
|
||||
let connection = NSXPCConnection(serviceName: "com.rsnous.TabFSService")
|
||||
|
||||
connection.remoteObjectInterface = NSXPCInterface(with: TabFSServiceProtocol.self)
|
||||
|
||||
connection.resume()
|
||||
|
||||
let service = connection.remoteObjectProxyWithErrorHandler { error in
|
||||
os_log(.default, "Received error: %{public}@", error as! CVarArg)
|
||||
} as? TabFSServiceProtocol
|
||||
|
||||
// need this one XPC call to actually initialize the service
|
||||
service?.upperCaseString("hello XPC") { response in
|
||||
os_log(.default, "Response from XPC service: %{public}@", response)
|
||||
}
|
||||
|
||||
// FIXME: report port back?
|
||||
let response = NSExtensionItem()
|
||||
response.userInfo = [ "message": [ "aResponse to": "moop" ] ]
|
||||
context.completeRequest(returningItems: [response]) { (what) in
|
||||
print(what)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
TabFSServiceManager.shared.response(message)
|
||||
//
|
||||
// os_log(.default, "Received message from browser.runtime.sendNativeMessage: %@", op as! CVarArg)
|
||||
|
||||
// let response = NSExtensionItem()
|
||||
// response.userInfo = [ "message": [ "Response to": op ] ]
|
||||
//
|
||||
// // How do I get too the app????
|
||||
//
|
||||
// context.completeRequest(returningItems: [response], completionHandler: nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -14,8 +14,8 @@
|
|||
filePath = "TabFS Extension/SafariWebExtensionHandler.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "57"
|
||||
endingLineNumber = "57"
|
||||
startingLineNumber = "17"
|
||||
endingLineNumber = "17"
|
||||
landmarkName = "beginRequest(with:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
|
@ -46,8 +46,8 @@
|
|||
filePath = "TabFS Extension/SafariWebExtensionHandler.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "60"
|
||||
endingLineNumber = "60"
|
||||
startingLineNumber = "20"
|
||||
endingLineNumber = "20"
|
||||
landmarkName = "beginRequest(with:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
|
@ -62,8 +62,8 @@
|
|||
filePath = "TabFS Extension/SafariWebExtensionHandler.swift"
|
||||
startingColumnNumber = "9223372036854775807"
|
||||
endingColumnNumber = "9223372036854775807"
|
||||
startingLineNumber = "55"
|
||||
endingLineNumber = "55"
|
||||
startingLineNumber = "15"
|
||||
endingLineNumber = "15"
|
||||
landmarkName = "beginRequest(with:)"
|
||||
landmarkType = "7">
|
||||
</BreakpointContent>
|
||||
|
|
|
@ -6,16 +6,14 @@
|
|||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
import os.log
|
||||
|
||||
class TabFSService: NSObject, TabFSServiceProtocol {
|
||||
var fs: Process!
|
||||
var fsInput: FileHandle!
|
||||
var fsOutput: FileHandle!
|
||||
|
||||
init(app: TabFSServiceConsumerProtocol) {
|
||||
super.init()
|
||||
|
||||
func startFs() {
|
||||
fs = Process()
|
||||
fs.executableURL = URL(fileURLWithPath: "/Users/osnr/Code/tabfs/fs/tabfs")
|
||||
fs.currentDirectoryURL = fs.executableURL?.deletingLastPathComponent()
|
||||
|
@ -32,6 +30,52 @@ class TabFSService: NSObject, TabFSServiceProtocol {
|
|||
os_log(.default, "TabFSmsg tfs service: willrun")
|
||||
try! fs.run()
|
||||
os_log(.default, "TabFSmsg tfs service: ran")
|
||||
}
|
||||
|
||||
var ws: NWListener!
|
||||
func startWs() {
|
||||
// websocket server
|
||||
let port = NWEndpoint.Port(rawValue: 9991)!
|
||||
let parameters = NWParameters(tls: nil)
|
||||
parameters.allowLocalEndpointReuse = true
|
||||
parameters.includePeerToPeer = true
|
||||
let opts = NWProtocolWebSocket.Options()
|
||||
opts.autoReplyPing = true
|
||||
parameters.defaultProtocolStack.applicationProtocols.insert(opts, at: 0)
|
||||
|
||||
ws = try! NWListener(using: parameters, on: port)
|
||||
ws.start(queue: .main)
|
||||
}
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
|
||||
startFs()
|
||||
startWs()
|
||||
|
||||
var handleRequest: ((_ req: Data) -> Void)?
|
||||
ws.newConnectionHandler = { conn in
|
||||
conn.start(queue: .main)
|
||||
handleRequest = { req in
|
||||
conn.send(content: req, completion: .contentProcessed({ err in
|
||||
if err != nil {
|
||||
// FIXME: ERROR
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
func read() {
|
||||
conn.receiveMessage { (resp, context, isComplete, err) in
|
||||
guard let resp = resp else {
|
||||
// FIXME err
|
||||
return
|
||||
}
|
||||
self.fsInput.write(withUnsafeBytes(of: UInt32(resp.count)) { Data($0) })
|
||||
self.fsInput.write(resp)
|
||||
read()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// split new thread
|
||||
DispatchQueue.global(qos: .default).async {
|
||||
|
@ -40,11 +84,15 @@ class TabFSService: NSObject, TabFSServiceProtocol {
|
|||
let length = self.fsOutput.readData(ofLength: 4).withUnsafeBytes { $0.load(as: UInt32.self) }
|
||||
os_log(.default, "TabFSmsg tfs service: read %{public}d", length)
|
||||
let req = self.fsOutput.readData(ofLength: Int(length))
|
||||
// send to other side of XPC conn
|
||||
app.request(req)
|
||||
}
|
||||
}
|
||||
|
||||
// send to other side of WEBSOCKET
|
||||
if let handleRequest = handleRequest {
|
||||
handleRequest(req)
|
||||
} else {
|
||||
// FIXME: ERROR
|
||||
}
|
||||
}
|
||||
}
|
||||
// FIXME: disable auto termination
|
||||
}
|
||||
|
||||
|
@ -52,22 +100,18 @@ class TabFSService: NSObject, TabFSServiceProtocol {
|
|||
let response = string.uppercased()
|
||||
reply(response)
|
||||
}
|
||||
|
||||
func response(_ resp: Data) {
|
||||
fsInput.write(withUnsafeBytes(of: UInt32(resp.count)) { Data($0) })
|
||||
fsInput.write(resp)
|
||||
}
|
||||
//
|
||||
// func response(_ resp: Data) {
|
||||
// fsInput.write(withUnsafeBytes(of: UInt32(resp.count)) { Data($0) })
|
||||
// fsInput.write(resp)
|
||||
// }
|
||||
}
|
||||
|
||||
class TabFSServiceDelegate: NSObject, NSXPCListenerDelegate {
|
||||
func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
|
||||
|
||||
|
||||
os_log(.default, "TabFSmsg tfs service: starting delegate")
|
||||
|
||||
newConnection.remoteObjectInterface = NSXPCInterface(with: TabFSServiceConsumerProtocol.self)
|
||||
|
||||
let exportedObject = TabFSService(app: newConnection.remoteObjectProxy as! TabFSServiceConsumerProtocol)
|
||||
let exportedObject = TabFSService()
|
||||
newConnection.exportedInterface = NSXPCInterface(with: TabFSServiceProtocol.self)
|
||||
newConnection.exportedObject = exportedObject
|
||||
|
||||
|
|
|
@ -7,11 +7,6 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
@objc public protocol TabFSServiceConsumerProtocol {
|
||||
func request(_ req: Data)
|
||||
}
|
||||
@objc public protocol TabFSServiceProtocol {
|
||||
func upperCaseString(_ string: String, withReply reply: @escaping (String) -> Void)
|
||||
|
||||
func response(_ resp: Data)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
ProcessInfo.processInfo.disableAutomaticTermination("ok")
|
||||
|
||||
let delegate = TabFSServiceDelegate()
|
||||
let listener = NSXPCListener.service()
|
||||
listener.delegate = delegate
|
||||
|
|
Loading…
Reference in a new issue