blob: dd5e019a6947c902d60064295f5028a37a1b7512 [file] [log] [blame]
Georg Brandle8f24432005-10-03 14:16:44 +00001#! /usr/bin/env python
Ka-Ping Yee0a8c29b2001-03-02 02:01:40 +00002"""Interfaces for launching and remotely controlling Web browsers."""
Fred Drakec70b4482000-07-09 16:45:56 +00003
4import os
Guido van Rossumd8faa362007-04-27 19:54:29 +00005import shlex
Fred Drakec70b4482000-07-09 16:45:56 +00006import sys
Georg Brandle8f24432005-10-03 14:16:44 +00007import stat
Georg Brandl23929f22006-01-20 21:03:35 +00008import subprocess
9import time
Fred Drakec70b4482000-07-09 16:45:56 +000010
Georg Brandle8f24432005-10-03 14:16:44 +000011__all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"]
Skip Montanaro40fc1602001-03-01 04:27:19 +000012
Fred Drakec70b4482000-07-09 16:45:56 +000013class Error(Exception):
14 pass
15
Tim Peters658cba62001-02-09 20:06:00 +000016_browsers = {} # Dictionary of available browser controllers
17_tryorder = [] # Preference order of available browsers
Fred Drakec70b4482000-07-09 16:45:56 +000018
Georg Brandle8f24432005-10-03 14:16:44 +000019def register(name, klass, instance=None, update_tryorder=1):
Fred Drakec70b4482000-07-09 16:45:56 +000020 """Register a browser connector and, optionally, connection."""
21 _browsers[name.lower()] = [klass, instance]
Georg Brandle8f24432005-10-03 14:16:44 +000022 if update_tryorder > 0:
23 _tryorder.append(name)
24 elif update_tryorder < 0:
25 _tryorder.insert(0, name)
Fred Drakec70b4482000-07-09 16:45:56 +000026
Eric S. Raymondf7f18512001-01-23 13:16:32 +000027def get(using=None):
28 """Return a browser launcher instance appropriate for the environment."""
Raymond Hettinger10ff7062002-06-02 03:04:52 +000029 if using is not None:
Eric S. Raymondf7f18512001-01-23 13:16:32 +000030 alternatives = [using]
31 else:
32 alternatives = _tryorder
33 for browser in alternatives:
Raymond Hettingerbac788a2004-05-04 09:21:43 +000034 if '%s' in browser:
Georg Brandl23929f22006-01-20 21:03:35 +000035 # User gave us a command line, split it into name and args
Guido van Rossumd8faa362007-04-27 19:54:29 +000036 browser = shlex.split(browser)
37 if browser[-1] == '&':
38 return BackgroundBrowser(browser[:-1])
39 else:
40 return GenericBrowser(browser)
Eric S. Raymondf7f18512001-01-23 13:16:32 +000041 else:
Georg Brandle8f24432005-10-03 14:16:44 +000042 # User gave us a browser name or path.
Fred Drakef4e5bd92001-04-12 22:07:27 +000043 try:
44 command = _browsers[browser.lower()]
45 except KeyError:
46 command = _synthesize(browser)
Georg Brandle8f24432005-10-03 14:16:44 +000047 if command[1] is not None:
Eric S. Raymondf7f18512001-01-23 13:16:32 +000048 return command[1]
Georg Brandle8f24432005-10-03 14:16:44 +000049 elif command[0] is not None:
50 return command[0]()
Eric S. Raymondf7f18512001-01-23 13:16:32 +000051 raise Error("could not locate runnable browser")
Fred Drakec70b4482000-07-09 16:45:56 +000052
53# Please note: the following definition hides a builtin function.
Georg Brandle8f24432005-10-03 14:16:44 +000054# It is recommended one does "import webbrowser" and uses webbrowser.open(url)
55# instead of "from webbrowser import *".
Fred Drakec70b4482000-07-09 16:45:56 +000056
Eric S. Raymondf79cb2d2001-01-23 13:49:44 +000057def open(url, new=0, autoraise=1):
Georg Brandle8f24432005-10-03 14:16:44 +000058 for name in _tryorder:
59 browser = get(name)
60 if browser.open(url, new, autoraise):
61 return True
62 return False
Fred Drakec70b4482000-07-09 16:45:56 +000063
Fred Drake3f8f1642001-07-19 03:46:26 +000064def open_new(url):
Georg Brandle8f24432005-10-03 14:16:44 +000065 return open(url, 1)
66
67def open_new_tab(url):
68 return open(url, 2)
Fred Drakec70b4482000-07-09 16:45:56 +000069
Fred Drakef4e5bd92001-04-12 22:07:27 +000070
Georg Brandle8f24432005-10-03 14:16:44 +000071def _synthesize(browser, update_tryorder=1):
Fred Drakef4e5bd92001-04-12 22:07:27 +000072 """Attempt to synthesize a controller base on existing controllers.
73
74 This is useful to create a controller when a user specifies a path to
75 an entry in the BROWSER environment variable -- we can copy a general
76 controller to operate using a specific installation of the desired
77 browser in this way.
78
79 If we can't create a controller in this way, or if there is no
80 executable for the requested browser, return [None, None].
81
82 """
Georg Brandle8f24432005-10-03 14:16:44 +000083 cmd = browser.split()[0]
84 if not _iscommand(cmd):
Fred Drakef4e5bd92001-04-12 22:07:27 +000085 return [None, None]
Georg Brandle8f24432005-10-03 14:16:44 +000086 name = os.path.basename(cmd)
Fred Drakef4e5bd92001-04-12 22:07:27 +000087 try:
88 command = _browsers[name.lower()]
89 except KeyError:
90 return [None, None]
91 # now attempt to clone to fit the new name:
92 controller = command[1]
93 if controller and name.lower() == controller.basename:
94 import copy
95 controller = copy.copy(controller)
96 controller.name = browser
97 controller.basename = os.path.basename(browser)
Georg Brandle8f24432005-10-03 14:16:44 +000098 register(browser, None, controller, update_tryorder)
Fred Drakef4e5bd92001-04-12 22:07:27 +000099 return [None, controller]
Andrew M. Kuchling118aa532001-08-13 14:37:23 +0000100 return [None, None]
Fred Drakef4e5bd92001-04-12 22:07:27 +0000101
Fred Drake3f8f1642001-07-19 03:46:26 +0000102
Georg Brandle8f24432005-10-03 14:16:44 +0000103if sys.platform[:3] == "win":
104 def _isexecutable(cmd):
105 cmd = cmd.lower()
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000106 if os.path.isfile(cmd) and cmd.endswith((".exe", ".bat")):
Georg Brandle8f24432005-10-03 14:16:44 +0000107 return True
108 for ext in ".exe", ".bat":
109 if os.path.isfile(cmd + ext):
110 return True
111 return False
112else:
113 def _isexecutable(cmd):
114 if os.path.isfile(cmd):
115 mode = os.stat(cmd)[stat.ST_MODE]
116 if mode & stat.S_IXUSR or mode & stat.S_IXGRP or mode & stat.S_IXOTH:
117 return True
118 return False
119
Fred Drake3f8f1642001-07-19 03:46:26 +0000120def _iscommand(cmd):
Georg Brandle8f24432005-10-03 14:16:44 +0000121 """Return True if cmd is executable or can be found on the executable
122 search path."""
123 if _isexecutable(cmd):
124 return True
Fred Drake3f8f1642001-07-19 03:46:26 +0000125 path = os.environ.get("PATH")
126 if not path:
Tim Petersbc0e9102002-04-04 22:55:58 +0000127 return False
Fred Drake3f8f1642001-07-19 03:46:26 +0000128 for d in path.split(os.pathsep):
129 exe = os.path.join(d, cmd)
Georg Brandle8f24432005-10-03 14:16:44 +0000130 if _isexecutable(exe):
Tim Petersbc0e9102002-04-04 22:55:58 +0000131 return True
132 return False
Fred Drake3f8f1642001-07-19 03:46:26 +0000133
134
Georg Brandle8f24432005-10-03 14:16:44 +0000135# General parent classes
136
137class BaseBrowser(object):
Georg Brandl23929f22006-01-20 21:03:35 +0000138 """Parent class for all browsers. Do not use directly."""
Tim Peters887c0802006-01-20 23:40:56 +0000139
Georg Brandl23929f22006-01-20 21:03:35 +0000140 args = ['%s']
Tim Peters887c0802006-01-20 23:40:56 +0000141
Georg Brandle8f24432005-10-03 14:16:44 +0000142 def __init__(self, name=""):
143 self.name = name
Georg Brandlb9801132005-10-08 20:47:38 +0000144 self.basename = name
Tim Peters536cf992005-12-25 23:18:31 +0000145
Neal Norwitz196f7332005-10-04 03:17:49 +0000146 def open(self, url, new=0, autoraise=1):
147 raise NotImplementedError
148
Georg Brandle8f24432005-10-03 14:16:44 +0000149 def open_new(self, url):
150 return self.open(url, 1)
151
152 def open_new_tab(self, url):
153 return self.open(url, 2)
Fred Drake3f8f1642001-07-19 03:46:26 +0000154
155
Georg Brandle8f24432005-10-03 14:16:44 +0000156class GenericBrowser(BaseBrowser):
157 """Class for all browsers started with a command
158 and without remote functionality."""
159
Georg Brandl23929f22006-01-20 21:03:35 +0000160 def __init__(self, name):
161 if isinstance(name, basestring):
162 self.name = name
163 else:
164 # name should be a list with arguments
165 self.name = name[0]
166 self.args = name[1:]
Georg Brandlb9801132005-10-08 20:47:38 +0000167 self.basename = os.path.basename(self.name)
Fred Drake3f8f1642001-07-19 03:46:26 +0000168
169 def open(self, url, new=0, autoraise=1):
Tim Peters887c0802006-01-20 23:40:56 +0000170 cmdline = [self.name] + [arg.replace("%s", url)
Georg Brandl23929f22006-01-20 21:03:35 +0000171 for arg in self.args]
172 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000173 if sys.platform[:3] == 'win':
174 p = subprocess.Popen(cmdline)
175 else:
176 p = subprocess.Popen(cmdline, close_fds=True)
Georg Brandl23929f22006-01-20 21:03:35 +0000177 return not p.wait()
178 except OSError:
179 return False
180
181
182class BackgroundBrowser(GenericBrowser):
183 """Class for all browsers which are to be started in the
184 background."""
185
186 def open(self, url, new=0, autoraise=1):
187 cmdline = [self.name] + [arg.replace("%s", url)
188 for arg in self.args]
Georg Brandl23929f22006-01-20 21:03:35 +0000189 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000190 if sys.platform[:3] == 'win':
191 p = subprocess.Popen(cmdline)
192 else:
193 setsid = getattr(os, 'setsid', None)
194 if not setsid:
195 setsid = getattr(os, 'setpgrp', None)
196 p = subprocess.Popen(cmdline, close_fds=True, preexec_fn=setsid)
Georg Brandl23929f22006-01-20 21:03:35 +0000197 return (p.poll() is None)
198 except OSError:
199 return False
Fred Drake3f8f1642001-07-19 03:46:26 +0000200
201
Georg Brandle8f24432005-10-03 14:16:44 +0000202class UnixBrowser(BaseBrowser):
203 """Parent class for all Unix browsers with remote functionality."""
Fred Drake3f8f1642001-07-19 03:46:26 +0000204
Georg Brandle8f24432005-10-03 14:16:44 +0000205 raise_opts = None
Georg Brandl23929f22006-01-20 21:03:35 +0000206 remote_args = ['%action', '%s']
Georg Brandle8f24432005-10-03 14:16:44 +0000207 remote_action = None
208 remote_action_newwin = None
209 remote_action_newtab = None
Georg Brandl23929f22006-01-20 21:03:35 +0000210 background = False
211 redirect_stdout = True
Georg Brandle8f24432005-10-03 14:16:44 +0000212
Georg Brandl23929f22006-01-20 21:03:35 +0000213 def _invoke(self, args, remote, autoraise):
214 raise_opt = []
215 if remote and self.raise_opts:
216 # use autoraise argument only for remote invocation
217 autoraise = int(bool(autoraise))
218 opt = self.raise_opts[autoraise]
219 if opt: raise_opt = [opt]
220
221 cmdline = [self.name] + raise_opt + args
Tim Peters887c0802006-01-20 23:40:56 +0000222
Georg Brandl23929f22006-01-20 21:03:35 +0000223 if remote or self.background:
Alex Martelli01c77c62006-08-24 02:58:11 +0000224 inout = open(os.devnull, "r+")
Georg Brandl23929f22006-01-20 21:03:35 +0000225 else:
226 # for TTY browsers, we need stdin/out
227 inout = None
228 # if possible, put browser in separate process group, so
229 # keyboard interrupts don't affect browser as well as Python
230 setsid = getattr(os, 'setsid', None)
231 if not setsid:
232 setsid = getattr(os, 'setpgrp', None)
Tim Peters887c0802006-01-20 23:40:56 +0000233
Georg Brandl23929f22006-01-20 21:03:35 +0000234 p = subprocess.Popen(cmdline, close_fds=True, stdin=inout,
235 stdout=(self.redirect_stdout and inout or None),
236 stderr=inout, preexec_fn=setsid)
237 if remote:
238 # wait five secons. If the subprocess is not finished, the
239 # remote invocation has (hopefully) started a new instance.
240 time.sleep(1)
241 rc = p.poll()
242 if rc is None:
243 time.sleep(4)
244 rc = p.poll()
245 if rc is None:
246 return True
247 # if remote call failed, open() will try direct invocation
248 return not rc
249 elif self.background:
250 if p.poll() is None:
251 return True
252 else:
253 return False
254 else:
255 return not p.wait()
Fred Drake3f8f1642001-07-19 03:46:26 +0000256
257 def open(self, url, new=0, autoraise=1):
Georg Brandle8f24432005-10-03 14:16:44 +0000258 if new == 0:
259 action = self.remote_action
260 elif new == 1:
261 action = self.remote_action_newwin
262 elif new == 2:
263 if self.remote_action_newtab is None:
264 action = self.remote_action_newwin
265 else:
266 action = self.remote_action_newtab
Fred Drake3f8f1642001-07-19 03:46:26 +0000267 else:
Georg Brandl23929f22006-01-20 21:03:35 +0000268 raise Error("Bad 'new' parameter to open(); " +
269 "expected 0, 1, or 2, got %s" % new)
Tim Peters887c0802006-01-20 23:40:56 +0000270
Georg Brandl23929f22006-01-20 21:03:35 +0000271 args = [arg.replace("%s", url).replace("%action", action)
272 for arg in self.remote_args]
273 success = self._invoke(args, True, autoraise)
274 if not success:
275 # remote invocation failed, try straight way
276 args = [arg.replace("%s", url) for arg in self.args]
277 return self._invoke(args, False, False)
278 else:
279 return True
Fred Drake3f8f1642001-07-19 03:46:26 +0000280
281
Georg Brandle8f24432005-10-03 14:16:44 +0000282class Mozilla(UnixBrowser):
283 """Launcher class for Mozilla/Netscape browsers."""
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000284
Georg Brandl23929f22006-01-20 21:03:35 +0000285 raise_opts = ["-noraise", "-raise"]
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000286
Georg Brandl23929f22006-01-20 21:03:35 +0000287 remote_args = ['-remote', 'openURL(%s%action)']
288 remote_action = ""
289 remote_action_newwin = ",new-window"
290 remote_action_newtab = ",new-tab"
Tim Peters887c0802006-01-20 23:40:56 +0000291
Georg Brandl23929f22006-01-20 21:03:35 +0000292 background = True
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000293
Georg Brandle8f24432005-10-03 14:16:44 +0000294Netscape = Mozilla
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000295
296
Georg Brandle8f24432005-10-03 14:16:44 +0000297class Galeon(UnixBrowser):
298 """Launcher class for Galeon/Epiphany browsers."""
299
Georg Brandl23929f22006-01-20 21:03:35 +0000300 raise_opts = ["-noraise", ""]
301 remote_args = ['%action', '%s']
302 remote_action = "-n"
303 remote_action_newwin = "-w"
Georg Brandle8f24432005-10-03 14:16:44 +0000304
Georg Brandl23929f22006-01-20 21:03:35 +0000305 background = True
Fred Drake3f8f1642001-07-19 03:46:26 +0000306
307
Georg Brandle8f24432005-10-03 14:16:44 +0000308class Opera(UnixBrowser):
309 "Launcher class for Opera browser."
310
Georg Brandl23929f22006-01-20 21:03:35 +0000311 raise_opts = ["", "-raise"]
Georg Brandle8f24432005-10-03 14:16:44 +0000312
Georg Brandl23929f22006-01-20 21:03:35 +0000313 remote_args = ['-remote', 'openURL(%s%action)']
314 remote_action = ""
315 remote_action_newwin = ",new-window"
316 remote_action_newtab = ",new-page"
317 background = True
Georg Brandle8f24432005-10-03 14:16:44 +0000318
319
320class Elinks(UnixBrowser):
321 "Launcher class for Elinks browsers."
322
Georg Brandl23929f22006-01-20 21:03:35 +0000323 remote_args = ['-remote', 'openURL(%s%action)']
324 remote_action = ""
325 remote_action_newwin = ",new-window"
326 remote_action_newtab = ",new-tab"
327 background = False
Georg Brandle8f24432005-10-03 14:16:44 +0000328
Georg Brandl23929f22006-01-20 21:03:35 +0000329 # elinks doesn't like its stdout to be redirected -
330 # it uses redirected stdout as a signal to do -dump
331 redirect_stdout = False
332
333
334class Konqueror(BaseBrowser):
335 """Controller for the KDE File Manager (kfm, or Konqueror).
336
337 See the output of ``kfmclient --commands``
338 for more information on the Konqueror remote-control interface.
339 """
340
341 def open(self, url, new=0, autoraise=1):
342 # XXX Currently I know no way to prevent KFM from opening a new win.
343 if new == 2:
344 action = "newTab"
345 else:
346 action = "openURL"
Tim Peters887c0802006-01-20 23:40:56 +0000347
Alex Martelli01c77c62006-08-24 02:58:11 +0000348 devnull = open(os.devnull, "r+")
Georg Brandl23929f22006-01-20 21:03:35 +0000349 # if possible, put browser in separate process group, so
350 # keyboard interrupts don't affect browser as well as Python
351 setsid = getattr(os, 'setsid', None)
352 if not setsid:
353 setsid = getattr(os, 'setpgrp', None)
Tim Peters887c0802006-01-20 23:40:56 +0000354
Georg Brandl23929f22006-01-20 21:03:35 +0000355 try:
356 p = subprocess.Popen(["kfmclient", action, url],
357 close_fds=True, stdin=devnull,
358 stdout=devnull, stderr=devnull)
359 except OSError:
360 # fall through to next variant
361 pass
362 else:
363 p.wait()
364 # kfmclient's return code unfortunately has no meaning as it seems
365 return True
366
367 try:
368 p = subprocess.Popen(["konqueror", "--silent", url],
369 close_fds=True, stdin=devnull,
370 stdout=devnull, stderr=devnull,
371 preexec_fn=setsid)
372 except OSError:
373 # fall through to next variant
374 pass
375 else:
376 if p.poll() is None:
377 # Should be running now.
378 return True
Tim Peters887c0802006-01-20 23:40:56 +0000379
Georg Brandl23929f22006-01-20 21:03:35 +0000380 try:
381 p = subprocess.Popen(["kfm", "-d", url],
382 close_fds=True, stdin=devnull,
383 stdout=devnull, stderr=devnull,
384 preexec_fn=setsid)
385 except OSError:
386 return False
387 else:
388 return (p.poll() is None)
Georg Brandle8f24432005-10-03 14:16:44 +0000389
390
391class Grail(BaseBrowser):
Fred Drake3f8f1642001-07-19 03:46:26 +0000392 # There should be a way to maintain a connection to Grail, but the
393 # Grail remote control protocol doesn't really allow that at this
Georg Brandl23929f22006-01-20 21:03:35 +0000394 # point. It probably never will!
Fred Drake3f8f1642001-07-19 03:46:26 +0000395 def _find_grail_rc(self):
396 import glob
397 import pwd
398 import socket
399 import tempfile
400 tempdir = os.path.join(tempfile.gettempdir(),
401 ".grail-unix")
Fred Drake16623fe2001-10-13 16:00:52 +0000402 user = pwd.getpwuid(os.getuid())[0]
Fred Drake3f8f1642001-07-19 03:46:26 +0000403 filename = os.path.join(tempdir, user + "-*")
404 maybes = glob.glob(filename)
405 if not maybes:
406 return None
407 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
408 for fn in maybes:
409 # need to PING each one until we find one that's live
410 try:
411 s.connect(fn)
412 except socket.error:
413 # no good; attempt to clean it out, but don't fail:
414 try:
415 os.unlink(fn)
416 except IOError:
417 pass
418 else:
419 return s
420
421 def _remote(self, action):
422 s = self._find_grail_rc()
423 if not s:
424 return 0
425 s.send(action)
426 s.close()
427 return 1
428
429 def open(self, url, new=0, autoraise=1):
430 if new:
Georg Brandle8f24432005-10-03 14:16:44 +0000431 ok = self._remote("LOADNEW " + url)
Fred Drake3f8f1642001-07-19 03:46:26 +0000432 else:
Georg Brandle8f24432005-10-03 14:16:44 +0000433 ok = self._remote("LOAD " + url)
434 return ok
Fred Drake3f8f1642001-07-19 03:46:26 +0000435
Fred Drakec70b4482000-07-09 16:45:56 +0000436
Tim Peters658cba62001-02-09 20:06:00 +0000437#
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000438# Platform support for Unix
439#
Fred Drakec70b4482000-07-09 16:45:56 +0000440
Georg Brandle8f24432005-10-03 14:16:44 +0000441# These are the right tests because all these Unix browsers require either
442# a console terminal or an X display to run.
Fred Drakec70b4482000-07-09 16:45:56 +0000443
Neal Norwitz196f7332005-10-04 03:17:49 +0000444def register_X_browsers():
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000445
Guido van Rossumd8faa362007-04-27 19:54:29 +0000446 # The default GNOME browser
447 if "GNOME_DESKTOP_SESSION_ID" in os.environ and _iscommand("gnome-open"):
448 register("gnome-open", None, BackgroundBrowser("gnome-open"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000449
Guido van Rossumd8faa362007-04-27 19:54:29 +0000450 # The default KDE browser
451 if "KDE_FULL_SESSION" in os.environ and _iscommand("kfmclient"):
452 register("kfmclient", Konqueror, Konqueror("kfmclient"))
453
454 # The Mozilla/Netscape browsers
Georg Brandl4a5a9182005-11-22 19:18:01 +0000455 for browser in ("mozilla-firefox", "firefox",
456 "mozilla-firebird", "firebird",
Thomas Wouters477c8d52006-05-27 19:21:47 +0000457 "seamonkey", "mozilla", "netscape"):
Georg Brandl4a5a9182005-11-22 19:18:01 +0000458 if _iscommand(browser):
459 register(browser, None, Mozilla(browser))
460
Georg Brandle8f24432005-10-03 14:16:44 +0000461 # Konqueror/kfm, the KDE browser.
Georg Brandlb9801132005-10-08 20:47:38 +0000462 if _iscommand("kfm"):
463 register("kfm", Konqueror, Konqueror("kfm"))
464 elif _iscommand("konqueror"):
465 register("konqueror", Konqueror, Konqueror("konqueror"))
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000466
Georg Brandle8f24432005-10-03 14:16:44 +0000467 # Gnome's Galeon and Epiphany
468 for browser in ("galeon", "epiphany"):
469 if _iscommand(browser):
470 register(browser, None, Galeon(browser))
Gustavo Niemeyer1456fde2002-11-25 17:25:04 +0000471
Georg Brandle8f24432005-10-03 14:16:44 +0000472 # Skipstone, another Gtk/Mozilla based browser
473 if _iscommand("skipstone"):
Georg Brandl23929f22006-01-20 21:03:35 +0000474 register("skipstone", None, BackgroundBrowser("skipstone"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000475
Georg Brandle8f24432005-10-03 14:16:44 +0000476 # Opera, quite popular
477 if _iscommand("opera"):
478 register("opera", None, Opera("opera"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000479
Georg Brandle8f24432005-10-03 14:16:44 +0000480 # Next, Mosaic -- old but still in use.
481 if _iscommand("mosaic"):
Georg Brandl23929f22006-01-20 21:03:35 +0000482 register("mosaic", None, BackgroundBrowser("mosaic"))
Fred Drake3f8f1642001-07-19 03:46:26 +0000483
Georg Brandle8f24432005-10-03 14:16:44 +0000484 # Grail, the Python browser. Does anybody still use it?
485 if _iscommand("grail"):
486 register("grail", Grail, None)
Fred Drake3f8f1642001-07-19 03:46:26 +0000487
Neal Norwitz196f7332005-10-04 03:17:49 +0000488# Prefer X browsers if present
489if os.environ.get("DISPLAY"):
490 register_X_browsers()
491
Georg Brandle8f24432005-10-03 14:16:44 +0000492# Also try console browsers
493if os.environ.get("TERM"):
494 # The Links/elinks browsers <http://artax.karlin.mff.cuni.cz/~mikulas/links/>
495 if _iscommand("links"):
Georg Brandl23929f22006-01-20 21:03:35 +0000496 register("links", None, GenericBrowser("links"))
Georg Brandle8f24432005-10-03 14:16:44 +0000497 if _iscommand("elinks"):
498 register("elinks", None, Elinks("elinks"))
499 # The Lynx browser <http://lynx.isc.org/>, <http://lynx.browser.org/>
500 if _iscommand("lynx"):
Georg Brandl23929f22006-01-20 21:03:35 +0000501 register("lynx", None, GenericBrowser("lynx"))
Georg Brandle8f24432005-10-03 14:16:44 +0000502 # The w3m browser <http://w3m.sourceforge.net/>
503 if _iscommand("w3m"):
Georg Brandl23929f22006-01-20 21:03:35 +0000504 register("w3m", None, GenericBrowser("w3m"))
Fred Drake3f8f1642001-07-19 03:46:26 +0000505
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000506#
507# Platform support for Windows
508#
Fred Drakec70b4482000-07-09 16:45:56 +0000509
510if sys.platform[:3] == "win":
Georg Brandle8f24432005-10-03 14:16:44 +0000511 class WindowsDefault(BaseBrowser):
512 def open(self, url, new=0, autoraise=1):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000513 try:
514 os.startfile(url)
515 except WindowsError:
516 # [Error 22] No application is associated with the specified
517 # file for this operation: '<URL>'
518 return False
519 else:
520 return True
Georg Brandle8f24432005-10-03 14:16:44 +0000521
522 _tryorder = []
523 _browsers = {}
Guido van Rossumd8faa362007-04-27 19:54:29 +0000524
525 # First try to use the default Windows browser
526 register("windows-default", WindowsDefault)
527
528 # Detect some common Windows browsers, fallback to IE
529 iexplore = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"),
530 "Internet Explorer\\IEXPLORE.EXE")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000531 for browser in ("firefox", "firebird", "seamonkey", "mozilla",
Guido van Rossumd8faa362007-04-27 19:54:29 +0000532 "netscape", "opera", iexplore):
Georg Brandle8f24432005-10-03 14:16:44 +0000533 if _iscommand(browser):
Georg Brandl23929f22006-01-20 21:03:35 +0000534 register(browser, None, BackgroundBrowser(browser))
Fred Drakec70b4482000-07-09 16:45:56 +0000535
Fred Drakec70b4482000-07-09 16:45:56 +0000536#
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000537# Platform support for MacOS
538#
Fred Drakec70b4482000-07-09 16:45:56 +0000539
540try:
541 import ic
542except ImportError:
543 pass
544else:
Georg Brandle8f24432005-10-03 14:16:44 +0000545 class InternetConfig(BaseBrowser):
546 def open(self, url, new=0, autoraise=1):
547 ic.launchurl(url)
548 return True # Any way to get status?
549
550 register("internet-config", InternetConfig, update_tryorder=-1)
551
552if sys.platform == 'darwin':
553 # Adapted from patch submitted to SourceForge by Steven J. Burr
554 class MacOSX(BaseBrowser):
555 """Launcher class for Aqua browsers on Mac OS X
556
557 Optionally specify a browser name on instantiation. Note that this
558 will not work for Aqua browsers if the user has moved the application
559 package after installation.
560
561 If no browser is specified, the default browser, as specified in the
562 Internet System Preferences panel, will be used.
563 """
564 def __init__(self, name):
565 self.name = name
566
567 def open(self, url, new=0, autoraise=1):
568 assert "'" not in url
Georg Brandl23929f22006-01-20 21:03:35 +0000569 # hack for local urls
570 if not ':' in url:
571 url = 'file:'+url
Tim Peters887c0802006-01-20 23:40:56 +0000572
Georg Brandle8f24432005-10-03 14:16:44 +0000573 # new must be 0 or 1
574 new = int(bool(new))
575 if self.name == "default":
576 # User called open, open_new or get without a browser parameter
Georg Brandl1cb179e2005-11-09 21:42:48 +0000577 script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser
Georg Brandle8f24432005-10-03 14:16:44 +0000578 else:
579 # User called get and chose a browser
580 if self.name == "OmniWeb":
581 toWindow = ""
582 else:
583 # Include toWindow parameter of OpenURL command for browsers
584 # that support it. 0 == new window; -1 == existing
585 toWindow = "toWindow %d" % (new - 1)
Georg Brandl1cb179e2005-11-09 21:42:48 +0000586 cmd = 'OpenURL "%s"' % url.replace('"', '%22')
Georg Brandle8f24432005-10-03 14:16:44 +0000587 script = '''tell application "%s"
588 activate
589 %s %s
590 end tell''' % (self.name, cmd, toWindow)
591 # Open pipe to AppleScript through osascript command
592 osapipe = os.popen("osascript", "w")
593 if osapipe is None:
594 return False
595 # Write script to osascript's stdin
596 osapipe.write(script)
597 rc = osapipe.close()
598 return not rc
599
600 # Don't clear _tryorder or _browsers since OS X can use above Unix support
601 # (but we prefer using the OS X specific stuff)
602 register("MacOSX", None, MacOSX('default'), -1)
603
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000604
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000605#
606# Platform support for OS/2
607#
608
Georg Brandle8f24432005-10-03 14:16:44 +0000609if sys.platform[:3] == "os2" and _iscommand("netscape"):
610 _tryorder = []
611 _browsers = {}
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000612 register("os2netscape", None,
Georg Brandl23929f22006-01-20 21:03:35 +0000613 GenericBrowser(["start", "netscape", "%s"]), -1)
Georg Brandle8f24432005-10-03 14:16:44 +0000614
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000615
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000616# OK, now that we know what the default preference orders for each
617# platform are, allow user to override them with the BROWSER variable.
Raymond Hettinger54f02222002-06-01 14:18:47 +0000618if "BROWSER" in os.environ:
Georg Brandle8f24432005-10-03 14:16:44 +0000619 _userchoices = os.environ["BROWSER"].split(os.pathsep)
620 _userchoices.reverse()
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000621
Georg Brandle8f24432005-10-03 14:16:44 +0000622 # Treat choices in same way as if passed into get() but do register
623 # and prepend to _tryorder
624 for cmdline in _userchoices:
625 if cmdline != '':
626 _synthesize(cmdline, -1)
627 cmdline = None # to make del work if _userchoices was empty
628 del cmdline
629 del _userchoices
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000630
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000631# what to do if _tryorder is now empty?
Georg Brandle8f24432005-10-03 14:16:44 +0000632
633
634def main():
635 import getopt
636 usage = """Usage: %s [-n | -t] url
637 -n: open new window
638 -t: open new tab""" % sys.argv[0]
639 try:
640 opts, args = getopt.getopt(sys.argv[1:], 'ntd')
Guido van Rossumb940e112007-01-10 16:19:56 +0000641 except getopt.error as msg:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000642 print(msg, file=sys.stderr)
643 print(usage, file=sys.stderr)
Georg Brandle8f24432005-10-03 14:16:44 +0000644 sys.exit(1)
645 new_win = 0
646 for o, a in opts:
647 if o == '-n': new_win = 1
648 elif o == '-t': new_win = 2
Guido van Rossumb053cd82006-08-24 03:53:23 +0000649 if len(args) != 1:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000650 print(usage, file=sys.stderr)
Georg Brandle8f24432005-10-03 14:16:44 +0000651 sys.exit(1)
652
653 url = args[0]
654 open(url, new_win)
655
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000656 print("\a")
Georg Brandl23929f22006-01-20 21:03:35 +0000657
Georg Brandle8f24432005-10-03 14:16:44 +0000658if __name__ == "__main__":
659 main()