1 // Package webbrowser provides a simple API for opening web pages on your
16 ErrCantOpenBrowser = errors.New("webbrowser: can't open browser")
17 ErrNoCandidates = errors.New("webbrowser: no browser candidate found for your OS")
20 // Candidates contains a list of registered `Browser`s that will be tried with Open.
21 var Candidates []Browser
23 type Browser interface {
24 // Command returns a ready to be used Cmd that will open an URL.
25 Command(string) (*exec.Cmd, error)
26 // Open tries to open a URL in your default browser. NOTE: This may cause
27 // your program to hang until the browser process is closed in some OSes,
28 // see https://github.com/toqueteos/webbrowser/issues/4.
32 // Open tries to open a URL in your default browser ensuring you have a display
33 // set up and not running this from SSH. NOTE: This may cause your program to
34 // hang until the browser process is closed in some OSes, see
35 // https://github.com/toqueteos/webbrowser/issues/4.
36 func Open(s string) (err error) {
37 if len(Candidates) == 0 {
38 return ErrNoCandidates
41 // Try to determine if there's a display available (only linux) and we
42 // aren't on a terminal (all but windows).
45 // No display, no need to open a browser. Lynx users **MAY** have
46 // something to say about this.
47 if os.Getenv("DISPLAY") == "" {
48 return fmt.Errorf("webbrowser: tried to open %q, no screen found", s)
52 // Check SSH env vars.
53 if os.Getenv("SSH_CLIENT") != "" || os.Getenv("SSH_TTY") != "" {
54 return fmt.Errorf("webbrowser: tried to open %q, but you are running a shell session", s)
59 for _, candidate := range Candidates {
60 err := candidate.Open(s)
66 return ErrCantOpenBrowser
70 // Register the default Browser for current OS, if it exists.
71 if os, ok := osCommand[runtime.GOOS]; ok {
72 Candidates = append(Candidates, browserCommand{os.cmd, os.args})
77 osCommand = map[string]*browserCommand{
78 "android": &browserCommand{"xdg-open", nil},
79 "darwin": &browserCommand{"open", nil},
80 "freebsd": &browserCommand{"xdg-open", nil},
81 "linux": &browserCommand{"xdg-open", nil},
82 "netbsd": &browserCommand{"xdg-open", nil},
83 "openbsd": &browserCommand{"xdg-open", nil}, // It may be open instead
84 "windows": &browserCommand{"cmd", []string{"/c", "start"}},
86 winSchemes = [3]string{"https", "http", "file"}
89 type browserCommand struct {
94 func (b browserCommand) Command(s string) (*exec.Cmd, error) {
95 u, err := url.Parse(s)
100 validUrl := ensureValidURL(u)
102 b.args = append(b.args, validUrl)
104 return exec.Command(b.cmd, b.args...), nil
107 func (b browserCommand) Open(s string) error {
108 cmd, err := b.Command(s)
116 func ensureScheme(u *url.URL) {
117 for _, s := range winSchemes {
125 func ensureValidURL(u *url.URL) string {
126 // Enforce a scheme (windows requires scheme to be set to work properly).
130 // Escape characters not allowed by cmd/bash
131 switch runtime.GOOS {
133 s = strings.Replace(s, "&", `^&`, -1)