webbrowser.py revision bc2ce57203e1f355004ec95953eae9dec780ef9a
1e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl#! /usr/bin/env python
20a8c29be4b5314d4bf72cb391ab078f7483cf3f7Ka-Ping Yee"""Interfaces for launching and remotely controlling Web browsers."""
3992d4a3e6e67a05b85350820157028a61d1f22cfGuido van Rossum# Maintained by Georg Brandl.
4c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
5c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drakeimport os
6d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossumimport shlex
7c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drakeimport sys
8e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlimport stat
923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandlimport subprocess
1023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandlimport time
11c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
12e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl__all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"]
1340fc16059f04ee8fda0b5956cc4883eb21ca8f8cSkip Montanaro
14c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drakeclass Error(Exception):
15c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake    pass
16c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
17658cba6706eb4a2ad8b3e235cf0db9fe1c8e9e6bTim Peters_browsers = {}          # Dictionary of available browser controllers
18658cba6706eb4a2ad8b3e235cf0db9fe1c8e9e6bTim Peters_tryorder = []          # Preference order of available browsers
19c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
20e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandldef register(name, klass, instance=None, update_tryorder=1):
21c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake    """Register a browser connector and, optionally, connection."""
22c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake    _browsers[name.lower()] = [klass, instance]
23e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if update_tryorder > 0:
24e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        _tryorder.append(name)
25e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    elif update_tryorder < 0:
26e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        _tryorder.insert(0, name)
27c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
28f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymonddef get(using=None):
29f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond    """Return a browser launcher instance appropriate for the environment."""
3010ff706e2788a7c7ef9f8ea0108a5ede625fedadRaymond Hettinger    if using is not None:
31f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond        alternatives = [using]
32f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond    else:
33f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond        alternatives = _tryorder
34f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond    for browser in alternatives:
35bac788a3cd348203a5fdabba52e5faf65bf35c5eRaymond Hettinger        if '%s' in browser:
3623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # User gave us a command line, split it into name and args
37d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum            browser = shlex.split(browser)
38d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum            if browser[-1] == '&':
39d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum                return BackgroundBrowser(browser[:-1])
40d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum            else:
41d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum                return GenericBrowser(browser)
42f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond        else:
43e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            # User gave us a browser name or path.
44f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake            try:
45f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake                command = _browsers[browser.lower()]
46f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake            except KeyError:
47f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake                command = _synthesize(browser)
48e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            if command[1] is not None:
49f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond                return command[1]
50e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            elif command[0] is not None:
51e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                return command[0]()
52f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond    raise Error("could not locate runnable browser")
53c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
54c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake# Please note: the following definition hides a builtin function.
55e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl# It is recommended one does "import webbrowser" and uses webbrowser.open(url)
56e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl# instead of "from webbrowser import *".
57c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
58f79cb2db3eae59f80e8031d45376dc5f48d2af04Eric S. Raymonddef open(url, new=0, autoraise=1):
59e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    for name in _tryorder:
60e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        browser = get(name)
61e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        if browser.open(url, new, autoraise):
62e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            return True
63e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    return False
64c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
653f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drakedef open_new(url):
66e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    return open(url, 1)
67c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
68e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandldef open_new_tab(url):
69e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    return open(url, 2)
70f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake
71e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
72e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandldef _synthesize(browser, update_tryorder=1):
73f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    """Attempt to synthesize a controller base on existing controllers.
74f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake
75f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    This is useful to create a controller when a user specifies a path to
76f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    an entry in the BROWSER environment variable -- we can copy a general
77f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    controller to operate using a specific installation of the desired
78f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    browser in this way.
79f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake
80f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    If we can't create a controller in this way, or if there is no
81f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    executable for the requested browser, return [None, None].
82f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake
83f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    """
84e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    cmd = browser.split()[0]
85e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if not _iscommand(cmd):
86f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake        return [None, None]
87e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    name = os.path.basename(cmd)
88f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    try:
89f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake        command = _browsers[name.lower()]
90f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    except KeyError:
91f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake        return [None, None]
92f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    # now attempt to clone to fit the new name:
93f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    controller = command[1]
94f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake    if controller and name.lower() == controller.basename:
95f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake        import copy
96f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake        controller = copy.copy(controller)
97f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake        controller.name = browser
98f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake        controller.basename = os.path.basename(browser)
99e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        register(browser, None, controller, update_tryorder)
100f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake        return [None, controller]
101118aa5337c745dece455f4782799c83b9e3a7d4fAndrew M. Kuchling    return [None, None]
102f4e5bd9df540aa5b24e47d03dbb798ed277793f7Fred Drake
1033f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
104e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlif sys.platform[:3] == "win":
105e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    def _isexecutable(cmd):
106e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        cmd = cmd.lower()
1070e3f591aeeef9ed715f8770320f4c4c7332a8794Thomas Wouters        if os.path.isfile(cmd) and cmd.endswith((".exe", ".bat")):
108e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            return True
109e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        for ext in ".exe", ".bat":
110e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            if os.path.isfile(cmd + ext):
111e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                return True
112e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        return False
113e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlelse:
114e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    def _isexecutable(cmd):
115e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        if os.path.isfile(cmd):
116e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            mode = os.stat(cmd)[stat.ST_MODE]
117e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            if mode & stat.S_IXUSR or mode & stat.S_IXGRP or mode & stat.S_IXOTH:
118e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                return True
119e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        return False
120e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
1213f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drakedef _iscommand(cmd):
122e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    """Return True if cmd is executable or can be found on the executable
123e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    search path."""
124e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if _isexecutable(cmd):
125e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        return True
1263f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake    path = os.environ.get("PATH")
1273f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake    if not path:
128bc0e9108261693b6278687f4fb4709ff76c2e543Tim Peters        return False
1293f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake    for d in path.split(os.pathsep):
1303f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        exe = os.path.join(d, cmd)
131e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        if _isexecutable(exe):
132bc0e9108261693b6278687f4fb4709ff76c2e543Tim Peters            return True
133bc0e9108261693b6278687f4fb4709ff76c2e543Tim Peters    return False
1343f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
1353f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
136e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl# General parent classes
137e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
138e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlclass BaseBrowser(object):
13923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    """Parent class for all browsers. Do not use directly."""
140887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters
14123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    args = ['%s']
142887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters
143e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    def __init__(self, name=""):
144e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        self.name = name
145b980113a8df699797b837f103ac6d2049a214551Georg Brandl        self.basename = name
146536cf99536bce562cfcb44a856fac1c84b9de4c3Tim Peters
147196f733d935bd51f8674c9761420ce990694f33aNeal Norwitz    def open(self, url, new=0, autoraise=1):
148196f733d935bd51f8674c9761420ce990694f33aNeal Norwitz        raise NotImplementedError
149196f733d935bd51f8674c9761420ce990694f33aNeal Norwitz
150e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    def open_new(self, url):
151e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        return self.open(url, 1)
152e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
153e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    def open_new_tab(self, url):
154e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        return self.open(url, 2)
155e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
1563f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
157e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlclass GenericBrowser(BaseBrowser):
158e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    """Class for all browsers started with a command
159e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl       and without remote functionality."""
1603f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
16123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    def __init__(self, name):
1623172c5d263eeffff1e89d03d79be3ccc1d60fbdeGuido van Rossum        if isinstance(name, str):
16323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            self.name = name
164992d4a3e6e67a05b85350820157028a61d1f22cfGuido van Rossum            self.args = ["%s"]
16523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        else:
16623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # name should be a list with arguments
16723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            self.name = name[0]
16823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            self.args = name[1:]
169b980113a8df699797b837f103ac6d2049a214551Georg Brandl        self.basename = os.path.basename(self.name)
1703f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
1713f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake    def open(self, url, new=0, autoraise=1):
172887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters        cmdline = [self.name] + [arg.replace("%s", url)
17323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 for arg in self.args]
17423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        try:
17589f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            if sys.platform[:3] == 'win':
17689f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                p = subprocess.Popen(cmdline)
17789f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            else:
17889f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                p = subprocess.Popen(cmdline, close_fds=True)
17923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return not p.wait()
18023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        except OSError:
18123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return False
18223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl
18323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl
18423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandlclass BackgroundBrowser(GenericBrowser):
18523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    """Class for all browsers which are to be started in the
18623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl       background."""
18723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl
18823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    def open(self, url, new=0, autoraise=1):
18923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        cmdline = [self.name] + [arg.replace("%s", url)
19023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 for arg in self.args]
19123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        try:
19289f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            if sys.platform[:3] == 'win':
19389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                p = subprocess.Popen(cmdline)
19489f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            else:
19589f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                setsid = getattr(os, 'setsid', None)
19689f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                if not setsid:
19789f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                    setsid = getattr(os, 'setpgrp', None)
19889f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                p = subprocess.Popen(cmdline, close_fds=True, preexec_fn=setsid)
19923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return (p.poll() is None)
20023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        except OSError:
20123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return False
2023f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
2033f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
204e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlclass UnixBrowser(BaseBrowser):
205e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    """Parent class for all Unix browsers with remote functionality."""
2063f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
207e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    raise_opts = None
20823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_args = ['%action', '%s']
209e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    remote_action = None
210e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    remote_action_newwin = None
211e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    remote_action_newtab = None
21223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    background = False
21323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    redirect_stdout = True
21423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl
21523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    def _invoke(self, args, remote, autoraise):
21623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        raise_opt = []
21723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        if remote and self.raise_opts:
21823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # use autoraise argument only for remote invocation
21923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            autoraise = int(bool(autoraise))
22023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            opt = self.raise_opts[autoraise]
22123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            if opt: raise_opt = [opt]
22223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl
22323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        cmdline = [self.name] + raise_opt + args
224887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters
22523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        if remote or self.background:
226bc2ce57203e1f355004ec95953eae9dec780ef9aAmaury Forgeot d'Arc            inout = io.open(os.devnull, "r+")
22723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        else:
22823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # for TTY browsers, we need stdin/out
22923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            inout = None
23023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        # if possible, put browser in separate process group, so
23123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        # keyboard interrupts don't affect browser as well as Python
23223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        setsid = getattr(os, 'setsid', None)
23323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        if not setsid:
23423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            setsid = getattr(os, 'setpgrp', None)
235887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters
23623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        p = subprocess.Popen(cmdline, close_fds=True, stdin=inout,
23723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                             stdout=(self.redirect_stdout and inout or None),
23823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                             stderr=inout, preexec_fn=setsid)
23923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        if remote:
24023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # wait five secons. If the subprocess is not finished, the
24123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # remote invocation has (hopefully) started a new instance.
24223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            time.sleep(1)
24323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            rc = p.poll()
24423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            if rc is None:
24523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                time.sleep(4)
24623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                rc = p.poll()
24723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                if rc is None:
24823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                    return True
24923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # if remote call failed, open() will try direct invocation
25023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return not rc
25123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        elif self.background:
25223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            if p.poll() is None:
25323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                return True
25423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            else:
25523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                return False
25623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        else:
25723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return not p.wait()
2583f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
2593f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake    def open(self, url, new=0, autoraise=1):
260e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        if new == 0:
261e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            action = self.remote_action
262e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        elif new == 1:
263e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            action = self.remote_action_newwin
264e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        elif new == 2:
265e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            if self.remote_action_newtab is None:
266e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                action = self.remote_action_newwin
267e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            else:
268e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                action = self.remote_action_newtab
2693f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        else:
27023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            raise Error("Bad 'new' parameter to open(); " +
27123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                        "expected 0, 1, or 2, got %s" % new)
272887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters
27323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        args = [arg.replace("%s", url).replace("%action", action)
27423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                for arg in self.remote_args]
27523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        success = self._invoke(args, True, autoraise)
27623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        if not success:
27723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # remote invocation failed, try straight way
27823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            args = [arg.replace("%s", url) for arg in self.args]
27923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return self._invoke(args, False, False)
28023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        else:
28123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return True
2823f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
2833f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
284e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlclass Mozilla(UnixBrowser):
285e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    """Launcher class for Mozilla/Netscape browsers."""
2863f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
28723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    raise_opts = ["-noraise", "-raise"]
2888dd28eb973a1c072448d961a6fb6a8a12cc3c950Neal Norwitz
28923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_args = ['-remote', 'openURL(%s%action)']
29023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action = ""
29123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action_newwin = ",new-window"
29223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action_newtab = ",new-tab"
293887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters
29423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    background = True
2958dd28eb973a1c072448d961a6fb6a8a12cc3c950Neal Norwitz
296e8f244305ef4f257f6999b69601f4316b31faa5eGeorg BrandlNetscape = Mozilla
2978dd28eb973a1c072448d961a6fb6a8a12cc3c950Neal Norwitz
2988dd28eb973a1c072448d961a6fb6a8a12cc3c950Neal Norwitz
299e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlclass Galeon(UnixBrowser):
300e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    """Launcher class for Galeon/Epiphany browsers."""
301e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
30223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    raise_opts = ["-noraise", ""]
30323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_args = ['%action', '%s']
30423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action = "-n"
30523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action_newwin = "-w"
3068dd28eb973a1c072448d961a6fb6a8a12cc3c950Neal Norwitz
30723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    background = True
308e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
309e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
31023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandlclass Opera(UnixBrowser):
31123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    "Launcher class for Opera browser."
3123f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
31323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    raise_opts = ["", "-raise"]
3143f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
31523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_args = ['-remote', 'openURL(%s%action)']
31623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action = ""
31723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action_newwin = ",new-window"
31823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action_newtab = ",new-page"
31923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    background = True
3203f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
321e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
32223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandlclass Elinks(UnixBrowser):
32323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    "Launcher class for Elinks browsers."
324e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
32523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_args = ['-remote', 'openURL(%s%action)']
32623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action = ""
32723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action_newwin = ",new-window"
32823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    remote_action_newtab = ",new-tab"
32923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    background = False
3303f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
33123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    # elinks doesn't like its stdout to be redirected -
33223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    # it uses redirected stdout as a signal to do -dump
33323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    redirect_stdout = False
3343f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
3353f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
33623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandlclass Konqueror(BaseBrowser):
33723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    """Controller for the KDE File Manager (kfm, or Konqueror).
338e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
33923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    See the output of ``kfmclient --commands``
34023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    for more information on the Konqueror remote-control interface.
34123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    """
342e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
34323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    def open(self, url, new=0, autoraise=1):
34423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        # XXX Currently I know no way to prevent KFM from opening a new win.
34523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        if new == 2:
34623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            action = "newTab"
34723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        else:
34823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            action = "openURL"
349887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters
3502b2b44dc2c2b95569e0e59c6dd4b12148ffba598Amaury Forgeot d'Arc        import io
3512b2b44dc2c2b95569e0e59c6dd4b12148ffba598Amaury Forgeot d'Arc        devnull = io.open(os.devnull, "r+")
35223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        # if possible, put browser in separate process group, so
35323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        # keyboard interrupts don't affect browser as well as Python
35423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        setsid = getattr(os, 'setsid', None)
35523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        if not setsid:
35623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            setsid = getattr(os, 'setpgrp', None)
357887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters
35823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        try:
35923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            p = subprocess.Popen(["kfmclient", action, url],
36023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 close_fds=True, stdin=devnull,
36123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 stdout=devnull, stderr=devnull)
36223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        except OSError:
36323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # fall through to next variant
36423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            pass
36523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        else:
36623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            p.wait()
36723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # kfmclient's return code unfortunately has no meaning as it seems
36823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return True
369e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
37023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        try:
37123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            p = subprocess.Popen(["konqueror", "--silent", url],
37223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 close_fds=True, stdin=devnull,
37323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 stdout=devnull, stderr=devnull,
37423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 preexec_fn=setsid)
37523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        except OSError:
37623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # fall through to next variant
37723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            pass
37823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        else:
37923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            if p.poll() is None:
38023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                # Should be running now.
38123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                return True
382887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters
38323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        try:
38423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            p = subprocess.Popen(["kfm", "-d", url],
38523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 close_fds=True, stdin=devnull,
38623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 stdout=devnull, stderr=devnull,
38723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                                 preexec_fn=setsid)
38823929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        except OSError:
38923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return False
39023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        else:
39123929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            return (p.poll() is None)
392e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
393e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
394e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlclass Grail(BaseBrowser):
3953f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake    # There should be a way to maintain a connection to Grail, but the
3963f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake    # Grail remote control protocol doesn't really allow that at this
39723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl    # point.  It probably never will!
3983f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake    def _find_grail_rc(self):
3993f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        import glob
4003f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        import pwd
4013f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        import socket
4023f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        import tempfile
4033f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        tempdir = os.path.join(tempfile.gettempdir(),
4043f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake                               ".grail-unix")
40516623fe3e606efdedead8ccccaf7ef583e1be97fFred Drake        user = pwd.getpwuid(os.getuid())[0]
4063f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        filename = os.path.join(tempdir, user + "-*")
4073f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        maybes = glob.glob(filename)
4083f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        if not maybes:
4093f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake            return None
4103f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
4113f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        for fn in maybes:
4123f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake            # need to PING each one until we find one that's live
4133f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake            try:
4143f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake                s.connect(fn)
4153f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake            except socket.error:
4163f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake                # no good; attempt to clean it out, but don't fail:
4173f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake                try:
4183f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake                    os.unlink(fn)
4193f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake                except IOError:
4203f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake                    pass
4213f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake            else:
4223f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake                return s
4233f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
4243f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake    def _remote(self, action):
4253f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        s = self._find_grail_rc()
4263f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        if not s:
4273f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake            return 0
4283f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        s.send(action)
4293f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        s.close()
4303f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        return 1
4313f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
4323f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake    def open(self, url, new=0, autoraise=1):
4333f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        if new:
434e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            ok = self._remote("LOADNEW " + url)
4353f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake        else:
436e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            ok = self._remote("LOAD " + url)
437e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        return ok
4383f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
439c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
440658cba6706eb4a2ad8b3e235cf0db9fe1c8e9e6bTim Peters#
441f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond# Platform support for Unix
442f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond#
443c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
444e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl# These are the right tests because all these Unix browsers require either
445e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl# a console terminal or an X display to run.
446e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
447196f733d935bd51f8674c9761420ce990694f33aNeal Norwitzdef register_X_browsers():
448d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum
449d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum    # The default GNOME browser
450d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum    if "GNOME_DESKTOP_SESSION_ID" in os.environ and _iscommand("gnome-open"):
451d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum        register("gnome-open", None, BackgroundBrowser("gnome-open"))
452d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum
453d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum    # The default KDE browser
454d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum    if "KDE_FULL_SESSION" in os.environ and _iscommand("kfmclient"):
455d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum        register("kfmclient", Konqueror, Konqueror("kfmclient"))
456d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum
457d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum    # The Mozilla/Netscape browsers
4584a5a91838b7318cc89a2ff2018b26940a5960a29Georg Brandl    for browser in ("mozilla-firefox", "firefox",
4594a5a91838b7318cc89a2ff2018b26940a5960a29Georg Brandl                    "mozilla-firebird", "firebird",
460477c8d5e70240744d24631b18341ad892c8a8e1cThomas Wouters                    "seamonkey", "mozilla", "netscape"):
4614a5a91838b7318cc89a2ff2018b26940a5960a29Georg Brandl        if _iscommand(browser):
4624a5a91838b7318cc89a2ff2018b26940a5960a29Georg Brandl            register(browser, None, Mozilla(browser))
4634a5a91838b7318cc89a2ff2018b26940a5960a29Georg Brandl
464e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # Konqueror/kfm, the KDE browser.
465b980113a8df699797b837f103ac6d2049a214551Georg Brandl    if _iscommand("kfm"):
466b980113a8df699797b837f103ac6d2049a214551Georg Brandl        register("kfm", Konqueror, Konqueror("kfm"))
467b980113a8df699797b837f103ac6d2049a214551Georg Brandl    elif _iscommand("konqueror"):
468b980113a8df699797b837f103ac6d2049a214551Georg Brandl        register("konqueror", Konqueror, Konqueror("konqueror"))
469e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
470e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # Gnome's Galeon and Epiphany
471e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    for browser in ("galeon", "epiphany"):
472e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        if _iscommand(browser):
473e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            register(browser, None, Galeon(browser))
474e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
475e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # Skipstone, another Gtk/Mozilla based browser
476e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if _iscommand("skipstone"):
47723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        register("skipstone", None, BackgroundBrowser("skipstone"))
478e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
479e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # Opera, quite popular
480e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if _iscommand("opera"):
481e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        register("opera", None, Opera("opera"))
482e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
483e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # Next, Mosaic -- old but still in use.
484e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if _iscommand("mosaic"):
48523929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        register("mosaic", None, BackgroundBrowser("mosaic"))
486e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
487e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # Grail, the Python browser. Does anybody still use it?
488e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if _iscommand("grail"):
489e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        register("grail", Grail, None)
490e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
491196f733d935bd51f8674c9761420ce990694f33aNeal Norwitz# Prefer X browsers if present
492196f733d935bd51f8674c9761420ce990694f33aNeal Norwitzif os.environ.get("DISPLAY"):
493196f733d935bd51f8674c9761420ce990694f33aNeal Norwitz    register_X_browsers()
494196f733d935bd51f8674c9761420ce990694f33aNeal Norwitz
495e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl# Also try console browsers
496e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlif os.environ.get("TERM"):
497e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # The Links/elinks browsers <http://artax.karlin.mff.cuni.cz/~mikulas/links/>
498e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if _iscommand("links"):
49923929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        register("links", None, GenericBrowser("links"))
500e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if _iscommand("elinks"):
501e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        register("elinks", None, Elinks("elinks"))
502e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # The Lynx browser <http://lynx.isc.org/>, <http://lynx.browser.org/>
503e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if _iscommand("lynx"):
50423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        register("lynx", None, GenericBrowser("lynx"))
505e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # The w3m browser <http://w3m.sourceforge.net/>
506e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    if _iscommand("w3m"):
50723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl        register("w3m", None, GenericBrowser("w3m"))
5083f8f1643c8418a3e1980138c6fdb218e5be8c186Fred Drake
509f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond#
510f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond# Platform support for Windows
511f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond#
512c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
513f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymondif sys.platform[:3] == "win":
514e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    class WindowsDefault(BaseBrowser):
515e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        def open(self, url, new=0, autoraise=1):
516d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum            try:
517d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum                os.startfile(url)
518d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum            except WindowsError:
519d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum                # [Error 22] No application is associated with the specified
520d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum                # file for this operation: '<URL>'
521d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum                return False
522d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum            else:
523d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum                return True
524e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
525e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    _tryorder = []
526e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    _browsers = {}
527d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum
528d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum    # First try to use the default Windows browser
529d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum    register("windows-default", WindowsDefault)
530d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum
531d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum    # Detect some common Windows browsers, fallback to IE
532d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum    iexplore = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"),
533d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum                            "Internet Explorer\\IEXPLORE.EXE")
534477c8d5e70240744d24631b18341ad892c8a8e1cThomas Wouters    for browser in ("firefox", "firebird", "seamonkey", "mozilla",
535d8faa3654c2887eaa146dcdb553a9f9793bd2e5aGuido van Rossum                    "netscape", "opera", iexplore):
536e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        if _iscommand(browser):
53723929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            register(browser, None, BackgroundBrowser(browser))
538c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
539c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake#
540f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond# Platform support for MacOS
541f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond#
542c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake
543c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Draketry:
544c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake    import ic
545c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drakeexcept ImportError:
546c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drake    pass
547c70b4483d2c5042c68198dc7c4945ef3cfc95b27Fred Drakeelse:
548e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    class InternetConfig(BaseBrowser):
549e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        def open(self, url, new=0, autoraise=1):
550e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            ic.launchurl(url)
551e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            return True # Any way to get status?
552e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
553e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    register("internet-config", InternetConfig, update_tryorder=-1)
554e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
555e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlif sys.platform == 'darwin':
556e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # Adapted from patch submitted to SourceForge by Steven J. Burr
557e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    class MacOSX(BaseBrowser):
558e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        """Launcher class for Aqua browsers on Mac OS X
559e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
560e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        Optionally specify a browser name on instantiation.  Note that this
561e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        will not work for Aqua browsers if the user has moved the application
562e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        package after installation.
563e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
564e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        If no browser is specified, the default browser, as specified in the
565e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        Internet System Preferences panel, will be used.
566e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        """
567e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        def __init__(self, name):
568e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            self.name = name
569e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
570e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        def open(self, url, new=0, autoraise=1):
571e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            assert "'" not in url
57223929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            # hack for local urls
57323929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl            if not ':' in url:
57423929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl                url = 'file:'+url
575887c080a800215250a9ebdcc589becc0bf6cac0dTim Peters
576e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            # new must be 0 or 1
577e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            new = int(bool(new))
578e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            if self.name == "default":
579e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                # User called open, open_new or get without a browser parameter
5801cb179e93fb0f698fdb5f215b3864c578d910d9aGeorg Brandl                script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser
581e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            else:
582e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                # User called get and chose a browser
583e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                if self.name == "OmniWeb":
584e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                    toWindow = ""
585e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                else:
586e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                    # Include toWindow parameter of OpenURL command for browsers
587e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                    # that support it.  0 == new window; -1 == existing
588e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                    toWindow = "toWindow %d" % (new - 1)
5891cb179e93fb0f698fdb5f215b3864c578d910d9aGeorg Brandl                cmd = 'OpenURL "%s"' % url.replace('"', '%22')
590e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                script = '''tell application "%s"
591e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                                activate
592e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                                %s %s
593e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                            end tell''' % (self.name, cmd, toWindow)
594e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            # Open pipe to AppleScript through osascript command
595e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            osapipe = os.popen("osascript", "w")
596e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            if osapipe is None:
597e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl                return False
598e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            # Write script to osascript's stdin
599e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            osapipe.write(script)
600e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            rc = osapipe.close()
601e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            return not rc
602e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
603e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # Don't clear _tryorder or _browsers since OS X can use above Unix support
604e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # (but we prefer using the OS X specific stuff)
605e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    register("MacOSX", None, MacOSX('default'), -1)
606e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
607f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond
6083a89b2b13134767475c8e0c14381c1a722d7b836Martin v. Löwis#
6093a89b2b13134767475c8e0c14381c1a722d7b836Martin v. Löwis# Platform support for OS/2
6103a89b2b13134767475c8e0c14381c1a722d7b836Martin v. Löwis#
6113a89b2b13134767475c8e0c14381c1a722d7b836Martin v. Löwis
612e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlif sys.platform[:3] == "os2" and _iscommand("netscape"):
613e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    _tryorder = []
614e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    _browsers = {}
6153a89b2b13134767475c8e0c14381c1a722d7b836Martin v. Löwis    register("os2netscape", None,
61623929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl             GenericBrowser(["start", "netscape", "%s"]), -1)
617e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
6183a89b2b13134767475c8e0c14381c1a722d7b836Martin v. Löwis
619f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond# OK, now that we know what the default preference orders for each
620f7f185116a8274b105edc1be64ffc9c8061c7f43Eric S. Raymond# platform are, allow user to override them with the BROWSER variable.
62154f0222547b1e92cd018ef132307a6f793dc9505Raymond Hettingerif "BROWSER" in os.environ:
622e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    _userchoices = os.environ["BROWSER"].split(os.pathsep)
623e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    _userchoices.reverse()
624e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
625e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # Treat choices in same way as if passed into get() but do register
626e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    # and prepend to _tryorder
627e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    for cmdline in _userchoices:
628e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        if cmdline != '':
629e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl            _synthesize(cmdline, -1)
630e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    cmdline = None # to make del work if _userchoices was empty
631e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    del cmdline
632e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    del _userchoices
633e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
634cdab3bf7eb0810fcda21be065868f3da330779a1Skip Montanaro# what to do if _tryorder is now empty?
635e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
636e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
637e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandldef main():
638e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    import getopt
639e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    usage = """Usage: %s [-n | -t] url
640e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    -n: open new window
641e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    -t: open new tab""" % sys.argv[0]
642e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    try:
643e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        opts, args = getopt.getopt(sys.argv[1:], 'ntd')
644b940e113bf90ff71b0ef57414ea2beea9d2a4bc0Guido van Rossum    except getopt.error as msg:
645be19ed77ddb047e02fe94d142181062af6d99dccGuido van Rossum        print(msg, file=sys.stderr)
646be19ed77ddb047e02fe94d142181062af6d99dccGuido van Rossum        print(usage, file=sys.stderr)
647e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        sys.exit(1)
648e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    new_win = 0
649e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    for o, a in opts:
650e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        if o == '-n': new_win = 1
651e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        elif o == '-t': new_win = 2
652b053cd8f40dd19985b16f50661640dcefb69888fGuido van Rossum    if len(args) != 1:
653be19ed77ddb047e02fe94d142181062af6d99dccGuido van Rossum        print(usage, file=sys.stderr)
654e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl        sys.exit(1)
655e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
656e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    url = args[0]
657e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    open(url, new_win)
658e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl
659be19ed77ddb047e02fe94d142181062af6d99dccGuido van Rossum    print("\a")
66023929f2828078ee8fb115968f732d26bb9e4c1e3Georg Brandl
661e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandlif __name__ == "__main__":
662e8f244305ef4f257f6999b69601f4316b31faa5eGeorg Brandl    main()
663