-
-
Notifications
You must be signed in to change notification settings - Fork 31
/
webbrowser.go
137 lines (116 loc) · 3.37 KB
/
webbrowser.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Package webbrowser provides a simple API for opening web pages on your
// default browser.
package webbrowser
import (
"errors"
"fmt"
"net/url"
"os"
"os/exec"
"runtime"
"strings"
)
var (
ErrCantOpenBrowser = errors.New("webbrowser: can't open browser")
ErrNoCandidates = errors.New("webbrowser: no browser candidate found for your OS")
)
// Candidates contains a list of registered `Browser`s that will be tried with Open.
var Candidates []Browser
type Browser interface {
// Command returns a ready to be used Cmd that will open an URL.
Command(string) (*exec.Cmd, error)
// Open tries to open a URL in your default browser. NOTE: This may cause
// your program to hang until the browser process is closed in some OSes,
// see https://github.com/toqueteos/webbrowser/issues/4.
Open(string) error
}
// Open tries to open a URL in your default browser ensuring you have a display
// set up and not running this from SSH. NOTE: This may cause your program to
// hang until the browser process is closed in some OSes, see
// https://github.com/toqueteos/webbrowser/issues/4.
func Open(s string) (err error) {
if len(Candidates) == 0 {
return ErrNoCandidates
}
// Try to determine if there's a display available (only linux) and we
// aren't on a terminal (all but windows).
switch runtime.GOOS {
case "linux":
// No display, no need to open a browser. Lynx users **MAY** have
// something to say about this.
if os.Getenv("DISPLAY") == "" {
return fmt.Errorf("webbrowser: tried to open %q, no screen found", s)
}
fallthrough
case "darwin":
// Check SSH env vars.
if os.Getenv("SSH_CLIENT") != "" || os.Getenv("SSH_TTY") != "" {
return fmt.Errorf("webbrowser: tried to open %q, but you are running a shell session", s)
}
}
// Try all candidates
for _, candidate := range Candidates {
err := candidate.Open(s)
if err == nil {
return nil
}
}
return ErrCantOpenBrowser
}
func init() {
// Register the default Browser for current OS, if it exists.
if os, ok := osCommand[runtime.GOOS]; ok {
Candidates = append(Candidates, browserCommand{os.cmd, os.args})
}
}
var (
osCommand = map[string]*browserCommand{
"android": &browserCommand{"xdg-open", nil},
"darwin": &browserCommand{"open", nil},
"freebsd": &browserCommand{"xdg-open", nil},
"linux": &browserCommand{"xdg-open", nil},
"netbsd": &browserCommand{"xdg-open", nil},
"openbsd": &browserCommand{"xdg-open", nil}, // It may be open instead
"windows": &browserCommand{"cmd", []string{"/c", "start"}},
}
winSchemes = [3]string{"https", "http", "file"}
)
type browserCommand struct {
cmd string
args []string
}
func (b browserCommand) Command(s string) (*exec.Cmd, error) {
u, err := url.Parse(s)
if err != nil {
return nil, err
}
validUrl := ensureValidURL(u)
b.args = append(b.args, validUrl)
return exec.Command(b.cmd, b.args...), nil
}
func (b browserCommand) Open(s string) error {
cmd, err := b.Command(s)
if err != nil {
return err
}
return cmd.Run()
}
func ensureScheme(u *url.URL) {
for _, s := range winSchemes {
if u.Scheme == s {
return
}
}
u.Scheme = "http"
}
func ensureValidURL(u *url.URL) string {
// Enforce a scheme (windows requires scheme to be set to work properly).
ensureScheme(u)
s := u.String()
// Escape characters not allowed by cmd/bash
switch runtime.GOOS {
case "windows":
s = strings.Replace(s, "&", `^&`, -1)
}
return s
}