blob: a028df0cf6f6998340a50b38596e64b323a8764e [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."""
Guido van Rossum992d4a32007-07-11 13:09:30 +00003# Maintained by Georg Brandl.
Fred Drakec70b4482000-07-09 16:45:56 +00004
5import os
Guido van Rossumd8faa362007-04-27 19:54:29 +00006import shlex
Fred Drakec70b4482000-07-09 16:45:56 +00007import sys
Georg Brandle8f24432005-10-03 14:16:44 +00008import stat
Georg Brandl23929f22006-01-20 21:03:35 +00009import subprocess
10import time
Fred Drakec70b4482000-07-09 16:45:56 +000011
Georg Brandle8f24432005-10-03 14:16:44 +000012__all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"]
Skip Montanaro40fc1602001-03-01 04:27:19 +000013
Fred Drakec70b4482000-07-09 16:45:56 +000014class Error(Exception):
15 pass
16
Tim Peters658cba62001-02-09 20:06:00 +000017_browsers = {} # Dictionary of available browser controllers
18_tryorder = [] # Preference order of available browsers
Fred Drakec70b4482000-07-09 16:45:56 +000019
Georg Brandle8f24432005-10-03 14:16:44 +000020def register(name, klass, instance=None, update_tryorder=1):
Fred Drakec70b4482000-07-09 16:45:56 +000021 """Register a browser connector and, optionally, connection."""
22 _browsers[name.lower()] = [klass, instance]
Georg Brandle8f24432005-10-03 14:16:44 +000023 if update_tryorder > 0:
24 _tryorder.append(name)
25 elif update_tryorder < 0:
26 _tryorder.insert(0, name)
Fred Drakec70b4482000-07-09 16:45:56 +000027
Eric S. Raymondf7f18512001-01-23 13:16:32 +000028def get(using=None):
29 """Return a browser launcher instance appropriate for the environment."""
Raymond Hettinger10ff7062002-06-02 03:04:52 +000030 if using is not None:
Eric S. Raymondf7f18512001-01-23 13:16:32 +000031 alternatives = [using]
32 else:
33 alternatives = _tryorder
34 for browser in alternatives:
Raymond Hettingerbac788a2004-05-04 09:21:43 +000035 if '%s' in browser:
Georg Brandl23929f22006-01-20 21:03:35 +000036 # User gave us a command line, split it into name and args
Guido van Rossumd8faa362007-04-27 19:54:29 +000037 browser = shlex.split(browser)
38 if browser[-1] == '&':
39 return BackgroundBrowser(browser[:-1])
40 else:
41 return GenericBrowser(browser)
Eric S. Raymondf7f18512001-01-23 13:16:32 +000042 else:
Georg Brandle8f24432005-10-03 14:16:44 +000043 # User gave us a browser name or path.
Fred Drakef4e5bd92001-04-12 22:07:27 +000044 try:
45 command = _browsers[browser.lower()]
46 except KeyError:
47 command = _synthesize(browser)
Georg Brandle8f24432005-10-03 14:16:44 +000048 if command[1] is not None:
Eric S. Raymondf7f18512001-01-23 13:16:32 +000049 return command[1]
Georg Brandle8f24432005-10-03 14:16:44 +000050 elif command[0] is not None:
51 return command[0]()
Eric S. Raymondf7f18512001-01-23 13:16:32 +000052 raise Error("could not locate runnable browser")
Fred Drakec70b4482000-07-09 16:45:56 +000053
54# Please note: the following definition hides a builtin function.
Georg Brandle8f24432005-10-03 14:16:44 +000055# It is recommended one does "import webbrowser" and uses webbrowser.open(url)
56# instead of "from webbrowser import *".
Fred Drakec70b4482000-07-09 16:45:56 +000057
Eric S. Raymondf79cb2d2001-01-23 13:49:44 +000058def open(url, new=0, autoraise=1):
Georg Brandle8f24432005-10-03 14:16:44 +000059 for name in _tryorder:
60 browser = get(name)
61 if browser.open(url, new, autoraise):
62 return True
63 return False
Fred Drakec70b4482000-07-09 16:45:56 +000064
Fred Drake3f8f1642001-07-19 03:46:26 +000065def open_new(url):
Georg Brandle8f24432005-10-03 14:16:44 +000066 return open(url, 1)
67
68def open_new_tab(url):
69 return open(url, 2)
Fred Drakec70b4482000-07-09 16:45:56 +000070
Fred Drakef4e5bd92001-04-12 22:07:27 +000071
Georg Brandle8f24432005-10-03 14:16:44 +000072def _synthesize(browser, update_tryorder=1):
Fred Drakef4e5bd92001-04-12 22:07:27 +000073 """Attempt to synthesize a controller base on existing controllers.
74
75 This is useful to create a controller when a user specifies a path to
76 an entry in the BROWSER environment variable -- we can copy a general
77 controller to operate using a specific installation of the desired
78 browser in this way.
79
80 If we can't create a controller in this way, or if there is no
81 executable for the requested browser, return [None, None].
82
83 """
Georg Brandle8f24432005-10-03 14:16:44 +000084 cmd = browser.split()[0]
85 if not _iscommand(cmd):
Fred Drakef4e5bd92001-04-12 22:07:27 +000086 return [None, None]
Georg Brandle8f24432005-10-03 14:16:44 +000087 name = os.path.basename(cmd)
Fred Drakef4e5bd92001-04-12 22:07:27 +000088 try:
89 command = _browsers[name.lower()]
90 except KeyError:
91 return [None, None]
92 # now attempt to clone to fit the new name:
93 controller = command[1]
94 if controller and name.lower() == controller.basename:
95 import copy
96 controller = copy.copy(controller)
97 controller.name = browser
98 controller.basename = os.path.basename(browser)
Georg Brandle8f24432005-10-03 14:16:44 +000099 register(browser, None, controller, update_tryorder)
Fred Drakef4e5bd92001-04-12 22:07:27 +0000100 return [None, controller]
Andrew M. Kuchling118aa532001-08-13 14:37:23 +0000101 return [None, None]
Fred Drakef4e5bd92001-04-12 22:07:27 +0000102
Fred Drake3f8f1642001-07-19 03:46:26 +0000103
Georg Brandle8f24432005-10-03 14:16:44 +0000104if sys.platform[:3] == "win":
105 def _isexecutable(cmd):
106 cmd = cmd.lower()
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000107 if os.path.isfile(cmd) and cmd.endswith((".exe", ".bat")):
Georg Brandle8f24432005-10-03 14:16:44 +0000108 return True
109 for ext in ".exe", ".bat":
110 if os.path.isfile(cmd + ext):
111 return True
112 return False
113else:
114 def _isexecutable(cmd):
115 if os.path.isfile(cmd):
116 mode = os.stat(cmd)[stat.ST_MODE]
117 if mode & stat.S_IXUSR or mode & stat.S_IXGRP or mode & stat.S_IXOTH:
118 return True
119 return False
120
Fred Drake3f8f1642001-07-19 03:46:26 +0000121def _iscommand(cmd):
Georg Brandle8f24432005-10-03 14:16:44 +0000122 """Return True if cmd is executable or can be found on the executable
123 search path."""
124 if _isexecutable(cmd):
125 return True
Fred Drake3f8f1642001-07-19 03:46:26 +0000126 path = os.environ.get("PATH")
127 if not path:
Tim Petersbc0e9102002-04-04 22:55:58 +0000128 return False
Fred Drake3f8f1642001-07-19 03:46:26 +0000129 for d in path.split(os.pathsep):
130 exe = os.path.join(d, cmd)
Georg Brandle8f24432005-10-03 14:16:44 +0000131 if _isexecutable(exe):
Tim Petersbc0e9102002-04-04 22:55:58 +0000132 return True
133 return False
Fred Drake3f8f1642001-07-19 03:46:26 +0000134
135
Georg Brandle8f24432005-10-03 14:16:44 +0000136# General parent classes
137
138class BaseBrowser(object):
Georg Brandl23929f22006-01-20 21:03:35 +0000139 """Parent class for all browsers. Do not use directly."""
Tim Peters887c0802006-01-20 23:40:56 +0000140
Georg Brandl23929f22006-01-20 21:03:35 +0000141 args = ['%s']
Tim Peters887c0802006-01-20 23:40:56 +0000142
Georg Brandle8f24432005-10-03 14:16:44 +0000143 def __init__(self, name=""):
144 self.name = name
Georg Brandlb9801132005-10-08 20:47:38 +0000145 self.basename = name
Tim Peters536cf992005-12-25 23:18:31 +0000146
Neal Norwitz196f7332005-10-04 03:17:49 +0000147 def open(self, url, new=0, autoraise=1):
148 raise NotImplementedError
149
Georg Brandle8f24432005-10-03 14:16:44 +0000150 def open_new(self, url):
151 return self.open(url, 1)
152
153 def open_new_tab(self, url):
154 return self.open(url, 2)
Fred Drake3f8f1642001-07-19 03:46:26 +0000155
156
Georg Brandle8f24432005-10-03 14:16:44 +0000157class GenericBrowser(BaseBrowser):
158 """Class for all browsers started with a command
159 and without remote functionality."""
160
Georg Brandl23929f22006-01-20 21:03:35 +0000161 def __init__(self, name):
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000162 if isinstance(name, str):
Georg Brandl23929f22006-01-20 21:03:35 +0000163 self.name = name
Guido van Rossum992d4a32007-07-11 13:09:30 +0000164 self.args = ["%s"]
Georg Brandl23929f22006-01-20 21:03:35 +0000165 else:
166 # name should be a list with arguments
167 self.name = name[0]
168 self.args = name[1:]
Georg Brandlb9801132005-10-08 20:47:38 +0000169 self.basename = os.path.basename(self.name)
Fred Drake3f8f1642001-07-19 03:46:26 +0000170
171 def open(self, url, new=0, autoraise=1):
Tim Peters887c0802006-01-20 23:40:56 +0000172 cmdline = [self.name] + [arg.replace("%s", url)
Georg Brandl23929f22006-01-20 21:03:35 +0000173 for arg in self.args]
174 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000175 if sys.platform[:3] == 'win':
176 p = subprocess.Popen(cmdline)
177 else:
178 p = subprocess.Popen(cmdline, close_fds=True)
Georg Brandl23929f22006-01-20 21:03:35 +0000179 return not p.wait()
180 except OSError:
181 return False
182
183
184class BackgroundBrowser(GenericBrowser):
185 """Class for all browsers which are to be started in the
186 background."""
187
188 def open(self, url, new=0, autoraise=1):
189 cmdline = [self.name] + [arg.replace("%s", url)
190 for arg in self.args]
Georg Brandl23929f22006-01-20 21:03:35 +0000191 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000192 if sys.platform[:3] == 'win':
193 p = subprocess.Popen(cmdline)
194 else:
195 setsid = getattr(os, 'setsid', None)
196 if not setsid:
197 setsid = getattr(os, 'setpgrp', None)
198 p = subprocess.Popen(cmdline, close_fds=True, preexec_fn=setsid)
Georg Brandl23929f22006-01-20 21:03:35 +0000199 return (p.poll() is None)
200 except OSError:
201 return False
Fred Drake3f8f1642001-07-19 03:46:26 +0000202
203
Georg Brandle8f24432005-10-03 14:16:44 +0000204class UnixBrowser(BaseBrowser):
205 """Parent class for all Unix browsers with remote functionality."""
Fred Drake3f8f1642001-07-19 03:46:26 +0000206
Georg Brandle8f24432005-10-03 14:16:44 +0000207 raise_opts = None
Georg Brandl23929f22006-01-20 21:03:35 +0000208 remote_args = ['%action', '%s']
Georg Brandle8f24432005-10-03 14:16:44 +0000209 remote_action = None
210 remote_action_newwin = None
211 remote_action_newtab = None
Georg Brandl23929f22006-01-20 21:03:35 +0000212 background = False
213 redirect_stdout = True
Georg Brandle8f24432005-10-03 14:16:44 +0000214
Georg Brandl23929f22006-01-20 21:03:35 +0000215 def _invoke(self, args, remote, autoraise):
216 raise_opt = []
217 if remote and self.raise_opts:
218 # use autoraise argument only for remote invocation
219 autoraise = int(bool(autoraise))
220 opt = self.raise_opts[autoraise]
221 if opt: raise_opt = [opt]
222
223 cmdline = [self.name] + raise_opt + args
Tim Peters887c0802006-01-20 23:40:56 +0000224
Georg Brandl23929f22006-01-20 21:03:35 +0000225 if remote or self.background:
Alex Martelli01c77c62006-08-24 02:58:11 +0000226 inout = open(os.devnull, "r+")
Georg Brandl23929f22006-01-20 21:03:35 +0000227 else:
228 # for TTY browsers, we need stdin/out
229 inout = None
230 # if possible, put browser in separate process group, so
231 # keyboard interrupts don't affect browser as well as Python
232 setsid = getattr(os, 'setsid', None)
233 if not setsid:
234 setsid = getattr(os, 'setpgrp', None)
Tim Peters887c0802006-01-20 23:40:56 +0000235
Georg Brandl23929f22006-01-20 21:03:35 +0000236 p = subprocess.Popen(cmdline, close_fds=True, stdin=inout,
237 stdout=(self.redirect_stdout and inout or None),
238 stderr=inout, preexec_fn=setsid)
239 if remote:
240 # wait five secons. If the subprocess is not finished, the
241 # remote invocation has (hopefully) started a new instance.
242 time.sleep(1)
243 rc = p.poll()
244 if rc is None:
245 time.sleep(4)
246 rc = p.poll()
247 if rc is None:
248 return True
249 # if remote call failed, open() will try direct invocation
250 return not rc
251 elif self.background:
252 if p.poll() is None:
253 return True
254 else:
255 return False
256 else:
257 return not p.wait()
Fred Drake3f8f1642001-07-19 03:46:26 +0000258
259 def open(self, url, new=0, autoraise=1):
Georg Brandle8f24432005-10-03 14:16:44 +0000260 if new == 0:
261 action = self.remote_action
262 elif new == 1:
263 action = self.remote_action_newwin
264 elif new == 2:
265 if self.remote_action_newtab is None:
266 action = self.remote_action_newwin
267 else:
268 action = self.remote_action_newtab
Fred Drake3f8f1642001-07-19 03:46:26 +0000269 else:
Georg Brandl23929f22006-01-20 21:03:35 +0000270 raise Error("Bad 'new' parameter to open(); " +
271 "expected 0, 1, or 2, got %s" % new)
Tim Peters887c0802006-01-20 23:40:56 +0000272
Georg Brandl23929f22006-01-20 21:03:35 +0000273 args = [arg.replace("%s", url).replace("%action", action)
274 for arg in self.remote_args]
275 success = self._invoke(args, True, autoraise)
276 if not success:
277 # remote invocation failed, try straight way
278 args = [arg.replace("%s", url) for arg in self.args]
279 return self._invoke(args, False, False)
280 else:
281 return True
Fred Drake3f8f1642001-07-19 03:46:26 +0000282
283
Georg Brandle8f24432005-10-03 14:16:44 +0000284class Mozilla(UnixBrowser):
285 """Launcher class for Mozilla/Netscape browsers."""
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000286
Georg Brandl23929f22006-01-20 21:03:35 +0000287 raise_opts = ["-noraise", "-raise"]
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000288
Georg Brandl23929f22006-01-20 21:03:35 +0000289 remote_args = ['-remote', 'openURL(%s%action)']
290 remote_action = ""
291 remote_action_newwin = ",new-window"
292 remote_action_newtab = ",new-tab"
Tim Peters887c0802006-01-20 23:40:56 +0000293
Georg Brandl23929f22006-01-20 21:03:35 +0000294 background = True
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000295
Georg Brandle8f24432005-10-03 14:16:44 +0000296Netscape = Mozilla
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000297
298
Georg Brandle8f24432005-10-03 14:16:44 +0000299class Galeon(UnixBrowser):
300 """Launcher class for Galeon/Epiphany browsers."""
301
Georg Brandl23929f22006-01-20 21:03:35 +0000302 raise_opts = ["-noraise", ""]
303 remote_args = ['%action', '%s']
304 remote_action = "-n"
305 remote_action_newwin = "-w"
Georg Brandle8f24432005-10-03 14:16:44 +0000306
Georg Brandl23929f22006-01-20 21:03:35 +0000307 background = True
Fred Drake3f8f1642001-07-19 03:46:26 +0000308
309
Georg Brandle8f24432005-10-03 14:16:44 +0000310class Opera(UnixBrowser):
311 "Launcher class for Opera browser."
312
Georg Brandl23929f22006-01-20 21:03:35 +0000313 raise_opts = ["", "-raise"]
Georg Brandle8f24432005-10-03 14:16:44 +0000314
Georg Brandl23929f22006-01-20 21:03:35 +0000315 remote_args = ['-remote', 'openURL(%s%action)']
316 remote_action = ""
317 remote_action_newwin = ",new-window"
318 remote_action_newtab = ",new-page"
319 background = True
Georg Brandle8f24432005-10-03 14:16:44 +0000320
321
322class Elinks(UnixBrowser):
323 "Launcher class for Elinks browsers."
324
Georg Brandl23929f22006-01-20 21:03:35 +0000325 remote_args = ['-remote', 'openURL(%s%action)']
326 remote_action = ""
327 remote_action_newwin = ",new-window"
328 remote_action_newtab = ",new-tab"
329 background = False
Georg Brandle8f24432005-10-03 14:16:44 +0000330
Georg Brandl23929f22006-01-20 21:03:35 +0000331 # elinks doesn't like its stdout to be redirected -
332 # it uses redirected stdout as a signal to do -dump
333 redirect_stdout = False
334
335
336class Konqueror(BaseBrowser):
337 """Controller for the KDE File Manager (kfm, or Konqueror).
338
339 See the output of ``kfmclient --commands``
340 for more information on the Konqueror remote-control interface.
341 """
342
343 def open(self, url, new=0, autoraise=1):
344 # XXX Currently I know no way to prevent KFM from opening a new win.
345 if new == 2:
346 action = "newTab"
347 else:
348 action = "openURL"
Tim Peters887c0802006-01-20 23:40:56 +0000349
Alex Martelli01c77c62006-08-24 02:58:11 +0000350 devnull = open(os.devnull, "r+")
Georg Brandl23929f22006-01-20 21:03:35 +0000351 # if possible, put browser in separate process group, so
352 # keyboard interrupts don't affect browser as well as Python
353 setsid = getattr(os, 'setsid', None)
354 if not setsid:
355 setsid = getattr(os, 'setpgrp', None)
Tim Peters887c0802006-01-20 23:40:56 +0000356
Georg Brandl23929f22006-01-20 21:03:35 +0000357 try:
358 p = subprocess.Popen(["kfmclient", action, url],
359 close_fds=True, stdin=devnull,
360 stdout=devnull, stderr=devnull)
361 except OSError:
362 # fall through to next variant
363 pass
364 else:
365 p.wait()
366 # kfmclient's return code unfortunately has no meaning as it seems
367 return True
368
369 try:
370 p = subprocess.Popen(["konqueror", "--silent", url],
371 close_fds=True, stdin=devnull,
372 stdout=devnull, stderr=devnull,
373 preexec_fn=setsid)
374 except OSError:
375 # fall through to next variant
376 pass
377 else:
378 if p.poll() is None:
379 # Should be running now.
380 return True
Tim Peters887c0802006-01-20 23:40:56 +0000381
Georg Brandl23929f22006-01-20 21:03:35 +0000382 try:
383 p = subprocess.Popen(["kfm", "-d", url],
384 close_fds=True, stdin=devnull,
385 stdout=devnull, stderr=devnull,
386 preexec_fn=setsid)
387 except OSError:
388 return False
389 else:
390 return (p.poll() is None)
Georg Brandle8f24432005-10-03 14:16:44 +0000391
392
393class Grail(BaseBrowser):
Fred Drake3f8f1642001-07-19 03:46:26 +0000394 # There should be a way to maintain a connection to Grail, but the
395 # Grail remote control protocol doesn't really allow that at this
Georg Brandl23929f22006-01-20 21:03:35 +0000396 # point. It probably never will!
Fred Drake3f8f1642001-07-19 03:46:26 +0000397 def _find_grail_rc(self):
398 import glob
399 import pwd
400 import socket
401 import tempfile
402 tempdir = os.path.join(tempfile.gettempdir(),
403 ".grail-unix")
Fred Drake16623fe2001-10-13 16:00:52 +0000404 user = pwd.getpwuid(os.getuid())[0]
Fred Drake3f8f1642001-07-19 03:46:26 +0000405 filename = os.path.join(tempdir, user + "-*")
406 maybes = glob.glob(filename)
407 if not maybes:
408 return None
409 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
410 for fn in maybes:
411 # need to PING each one until we find one that's live
412 try:
413 s.connect(fn)
414 except socket.error:
415 # no good; attempt to clean it out, but don't fail:
416 try:
417 os.unlink(fn)
418 except IOError:
419 pass
420 else:
421 return s
422
423 def _remote(self, action):
424 s = self._find_grail_rc()
425 if not s:
426 return 0
427 s.send(action)
428 s.close()
429 return 1
430
431 def open(self, url, new=0, autoraise=1):
432 if new:
Georg Brandle8f24432005-10-03 14:16:44 +0000433 ok = self._remote("LOADNEW " + url)
Fred Drake3f8f1642001-07-19 03:46:26 +0000434 else:
Georg Brandle8f24432005-10-03 14:16:44 +0000435 ok = self._remote("LOAD " + url)
436 return ok
Fred Drake3f8f1642001-07-19 03:46:26 +0000437
Fred Drakec70b4482000-07-09 16:45:56 +0000438
Tim Peters658cba62001-02-09 20:06:00 +0000439#
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000440# Platform support for Unix
441#
Fred Drakec70b4482000-07-09 16:45:56 +0000442
Georg Brandle8f24432005-10-03 14:16:44 +0000443# These are the right tests because all these Unix browsers require either
444# a console terminal or an X display to run.
Fred Drakec70b4482000-07-09 16:45:56 +0000445
Neal Norwitz196f7332005-10-04 03:17:49 +0000446def register_X_browsers():
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000447
Guido van Rossumd8faa362007-04-27 19:54:29 +0000448 # The default GNOME browser
449 if "GNOME_DESKTOP_SESSION_ID" in os.environ and _iscommand("gnome-open"):
450 register("gnome-open", None, BackgroundBrowser("gnome-open"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000451
Guido van Rossumd8faa362007-04-27 19:54:29 +0000452 # The default KDE browser
453 if "KDE_FULL_SESSION" in os.environ and _iscommand("kfmclient"):
454 register("kfmclient", Konqueror, Konqueror("kfmclient"))
455
456 # The Mozilla/Netscape browsers
Georg Brandl4a5a9182005-11-22 19:18:01 +0000457 for browser in ("mozilla-firefox", "firefox",
458 "mozilla-firebird", "firebird",
Thomas Wouters477c8d52006-05-27 19:21:47 +0000459 "seamonkey", "mozilla", "netscape"):
Georg Brandl4a5a9182005-11-22 19:18:01 +0000460 if _iscommand(browser):
461 register(browser, None, Mozilla(browser))
462
Georg Brandle8f24432005-10-03 14:16:44 +0000463 # Konqueror/kfm, the KDE browser.
Georg Brandlb9801132005-10-08 20:47:38 +0000464 if _iscommand("kfm"):
465 register("kfm", Konqueror, Konqueror("kfm"))
466 elif _iscommand("konqueror"):
467 register("konqueror", Konqueror, Konqueror("konqueror"))
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000468
Georg Brandle8f24432005-10-03 14:16:44 +0000469 # Gnome's Galeon and Epiphany
470 for browser in ("galeon", "epiphany"):
471 if _iscommand(browser):
472 register(browser, None, Galeon(browser))
Gustavo Niemeyer1456fde2002-11-25 17:25:04 +0000473
Georg Brandle8f24432005-10-03 14:16:44 +0000474 # Skipstone, another Gtk/Mozilla based browser
475 if _iscommand("skipstone"):
Georg Brandl23929f22006-01-20 21:03:35 +0000476 register("skipstone", None, BackgroundBrowser("skipstone"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000477
Georg Brandle8f24432005-10-03 14:16:44 +0000478 # Opera, quite popular
479 if _iscommand("opera"):
480 register("opera", None, Opera("opera"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000481
Georg Brandle8f24432005-10-03 14:16:44 +0000482 # Next, Mosaic -- old but still in use.
483 if _iscommand("mosaic"):
Georg Brandl23929f22006-01-20 21:03:35 +0000484 register("mosaic", None, BackgroundBrowser("mosaic"))
Fred Drake3f8f1642001-07-19 03:46:26 +0000485
Georg Brandle8f24432005-10-03 14:16:44 +0000486 # Grail, the Python browser. Does anybody still use it?
487 if _iscommand("grail"):
488 register("grail", Grail, None)
Fred Drake3f8f1642001-07-19 03:46:26 +0000489
Neal Norwitz196f7332005-10-04 03:17:49 +0000490# Prefer X browsers if present
491if os.environ.get("DISPLAY"):
492 register_X_browsers()
493
Georg Brandle8f24432005-10-03 14:16:44 +0000494# Also try console browsers
495if os.environ.get("TERM"):
496 # The Links/elinks browsers <http://artax.karlin.mff.cuni.cz/~mikulas/links/>
497 if _iscommand("links"):
Georg Brandl23929f22006-01-20 21:03:35 +0000498 register("links", None, GenericBrowser("links"))
Georg Brandle8f24432005-10-03 14:16:44 +0000499 if _iscommand("elinks"):
500 register("elinks", None, Elinks("elinks"))
501 # The Lynx browser <http://lynx.isc.org/>, <http://lynx.browser.org/>
502 if _iscommand("lynx"):
Georg Brandl23929f22006-01-20 21:03:35 +0000503 register("lynx", None, GenericBrowser("lynx"))
Georg Brandle8f24432005-10-03 14:16:44 +0000504 # The w3m browser <http://w3m.sourceforge.net/>
505 if _iscommand("w3m"):
Georg Brandl23929f22006-01-20 21:03:35 +0000506 register("w3m", None, GenericBrowser("w3m"))
Fred Drake3f8f1642001-07-19 03:46:26 +0000507
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000508#
509# Platform support for Windows
510#
Fred Drakec70b4482000-07-09 16:45:56 +0000511
512if sys.platform[:3] == "win":
Georg Brandle8f24432005-10-03 14:16:44 +0000513 class WindowsDefault(BaseBrowser):
514 def open(self, url, new=0, autoraise=1):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000515 try:
516 os.startfile(url)
517 except WindowsError:
518 # [Error 22] No application is associated with the specified
519 # file for this operation: '<URL>'
520 return False
521 else:
522 return True
Georg Brandle8f24432005-10-03 14:16:44 +0000523
524 _tryorder = []
525 _browsers = {}
Guido van Rossumd8faa362007-04-27 19:54:29 +0000526
527 # First try to use the default Windows browser
528 register("windows-default", WindowsDefault)
529
530 # Detect some common Windows browsers, fallback to IE
531 iexplore = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"),
532 "Internet Explorer\\IEXPLORE.EXE")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000533 for browser in ("firefox", "firebird", "seamonkey", "mozilla",
Guido van Rossumd8faa362007-04-27 19:54:29 +0000534 "netscape", "opera", iexplore):
Georg Brandle8f24432005-10-03 14:16:44 +0000535 if _iscommand(browser):
Georg Brandl23929f22006-01-20 21:03:35 +0000536 register(browser, None, BackgroundBrowser(browser))
Fred Drakec70b4482000-07-09 16:45:56 +0000537
Fred Drakec70b4482000-07-09 16:45:56 +0000538#
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000539# Platform support for MacOS
540#
Fred Drakec70b4482000-07-09 16:45:56 +0000541
542try:
543 import ic
544except ImportError:
545 pass
546else:
Georg Brandle8f24432005-10-03 14:16:44 +0000547 class InternetConfig(BaseBrowser):
548 def open(self, url, new=0, autoraise=1):
549 ic.launchurl(url)
550 return True # Any way to get status?
551
552 register("internet-config", InternetConfig, update_tryorder=-1)
553
554if sys.platform == 'darwin':
555 # Adapted from patch submitted to SourceForge by Steven J. Burr
556 class MacOSX(BaseBrowser):
557 """Launcher class for Aqua browsers on Mac OS X
558
559 Optionally specify a browser name on instantiation. Note that this
560 will not work for Aqua browsers if the user has moved the application
561 package after installation.
562
563 If no browser is specified, the default browser, as specified in the
564 Internet System Preferences panel, will be used.
565 """
566 def __init__(self, name):
567 self.name = name
568
569 def open(self, url, new=0, autoraise=1):
570 assert "'" not in url
Georg Brandl23929f22006-01-20 21:03:35 +0000571 # hack for local urls
572 if not ':' in url:
573 url = 'file:'+url
Tim Peters887c0802006-01-20 23:40:56 +0000574
Georg Brandle8f24432005-10-03 14:16:44 +0000575 # new must be 0 or 1
576 new = int(bool(new))
577 if self.name == "default":
578 # User called open, open_new or get without a browser parameter
Georg Brandl1cb179e2005-11-09 21:42:48 +0000579 script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser
Georg Brandle8f24432005-10-03 14:16:44 +0000580 else:
581 # User called get and chose a browser
582 if self.name == "OmniWeb":
583 toWindow = ""
584 else:
585 # Include toWindow parameter of OpenURL command for browsers
586 # that support it. 0 == new window; -1 == existing
587 toWindow = "toWindow %d" % (new - 1)
Georg Brandl1cb179e2005-11-09 21:42:48 +0000588 cmd = 'OpenURL "%s"' % url.replace('"', '%22')
Georg Brandle8f24432005-10-03 14:16:44 +0000589 script = '''tell application "%s"
590 activate
591 %s %s
592 end tell''' % (self.name, cmd, toWindow)
593 # Open pipe to AppleScript through osascript command
594 osapipe = os.popen("osascript", "w")
595 if osapipe is None:
596 return False
597 # Write script to osascript's stdin
598 osapipe.write(script)
599 rc = osapipe.close()
600 return not rc
601
602 # Don't clear _tryorder or _browsers since OS X can use above Unix support
603 # (but we prefer using the OS X specific stuff)
604 register("MacOSX", None, MacOSX('default'), -1)
605
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000606
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000607#
608# Platform support for OS/2
609#
610
Georg Brandle8f24432005-10-03 14:16:44 +0000611if sys.platform[:3] == "os2" and _iscommand("netscape"):
612 _tryorder = []
613 _browsers = {}
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000614 register("os2netscape", None,
Georg Brandl23929f22006-01-20 21:03:35 +0000615 GenericBrowser(["start", "netscape", "%s"]), -1)
Georg Brandle8f24432005-10-03 14:16:44 +0000616
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000617
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000618# OK, now that we know what the default preference orders for each
619# platform are, allow user to override them with the BROWSER variable.
Raymond Hettinger54f02222002-06-01 14:18:47 +0000620if "BROWSER" in os.environ:
Georg Brandle8f24432005-10-03 14:16:44 +0000621 _userchoices = os.environ["BROWSER"].split(os.pathsep)
622 _userchoices.reverse()
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000623
Georg Brandle8f24432005-10-03 14:16:44 +0000624 # Treat choices in same way as if passed into get() but do register
625 # and prepend to _tryorder
626 for cmdline in _userchoices:
627 if cmdline != '':
628 _synthesize(cmdline, -1)
629 cmdline = None # to make del work if _userchoices was empty
630 del cmdline
631 del _userchoices
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000632
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000633# what to do if _tryorder is now empty?
Georg Brandle8f24432005-10-03 14:16:44 +0000634
635
636def main():
637 import getopt
638 usage = """Usage: %s [-n | -t] url
639 -n: open new window
640 -t: open new tab""" % sys.argv[0]
641 try:
642 opts, args = getopt.getopt(sys.argv[1:], 'ntd')
Guido van Rossumb940e112007-01-10 16:19:56 +0000643 except getopt.error as msg:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000644 print(msg, file=sys.stderr)
645 print(usage, file=sys.stderr)
Georg Brandle8f24432005-10-03 14:16:44 +0000646 sys.exit(1)
647 new_win = 0
648 for o, a in opts:
649 if o == '-n': new_win = 1
650 elif o == '-t': new_win = 2
Guido van Rossumb053cd82006-08-24 03:53:23 +0000651 if len(args) != 1:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000652 print(usage, file=sys.stderr)
Georg Brandle8f24432005-10-03 14:16:44 +0000653 sys.exit(1)
654
655 url = args[0]
656 open(url, new_win)
657
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000658 print("\a")
Georg Brandl23929f22006-01-20 21:03:35 +0000659
Georg Brandle8f24432005-10-03 14:16:44 +0000660if __name__ == "__main__":
661 main()