1// Copyright 2016 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package browser provides utilities for interacting with users' browsers.
6package browser
7
8import (
9	"os"
10	"os/exec"
11	"runtime"
12	"time"
13)
14
15// Commands returns a list of possible commands to use to open a url.
16func Commands() [][]string {
17	var cmds [][]string
18	if exe := os.Getenv("BROWSER"); exe != "" {
19		cmds = append(cmds, []string{exe})
20	}
21	switch runtime.GOOS {
22	case "darwin":
23		cmds = append(cmds, []string{"/usr/bin/open"})
24	case "windows":
25		cmds = append(cmds, []string{"cmd", "/c", "start"})
26	default:
27		if os.Getenv("DISPLAY") != "" {
28			// xdg-open is only for use in a desktop environment.
29			cmds = append(cmds, []string{"xdg-open"})
30		}
31	}
32	cmds = append(cmds,
33		[]string{"chrome"},
34		[]string{"google-chrome"},
35		[]string{"chromium"},
36		[]string{"firefox"},
37	)
38	return cmds
39}
40
41// Open tries to open url in a browser and reports whether it succeeded.
42func Open(url string) bool {
43	for _, args := range Commands() {
44		cmd := exec.Command(args[0], append(args[1:], url)...)
45		if cmd.Start() == nil && appearsSuccessful(cmd, 3*time.Second) {
46			return true
47		}
48	}
49	return false
50}
51
52// appearsSuccessful reports whether the command appears to have run successfully.
53// If the command runs longer than the timeout, it's deemed successful.
54// If the command runs within the timeout, it's deemed successful if it exited cleanly.
55func appearsSuccessful(cmd *exec.Cmd, timeout time.Duration) bool {
56	errc := make(chan error, 1)
57	go func() {
58		errc <- cmd.Wait()
59	}()
60
61	select {
62	case <-time.After(timeout):
63		return true
64	case err := <-errc:
65		return err == nil
66	}
67}
68