blob: 180803c2544a3523322355cc4f9bb14d6044fcc5 [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()
Georg Brandlb2afe852006-06-09 20:43:48 +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:
Georg Brandl2c94bf72006-09-24 10:36:01 +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:
Georg Brandl2c94bf72006-09-24 10:36:01 +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:
219 inout = file(os.devnull, "r+")
220 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
Georg Brandl23929f22006-01-20 21:03:35 +0000343 devnull = file(os.devnull, "r+")
344 # 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():
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000440
Georg Brandl8f06d022007-03-16 07:55:09 +0000441 # The default GNOME browser
442 if "GNOME_DESKTOP_SESSION_ID" in os.environ and _iscommand("gnome-open"):
443 register("gnome-open", None, BackgroundBrowser("gnome-open"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000444
Georg Brandl8f06d022007-03-16 07:55:09 +0000445 # The default KDE browser
446 if "KDE_FULL_SESSION" in os.environ and _iscommand("kfmclient"):
447 register("kfmclient", Konqueror, Konqueror("kfmclient"))
448
449 # The Mozilla/Netscape browsers
Georg Brandl4a5a9182005-11-22 19:18:01 +0000450 for browser in ("mozilla-firefox", "firefox",
451 "mozilla-firebird", "firebird",
Georg Brandl314acac2006-04-28 16:31:17 +0000452 "seamonkey", "mozilla", "netscape"):
Georg Brandl4a5a9182005-11-22 19:18:01 +0000453 if _iscommand(browser):
454 register(browser, None, Mozilla(browser))
455
Georg Brandle8f24432005-10-03 14:16:44 +0000456 # Konqueror/kfm, the KDE browser.
Georg Brandlb9801132005-10-08 20:47:38 +0000457 if _iscommand("kfm"):
458 register("kfm", Konqueror, Konqueror("kfm"))
459 elif _iscommand("konqueror"):
460 register("konqueror", Konqueror, Konqueror("konqueror"))
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000461
Georg Brandle8f24432005-10-03 14:16:44 +0000462 # Gnome's Galeon and Epiphany
463 for browser in ("galeon", "epiphany"):
464 if _iscommand(browser):
465 register(browser, None, Galeon(browser))
Gustavo Niemeyer1456fde2002-11-25 17:25:04 +0000466
Georg Brandle8f24432005-10-03 14:16:44 +0000467 # Skipstone, another Gtk/Mozilla based browser
468 if _iscommand("skipstone"):
Georg Brandl23929f22006-01-20 21:03:35 +0000469 register("skipstone", None, BackgroundBrowser("skipstone"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000470
Georg Brandle8f24432005-10-03 14:16:44 +0000471 # Opera, quite popular
472 if _iscommand("opera"):
473 register("opera", None, Opera("opera"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000474
Georg Brandle8f24432005-10-03 14:16:44 +0000475 # Next, Mosaic -- old but still in use.
476 if _iscommand("mosaic"):
Georg Brandl23929f22006-01-20 21:03:35 +0000477 register("mosaic", None, BackgroundBrowser("mosaic"))
Fred Drake3f8f1642001-07-19 03:46:26 +0000478
Georg Brandle8f24432005-10-03 14:16:44 +0000479 # Grail, the Python browser. Does anybody still use it?
480 if _iscommand("grail"):
481 register("grail", Grail, None)
Fred Drake3f8f1642001-07-19 03:46:26 +0000482
Neal Norwitz196f7332005-10-04 03:17:49 +0000483# Prefer X browsers if present
484if os.environ.get("DISPLAY"):
485 register_X_browsers()
486
Georg Brandle8f24432005-10-03 14:16:44 +0000487# Also try console browsers
488if os.environ.get("TERM"):
489 # The Links/elinks browsers <http://artax.karlin.mff.cuni.cz/~mikulas/links/>
490 if _iscommand("links"):
Georg Brandl23929f22006-01-20 21:03:35 +0000491 register("links", None, GenericBrowser("links"))
Georg Brandle8f24432005-10-03 14:16:44 +0000492 if _iscommand("elinks"):
493 register("elinks", None, Elinks("elinks"))
494 # The Lynx browser <http://lynx.isc.org/>, <http://lynx.browser.org/>
495 if _iscommand("lynx"):
Georg Brandl23929f22006-01-20 21:03:35 +0000496 register("lynx", None, GenericBrowser("lynx"))
Georg Brandle8f24432005-10-03 14:16:44 +0000497 # The w3m browser <http://w3m.sourceforge.net/>
498 if _iscommand("w3m"):
Georg Brandl23929f22006-01-20 21:03:35 +0000499 register("w3m", None, GenericBrowser("w3m"))
Fred Drake3f8f1642001-07-19 03:46:26 +0000500
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000501#
502# Platform support for Windows
503#
Fred Drakec70b4482000-07-09 16:45:56 +0000504
505if sys.platform[:3] == "win":
Georg Brandle8f24432005-10-03 14:16:44 +0000506 class WindowsDefault(BaseBrowser):
507 def open(self, url, new=0, autoraise=1):
Georg Brandl8f06d022007-03-16 07:55:09 +0000508 try:
509 os.startfile(url)
510 except WindowsError:
511 # [Error 22] No application is associated with the specified
512 # file for this operation: '<URL>'
513 return False
514 else:
515 return True
Georg Brandle8f24432005-10-03 14:16:44 +0000516
517 _tryorder = []
518 _browsers = {}
Georg Brandl8f06d022007-03-16 07:55:09 +0000519
520 # First try to use the default Windows browser
521 register("windows-default", WindowsDefault)
522
523 # Detect some common Windows browsers, fallback to IE
524 iexplore = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"),
525 "Internet Explorer\\IEXPLORE.EXE")
Georg Brandl7377ad22006-05-03 17:46:13 +0000526 for browser in ("firefox", "firebird", "seamonkey", "mozilla",
Georg Brandl8f06d022007-03-16 07:55:09 +0000527 "netscape", "opera", iexplore):
Georg Brandle8f24432005-10-03 14:16:44 +0000528 if _iscommand(browser):
Georg Brandl23929f22006-01-20 21:03:35 +0000529 register(browser, None, BackgroundBrowser(browser))
Fred Drakec70b4482000-07-09 16:45:56 +0000530
Fred Drakec70b4482000-07-09 16:45:56 +0000531#
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000532# Platform support for MacOS
533#
Fred Drakec70b4482000-07-09 16:45:56 +0000534
535try:
536 import ic
537except ImportError:
538 pass
539else:
Georg Brandle8f24432005-10-03 14:16:44 +0000540 class InternetConfig(BaseBrowser):
541 def open(self, url, new=0, autoraise=1):
542 ic.launchurl(url)
543 return True # Any way to get status?
544
545 register("internet-config", InternetConfig, update_tryorder=-1)
546
547if sys.platform == 'darwin':
548 # Adapted from patch submitted to SourceForge by Steven J. Burr
549 class MacOSX(BaseBrowser):
550 """Launcher class for Aqua browsers on Mac OS X
551
552 Optionally specify a browser name on instantiation. Note that this
553 will not work for Aqua browsers if the user has moved the application
554 package after installation.
555
556 If no browser is specified, the default browser, as specified in the
557 Internet System Preferences panel, will be used.
558 """
559 def __init__(self, name):
560 self.name = name
561
562 def open(self, url, new=0, autoraise=1):
563 assert "'" not in url
Georg Brandl23929f22006-01-20 21:03:35 +0000564 # hack for local urls
565 if not ':' in url:
566 url = 'file:'+url
Tim Peters887c0802006-01-20 23:40:56 +0000567
Georg Brandle8f24432005-10-03 14:16:44 +0000568 # new must be 0 or 1
569 new = int(bool(new))
570 if self.name == "default":
571 # User called open, open_new or get without a browser parameter
Georg Brandl1cb179e2005-11-09 21:42:48 +0000572 script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser
Georg Brandle8f24432005-10-03 14:16:44 +0000573 else:
574 # User called get and chose a browser
575 if self.name == "OmniWeb":
576 toWindow = ""
577 else:
578 # Include toWindow parameter of OpenURL command for browsers
579 # that support it. 0 == new window; -1 == existing
580 toWindow = "toWindow %d" % (new - 1)
Georg Brandl1cb179e2005-11-09 21:42:48 +0000581 cmd = 'OpenURL "%s"' % url.replace('"', '%22')
Georg Brandle8f24432005-10-03 14:16:44 +0000582 script = '''tell application "%s"
583 activate
584 %s %s
585 end tell''' % (self.name, cmd, toWindow)
586 # Open pipe to AppleScript through osascript command
587 osapipe = os.popen("osascript", "w")
588 if osapipe is None:
589 return False
590 # Write script to osascript's stdin
591 osapipe.write(script)
592 rc = osapipe.close()
593 return not rc
594
595 # Don't clear _tryorder or _browsers since OS X can use above Unix support
596 # (but we prefer using the OS X specific stuff)
597 register("MacOSX", None, MacOSX('default'), -1)
598
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000599
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000600#
601# Platform support for OS/2
602#
603
Georg Brandle8f24432005-10-03 14:16:44 +0000604if sys.platform[:3] == "os2" and _iscommand("netscape"):
605 _tryorder = []
606 _browsers = {}
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000607 register("os2netscape", None,
Georg Brandl23929f22006-01-20 21:03:35 +0000608 GenericBrowser(["start", "netscape", "%s"]), -1)
Georg Brandle8f24432005-10-03 14:16:44 +0000609
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000610
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000611# OK, now that we know what the default preference orders for each
612# platform are, allow user to override them with the BROWSER variable.
Raymond Hettinger54f02222002-06-01 14:18:47 +0000613if "BROWSER" in os.environ:
Georg Brandle8f24432005-10-03 14:16:44 +0000614 _userchoices = os.environ["BROWSER"].split(os.pathsep)
615 _userchoices.reverse()
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000616
Georg Brandle8f24432005-10-03 14:16:44 +0000617 # Treat choices in same way as if passed into get() but do register
618 # and prepend to _tryorder
619 for cmdline in _userchoices:
620 if cmdline != '':
621 _synthesize(cmdline, -1)
622 cmdline = None # to make del work if _userchoices was empty
623 del cmdline
624 del _userchoices
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000625
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000626# what to do if _tryorder is now empty?
Georg Brandle8f24432005-10-03 14:16:44 +0000627
628
629def main():
630 import getopt
631 usage = """Usage: %s [-n | -t] url
632 -n: open new window
633 -t: open new tab""" % sys.argv[0]
634 try:
635 opts, args = getopt.getopt(sys.argv[1:], 'ntd')
636 except getopt.error, msg:
637 print >>sys.stderr, msg
638 print >>sys.stderr, usage
639 sys.exit(1)
640 new_win = 0
641 for o, a in opts:
642 if o == '-n': new_win = 1
643 elif o == '-t': new_win = 2
644 if len(args) <> 1:
645 print >>sys.stderr, usage
646 sys.exit(1)
647
648 url = args[0]
649 open(url, new_win)
650
Georg Brandl23929f22006-01-20 21:03:35 +0000651 print "\a"
652
Georg Brandle8f24432005-10-03 14:16:44 +0000653if __name__ == "__main__":
654 main()