Skip to content
This repository was archived by the owner on Apr 28, 2020. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 74 additions & 12 deletions chrome.go → extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,23 +91,23 @@ 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 {
if dir == "" {
continue
}
Expand All @@ -116,14 +116,34 @@ func (c *chromeExtInstall) Run(fl *flag.FlagSet) {
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 {
if dir == "" {
continue
}

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
Expand All @@ -143,7 +163,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": [
"sail@coder.com"
]
}`, 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)
Expand Down Expand Up @@ -171,9 +211,31 @@ func nativeMessageHostManifestDirectories() ([]string, error) {

return []string{
chromeDir,
chromiumDir,
chromeBetaDir,
chromeDevDir,
chromeCanaryDir,
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
}
Binary file added extension/logo128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 12 additions & 4 deletions extension/manifest.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
{
"manifest_version": 2,

"name": "Sail",
"version": "1.0.5",
"author": "Coder",
"version": "1.0.8",
"author": "Coder",
"description": "Work in immutable, pre-configured development environments.",

"background": {
Expand All @@ -28,5 +27,14 @@
"browser_action": {
"default_title": "Sail",
"default_popup": "out/popup.html"
}
},
"icons": {
"128": "logo128.png"
},
"browser_specific_settings": {
"gecko": {
"id": "sail@coder.com",
"strict_min_version": "48.0"
}
}
}
27 changes: 26 additions & 1 deletion extension/src/background.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ExtensionMessage } from "./common";
import { ExtensionMessage, SocketMessage } from "./common";

export class SailConnector {
private port: chrome.runtime.Port;
Expand Down Expand Up @@ -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)
})
5 changes: 5 additions & 0 deletions extension/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> => {
return new Promise<string>((resolve, reject) => {
chrome.runtime.sendMessage({
Expand Down
73 changes: 38 additions & 35 deletions extension/src/content.ts
Original file line number Diff line number Diff line change
@@ -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<WebSocket> => {
return new Promise<WebSocket>((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<chrome.runtime.Port> => {
return new Promise<chrome.runtime.Port>((resolve, reject) => {
const port = chrome.runtime.connect();
Comment thread
lucacasonato marked this conversation as resolved.

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 => {
Expand Down Expand Up @@ -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();
Expand Down
18 changes: 14 additions & 4 deletions extension/src/popup.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
<!DOCTYPE html>
<html>
<body>
<ul id="root"></ul>
<script src="/out/popup.js"></script>
</body>
<head>
<meta charset="UTF-8" />
<style>
body {
font-size: 12pt;
font-family: sans-serif;
color: black;
}
</style>
<script src="/out/popup.js"></script>
</head>
<body>
After installing sail, run `sail setup-extension`.
</body>
</html>
6 changes: 1 addition & 5 deletions extension/src/popup.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
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) => {
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();
}
Expand Down
5 changes: 3 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func (r rootCmd) Subcommands() []cli.Command {
&lscmd{},
&rmcmd{gf: &r.globalFlags},
&proxycmd{},
&chromeExtInstall{},
&extensionSetup{},
&versioncmd{},
}
}
Expand All @@ -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
}
Expand Down