mirror of
https://github.com/osnr/TabFS.git
synced 2024-06-01 21:42:33 +02:00
safari: fix some races when you reload Web inspector, make ws connection retry
This commit is contained in:
parent
2f639e2a02
commit
0f2ab4b4de
|
@ -720,15 +720,26 @@ function tryConnect() {
|
||||||
if (chrome.runtime.getURL('/').startsWith('safari-web-extension://')) { // Safari-only
|
if (chrome.runtime.getURL('/').startsWith('safari-web-extension://')) { // Safari-only
|
||||||
chrome.runtime.sendNativeMessage('com.rsnous.tabfs', {op: 'safari_did_connect'}, resp => {
|
chrome.runtime.sendNativeMessage('com.rsnous.tabfs', {op: 'safari_did_connect'}, resp => {
|
||||||
console.log(resp);
|
console.log(resp);
|
||||||
const socket = new WebSocket('ws://localhost:9991');
|
|
||||||
|
|
||||||
socket.addEventListener('message', event => {
|
let socket;
|
||||||
onMessage(JSON.parse(event.data));
|
function connectSocket(checkAfterTime) {
|
||||||
});
|
socket = new WebSocket('ws://localhost:9991');
|
||||||
|
socket.addEventListener('message', event => {
|
||||||
|
onMessage(JSON.parse(event.data));
|
||||||
|
});
|
||||||
|
|
||||||
port = { postMessage(message) {
|
port = { postMessage(message) {
|
||||||
socket.send(JSON.stringify(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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,12 @@ it's mounted.
|
||||||
### tips
|
### tips
|
||||||
|
|
||||||
- To open Web inspector: Safari -> Develop menu -> Web Extension
|
- 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
|
Refreshing this inspector should reload the tabfs filesystem, also.
|
||||||
annoying.
|
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
|
|
@ -17,16 +17,20 @@ class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling {
|
||||||
|
|
||||||
guard message["op"] as! String == "safari_did_connect" else { return }
|
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.)
|
// (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.
|
// 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
|
// We only use one native message, to bootstrap the XPC service (TabFSService).
|
||||||
// to that service (which in turn talks to tabfs.c) over WebSocket instead.
|
// 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.
|
// (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,
|
// 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
|
// 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.)
|
// native messaging would require us here to sit in the middle.)
|
||||||
|
|
||||||
let connection = NSXPCConnection(serviceName: "com.rsnous.TabFSService")
|
let connection = NSXPCConnection(serviceName: "com.rsnous.TabFSService")
|
||||||
|
@ -44,8 +48,9 @@ class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling {
|
||||||
// FIXME: report port back?
|
// FIXME: report port back?
|
||||||
let response = NSExtensionItem()
|
let response = NSExtensionItem()
|
||||||
response.userInfo = [ "message": "alive" ]
|
response.userInfo = [ "message": "alive" ]
|
||||||
// This response (over native messaging) prompts background.js to
|
// This response (over native messaging) will prompt background.js to
|
||||||
// connect to the WebSocket server that the XPC service should now be running.
|
// connect to the WebSocket server of TabFSServer, which should
|
||||||
|
// now be running.
|
||||||
context.completeRequest(returningItems: [response]) { (what) in
|
context.completeRequest(returningItems: [response]) { (what) in
|
||||||
print(what)
|
print(what)
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
|
@ -75,8 +75,9 @@ class TabFSServer {
|
||||||
func read() {
|
func read() {
|
||||||
conn.receiveMessage { (resp, context, isComplete, err) in
|
conn.receiveMessage { (resp, context, isComplete, err) in
|
||||||
guard let resp = resp else {
|
guard let resp = resp else {
|
||||||
// FIXME err
|
if let err = err {
|
||||||
os_log(.default, "resp error: %{public}@", err!.debugDescription as CVarArg)
|
os_log(.default, "resp error: %{public}@", err.debugDescription as CVarArg)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,10 +105,10 @@ class TabFSServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: notify
|
print("OK")
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let server = TabFSServer()
|
let server = TabFSServer()
|
||||||
|
|
||||||
dispatchMain()
|
dispatchMain()
|
||||||
|
|
|
@ -12,13 +12,36 @@ import os.log
|
||||||
class TabFSService: NSObject, TabFSServiceProtocol {
|
class TabFSService: NSObject, TabFSServiceProtocol {
|
||||||
func start(withReply reply: @escaping () -> Void) {
|
func start(withReply reply: @escaping () -> Void) {
|
||||||
// This XPC call is enough to just force the XPC service to be started.
|
// 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()
|
let server = Process()
|
||||||
os_log("HOW ARE YOU?")
|
let serverOutput = Pipe()
|
||||||
server.executableURL = Bundle.main.url(forResource: "TabFSServer", withExtension: "")!
|
server.executableURL = Bundle.main.url(forResource: "TabFSServer", withExtension: "")!
|
||||||
os_log("I AM GOOD")
|
server.standardOutput = serverOutput
|
||||||
server.launch()
|
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()
|
reply()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue