blob: 7a1a3b4994c0d1b9b0ada43824328bdb39089d17 [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:
168 p = subprocess.Popen(cmdline, close_fds=True)
169 return not p.wait()
170 except OSError:
171 return False
172
173
174class BackgroundBrowser(GenericBrowser):
175 """Class for all browsers which are to be started in the
176 background."""
177
178 def open(self, url, new=0, autoraise=1):
179 cmdline = [self.name] + [arg.replace("%s", url)
180 for arg in self.args]
181 setsid = getattr(os, 'setsid', None)
182 if not setsid:
183 setsid = getattr(os, 'setpgrp', None)
184 try:
185 p = subprocess.Popen(cmdline, close_fds=True, preexec_fn=setsid)
186 return (p.poll() is None)
187 except OSError:
188 return False
Fred Drake3f8f1642001-07-19 03:46:26 +0000189
190
Georg Brandle8f24432005-10-03 14:16:44 +0000191class UnixBrowser(BaseBrowser):
192 """Parent class for all Unix browsers with remote functionality."""
Fred Drake3f8f1642001-07-19 03:46:26 +0000193
Georg Brandle8f24432005-10-03 14:16:44 +0000194 raise_opts = None
Georg Brandl23929f22006-01-20 21:03:35 +0000195 remote_args = ['%action', '%s']
Georg Brandle8f24432005-10-03 14:16:44 +0000196 remote_action = None
197 remote_action_newwin = None
198 remote_action_newtab = None
Georg Brandl23929f22006-01-20 21:03:35 +0000199 background = False
200 redirect_stdout = True
Georg Brandle8f24432005-10-03 14:16:44 +0000201
Georg Brandl23929f22006-01-20 21:03:35 +0000202 def _invoke(self, args, remote, autoraise):
203 raise_opt = []
204 if remote and self.raise_opts:
205 # use autoraise argument only for remote invocation
206 autoraise = int(bool(autoraise))
207 opt = self.raise_opts[autoraise]
208 if opt: raise_opt = [opt]
209
210 cmdline = [self.name] + raise_opt + args
Tim Peters887c0802006-01-20 23:40:56 +0000211
Georg Brandl23929f22006-01-20 21:03:35 +0000212 if remote or self.background:
213 inout = file(os.devnull, "r+")
214 else:
215 # for TTY browsers, we need stdin/out
216 inout = None
217 # if possible, put browser in separate process group, so
218 # keyboard interrupts don't affect browser as well as Python
219 setsid = getattr(os, 'setsid', None)
220 if not setsid:
221 setsid = getattr(os, 'setpgrp', None)
Tim Peters887c0802006-01-20 23:40:56 +0000222
Georg Brandl23929f22006-01-20 21:03:35 +0000223 p = subprocess.Popen(cmdline, close_fds=True, stdin=inout,
224 stdout=(self.redirect_stdout and inout or None),
225 stderr=inout, preexec_fn=setsid)
226 if remote:
227 # wait five secons. If the subprocess is not finished, the
228 # remote invocation has (hopefully) started a new instance.
229 time.sleep(1)
230 rc = p.poll()
231 if rc is None:
232 time.sleep(4)
233 rc = p.poll()
234 if rc is None:
235 return True
236 # if remote call failed, open() will try direct invocation
237 return not rc
238 elif self.background:
239 if p.poll() is None:
240 return True
241 else:
242 return False
243 else:
244 return not p.wait()
Fred Drake3f8f1642001-07-19 03:46:26 +0000245
246 def open(self, url, new=0, autoraise=1):
Georg Brandle8f24432005-10-03 14:16:44 +0000247 if new == 0:
248 action = self.remote_action
249 elif new == 1:
250 action = self.remote_action_newwin
251 elif new == 2:
252 if self.remote_action_newtab is None:
253 action = self.remote_action_newwin
254 else:
255 action = self.remote_action_newtab
Fred Drake3f8f1642001-07-19 03:46:26 +0000256 else:
Georg Brandl23929f22006-01-20 21:03:35 +0000257 raise Error("Bad 'new' parameter to open(); " +
258 "expected 0, 1, or 2, got %s" % new)
Tim Peters887c0802006-01-20 23:40:56 +0000259
Georg Brandl23929f22006-01-20 21:03:35 +0000260 args = [arg.replace("%s", url).replace("%action", action)
261 for arg in self.remote_args]
262 success = self._invoke(args, True, autoraise)
263 if not success:
264 # remote invocation failed, try straight way
265 args = [arg.replace("%s", url) for arg in self.args]
266 return self._invoke(args, False, False)
267 else:
268 return True
Fred Drake3f8f1642001-07-19 03:46:26 +0000269
270
Georg Brandle8f24432005-10-03 14:16:44 +0000271class Mozilla(UnixBrowser):
272 """Launcher class for Mozilla/Netscape browsers."""
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000273
Georg Brandl23929f22006-01-20 21:03:35 +0000274 raise_opts = ["-noraise", "-raise"]
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000275
Georg Brandl23929f22006-01-20 21:03:35 +0000276 remote_args = ['-remote', 'openURL(%s%action)']
277 remote_action = ""
278 remote_action_newwin = ",new-window"
279 remote_action_newtab = ",new-tab"
Tim Peters887c0802006-01-20 23:40:56 +0000280
Georg Brandl23929f22006-01-20 21:03:35 +0000281 background = True
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000282
Georg Brandle8f24432005-10-03 14:16:44 +0000283Netscape = Mozilla
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000284
285
Georg Brandle8f24432005-10-03 14:16:44 +0000286class Galeon(UnixBrowser):
287 """Launcher class for Galeon/Epiphany browsers."""
288
Georg Brandl23929f22006-01-20 21:03:35 +0000289 raise_opts = ["-noraise", ""]
290 remote_args = ['%action', '%s']
291 remote_action = "-n"
292 remote_action_newwin = "-w"
Georg Brandle8f24432005-10-03 14:16:44 +0000293
Georg Brandl23929f22006-01-20 21:03:35 +0000294 background = True
Fred Drake3f8f1642001-07-19 03:46:26 +0000295
296
Georg Brandle8f24432005-10-03 14:16:44 +0000297class Opera(UnixBrowser):
298 "Launcher class for Opera browser."
299
Georg Brandl23929f22006-01-20 21:03:35 +0000300 raise_opts = ["", "-raise"]
Georg Brandle8f24432005-10-03 14:16:44 +0000301
Georg Brandl23929f22006-01-20 21:03:35 +0000302 remote_args = ['-remote', 'openURL(%s%action)']
303 remote_action = ""
304 remote_action_newwin = ",new-window"
305 remote_action_newtab = ",new-page"
306 background = True
Georg Brandle8f24432005-10-03 14:16:44 +0000307
308
309class Elinks(UnixBrowser):
310 "Launcher class for Elinks browsers."
311
Georg Brandl23929f22006-01-20 21:03:35 +0000312 remote_args = ['-remote', 'openURL(%s%action)']
313 remote_action = ""
314 remote_action_newwin = ",new-window"
315 remote_action_newtab = ",new-tab"
316 background = False
Georg Brandle8f24432005-10-03 14:16:44 +0000317
Georg Brandl23929f22006-01-20 21:03:35 +0000318 # elinks doesn't like its stdout to be redirected -
319 # it uses redirected stdout as a signal to do -dump
320 redirect_stdout = False
321
322
323class Konqueror(BaseBrowser):
324 """Controller for the KDE File Manager (kfm, or Konqueror).
325
326 See the output of ``kfmclient --commands``
327 for more information on the Konqueror remote-control interface.
328 """
329
330 def open(self, url, new=0, autoraise=1):
331 # XXX Currently I know no way to prevent KFM from opening a new win.
332 if new == 2:
333 action = "newTab"
334 else:
335 action = "openURL"
Tim Peters887c0802006-01-20 23:40:56 +0000336
Georg Brandl23929f22006-01-20 21:03:35 +0000337 devnull = file(os.devnull, "r+")
338 # if possible, put browser in separate process group, so
339 # keyboard interrupts don't affect browser as well as Python
340 setsid = getattr(os, 'setsid', None)
341 if not setsid:
342 setsid = getattr(os, 'setpgrp', None)
Tim Peters887c0802006-01-20 23:40:56 +0000343
Georg Brandl23929f22006-01-20 21:03:35 +0000344 try:
345 p = subprocess.Popen(["kfmclient", action, url],
346 close_fds=True, stdin=devnull,
347 stdout=devnull, stderr=devnull)
348 except OSError:
349 # fall through to next variant
350 pass
351 else:
352 p.wait()
353 # kfmclient's return code unfortunately has no meaning as it seems
354 return True
355
356 try:
357 p = subprocess.Popen(["konqueror", "--silent", url],
358 close_fds=True, stdin=devnull,
359 stdout=devnull, stderr=devnull,
360 preexec_fn=setsid)
361 except OSError:
362 # fall through to next variant
363 pass
364 else:
365 if p.poll() is None:
366 # Should be running now.
367 return True
Tim Peters887c0802006-01-20 23:40:56 +0000368
Georg Brandl23929f22006-01-20 21:03:35 +0000369 try:
370 p = subprocess.Popen(["kfm", "-d", url],
371 close_fds=True, stdin=devnull,
372 stdout=devnull, stderr=devnull,
373 preexec_fn=setsid)
374 except OSError:
375 return False
376 else:
377 return (p.poll() is None)
Georg Brandle8f24432005-10-03 14:16:44 +0000378
379
380class Grail(BaseBrowser):
Fred Drake3f8f1642001-07-19 03:46:26 +0000381 # There should be a way to maintain a connection to Grail, but the
382 # Grail remote control protocol doesn't really allow that at this
Georg Brandl23929f22006-01-20 21:03:35 +0000383 # point. It probably never will!
Fred Drake3f8f1642001-07-19 03:46:26 +0000384 def _find_grail_rc(self):
385 import glob
386 import pwd
387 import socket
388 import tempfile
389 tempdir = os.path.join(tempfile.gettempdir(),
390 ".grail-unix")
Fred Drake16623fe2001-10-13 16:00:52 +0000391 user = pwd.getpwuid(os.getuid())[0]
Fred Drake3f8f1642001-07-19 03:46:26 +0000392 filename = os.path.join(tempdir, user + "-*")
393 maybes = glob.glob(filename)
394 if not maybes:
395 return None
396 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
397 for fn in maybes:
398 # need to PING each one until we find one that's live
399 try:
400 s.connect(fn)
401 except socket.error:
402 # no good; attempt to clean it out, but don't fail:
403 try:
404 os.unlink(fn)
405 except IOError:
406 pass
407 else:
408 return s
409
410 def _remote(self, action):
411 s = self._find_grail_rc()
412 if not s:
413 return 0
414 s.send(action)
415 s.close()
416 return 1
417
418 def open(self, url, new=0, autoraise=1):
419 if new:
Georg Brandle8f24432005-10-03 14:16:44 +0000420 ok = self._remote("LOADNEW " + url)
Fred Drake3f8f1642001-07-19 03:46:26 +0000421 else:
Georg Brandle8f24432005-10-03 14:16:44 +0000422 ok = self._remote("LOAD " + url)
423 return ok
Fred Drake3f8f1642001-07-19 03:46:26 +0000424
Fred Drakec70b4482000-07-09 16:45:56 +0000425
Tim Peters658cba62001-02-09 20:06:00 +0000426#
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000427# Platform support for Unix
428#
Fred Drakec70b4482000-07-09 16:45:56 +0000429
Georg Brandle8f24432005-10-03 14:16:44 +0000430# These are the right tests because all these Unix browsers require either
431# a console terminal or an X display to run.
Fred Drakec70b4482000-07-09 16:45:56 +0000432
Neal Norwitz196f7332005-10-04 03:17:49 +0000433def register_X_browsers():
Georg Brandle8f24432005-10-03 14:16:44 +0000434 # The default Gnome browser
435 if _iscommand("gconftool-2"):
436 # get the web browser string from gconftool
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000437 gc = 'gconftool-2 -g /desktop/gnome/url-handlers/http/command 2>/dev/null'
Georg Brandle8f24432005-10-03 14:16:44 +0000438 out = os.popen(gc)
439 commd = out.read().strip()
440 retncode = out.close()
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000441
Georg Brandle8f24432005-10-03 14:16:44 +0000442 # if successful, register it
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000443 if retncode is None and commd:
Georg Brandl23929f22006-01-20 21:03:35 +0000444 register("gnome", None, BackgroundBrowser(commd))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000445
Georg Brandl23929f22006-01-20 21:03:35 +0000446 # First, the Mozilla/Netscape browsers
Georg Brandl4a5a9182005-11-22 19:18:01 +0000447 for browser in ("mozilla-firefox", "firefox",
448 "mozilla-firebird", "firebird",
Thomas Wouters477c8d52006-05-27 19:21:47 +0000449 "seamonkey", "mozilla", "netscape"):
Georg Brandl4a5a9182005-11-22 19:18:01 +0000450 if _iscommand(browser):
451 register(browser, None, Mozilla(browser))
452
Georg Brandle8f24432005-10-03 14:16:44 +0000453 # Konqueror/kfm, the KDE browser.
Georg Brandlb9801132005-10-08 20:47:38 +0000454 if _iscommand("kfm"):
455 register("kfm", Konqueror, Konqueror("kfm"))
456 elif _iscommand("konqueror"):
457 register("konqueror", Konqueror, Konqueror("konqueror"))
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000458
Georg Brandle8f24432005-10-03 14:16:44 +0000459 # Gnome's Galeon and Epiphany
460 for browser in ("galeon", "epiphany"):
461 if _iscommand(browser):
462 register(browser, None, Galeon(browser))
Gustavo Niemeyer1456fde2002-11-25 17:25:04 +0000463
Georg Brandle8f24432005-10-03 14:16:44 +0000464 # Skipstone, another Gtk/Mozilla based browser
465 if _iscommand("skipstone"):
Georg Brandl23929f22006-01-20 21:03:35 +0000466 register("skipstone", None, BackgroundBrowser("skipstone"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000467
Georg Brandle8f24432005-10-03 14:16:44 +0000468 # Opera, quite popular
469 if _iscommand("opera"):
470 register("opera", None, Opera("opera"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000471
Georg Brandle8f24432005-10-03 14:16:44 +0000472 # Next, Mosaic -- old but still in use.
473 if _iscommand("mosaic"):
Georg Brandl23929f22006-01-20 21:03:35 +0000474 register("mosaic", None, BackgroundBrowser("mosaic"))
Fred Drake3f8f1642001-07-19 03:46:26 +0000475
Georg Brandle8f24432005-10-03 14:16:44 +0000476 # Grail, the Python browser. Does anybody still use it?
477 if _iscommand("grail"):
478 register("grail", Grail, None)
Fred Drake3f8f1642001-07-19 03:46:26 +0000479
Neal Norwitz196f7332005-10-04 03:17:49 +0000480# Prefer X browsers if present
481if os.environ.get("DISPLAY"):
482 register_X_browsers()
483
Georg Brandle8f24432005-10-03 14:16:44 +0000484# Also try console browsers
485if os.environ.get("TERM"):
486 # The Links/elinks browsers <http://artax.karlin.mff.cuni.cz/~mikulas/links/>
487 if _iscommand("links"):
Georg Brandl23929f22006-01-20 21:03:35 +0000488 register("links", None, GenericBrowser("links"))
Georg Brandle8f24432005-10-03 14:16:44 +0000489 if _iscommand("elinks"):
490 register("elinks", None, Elinks("elinks"))
491 # The Lynx browser <http://lynx.isc.org/>, <http://lynx.browser.org/>
492 if _iscommand("lynx"):
Georg Brandl23929f22006-01-20 21:03:35 +0000493 register("lynx", None, GenericBrowser("lynx"))
Georg Brandle8f24432005-10-03 14:16:44 +0000494 # The w3m browser <http://w3m.sourceforge.net/>
495 if _iscommand("w3m"):
Georg Brandl23929f22006-01-20 21:03:35 +0000496 register("w3m", None, GenericBrowser("w3m"))
Fred Drake3f8f1642001-07-19 03:46:26 +0000497
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000498#
499# Platform support for Windows
500#
Fred Drakec70b4482000-07-09 16:45:56 +0000501
502if sys.platform[:3] == "win":
Georg Brandle8f24432005-10-03 14:16:44 +0000503 class WindowsDefault(BaseBrowser):
504 def open(self, url, new=0, autoraise=1):
505 os.startfile(url)
506 return True # Oh, my...
507
508 _tryorder = []
509 _browsers = {}
510 # Prefer mozilla/netscape/opera if present
Thomas Wouters477c8d52006-05-27 19:21:47 +0000511 for browser in ("firefox", "firebird", "seamonkey", "mozilla",
512 "netscape", "opera"):
Georg Brandle8f24432005-10-03 14:16:44 +0000513 if _iscommand(browser):
Georg Brandl23929f22006-01-20 21:03:35 +0000514 register(browser, None, BackgroundBrowser(browser))
Fred Drakec70b4482000-07-09 16:45:56 +0000515 register("windows-default", WindowsDefault)
Fred Drakec70b4482000-07-09 16:45:56 +0000516
Fred Drakec70b4482000-07-09 16:45:56 +0000517#
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000518# Platform support for MacOS
519#
Fred Drakec70b4482000-07-09 16:45:56 +0000520
521try:
522 import ic
523except ImportError:
524 pass
525else:
Georg Brandle8f24432005-10-03 14:16:44 +0000526 class InternetConfig(BaseBrowser):
527 def open(self, url, new=0, autoraise=1):
528 ic.launchurl(url)
529 return True # Any way to get status?
530
531 register("internet-config", InternetConfig, update_tryorder=-1)
532
533if sys.platform == 'darwin':
534 # Adapted from patch submitted to SourceForge by Steven J. Burr
535 class MacOSX(BaseBrowser):
536 """Launcher class for Aqua browsers on Mac OS X
537
538 Optionally specify a browser name on instantiation. Note that this
539 will not work for Aqua browsers if the user has moved the application
540 package after installation.
541
542 If no browser is specified, the default browser, as specified in the
543 Internet System Preferences panel, will be used.
544 """
545 def __init__(self, name):
546 self.name = name
547
548 def open(self, url, new=0, autoraise=1):
549 assert "'" not in url
Georg Brandl23929f22006-01-20 21:03:35 +0000550 # hack for local urls
551 if not ':' in url:
552 url = 'file:'+url
Tim Peters887c0802006-01-20 23:40:56 +0000553
Georg Brandle8f24432005-10-03 14:16:44 +0000554 # new must be 0 or 1
555 new = int(bool(new))
556 if self.name == "default":
557 # User called open, open_new or get without a browser parameter
Georg Brandl1cb179e2005-11-09 21:42:48 +0000558 script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser
Georg Brandle8f24432005-10-03 14:16:44 +0000559 else:
560 # User called get and chose a browser
561 if self.name == "OmniWeb":
562 toWindow = ""
563 else:
564 # Include toWindow parameter of OpenURL command for browsers
565 # that support it. 0 == new window; -1 == existing
566 toWindow = "toWindow %d" % (new - 1)
Georg Brandl1cb179e2005-11-09 21:42:48 +0000567 cmd = 'OpenURL "%s"' % url.replace('"', '%22')
Georg Brandle8f24432005-10-03 14:16:44 +0000568 script = '''tell application "%s"
569 activate
570 %s %s
571 end tell''' % (self.name, cmd, toWindow)
572 # Open pipe to AppleScript through osascript command
573 osapipe = os.popen("osascript", "w")
574 if osapipe is None:
575 return False
576 # Write script to osascript's stdin
577 osapipe.write(script)
578 rc = osapipe.close()
579 return not rc
580
581 # Don't clear _tryorder or _browsers since OS X can use above Unix support
582 # (but we prefer using the OS X specific stuff)
583 register("MacOSX", None, MacOSX('default'), -1)
584
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000585
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000586#
587# Platform support for OS/2
588#
589
Georg Brandle8f24432005-10-03 14:16:44 +0000590if sys.platform[:3] == "os2" and _iscommand("netscape"):
591 _tryorder = []
592 _browsers = {}
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000593 register("os2netscape", None,
Georg Brandl23929f22006-01-20 21:03:35 +0000594 GenericBrowser(["start", "netscape", "%s"]), -1)
Georg Brandle8f24432005-10-03 14:16:44 +0000595
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000596
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000597# OK, now that we know what the default preference orders for each
598# platform are, allow user to override them with the BROWSER variable.
Raymond Hettinger54f02222002-06-01 14:18:47 +0000599if "BROWSER" in os.environ:
Georg Brandle8f24432005-10-03 14:16:44 +0000600 _userchoices = os.environ["BROWSER"].split(os.pathsep)
601 _userchoices.reverse()
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000602
Georg Brandle8f24432005-10-03 14:16:44 +0000603 # Treat choices in same way as if passed into get() but do register
604 # and prepend to _tryorder
605 for cmdline in _userchoices:
606 if cmdline != '':
607 _synthesize(cmdline, -1)
608 cmdline = None # to make del work if _userchoices was empty
609 del cmdline
610 del _userchoices
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000611
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000612# what to do if _tryorder is now empty?
Georg Brandle8f24432005-10-03 14:16:44 +0000613
614
615def main():
616 import getopt
617 usage = """Usage: %s [-n | -t] url
618 -n: open new window
619 -t: open new tab""" % sys.argv[0]
620 try:
621 opts, args = getopt.getopt(sys.argv[1:], 'ntd')
622 except getopt.error, msg:
623 print >>sys.stderr, msg
624 print >>sys.stderr, usage
625 sys.exit(1)
626 new_win = 0
627 for o, a in opts:
628 if o == '-n': new_win = 1
629 elif o == '-t': new_win = 2
630 if len(args) <> 1:
631 print >>sys.stderr, usage
632 sys.exit(1)
633
634 url = args[0]
635 open(url, new_win)
636
Georg Brandl23929f22006-01-20 21:03:35 +0000637 print "\a"
638
Georg Brandle8f24432005-10-03 14:16:44 +0000639if __name__ == "__main__":
640 main()