From a465ad6a8fbb64184dcdcfa451dbb2aa9346ac2d Mon Sep 17 00:00:00 2001 From: Omar Rizwan Date: Tue, 2 Feb 2021 23:07:24 -0800 Subject: safari: checkpoint as I try to get the terrible wiring right --- extension/background.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'extension/background.js') diff --git a/extension/background.js b/extension/background.js index 5e487cf..e6fba2b 100644 --- a/extension/background.js +++ b/extension/background.js @@ -673,6 +673,10 @@ function findRoute(path) { let port; async function onMessage(req) { + // Safari / Safari extension app API forces you to adopt their + // {name, userInfo} structure for the request. + if (req.name === 'ToSafari') req = req.userInfo; + if (req.buf) req.buf = atob(req.buf); console.log('req', req); @@ -712,6 +716,16 @@ function tryConnect() { port = chrome.runtime.connectNative('com.rsnous.tabfs'); port.onMessage.addListener(onMessage); port.onDisconnect.addListener(p => {console.log('disconnect', p)}); + + // 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. + // 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(resp); + }); + } } if (!TESTING) { -- cgit v1.2.3 From 33e5911cb2741ae57ef41ec245636733568f362c Mon Sep 17 00:00:00 2001 From: Omar Rizwan Date: Sun, 7 Feb 2021 02:46:07 -0800 Subject: trying to just run straight from sandbox instead of xpc -- kinda works! blocked on libfuse --- extension/background.js | 5 +++++ .../TabFS/TabFS Extension/FSProcessManager.swift | 6 +++++- .../TabFS Extension/SafariWebExtensionHandler.swift | 8 ++++---- .../TabFS Extension/TabFS_Extension.entitlements | 8 ++++---- .../osnr.xcuserdatad/UserInterfaceState.xcuserstate | Bin 34829 -> 40591 bytes .../xcdebugger/Breakpoints_v2.xcbkptlist | 6 +++--- 6 files changed, 21 insertions(+), 12 deletions(-) (limited to 'extension/background.js') diff --git a/extension/background.js b/extension/background.js index e6fba2b..f38f29e 100644 --- a/extension/background.js +++ b/extension/background.js @@ -713,19 +713,24 @@ 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. // 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'); console.log(resp); }); } + console.log('tryConnect - did sNM'); } if (!TESTING) { diff --git a/extension/safari/TabFS/TabFS Extension/FSProcessManager.swift b/extension/safari/TabFS/TabFS Extension/FSProcessManager.swift index d17d934..ecfea87 100644 --- a/extension/safari/TabFS/TabFS Extension/FSProcessManager.swift +++ b/extension/safari/TabFS/TabFS Extension/FSProcessManager.swift @@ -8,6 +8,7 @@ import Foundation import SafariServices.SFSafariApplication +import os.log let extensionBundleIdentifier = "com.rsnous.TabFS-Extension" @@ -24,6 +25,9 @@ class FSProcessManager { func start() { fs = Process() fs.executableURL = URL(fileURLWithPath: "/Users/osnr/Code/tabfs/fs/tabfs") + + os_log(.default, "url: %{public}@", fs.executableURL as! NSURL) + fs.arguments = [] let inputPipe = Pipe(), outputPipe = Pipe() @@ -34,7 +38,7 @@ class FSProcessManager { fsInput = inputPipe.fileHandleForWriting fsOutput = outputPipe.fileHandleForReading -// +// // SFSafariApplication.dispatchMessage( // withName: "ToSafari", // toExtensionWithIdentifier: extensionBundleIdentifier, diff --git a/extension/safari/TabFS/TabFS Extension/SafariWebExtensionHandler.swift b/extension/safari/TabFS/TabFS Extension/SafariWebExtensionHandler.swift index 6ae1aae..e582e96 100644 --- a/extension/safari/TabFS/TabFS Extension/SafariWebExtensionHandler.swift +++ b/extension/safari/TabFS/TabFS Extension/SafariWebExtensionHandler.swift @@ -20,10 +20,10 @@ class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling { if message["op"] as! String == "safari_did_connect" { FSProcessManager.shared.start() - - let response = NSExtensionItem() - response.userInfo = [ "message": [ "aResponse to": "moop" ] ] - context.completeRequest(returningItems: [response], completionHandler: nil) +// +// let response = NSExtensionItem() +// response.userInfo = [ "message": [ "aResponse to": "moop" ] ] +// context.completeRequest(returningItems: [response], completionHandler: nil) return } diff --git a/extension/safari/TabFS/TabFS Extension/TabFS_Extension.entitlements b/extension/safari/TabFS/TabFS Extension/TabFS_Extension.entitlements index f2ef3ae..3ebfe4c 100644 --- a/extension/safari/TabFS/TabFS Extension/TabFS_Extension.entitlements +++ b/extension/safari/TabFS/TabFS Extension/TabFS_Extension.entitlements @@ -2,9 +2,9 @@ - com.apple.security.app-sandbox - - com.apple.security.files.user-selected.read-only - + com.apple.security.app-sandbox + + com.apple.security.files.all + diff --git a/extension/safari/TabFS/TabFS.xcodeproj/project.xcworkspace/xcuserdata/osnr.xcuserdatad/UserInterfaceState.xcuserstate b/extension/safari/TabFS/TabFS.xcodeproj/project.xcworkspace/xcuserdata/osnr.xcuserdatad/UserInterfaceState.xcuserstate index 231f2f7..90dd5fe 100644 Binary files a/extension/safari/TabFS/TabFS.xcodeproj/project.xcworkspace/xcuserdata/osnr.xcuserdatad/UserInterfaceState.xcuserstate and b/extension/safari/TabFS/TabFS.xcodeproj/project.xcworkspace/xcuserdata/osnr.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/extension/safari/TabFS/TabFS.xcodeproj/xcuserdata/osnr.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/extension/safari/TabFS/TabFS.xcodeproj/xcuserdata/osnr.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 3f0b7de..4af142b 100644 --- a/extension/safari/TabFS/TabFS.xcodeproj/xcuserdata/osnr.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/extension/safari/TabFS/TabFS.xcodeproj/xcuserdata/osnr.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -30,8 +30,8 @@ filePath = "TabFS Extension/FSProcessManager.swift" startingColumnNumber = "9223372036854775807" endingColumnNumber = "9223372036854775807" - startingLineNumber = "25" - endingLineNumber = "25" + startingLineNumber = "26" + endingLineNumber = "26" landmarkName = "start()" landmarkType = "7"> @@ -56,7 +56,7 @@ BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint"> Date: Mon, 8 Feb 2021 02:32:21 -0800 Subject: safari: start migration to using out-of-band WebSocket to do extension<=>fs comm --- extension/background.js | 29 +++++--- .../SafariWebExtensionHandler.swift | 82 +++++++-------------- .../UserInterfaceState.xcuserstate | Bin 61059 -> 58463 bytes .../xcdebugger/Breakpoints_v2.xcbkptlist | 12 +-- .../safari/TabFS/TabFSService/TabFSService.swift | 78 +++++++++++++++----- .../TabFS/TabFSService/TabFSServiceProtocols.swift | 5 -- extension/safari/TabFS/TabFSService/main.swift | 2 + 7 files changed, 111 insertions(+), 97 deletions(-) (limited to 'extension/background.js') diff --git a/extension/background.js b/extension/background.js index f38f29e..62ec7d0 100644 --- a/extension/background.js +++ b/extension/background.js @@ -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) { diff --git a/extension/safari/TabFS/TabFS Extension/SafariWebExtensionHandler.swift b/extension/safari/TabFS/TabFS Extension/SafariWebExtensionHandler.swift index 67da485..a1ae4ff 100644 --- a/extension/safari/TabFS/TabFS Extension/SafariWebExtensionHandler.swift +++ b/extension/safari/TabFS/TabFS Extension/SafariWebExtensionHandler.swift @@ -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) } } diff --git a/extension/safari/TabFS/TabFS.xcodeproj/project.xcworkspace/xcuserdata/osnr.xcuserdatad/UserInterfaceState.xcuserstate b/extension/safari/TabFS/TabFS.xcodeproj/project.xcworkspace/xcuserdata/osnr.xcuserdatad/UserInterfaceState.xcuserstate index 1fff19a..93b8259 100644 Binary files a/extension/safari/TabFS/TabFS.xcodeproj/project.xcworkspace/xcuserdata/osnr.xcuserdatad/UserInterfaceState.xcuserstate and b/extension/safari/TabFS/TabFS.xcodeproj/project.xcworkspace/xcuserdata/osnr.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/extension/safari/TabFS/TabFS.xcodeproj/xcuserdata/osnr.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/extension/safari/TabFS/TabFS.xcodeproj/xcuserdata/osnr.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 87a2930..0a7caef 100644 --- a/extension/safari/TabFS/TabFS.xcodeproj/xcuserdata/osnr.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/extension/safari/TabFS/TabFS.xcodeproj/xcuserdata/osnr.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -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"> @@ -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"> @@ -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"> diff --git a/extension/safari/TabFS/TabFSService/TabFSService.swift b/extension/safari/TabFS/TabFSService/TabFSService.swift index eb7508f..e49d80f 100644 --- a/extension/safari/TabFS/TabFSService/TabFSService.swift +++ b/extension/safari/TabFS/TabFSService/TabFSService.swift @@ -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 diff --git a/extension/safari/TabFS/TabFSService/TabFSServiceProtocols.swift b/extension/safari/TabFS/TabFSService/TabFSServiceProtocols.swift index 298015a..8aedd87 100644 --- a/extension/safari/TabFS/TabFSService/TabFSServiceProtocols.swift +++ b/extension/safari/TabFS/TabFSService/TabFSServiceProtocols.swift @@ -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) } diff --git a/extension/safari/TabFS/TabFSService/main.swift b/extension/safari/TabFS/TabFSService/main.swift index 39c5a75..f0a4486 100644 --- a/extension/safari/TabFS/TabFSService/main.swift +++ b/extension/safari/TabFS/TabFSService/main.swift @@ -7,6 +7,8 @@ import Foundation +ProcessInfo.processInfo.disableAutomaticTermination("ok") + let delegate = TabFSServiceDelegate() let listener = NSXPCListener.service() listener.delegate = delegate -- cgit v1.2.3 From 0f2ab4b4de7e828757091e323be1caab0a70b770 Mon Sep 17 00:00:00 2001 From: Omar Rizwan Date: Mon, 8 Feb 2021 13:45:26 -0800 Subject: safari: fix some races when you reload Web inspector, make ws connection retry --- extension/background.js | 25 ++++++++++++----- extension/safari/README.md | 10 +++++-- .../SafariWebExtensionHandler.swift | 17 +++++++---- .../UserInterfaceState.xcuserstate | Bin 61961 -> 64115 bytes extension/safari/TabFS/TabFSServer/main.swift | 9 +++--- .../safari/TabFS/TabFSService/TabFSService.swift | 31 ++++++++++++++++++--- 6 files changed, 68 insertions(+), 24 deletions(-) (limited to 'extension/background.js') diff --git a/extension/background.js b/extension/background.js index 62ec7d0..e31a4c4 100644 --- a/extension/background.js +++ b/extension/background.js @@ -720,15 +720,26 @@ function tryConnect() { if (chrome.runtime.getURL('/').startsWith('safari-web-extension://')) { // Safari-only 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)); - }); + let socket; + function connectSocket(checkAfterTime) { + socket = new WebSocket('ws://localhost:9991'); + socket.addEventListener('message', event => { + onMessage(JSON.parse(event.data)); + }); + + port = { postMessage(message) { + socket.send(JSON.stringify(message)); + } }; - port = { postMessage(message) { - socket.send(JSON.stringify(message)); - } }; + setTimeout(() => { + if (socket.readyState !== 1) { + console.log('ws connection failed, retrying in', checkAfterTime); + connectSocket(checkAfterTime * 2); + } + }, checkAfterTime); + } + connectSocket(200); }); return; } diff --git a/extension/safari/README.md b/extension/safari/README.md index 9ceafdc..8a47d23 100644 --- a/extension/safari/README.md +++ b/extension/safari/README.md @@ -24,8 +24,12 @@ it's mounted. ### tips - To open Web inspector: Safari -> Develop menu -> Web Extension - Background Pages -> TabFS + Background Pages -> TabFS. -- You need to rebuild if you change background.js. This is pretty - annoying. + Refreshing this inspector should reload the tabfs filesystem, also. + +- You need to rebuild in Xcode any time you change background.js + (because the extension files are copied into the extension, rather + than running directly from folder as in Firefox and Chrome). This is + pretty annoying. diff --git a/extension/safari/TabFS/TabFS Extension/SafariWebExtensionHandler.swift b/extension/safari/TabFS/TabFS Extension/SafariWebExtensionHandler.swift index 75790fa..dc30cc4 100644 --- a/extension/safari/TabFS/TabFS Extension/SafariWebExtensionHandler.swift +++ b/extension/safari/TabFS/TabFS Extension/SafariWebExtensionHandler.swift @@ -17,16 +17,20 @@ class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling { guard message["op"] as! String == "safari_did_connect" else { return } - // The XPC service is a subprocess that lives outside the macOS App Sandbox. + // The XPC service is a process that can live outside the macOS App Sandbox. // (Safari extension native code, including this file, has to live in the sandbox.) // It can do forbidden things like spawn tabfs filesystem and set up WebSocket server. - // We only use one native message to bootstrap the XPC service, then do all communications - // to that service (which in turn talks to tabfs.c) over WebSocket instead. + // We only use one native message, to bootstrap the XPC service (TabFSService). + // Then the XPC service starts TabFSServer. TabFSServer is separate because + // XPC services get killed by the OS after a minute or two; TabFSServer + // is just a normal process that can live on. It talks straight + // to background.js (which in turn talks to tabfs.c) over a WebSocket. + // (Safari makes doing native messaging quite painful, so we try to avoid it. // It forces the browser to pop to front if you message Safari in the obvious way, // for instance: https://developer.apple.com/forums/thread/122232 - // And with the WebSocket, the XPC service can talk straight to background.js, whereas + // And with the WebSocket, the server can talk straight to background.js, whereas // native messaging would require us here to sit in the middle.) let connection = NSXPCConnection(serviceName: "com.rsnous.TabFSService") @@ -44,8 +48,9 @@ class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling { // FIXME: report port back? let response = NSExtensionItem() response.userInfo = [ "message": "alive" ] - // This response (over native messaging) prompts background.js to - // connect to the WebSocket server that the XPC service should now be running. + // This response (over native messaging) will prompt background.js to + // connect to the WebSocket server of TabFSServer, which should + // now be running. context.completeRequest(returningItems: [response]) { (what) in print(what) } diff --git a/extension/safari/TabFS/TabFS.xcodeproj/project.xcworkspace/xcuserdata/osnr.xcuserdatad/UserInterfaceState.xcuserstate b/extension/safari/TabFS/TabFS.xcodeproj/project.xcworkspace/xcuserdata/osnr.xcuserdatad/UserInterfaceState.xcuserstate index db4d28a..96586ef 100644 Binary files a/extension/safari/TabFS/TabFS.xcodeproj/project.xcworkspace/xcuserdata/osnr.xcuserdatad/UserInterfaceState.xcuserstate and b/extension/safari/TabFS/TabFS.xcodeproj/project.xcworkspace/xcuserdata/osnr.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/extension/safari/TabFS/TabFSServer/main.swift b/extension/safari/TabFS/TabFSServer/main.swift index 245bfb1..76da2a0 100644 --- a/extension/safari/TabFS/TabFSServer/main.swift +++ b/extension/safari/TabFS/TabFSServer/main.swift @@ -75,8 +75,9 @@ class TabFSServer { func read() { conn.receiveMessage { (resp, context, isComplete, err) in guard let resp = resp else { - // FIXME err - os_log(.default, "resp error: %{public}@", err!.debugDescription as CVarArg) + if let err = err { + os_log(.default, "resp error: %{public}@", err.debugDescription as CVarArg) + } return } @@ -104,10 +105,10 @@ class TabFSServer { } } - // FIXME: notify - + print("OK") } } let server = TabFSServer() + dispatchMain() diff --git a/extension/safari/TabFS/TabFSService/TabFSService.swift b/extension/safari/TabFS/TabFSService/TabFSService.swift index 4a86dd6..5bf55ee 100644 --- a/extension/safari/TabFS/TabFSService/TabFSService.swift +++ b/extension/safari/TabFS/TabFSService/TabFSService.swift @@ -12,13 +12,36 @@ import os.log class TabFSService: NSObject, TabFSServiceProtocol { func start(withReply reply: @escaping () -> Void) { // This XPC call is enough to just force the XPC service to be started. - os_log("HELLO") + + // kill old copies of TabFSServer + let killall = Process() + killall.launchPath = "/usr/bin/killall" + killall.arguments = ["TabFSServer"] + killall.launch() + killall.waitUntilExit() + + // spin until old TabFSServer (if any) is gone + while true { + let pgrep = Process() + pgrep.launchPath = "/usr/bin/pgrep" + pgrep.arguments = ["TabFSServer"] + pgrep.launch() + pgrep.waitUntilExit() + if pgrep.terminationStatus != 0 { break } + + Thread.sleep(forTimeInterval: 0.01) + } + let server = Process() - os_log("HOW ARE YOU?") + let serverOutput = Pipe() server.executableURL = Bundle.main.url(forResource: "TabFSServer", withExtension: "")! - os_log("I AM GOOD") + server.standardOutput = serverOutput server.launch() - os_log("GREAT") + + // FIXME: should we wait for some signal that the server is ready? + // right now, background.js will just periodically retry until it can connect. + + // tell background.js to try to connect. reply() } } -- cgit v1.2.3