From 6a6b4dc3384452ae0646d94aabe3fde2ac912e1d Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Thu, 20 Jun 2019 13:46:28 +0200 Subject: [PATCH 1/5] Added firefox support to extension --- chrome.go => extension.go | 80 +++++++++++++++++++++++++++++++++------ extension/manifest.json | 8 +++- extension/src/popup.html | 3 ++ extension/src/popup.ts | 2 +- main.go | 5 ++- 5 files changed, 83 insertions(+), 15 deletions(-) rename chrome.go => extension.go (61%) diff --git a/chrome.go b/extension.go similarity index 61% rename from chrome.go rename to extension.go index cd603d4..cf5a267 100644 --- a/chrome.go +++ b/extension.go @@ -91,35 +91,51 @@ func handleRun(w http.ResponseWriter, r *http.Request) { } } -type chromeExtInstall struct{} +type extensionSetup struct{} -func (c *chromeExtInstall) Spec() cli.CommandSpec { +func (e *extensionSetup) Spec() cli.CommandSpec { return cli.CommandSpec{ - Name: "install-for-chrome-ext", - Desc: `Installs the chrome native message host manifest. -This allows the sail chrome extension to manage sail.`, + Name: "setup-extension", + Desc: `Installs the extension API native message host manifest. +This allows the sail extension to manage sail.`, } } -func (c *chromeExtInstall) Run(fl *flag.FlagSet) { - nativeHostDirs, err := nativeMessageHostManifestDirectories() +func (e *extensionSetup) Run(fl *flag.FlagSet) { + nativeHostDirsChrome, err := nativeMessageHostManifestDirectoriesChrome() if err != nil { flog.Fatal("failed to get native message host manifest directory: %v", err) } - for _, dir := range nativeHostDirs { + for _, dir := range nativeHostDirsChrome { err = os.MkdirAll(dir, 0755) if err != nil { flog.Fatal("failed to ensure manifest directory exists: %v", err) } - err = writeNativeHostManifest(dir) + err = writeNativeHostManifestChrome(dir) + if err != nil { + flog.Fatal("failed to write native messaging host manifest: %v", err) + } + } + + nativeHostDirsFirefox, err := nativeMessageHostManifestDirectoriesFirefox() + if err != nil { + flog.Fatal("failed to get native message host manifest directory: %v", err) + } + + for _, dir := range nativeHostDirsFirefox { + err = os.MkdirAll(dir, 0755) + if err != nil { + flog.Fatal("failed to ensure manifest directory exists: %v", err) + } + err = writeNativeHostManifestFirefox(dir) if err != nil { flog.Fatal("failed to write native messaging host manifest: %v", err) } } } -func writeNativeHostManifest(dir string) error { +func writeNativeHostManifestChrome(dir string) error { binPath, err := os.Executable() if err != nil { return err @@ -139,7 +155,27 @@ func writeNativeHostManifest(dir string) error { return ioutil.WriteFile(dst, []byte(manifest), 0644) } -func nativeMessageHostManifestDirectories() ([]string, error) { +func writeNativeHostManifestFirefox(dir string) error { + binPath, err := os.Executable() + if err != nil { + return err + } + + manifest := fmt.Sprintf(`{ + "name": "com.coder.sail", + "description": "sail message host", + "path": "%v", + "type": "stdio", + "allowed_extensions": [ + "2dcddda6bd28a9237755003f9cb1fcf60c2a7866@temporary-addon" + ] + }`, binPath) + + dst := path.Join(dir, "com.coder.sail.json") + return ioutil.WriteFile(dst, []byte(manifest), 0644) +} + +func nativeMessageHostManifestDirectoriesChrome() ([]string, error) { homeDir, err := os.UserHomeDir() if err != nil { return nil, xerrors.Errorf("failed to get user home dir: %w", err) @@ -164,3 +200,25 @@ func nativeMessageHostManifestDirectories() ([]string, error) { chromiumDir, }, nil } + +func nativeMessageHostManifestDirectoriesFirefox() ([]string, error) { + homeDir, err := os.UserHomeDir() + if err != nil { + return nil, xerrors.Errorf("failed to get user home dir: %w", err) + } + + var firefoxDir string + + switch runtime.GOOS { + case "linux": + firefoxDir = path.Join(homeDir, ".mozilla", "native-messaging-hosts") + case "darwin": + firefoxDir = path.Join(homeDir, "Library", "Application Support", "Mozilla", "NativeMessagingHosts") + default: + return nil, xerrors.Errorf("unsupported os %q", runtime.GOOS) + } + + return []string{ + firefoxDir, + }, nil +} diff --git a/extension/manifest.json b/extension/manifest.json index fbc3d59..abdb558 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -28,5 +28,11 @@ "browser_action": { "default_title": "Sail", "default_popup": "out/popup.html" - } + }, + "browser_specific_settings": { + "gecko": { + "id": "sail@coder.com", + "strict_min_version": "48.0" + } + } } \ No newline at end of file diff --git a/extension/src/popup.html b/extension/src/popup.html index 79a83ae..67dd596 100644 --- a/extension/src/popup.html +++ b/extension/src/popup.html @@ -1,5 +1,8 @@ + + + diff --git a/extension/src/popup.ts b/extension/src/popup.ts index 24a53d2..70f54f9 100644 --- a/extension/src/popup.ts +++ b/extension/src/popup.ts @@ -10,7 +10,7 @@ requestSail().then((url) => { const has = (str: string) => ex.toString().indexOf(str) !== -1; if (has("not found")) { - document.body.innerText = "After installing sail, run `sail install-for-chrome-ext`."; + document.body.innerText = "After installing sail, run `sail setup-extension`."; } else { document.body.innerText = ex.toString(); } diff --git a/main.go b/main.go index 0423173..5846776 100644 --- a/main.go +++ b/main.go @@ -65,7 +65,7 @@ func (r rootCmd) Subcommands() []cli.Command { &lscmd{}, &rmcmd{gf: &r.globalFlags}, &proxycmd{}, - &chromeExtInstall{}, + &extensionSetup{}, &versioncmd{}, } } @@ -74,7 +74,8 @@ func main() { root := &rootCmd{} if (len(os.Args) >= 2 && strings.HasPrefix(os.Args[1], "chrome-extension://")) || - (len(os.Args) >= 3 && strings.HasPrefix(os.Args[2], "chrome-extension://")) { + (len(os.Args) >= 3 && strings.HasPrefix(os.Args[2], "chrome-extension://")) || + (len(os.Args) >= 2 && strings.HasSuffix(os.Args[1], "com.coder.sail.json")) { runNativeMsgHost() return } From 2381787e635122a77cbec32f8c81378b1d38edf0 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Thu, 20 Jun 2019 13:47:20 +0200 Subject: [PATCH 2/5] Added firefox extension id --- extension.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension.go b/extension.go index cf5a267..49c0035 100644 --- a/extension.go +++ b/extension.go @@ -167,7 +167,7 @@ func writeNativeHostManifestFirefox(dir string) error { "path": "%v", "type": "stdio", "allowed_extensions": [ - "2dcddda6bd28a9237755003f9cb1fcf60c2a7866@temporary-addon" + "sail@coder.com" ] }`, binPath) From aa76e1bb78f9c33ecad0cdf98328457ea71bd5c4 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Thu, 20 Jun 2019 16:11:16 +0200 Subject: [PATCH 3/5] Moved websocket connection to background --- extension/src/background.ts | 27 +++++++++++++- extension/src/common.ts | 5 +++ extension/src/content.ts | 73 +++++++++++++++++++------------------ 3 files changed, 69 insertions(+), 36 deletions(-) diff --git a/extension/src/background.ts b/extension/src/background.ts index a11cf67..2ceb126 100644 --- a/extension/src/background.ts +++ b/extension/src/background.ts @@ -1,4 +1,4 @@ -import { ExtensionMessage } from "./common"; +import { ExtensionMessage, SocketMessage } from "./common"; export class SailConnector { private port: chrome.runtime.Port; @@ -60,3 +60,28 @@ chrome.runtime.onMessage.addListener((data: ExtensionMessage, sender, sendRespon return true; } }); + +chrome.runtime.onConnect.addListener((port) => { + let socket: WebSocket | null; + + port.onMessage.addListener((message: SocketMessage) => { + switch (message.type) { + case "init": + socket = new WebSocket(message.data); + + socket.addEventListener("open", () => port.postMessage({ type: "open" } as SocketMessage)) + socket.addEventListener("close", e => port.disconnect()) + socket.addEventListener("message", e => port.postMessage({ type: "message", data: e.data } as SocketMessage)) + break; + case "message": + if (socket) { + socket.send(message.data) + } + break; + default: + throw new Error('unknown message type: ' + message.type); + } + }) + + port.postMessage({ type: "init" } as SocketMessage) +}) \ No newline at end of file diff --git a/extension/src/common.ts b/extension/src/common.ts index a2ce071..6845426 100644 --- a/extension/src/common.ts +++ b/extension/src/common.ts @@ -4,6 +4,11 @@ export interface ExtensionMessage { readonly url?: string; } +export interface SocketMessage { + readonly type: "init" | "open" | "message"; + readonly data?: string; +} + export const requestSail = (): Promise => { return new Promise((resolve, reject) => { chrome.runtime.sendMessage({ diff --git a/extension/src/content.ts b/extension/src/content.ts index 80f1163..74545be 100644 --- a/extension/src/content.ts +++ b/extension/src/content.ts @@ -1,40 +1,43 @@ -import { requestSail } from "./common"; +import { requestSail, SocketMessage } from "./common"; const doConnection = (socketUrl: string, projectUrl: string, onMessage: (data: { readonly type: "data" | "error"; readonly v: string; -}) => void): Promise => { - return new Promise((resolve, reject) => { - const socket = new WebSocket(socketUrl); - socket.addEventListener("open", () => { - socket.send(JSON.stringify({ - project: projectUrl, - })); - - resolve(socket); - }); - socket.addEventListener("close", (event) => { - reject(`socket closed: ${event.code}`); - }); - - socket.addEventListener("message", (event) => { - const data = JSON.parse(event.data); - if (!data) { - return; - } - const type = data.type; - const content = atob(data.v); - - switch (type) { - case "data": - case "error": - onMessage({ type, v: content }); - break; - default: - throw new Error("unknown message type: " + type); - } - }); - }); +}) => void): Promise => { + return new Promise((resolve, reject) => { + const port = chrome.runtime.connect(); + + port.onMessage.addListener((message: SocketMessage) => { + switch (message.type) { + case 'init': + port.postMessage({ type: 'init', data: socketUrl } as SocketMessage); + break; + case 'open': + port.postMessage({ type: 'message', data: JSON.stringify({ project: projectUrl }) } as SocketMessage); + resolve(port); + break; + case 'message': + const data = JSON.parse(message.data); + if (!data) { + return; + } + const type = data.type; + const content = atob(data.v); + + switch (type) { + case 'data': + case 'error': + onMessage({ type, v: content }); + break; + default: + throw new Error('unknown message type: ' + type); + } + break; + default: + throw new Error('unknown message type: ' + message.type); + } + }); + }); }; const ensureButton = (): void | HTMLElement => { @@ -123,8 +126,8 @@ const ensureButton = (): void | HTMLElement => { term.scrollTop = term.scrollHeight; } }); - }).then((socket) => { - socket.addEventListener("close", () => { + }).then((port) => { + port.onDisconnect.addListener(() => { btn.innerText = "Open in Sail"; btn.classList.remove("disabled"); term.remove(); From 0b15ec2af4ab1365fcf54fda3fdd351c5f13d6db Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Thu, 20 Jun 2019 16:24:15 +0200 Subject: [PATCH 4/5] Added logo to extension folder --- extension/logo128.png | Bin 0 -> 1256 bytes extension/manifest.json | 8 +++++--- 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 extension/logo128.png diff --git a/extension/logo128.png b/extension/logo128.png new file mode 100644 index 0000000000000000000000000000000000000000..308d886cd17b81211a22ad0bf0b2c6e12eb9041e GIT binary patch literal 1256 zcmVP)Qasq{_>=jM`?6Si*fE$n-;2Xe9GO1LsWBfCtnV$Y$l`1PcmfqLh zGtag>DirA5$3LCjt1UU7*2sy#UY!0HCN$7r4NBy#QFv4FnVH*9*XeQJ@0 z0O*$ba7a4<>bHdepgbG`0Oef(03M_^90GtRsSJkz;7Qi}766p51+;}j0HAz92mq^U zG=)O|pu7tJzI=@0Jen_0MM)K!yy3Jt_xHEz_t(o6f4_s2mq9K z0RY%uS%yOZu$wvn(5>vkp^>B)>$YRl;_1G>$L6@Zy&j$Y=W*YQ{cjcfua@RGGXHa# z0{~+orq9h`=kL1x_+sFf^cW6ZFap9FU%Sfv;YhZ^E=_@TUC<3 zTv|pnzbW$hutg{JlS5@B^KFusQ?4%dlS8pOKrd{yzm4V+!=W-l_1o>trx zY`(3Kd6BOgH{Jl?1)XQu`d1c&0IkPJh+elP6R1RGITGRu;>g=x-0U#zEDyjZ( zYXJ%2P&q*LPn695b=$(04xrQgMp-5HeR?^{6=nn9qK=%tm ztpLpM{$9z3L%{%`{jaFk)&Z#g=Sngh3JJh_JYQW4q!tbZ0l=N)6u;MQ3tJ5xpvLPL zmDFwNLjgebZ|(wJ?f|^S=Z$JoiHI2i%D08y0g(2YxQI$39176^0APo0VU7SCrb&>%<9*StlaL6|R>;n8~uyBYDkf!WzpalTk8YCR@1^^+DcMTE_ z(E-v{@NftK(pL`vx)n4W@(BQIfPmXVIsjdu0RXxH04P@FZnFyjisd~V0svX^e$OEQ zkf+vXI0OJP)d7HR&G-r)LI@#*5JCu{Jv8$5MDC4>Shq%heS1AxV(0G4B?uxENdSOF z#Pi^`w6RBk8w0+(mMs9L1ur6Y z0jtIwKcxIX&fhRzaZuJLQ1ir8^>|(YjD)&)_+4Sf!fN#o`kuyL0R{kt0zQz< Sf&iKT0000 Date: Thu, 20 Jun 2019 16:34:10 +0200 Subject: [PATCH 5/5] Fixed styling of popup dialog on firefox --- extension/src/popup.html | 17 ++++++++++++----- extension/src/popup.ts | 4 ---- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/extension/src/popup.html b/extension/src/popup.html index 67dd596..95eb16d 100644 --- a/extension/src/popup.html +++ b/extension/src/popup.html @@ -1,10 +1,17 @@ - + + + - -
    - - + + After installing sail, run `sail setup-extension`. + diff --git a/extension/src/popup.ts b/extension/src/popup.ts index 70f54f9..3fff916 100644 --- a/extension/src/popup.ts +++ b/extension/src/popup.ts @@ -1,9 +1,5 @@ import { requestSail } from "./common"; -const root = document.getElementById("root") as HTMLElement; -// const projects = document.getElementById("projects") as HTMLUListElement; -document.body.style.width = "150px"; - requestSail().then((url) => { document.body.innerText = "Sail is setup and working properly!"; }).catch((ex) => {