87 lines
1.5 KiB
Go
87 lines
1.5 KiB
Go
package marionette
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"net"
|
|
"os"
|
|
"os/exec"
|
|
)
|
|
|
|
func haveProg(prog string) bool {
|
|
_, err := exec.LookPath(prog)
|
|
return err == nil
|
|
}
|
|
|
|
func WithGecko(ctx context.Context, fn func(context.Context, *TCPTransport) error) (_retErr error) {
|
|
maybeSetErr := func(_err error) {
|
|
if _retErr == nil && _err != nil {
|
|
_retErr = _err
|
|
}
|
|
}
|
|
|
|
profile, err := os.MkdirTemp("", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer func() { maybeSetErr(os.RemoveAll(profile)) }()
|
|
|
|
var prog string
|
|
switch {
|
|
case haveProg("firefox"):
|
|
prog = "firefox"
|
|
case haveProg("iceweasel"):
|
|
prog = "iceweasel"
|
|
default:
|
|
return errors.New("no gecko browser found")
|
|
}
|
|
cmd := exec.CommandContext(ctx, prog,
|
|
"--new-instance",
|
|
"--profile", profile,
|
|
"--marionette")
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
if err := cmd.Start(); err != nil {
|
|
return err
|
|
}
|
|
|
|
defer func() { maybeSetErr(cmd.Wait()) }()
|
|
defer func() {
|
|
if _retErr != nil {
|
|
cmd.Process.Signal(os.Interrupt)
|
|
}
|
|
}()
|
|
|
|
var conn *net.TCPConn
|
|
for conn == nil {
|
|
select {
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
default:
|
|
_conn, _ := net.Dial("tcp", "localhost:2828")
|
|
conn, _ = _conn.(*net.TCPConn)
|
|
}
|
|
}
|
|
defer func() { maybeSetErr(conn.Close()) }()
|
|
|
|
trans, err := NewTransport(conn)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := trans.CmdNewSession(nil); err != nil {
|
|
return err
|
|
}
|
|
|
|
defer func() {
|
|
_, err := trans.CmdQuit()
|
|
maybeSetErr(err)
|
|
maybeSetErr(conn.CloseWrite())
|
|
_, err = io.Copy(io.Discard, conn)
|
|
maybeSetErr(err)
|
|
}()
|
|
|
|
return fn(ctx, trans)
|
|
}
|