blob: 55aa04c8638fc93d1a2a0f7cbadebc2b6ced20b5 [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
5import sys
Georg Brandle8f24432005-10-03 14:16:44 +00006import stat
Georg Brandl23929f22006-01-20 21:03:35 +00007import subprocess
8import time
Fred Drakec70b4482000-07-09 16:45:56 +00009
Georg Brandle8f24432005-10-03 14:16:44 +000010__all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"]
Skip Montanaro40fc1602001-03-01 04:27:19 +000011
Fred Drakec70b4482000-07-09 16:45:56 +000012class Error(Exception):
13 pass
14
Tim Peters658cba62001-02-09 20:06:00 +000015_browsers = {} # Dictionary of available browser controllers
16_tryorder = [] # Preference order of available browsers
Fred Drakec70b4482000-07-09 16:45:56 +000017
Georg Brandle8f24432005-10-03 14:16:44 +000018def register(name, klass, instance=None, update_tryorder=1):
Fred Drakec70b4482000-07-09 16:45:56 +000019 """Register a browser connector and, optionally, connection."""
20 _browsers[name.lower()] = [klass, instance]
Georg Brandle8f24432005-10-03 14:16:44 +000021 if update_tryorder > 0:
22 _tryorder.append(name)
23 elif update_tryorder < 0:
24 _tryorder.insert(0, name)
Fred Drakec70b4482000-07-09 16:45:56 +000025
Eric S. Raymondf7f18512001-01-23 13:16:32 +000026def get(using=None):
27 """Return a browser launcher instance appropriate for the environment."""
Raymond Hettinger10ff7062002-06-02 03:04:52 +000028 if using is not None:
Eric S. Raymondf7f18512001-01-23 13:16:32 +000029 alternatives = [using]
30 else:
31 alternatives = _tryorder
32 for browser in alternatives:
Raymond Hettingerbac788a2004-05-04 09:21:43 +000033 if '%s' in browser:
Georg Brandl23929f22006-01-20 21:03:35 +000034 # User gave us a command line, split it into name and args
35 return GenericBrowser(browser.split())
Eric S. Raymondf7f18512001-01-23 13:16:32 +000036 else:
Georg Brandle8f24432005-10-03 14:16:44 +000037 # User gave us a browser name or path.
Fred Drakef4e5bd92001-04-12 22:07:27 +000038 try:
39 command = _browsers[browser.lower()]
40 except KeyError:
41 command = _synthesize(browser)
Georg Brandle8f24432005-10-03 14:16:44 +000042 if command[1] is not None:
Eric S. Raymondf7f18512001-01-23 13:16:32 +000043 return command[1]
Georg Brandle8f24432005-10-03 14:16:44 +000044 elif command[0] is not None:
45 return command[0]()
Eric S. Raymondf7f18512001-01-23 13:16:32 +000046 raise Error("could not locate runnable browser")
Fred Drakec70b4482000-07-09 16:45:56 +000047
48# Please note: the following definition hides a builtin function.
Georg Brandle8f24432005-10-03 14:16:44 +000049# It is recommended one does "import webbrowser" and uses webbrowser.open(url)
50# instead of "from webbrowser import *".
Fred Drakec70b4482000-07-09 16:45:56 +000051
Eric S. Raymondf79cb2d2001-01-23 13:49:44 +000052def open(url, new=0, autoraise=1):
Georg Brandle8f24432005-10-03 14:16:44 +000053 for name in _tryorder:
54 browser = get(name)
55 if browser.open(url, new, autoraise):
56 return True
57 return False
Fred Drakec70b4482000-07-09 16:45:56 +000058
Fred Drake3f8f1642001-07-19 03:46:26 +000059def open_new(url):
Georg Brandle8f24432005-10-03 14:16:44 +000060 return open(url, 1)
61
62def open_new_tab(url):
63 return open(url, 2)
Fred Drakec70b4482000-07-09 16:45:56 +000064
Fred Drakef4e5bd92001-04-12 22:07:27 +000065
Georg Brandle8f24432005-10-03 14:16:44 +000066def _synthesize(browser, update_tryorder=1):
Fred Drakef4e5bd92001-04-12 22:07:27 +000067 """Attempt to synthesize a controller base on existing controllers.
68
69 This is useful to create a controller when a user specifies a path to
70 an entry in the BROWSER environment variable -- we can copy a general
71 controller to operate using a specific installation of the desired
72 browser in this way.
73
74 If we can't create a controller in this way, or if there is no
75 executable for the requested browser, return [None, None].
76
77 """
Georg Brandle8f24432005-10-03 14:16:44 +000078 cmd = browser.split()[0]
79 if not _iscommand(cmd):
Fred Drakef4e5bd92001-04-12 22:07:27 +000080 return [None, None]
Georg Brandle8f24432005-10-03 14:16:44 +000081 name = os.path.basename(cmd)
Fred Drakef4e5bd92001-04-12 22:07:27 +000082 try:
83 command = _browsers[name.lower()]
84 except KeyError:
85 return [None, None]
86 # now attempt to clone to fit the new name:
87 controller = command[1]
88 if controller and name.lower() == controller.basename:
89 import copy
90 controller = copy.copy(controller)
91 controller.name = browser
92 controller.basename = os.path.basename(browser)
Georg Brandle8f24432005-10-03 14:16:44 +000093 register(browser, None, controller, update_tryorder)
Fred Drakef4e5bd92001-04-12 22:07:27 +000094 return [None, controller]
Andrew M. Kuchling118aa532001-08-13 14:37:23 +000095 return [None, None]
Fred Drakef4e5bd92001-04-12 22:07:27 +000096
Fred Drake3f8f1642001-07-19 03:46:26 +000097
Georg Brandle8f24432005-10-03 14:16:44 +000098if sys.platform[:3] == "win":
99 def _isexecutable(cmd):
100 cmd = cmd.lower()
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000101 if os.path.isfile(cmd) and cmd.endswith((".exe", ".bat")):
Georg Brandle8f24432005-10-03 14:16:44 +0000102 return True
103 for ext in ".exe", ".bat":
104 if os.path.isfile(cmd + ext):
105 return True
106 return False
107else:
108 def _isexecutable(cmd):
109 if os.path.isfile(cmd):
110 mode = os.stat(cmd)[stat.ST_MODE]
111 if mode & stat.S_IXUSR or mode & stat.S_IXGRP or mode & stat.S_IXOTH:
112 return True
113 return False
114
Fred Drake3f8f1642001-07-19 03:46:26 +0000115def _iscommand(cmd):
Georg Brandle8f24432005-10-03 14:16:44 +0000116 """Return True if cmd is executable or can be found on the executable
117 search path."""
118 if _isexecutable(cmd):
119 return True
Fred Drake3f8f1642001-07-19 03:46:26 +0000120 path = os.environ.get("PATH")
121 if not path:
Tim Petersbc0e9102002-04-04 22:55:58 +0000122 return False
Fred Drake3f8f1642001-07-19 03:46:26 +0000123 for d in path.split(os.pathsep):
124 exe = os.path.join(d, cmd)
Georg Brandle8f24432005-10-03 14:16:44 +0000125 if _isexecutable(exe):
Tim Petersbc0e9102002-04-04 22:55:58 +0000126 return True
127 return False
Fred Drake3f8f1642001-07-19 03:46:26 +0000128
129
Georg Brandle8f24432005-10-03 14:16:44 +0000130# General parent classes
131
132class BaseBrowser(object):
Georg Brandl23929f22006-01-20 21:03:35 +0000133 """Parent class for all browsers. Do not use directly."""
Tim Peters887c0802006-01-20 23:40:56 +0000134
Georg Brandl23929f22006-01-20 21:03:35 +0000135 args = ['%s']
Tim Peters887c0802006-01-20 23:40:56 +0000136
Georg Brandle8f24432005-10-03 14:16:44 +0000137 def __init__(self, name=""):
138 self.name = name
Georg Brandlb9801132005-10-08 20:47:38 +0000139 self.basename = name
Tim Peters536cf992005-12-25 23:18:31 +0000140
Neal Norwitz196f7332005-10-04 03:17:49 +0000141 def open(self, url, new=0, autoraise=1):
142 raise NotImplementedError
143
Georg Brandle8f24432005-10-03 14:16:44 +0000144 def open_new(self, url):
145 return self.open(url, 1)
146
147 def open_new_tab(self, url):
148 return self.open(url, 2)
Fred Drake3f8f1642001-07-19 03:46:26 +0000149
150
Georg Brandle8f24432005-10-03 14:16:44 +0000151class GenericBrowser(BaseBrowser):
152 """Class for all browsers started with a command
153 and without remote functionality."""
154
Georg Brandl23929f22006-01-20 21:03:35 +0000155 def __init__(self, name):
156 if isinstance(name, basestring):
157 self.name = name
158 else:
159 # name should be a list with arguments
160 self.name = name[0]
161 self.args = name[1:]
Georg Brandlb9801132005-10-08 20:47:38 +0000162 self.basename = os.path.basename(self.name)
Fred Drake3f8f1642001-07-19 03:46:26 +0000163
164 def open(self, url, new=0, autoraise=1):
Tim Peters887c0802006-01-20 23:40:56 +0000165 cmdline = [self.name] + [arg.replace("%s", url)
Georg Brandl23929f22006-01-20 21:03:35 +0000166 for arg in self.args]
167 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000168 if sys.platform[:3] == 'win':
169 p = subprocess.Popen(cmdline)
170 else:
171 p = subprocess.Popen(cmdline, close_fds=True)
Georg Brandl23929f22006-01-20 21:03:35 +0000172 return not p.wait()
173 except OSError:
174 return False
175
176
177class BackgroundBrowser(GenericBrowser):
178 """Class for all browsers which are to be started in the
179 background."""
180
181 def open(self, url, new=0, autoraise=1):
182 cmdline = [self.name] + [arg.replace("%s", url)
183 for arg in self.args]
Georg Brandl23929f22006-01-20 21:03:35 +0000184 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000185 if sys.platform[:3] == 'win':
186 p = subprocess.Popen(cmdline)
187 else:
188 setsid = getattr(os, 'setsid', None)
189 if not setsid:
190 setsid = getattr(os, 'setpgrp', None)
191 p = subprocess.Popen(cmdline, close_fds=True, preexec_fn=setsid)
Georg Brandl23929f22006-01-20 21:03:35 +0000192 return (p.poll() is None)
193 except OSError:
194 return False
Fred Drake3f8f1642001-07-19 03:46:26 +0000195
196
Georg Brandle8f24432005-10-03 14:16:44 +0000197class UnixBrowser(BaseBrowser):
198 """Parent class for all Unix browsers with remote functionality."""
Fred Drake3f8f1642001-07-19 03:46:26 +0000199
Georg Brandle8f24432005-10-03 14:16:44 +0000200 raise_opts = None
Georg Brandl23929f22006-01-20 21:03:35 +0000201 remote_args = ['%action', '%s']
Georg Brandle8f24432005-10-03 14:16:44 +0000202 remote_action = None
203 remote_action_newwin = None
204 remote_action_newtab = None
Georg Brandl23929f22006-01-20 21:03:35 +0000205 background = False
206 redirect_stdout = True
Georg Brandle8f24432005-10-03 14:16:44 +0000207
Georg Brandl23929f22006-01-20 21:03:35 +0000208 def _invoke(self, args, remote, autoraise):
209 raise_opt = []
210 if remote and self.raise_opts:
211 # use autoraise argument only for remote invocation
212 autoraise = int(bool(autoraise))
213 opt = self.raise_opts[autoraise]
214 if opt: raise_opt = [opt]
215
216 cmdline = [self.name] + raise_opt + args
Tim Peters887c0802006-01-20 23:40:56 +0000217
Georg Brandl23929f22006-01-20 21:03:35 +0000218 if remote or self.background:
Alex Martelli01c77c62006-08-24 02:58:11 +0000219 inout = open(os.devnull, "r+")
Georg Brandl23929f22006-01-20 21:03:35 +0000220 else:
221 # for TTY browsers, we need stdin/out
222 inout = None
223 # if possible, put browser in separate process group, so
224 # keyboard interrupts don't affect browser as well as Python
225 setsid = getattr(os, 'setsid', None)
226 if not setsid:
227 setsid = getattr(os, 'setpgrp', None)
Tim Peters887c0802006-01-20 23:40:56 +0000228
Georg Brandl23929f22006-01-20 21:03:35 +0000229 p = subprocess.Popen(cmdline, close_fds=True, stdin=inout,
230 stdout=(self.redirect_stdout and inout or None),
231 stderr=inout, preexec_fn=setsid)
232 if remote:
233 # wait five secons. If the subprocess is not finished, the
234 # remote invocation has (hopefully) started a new instance.
235 time.sleep(1)
236 rc = p.poll()
237 if rc is None:
238 time.sleep(4)
239 rc = p.poll()
240 if rc is None:
241 return True
242 # if remote call failed, open() will try direct invocation
243 return not rc
244 elif self.background:
245 if p.poll() is None:
246 return True
247 else:
248 return False
249 else:
250 return not p.wait()
Fred Drake3f8f1642001-07-19 03:46:26 +0000251
252 def open(self, url, new=0, autoraise=1):
Georg Brandle8f24432005-10-03 14:16:44 +0000253 if new == 0:
254 action = self.remote_action
255 elif new == 1:
256 action = self.remote_action_newwin
257 elif new == 2:
258 if self.remote_action_newtab is None:
259 action = self.remote_action_newwin
260 else:
261 action = self.remote_action_newtab
Fred Drake3f8f1642001-07-19 03:46:26 +0000262 else:
Georg Brandl23929f22006-01-20 21:03:35 +0000263 raise Error("Bad 'new' parameter to open(); " +
264 "expected 0, 1, or 2, got %s" % new)
Tim Peters887c0802006-01-20 23:40:56 +0000265
Georg Brandl23929f22006-01-20 21:03:35 +0000266 args = [arg.replace("%s", url).replace("%action", action)
267 for arg in self.remote_args]
268 success = self._invoke(args, True, autoraise)
269 if not success:
270 # remote invocation failed, try straight way
271 args = [arg.replace("%s", url) for arg in self.args]
272 return self._invoke(args, False, False)
273 else:
274 return True
Fred Drake3f8f1642001-07-19 03:46:26 +0000275
276
Georg Brandle8f24432005-10-03 14:16:44 +0000277class Mozilla(UnixBrowser):
278 """Launcher class for Mozilla/Netscape browsers."""
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000279
Georg Brandl23929f22006-01-20 21:03:35 +0000280 raise_opts = ["-noraise", "-raise"]
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000281
Georg Brandl23929f22006-01-20 21:03:35 +0000282 remote_args = ['-remote', 'openURL(%s%action)']
283 remote_action = ""
284 remote_action_newwin = ",new-window"
285 remote_action_newtab = ",new-tab"
Tim Peters887c0802006-01-20 23:40:56 +0000286
Georg Brandl23929f22006-01-20 21:03:35 +0000287 background = True
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000288
Georg Brandle8f24432005-10-03 14:16:44 +0000289Netscape = Mozilla
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000290
291
Georg Brandle8f24432005-10-03 14:16:44 +0000292class Galeon(UnixBrowser):
293 """Launcher class for Galeon/Epiphany browsers."""
294
Georg Brandl23929f22006-01-20 21:03:35 +0000295 raise_opts = ["-noraise", ""]
296 remote_args = ['%action', '%s']
297 remote_action = "-n"
298 remote_action_newwin = "-w"
Georg Brandle8f24432005-10-03 14:16:44 +0000299
Georg Brandl23929f22006-01-20 21:03:35 +0000300 background = True
Fred Drake3f8f1642001-07-19 03:46:26 +0000301
302
Georg Brandle8f24432005-10-03 14:16:44 +0000303class Opera(UnixBrowser):
304 "Launcher class for Opera browser."
305
Georg Brandl23929f22006-01-20 21:03:35 +0000306 raise_opts = ["", "-raise"]
Georg Brandle8f24432005-10-03 14:16:44 +0000307
Georg Brandl23929f22006-01-20 21:03:35 +0000308 remote_args = ['-remote', 'openURL(%s%action)']
309 remote_action = ""
310 remote_action_newwin = ",new-window"
311 remote_action_newtab = ",new-page"
312 background = True
Georg Brandle8f24432005-10-03 14:16:44 +0000313
314
315class Elinks(UnixBrowser):
316 "Launcher class for Elinks browsers."
317
Georg Brandl23929f22006-01-20 21:03:35 +0000318 remote_args = ['-remote', 'openURL(%s%action)']
319 remote_action = ""
320 remote_action_newwin = ",new-window"
321 remote_action_newtab = ",new-tab"
322 background = False
Georg Brandle8f24432005-10-03 14:16:44 +0000323
Georg Brandl23929f22006-01-20 21:03:35 +0000324 # elinks doesn't like its stdout to be redirected -
325 # it uses redirected stdout as a signal to do -dump
326 redirect_stdout = False
327
328
329class Konqueror(BaseBrowser):
330 """Controller for the KDE File Manager (kfm, or Konqueror).
331
332 See the output of ``kfmclient --commands``
333 for more information on the Konqueror remote-control interface.
334 """
335
336 def open(self, url, new=0, autoraise=1):
337 # XXX Currently I know no way to prevent KFM from opening a new win.
338 if new == 2:
339 action = "newTab"
340 else:
341 action = "openURL"
Tim Peters887c0802006-01-20 23:40:56 +0000342
Alex Martelli01c77c62006-08-24 02:58:11 +0000343 devnull = open(os.devnull, "r+")
Georg Brandl23929f22006-01-20 21:03:35 +0000344 # if possible, put browser in separate process group, so
345 # keyboard interrupts don't affect browser as well as Python
346 setsid = getattr(os, 'setsid', None)
347 if not setsid:
348 setsid = getattr(os, 'setpgrp', None)
Tim Peters887c0802006-01-20 23:40:56 +0000349
Georg Brandl23929f22006-01-20 21:03:35 +0000350 try:
351 p = subprocess.Popen(["kfmclient", action, url],
352 close_fds=True, stdin=devnull,
353 stdout=devnull, stderr=devnull)
354 except OSError:
355 # fall through to next variant
356 pass
357 else:
358 p.wait()
359 # kfmclient's return code unfortunately has no meaning as it seems
360 return True
361
362 try:
363 p = subprocess.Popen(["konqueror", "--silent", url],
364 close_fds=True, stdin=devnull,
365 stdout=devnull, stderr=devnull,
366 preexec_fn=setsid)
367 except OSError:
368 # fall through to next variant
369 pass
370 else:
371 if p.poll() is None:
372 # Should be running now.
373 return True
Tim Peters887c0802006-01-20 23:40:56 +0000374
Georg Brandl23929f22006-01-20 21:03:35 +0000375 try:
376 p = subprocess.Popen(["kfm", "-d", url],
377 close_fds=True, stdin=devnull,
378 stdout=devnull, stderr=devnull,
379 preexec_fn=setsid)
380 except OSError:
381 return False
382 else:
383 return (p.poll() is None)
Georg Brandle8f24432005-10-03 14:16:44 +0000384
385
386class Grail(BaseBrowser):
Fred Drake3f8f1642001-07-19 03:46:26 +0000387 # There should be a way to maintain a connection to Grail, but the
388 # Grail remote control protocol doesn't really allow that at this
Georg Brandl23929f22006-01-20 21:03:35 +0000389 # point. It probably never will!
Fred Drake3f8f1642001-07-19 03:46:26 +0000390 def _find_grail_rc(self):
391 import glob
392 import pwd
393 import socket
394 import tempfile
395 tempdir = os.path.join(tempfile.gettempdir(),
396 ".grail-unix")
Fred Drake16623fe2001-10-13 16:00:52 +0000397 user = pwd.getpwuid(os.getuid())[0]
Fred Drake3f8f1642001-07-19 03:46:26 +0000398 filename = os.path.join(tempdir, user + "-*")
399 maybes = glob.glob(filename)
400 if not maybes:
401 return None
402 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
403 for fn in maybes:
404 # need to PING each one until we find one that's live
405 try:
406 s.connect(fn)
407 except socket.error:
408 # no good; attempt to clean it out, but don't fail:
409 try:
410 os.unlink(fn)
411 except IOError:
412 pass
413 else:
414 return s
415
416 def _remote(self, action):
417 s = self._find_grail_rc()
418 if not s:
419 return 0
420 s.send(action)
421 s.close()
422 return 1
423
424 def open(self, url, new=0, autoraise=1):
425 if new:
Georg Brandle8f24432005-10-03 14:16:44 +0000426 ok = self._remote("LOADNEW " + url)
Fred Drake3f8f1642001-07-19 03:46:26 +0000427 else:
Georg Brandle8f24432005-10-03 14:16:44 +0000428 ok = self._remote("LOAD " + url)
429 return ok
Fred Drake3f8f1642001-07-19 03:46:26 +0000430
Fred Drakec70b4482000-07-09 16:45:56 +0000431
Tim Peters658cba62001-02-09 20:06:00 +0000432#
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000433# Platform support for Unix
434#
Fred Drakec70b4482000-07-09 16:45:56 +0000435
Georg Brandle8f24432005-10-03 14:16:44 +0000436# These are the right tests because all these Unix browsers require either
437# a console terminal or an X display to run.
Fred Drakec70b4482000-07-09 16:45:56 +0000438
Neal Norwitz196f7332005-10-04 03:17:49 +0000439def register_X_browsers():
Georg Brandle8f24432005-10-03 14:16:44 +0000440 # The default Gnome browser
441 if _iscommand("gconftool-2"):
442 # get the web browser string from gconftool
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000443 gc = 'gconftool-2 -g /desktop/gnome/url-handlers/http/command 2>/dev/null'
Georg Brandle8f24432005-10-03 14:16:44 +0000444 out = os.popen(gc)
445 commd = out.read().strip()
446 retncode = out.close()
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000447
Georg Brandle8f24432005-10-03 14:16:44 +0000448 # if successful, register it
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000449 if retncode is None and commd:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000450 register("gnome", None, BackgroundBrowser(commd.split()))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000451
Georg Brandl23929f22006-01-20 21:03:35 +0000452 # First, the Mozilla/Netscape browsers
Georg Brandl4a5a9182005-11-22 19:18:01 +0000453 for browser in ("mozilla-firefox", "firefox",
454 "mozilla-firebird", "firebird",
Thomas Wouters477c8d52006-05-27 19:21:47 +0000455 "seamonkey", "mozilla", "netscape"):
Georg Brandl4a5a9182005-11-22 19:18:01 +0000456 if _iscommand(browser):
457 register(browser, None, Mozilla(browser))
458
Georg Brandle8f24432005-10-03 14:16:44 +0000459 # Konqueror/kfm, the KDE browser.
Georg Brandlb9801132005-10-08 20:47:38 +0000460 if _iscommand("kfm"):
461 register("kfm", Konqueror, Konqueror("kfm"))
462 elif _iscommand("konqueror"):
463 register("konqueror", Konqueror, Konqueror("konqueror"))
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000464
Georg Brandle8f24432005-10-03 14:16:44 +0000465 # Gnome's Galeon and Epiphany
466 for browser in ("galeon", "epiphany"):
467 if _iscommand(browser):
468 register(browser, None, Galeon(browser))
Gustavo Niemeyer1456fde2002-11-25 17:25:04 +0000469
Georg Brandle8f24432005-10-03 14:16:44 +0000470 # Skipstone, another Gtk/Mozilla based browser
471 if _iscommand("skipstone"):
Georg Brandl23929f22006-01-20 21:03:35 +0000472 register("skipstone", None, BackgroundBrowser("skipstone"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000473
Georg Brandle8f24432005-10-03 14:16:44 +0000474 # Opera, quite popular
475 if _iscommand("opera"):
476 register("opera", None, Opera("opera"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000477
Georg Brandle8f24432005-10-03 14:16:44 +0000478 # Next, Mosaic -- old but still in use.
479 if _iscommand("mosaic"):
Georg Brandl23929f22006-01-20 21:03:35 +0000480 register("mosaic", None, BackgroundBrowser("mosaic"))
Fred Drake3f8f1642001-07-19 03:46:26 +0000481
Georg Brandle8f24432005-10-03 14:16:44 +0000482 # Grail, the Python browser. Does anybody still use it?
483 if _iscommand("grail"):
484 register("grail", Grail, None)
Fred Drake3f8f1642001-07-19 03:46:26 +0000485
Neal Norwitz196f7332005-10-04 03:17:49 +0000486# Prefer X browsers if present
487if os.environ.get("DISPLAY"):
488 register_X_browsers()
489
Georg Brandle8f24432005-10-03 14:16:44 +0000490# Also try console browsers
491if os.environ.get("TERM"):
492 # The Links/elinks browsers <http://artax.karlin.mff.cuni.cz/~mikulas/links/>
493 if _iscommand("links"):
Georg Brandl23929f22006-01-20 21:03:35 +0000494 register("links", None, GenericBrowser("links"))
Georg Brandle8f24432005-10-03 14:16:44 +0000495 if _iscommand("elinks"):
496 register("elinks", None, Elinks("elinks"))
497 # The Lynx browser <http://lynx.isc.org/>, <http://lynx.browser.org/>
498 if _iscommand("lynx"):
Georg Brandl23929f22006-01-20 21:03:35 +0000499 register("lynx", None, GenericBrowser("lynx"))
Georg Brandle8f24432005-10-03 14:16:44 +0000500 # The w3m browser <http://w3m.sourceforge.net/>
501 if _iscommand("w3m"):
Georg Brandl23929f22006-01-20 21:03:35 +0000502 register("w3m", None, GenericBrowser("w3m"))
Fred Drake3f8f1642001-07-19 03:46:26 +0000503
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000504#
505# Platform support for Windows
506#
Fred Drakec70b4482000-07-09 16:45:56 +0000507
508if sys.platform[:3] == "win":
Georg Brandle8f24432005-10-03 14:16:44 +0000509 class WindowsDefault(BaseBrowser):
510 def open(self, url, new=0, autoraise=1):
511 os.startfile(url)
512 return True # Oh, my...
513
514 _tryorder = []
515 _browsers = {}
516 # Prefer mozilla/netscape/opera if present
Thomas Wouters477c8d52006-05-27 19:21:47 +0000517 for browser in ("firefox", "firebird", "seamonkey", "mozilla",
518 "netscape", "opera"):
Georg Brandle8f24432005-10-03 14:16:44 +0000519 if _iscommand(browser):
Georg Brandl23929f22006-01-20 21:03:35 +0000520 register(browser, None, BackgroundBrowser(browser))
Fred Drakec70b4482000-07-09 16:45:56 +0000521 register("windows-default", WindowsDefault)
Fred Drakec70b4482000-07-09 16:45:56 +0000522
Fred Drakec70b4482000-07-09 16:45:56 +0000523#
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000524# Platform support for MacOS
525#
Fred Drakec70b4482000-07-09 16:45:56 +0000526
527try:
528 import ic
529except ImportError:
530 pass
531else:
Georg Brandle8f24432005-10-03 14:16:44 +0000532 class InternetConfig(BaseBrowser):
533 def open(self, url, new=0, autoraise=1):
534 ic.launchurl(url)
535 return True # Any way to get status?
536
537 register("internet-config", InternetConfig, update_tryorder=-1)
538
539if sys.platform == 'darwin':
540 # Adapted from patch submitted to SourceForge by Steven J. Burr
541 class MacOSX(BaseBrowser):
542 """Launcher class for Aqua browsers on Mac OS X
543
544 Optionally specify a browser name on instantiation. Note that this
545 will not work for Aqua browsers if the user has moved the application
546 package after installation.
547
548 If no browser is specified, the default browser, as specified in the
549 Internet System Preferences panel, will be used.
550 """
551 def __init__(self, name):
552 self.name = name
553
554 def open(self, url, new=0, autoraise=1):
555 assert "'" not in url
Georg Brandl23929f22006-01-20 21:03:35 +0000556 # hack for local urls
557 if not ':' in url:
558 url = 'file:'+url
Tim Peters887c0802006-01-20 23:40:56 +0000559
Georg Brandle8f24432005-10-03 14:16:44 +0000560 # new must be 0 or 1
561 new = int(bool(new))
562 if self.name == "default":
563 # User called open, open_new or get without a browser parameter
Georg Brandl1cb179e2005-11-09 21:42:48 +0000564 script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser
Georg Brandle8f24432005-10-03 14:16:44 +0000565 else:
566 # User called get and chose a browser
567 if self.name == "OmniWeb":
568 toWindow = ""
569 else:
570 # Include toWindow parameter of OpenURL command for browsers
571 # that support it. 0 == new window; -1 == existing
572 toWindow = "toWindow %d" % (new - 1)
Georg Brandl1cb179e2005-11-09 21:42:48 +0000573 cmd = 'OpenURL "%s"' % url.replace('"', '%22')
Georg Brandle8f24432005-10-03 14:16:44 +0000574 script = '''tell application "%s"
575 activate
576 %s %s
577 end tell''' % (self.name, cmd, toWindow)
578 # Open pipe to AppleScript through osascript command
579 osapipe = os.popen("osascript", "w")
580 if osapipe is None:
581 return False
582 # Write script to osascript's stdin
583 osapipe.write(script)
584 rc = osapipe.close()
585 return not rc
586
587 # Don't clear _tryorder or _browsers since OS X can use above Unix support
588 # (but we prefer using the OS X specific stuff)
589 register("MacOSX", None, MacOSX('default'), -1)
590
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000591
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000592#
593# Platform support for OS/2
594#
595
Georg Brandle8f24432005-10-03 14:16:44 +0000596if sys.platform[:3] == "os2" and _iscommand("netscape"):
597 _tryorder = []
598 _browsers = {}
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000599 register("os2netscape", None,
Georg Brandl23929f22006-01-20 21:03:35 +0000600 GenericBrowser(["start", "netscape", "%s"]), -1)
Georg Brandle8f24432005-10-03 14:16:44 +0000601
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000602
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000603# OK, now that we know what the default preference orders for each
604# platform are, allow user to override them with the BROWSER variable.
Raymond Hettinger54f02222002-06-01 14:18:47 +0000605if "BROWSER" in os.environ:
Georg Brandle8f24432005-10-03 14:16:44 +0000606 _userchoices = os.environ["BROWSER"].split(os.pathsep)
607 _userchoices.reverse()
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000608
Georg Brandle8f24432005-10-03 14:16:44 +0000609 # Treat choices in same way as if passed into get() but do register
610 # and prepend to _tryorder
611 for cmdline in _userchoices:
612 if cmdline != '':
613 _synthesize(cmdline, -1)
614 cmdline = None # to make del work if _userchoices was empty
615 del cmdline
616 del _userchoices
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000617
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000618# what to do if _tryorder is now empty?
Georg Brandle8f24432005-10-03 14:16:44 +0000619
620
621def main():
622 import getopt
623 usage = """Usage: %s [-n | -t] url
624 -n: open new window
625 -t: open new tab""" % sys.argv[0]
626 try:
627 opts, args = getopt.getopt(sys.argv[1:], 'ntd')
Guido van Rossumb940e112007-01-10 16:19:56 +0000628 except getopt.error as msg:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000629 print(msg, file=sys.stderr)
630 print(usage, file=sys.stderr)
Georg Brandle8f24432005-10-03 14:16:44 +0000631 sys.exit(1)
632 new_win = 0
633 for o, a in opts:
634 if o == '-n': new_win = 1
635 elif o == '-t': new_win = 2
Guido van Rossumb053cd82006-08-24 03:53:23 +0000636 if len(args) != 1:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000637 print(usage, file=sys.stderr)
Georg Brandle8f24432005-10-03 14:16:44 +0000638 sys.exit(1)
639
640 url = args[0]
641 open(url, new_win)
642
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000643 print("\a")
Georg Brandl23929f22006-01-20 21:03:35 +0000644
Georg Brandle8f24432005-10-03 14:16:44 +0000645if __name__ == "__main__":
646 main()