blob: 39f9b99390c4ce5b5578713e3a389918ca6dfb8b [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:
Fred Drake608d5742008-12-05 02:47:42 +0000226 import io
Amaury Forgeot d'Arcbc2ce572008-12-05 01:02:21 +0000227 inout = io.open(os.devnull, "r+")
Georg Brandl23929f22006-01-20 21:03:35 +0000228 else:
229 # for TTY browsers, we need stdin/out
230 inout = None
231 # if possible, put browser in separate process group, so
232 # keyboard interrupts don't affect browser as well as Python
233 setsid = getattr(os, 'setsid', None)
234 if not setsid:
235 setsid = getattr(os, 'setpgrp', None)
Tim Peters887c0802006-01-20 23:40:56 +0000236
Georg Brandl23929f22006-01-20 21:03:35 +0000237 p = subprocess.Popen(cmdline, close_fds=True, stdin=inout,
238 stdout=(self.redirect_stdout and inout or None),
239 stderr=inout, preexec_fn=setsid)
240 if remote:
241 # wait five secons. If the subprocess is not finished, the
242 # remote invocation has (hopefully) started a new instance.
243 time.sleep(1)
244 rc = p.poll()
245 if rc is None:
246 time.sleep(4)
247 rc = p.poll()
248 if rc is None:
249 return True
250 # if remote call failed, open() will try direct invocation
251 return not rc
252 elif self.background:
253 if p.poll() is None:
254 return True
255 else:
256 return False
257 else:
258 return not p.wait()
Fred Drake3f8f1642001-07-19 03:46:26 +0000259
260 def open(self, url, new=0, autoraise=1):
Georg Brandle8f24432005-10-03 14:16:44 +0000261 if new == 0:
262 action = self.remote_action
263 elif new == 1:
264 action = self.remote_action_newwin
265 elif new == 2:
266 if self.remote_action_newtab is None:
267 action = self.remote_action_newwin
268 else:
269 action = self.remote_action_newtab
Fred Drake3f8f1642001-07-19 03:46:26 +0000270 else:
Georg Brandl23929f22006-01-20 21:03:35 +0000271 raise Error("Bad 'new' parameter to open(); " +
272 "expected 0, 1, or 2, got %s" % new)
Tim Peters887c0802006-01-20 23:40:56 +0000273
Georg Brandl23929f22006-01-20 21:03:35 +0000274 args = [arg.replace("%s", url).replace("%action", action)
275 for arg in self.remote_args]
276 success = self._invoke(args, True, autoraise)
277 if not success:
278 # remote invocation failed, try straight way
279 args = [arg.replace("%s", url) for arg in self.args]
280 return self._invoke(args, False, False)
281 else:
282 return True
Fred Drake3f8f1642001-07-19 03:46:26 +0000283
284
Georg Brandle8f24432005-10-03 14:16:44 +0000285class Mozilla(UnixBrowser):
286 """Launcher class for Mozilla/Netscape browsers."""
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000287
Georg Brandl23929f22006-01-20 21:03:35 +0000288 raise_opts = ["-noraise", "-raise"]
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000289
Georg Brandl23929f22006-01-20 21:03:35 +0000290 remote_args = ['-remote', 'openURL(%s%action)']
291 remote_action = ""
292 remote_action_newwin = ",new-window"
293 remote_action_newtab = ",new-tab"
Tim Peters887c0802006-01-20 23:40:56 +0000294
Georg Brandl23929f22006-01-20 21:03:35 +0000295 background = True
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000296
Georg Brandle8f24432005-10-03 14:16:44 +0000297Netscape = Mozilla
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000298
299
Georg Brandle8f24432005-10-03 14:16:44 +0000300class Galeon(UnixBrowser):
301 """Launcher class for Galeon/Epiphany browsers."""
302
Georg Brandl23929f22006-01-20 21:03:35 +0000303 raise_opts = ["-noraise", ""]
304 remote_args = ['%action', '%s']
305 remote_action = "-n"
306 remote_action_newwin = "-w"
Georg Brandle8f24432005-10-03 14:16:44 +0000307
Georg Brandl23929f22006-01-20 21:03:35 +0000308 background = True
Fred Drake3f8f1642001-07-19 03:46:26 +0000309
310
Georg Brandle8f24432005-10-03 14:16:44 +0000311class Opera(UnixBrowser):
312 "Launcher class for Opera browser."
313
Georg Brandl23929f22006-01-20 21:03:35 +0000314 raise_opts = ["", "-raise"]
Georg Brandle8f24432005-10-03 14:16:44 +0000315
Georg Brandl23929f22006-01-20 21:03:35 +0000316 remote_args = ['-remote', 'openURL(%s%action)']
317 remote_action = ""
318 remote_action_newwin = ",new-window"
319 remote_action_newtab = ",new-page"
320 background = True
Georg Brandle8f24432005-10-03 14:16:44 +0000321
322
323class Elinks(UnixBrowser):
324 "Launcher class for Elinks browsers."
325
Georg Brandl23929f22006-01-20 21:03:35 +0000326 remote_args = ['-remote', 'openURL(%s%action)']
327 remote_action = ""
328 remote_action_newwin = ",new-window"
329 remote_action_newtab = ",new-tab"
330 background = False
Georg Brandle8f24432005-10-03 14:16:44 +0000331
Georg Brandl23929f22006-01-20 21:03:35 +0000332 # elinks doesn't like its stdout to be redirected -
333 # it uses redirected stdout as a signal to do -dump
334 redirect_stdout = False
335
336
337class Konqueror(BaseBrowser):
338 """Controller for the KDE File Manager (kfm, or Konqueror).
339
340 See the output of ``kfmclient --commands``
341 for more information on the Konqueror remote-control interface.
342 """
343
344 def open(self, url, new=0, autoraise=1):
345 # XXX Currently I know no way to prevent KFM from opening a new win.
346 if new == 2:
347 action = "newTab"
348 else:
349 action = "openURL"
Tim Peters887c0802006-01-20 23:40:56 +0000350
Amaury Forgeot d'Arc2b2b44d2008-05-12 14:41:00 +0000351 import io
352 devnull = io.open(os.devnull, "r+")
Georg Brandl23929f22006-01-20 21:03:35 +0000353 # if possible, put browser in separate process group, so
354 # keyboard interrupts don't affect browser as well as Python
355 setsid = getattr(os, 'setsid', None)
356 if not setsid:
357 setsid = getattr(os, 'setpgrp', None)
Tim Peters887c0802006-01-20 23:40:56 +0000358
Georg Brandl23929f22006-01-20 21:03:35 +0000359 try:
360 p = subprocess.Popen(["kfmclient", action, url],
361 close_fds=True, stdin=devnull,
362 stdout=devnull, stderr=devnull)
363 except OSError:
364 # fall through to next variant
365 pass
366 else:
367 p.wait()
368 # kfmclient's return code unfortunately has no meaning as it seems
369 return True
370
371 try:
372 p = subprocess.Popen(["konqueror", "--silent", url],
373 close_fds=True, stdin=devnull,
374 stdout=devnull, stderr=devnull,
375 preexec_fn=setsid)
376 except OSError:
377 # fall through to next variant
378 pass
379 else:
380 if p.poll() is None:
381 # Should be running now.
382 return True
Tim Peters887c0802006-01-20 23:40:56 +0000383
Georg Brandl23929f22006-01-20 21:03:35 +0000384 try:
385 p = subprocess.Popen(["kfm", "-d", url],
386 close_fds=True, stdin=devnull,
387 stdout=devnull, stderr=devnull,
388 preexec_fn=setsid)
389 except OSError:
390 return False
391 else:
392 return (p.poll() is None)
Georg Brandle8f24432005-10-03 14:16:44 +0000393
394
395class Grail(BaseBrowser):
Fred Drake3f8f1642001-07-19 03:46:26 +0000396 # There should be a way to maintain a connection to Grail, but the
397 # Grail remote control protocol doesn't really allow that at this
Georg Brandl23929f22006-01-20 21:03:35 +0000398 # point. It probably never will!
Fred Drake3f8f1642001-07-19 03:46:26 +0000399 def _find_grail_rc(self):
400 import glob
401 import pwd
402 import socket
403 import tempfile
404 tempdir = os.path.join(tempfile.gettempdir(),
405 ".grail-unix")
Fred Drake16623fe2001-10-13 16:00:52 +0000406 user = pwd.getpwuid(os.getuid())[0]
Fred Drake3f8f1642001-07-19 03:46:26 +0000407 filename = os.path.join(tempdir, user + "-*")
408 maybes = glob.glob(filename)
409 if not maybes:
410 return None
411 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
412 for fn in maybes:
413 # need to PING each one until we find one that's live
414 try:
415 s.connect(fn)
416 except socket.error:
417 # no good; attempt to clean it out, but don't fail:
418 try:
419 os.unlink(fn)
420 except IOError:
421 pass
422 else:
423 return s
424
425 def _remote(self, action):
426 s = self._find_grail_rc()
427 if not s:
428 return 0
429 s.send(action)
430 s.close()
431 return 1
432
433 def open(self, url, new=0, autoraise=1):
434 if new:
Georg Brandle8f24432005-10-03 14:16:44 +0000435 ok = self._remote("LOADNEW " + url)
Fred Drake3f8f1642001-07-19 03:46:26 +0000436 else:
Georg Brandle8f24432005-10-03 14:16:44 +0000437 ok = self._remote("LOAD " + url)
438 return ok
Fred Drake3f8f1642001-07-19 03:46:26 +0000439
Fred Drakec70b4482000-07-09 16:45:56 +0000440
Tim Peters658cba62001-02-09 20:06:00 +0000441#
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000442# Platform support for Unix
443#
Fred Drakec70b4482000-07-09 16:45:56 +0000444
Georg Brandle8f24432005-10-03 14:16:44 +0000445# These are the right tests because all these Unix browsers require either
446# a console terminal or an X display to run.
Fred Drakec70b4482000-07-09 16:45:56 +0000447
Neal Norwitz196f7332005-10-04 03:17:49 +0000448def register_X_browsers():
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000449
Guido van Rossumd8faa362007-04-27 19:54:29 +0000450 # The default GNOME browser
451 if "GNOME_DESKTOP_SESSION_ID" in os.environ and _iscommand("gnome-open"):
452 register("gnome-open", None, BackgroundBrowser("gnome-open"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000453
Guido van Rossumd8faa362007-04-27 19:54:29 +0000454 # The default KDE browser
455 if "KDE_FULL_SESSION" in os.environ and _iscommand("kfmclient"):
456 register("kfmclient", Konqueror, Konqueror("kfmclient"))
457
458 # The Mozilla/Netscape browsers
Georg Brandl4a5a9182005-11-22 19:18:01 +0000459 for browser in ("mozilla-firefox", "firefox",
460 "mozilla-firebird", "firebird",
Thomas Wouters477c8d52006-05-27 19:21:47 +0000461 "seamonkey", "mozilla", "netscape"):
Georg Brandl4a5a9182005-11-22 19:18:01 +0000462 if _iscommand(browser):
463 register(browser, None, Mozilla(browser))
464
Georg Brandle8f24432005-10-03 14:16:44 +0000465 # Konqueror/kfm, the KDE browser.
Georg Brandlb9801132005-10-08 20:47:38 +0000466 if _iscommand("kfm"):
467 register("kfm", Konqueror, Konqueror("kfm"))
468 elif _iscommand("konqueror"):
469 register("konqueror", Konqueror, Konqueror("konqueror"))
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000470
Georg Brandle8f24432005-10-03 14:16:44 +0000471 # Gnome's Galeon and Epiphany
472 for browser in ("galeon", "epiphany"):
473 if _iscommand(browser):
474 register(browser, None, Galeon(browser))
Gustavo Niemeyer1456fde2002-11-25 17:25:04 +0000475
Georg Brandle8f24432005-10-03 14:16:44 +0000476 # Skipstone, another Gtk/Mozilla based browser
477 if _iscommand("skipstone"):
Georg Brandl23929f22006-01-20 21:03:35 +0000478 register("skipstone", None, BackgroundBrowser("skipstone"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000479
Georg Brandle8f24432005-10-03 14:16:44 +0000480 # Opera, quite popular
481 if _iscommand("opera"):
482 register("opera", None, Opera("opera"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000483
Georg Brandle8f24432005-10-03 14:16:44 +0000484 # Next, Mosaic -- old but still in use.
485 if _iscommand("mosaic"):
Georg Brandl23929f22006-01-20 21:03:35 +0000486 register("mosaic", None, BackgroundBrowser("mosaic"))
Fred Drake3f8f1642001-07-19 03:46:26 +0000487
Georg Brandle8f24432005-10-03 14:16:44 +0000488 # Grail, the Python browser. Does anybody still use it?
489 if _iscommand("grail"):
490 register("grail", Grail, None)
Fred Drake3f8f1642001-07-19 03:46:26 +0000491
Neal Norwitz196f7332005-10-04 03:17:49 +0000492# Prefer X browsers if present
493if os.environ.get("DISPLAY"):
494 register_X_browsers()
495
Georg Brandle8f24432005-10-03 14:16:44 +0000496# Also try console browsers
497if os.environ.get("TERM"):
498 # The Links/elinks browsers <http://artax.karlin.mff.cuni.cz/~mikulas/links/>
499 if _iscommand("links"):
Georg Brandl23929f22006-01-20 21:03:35 +0000500 register("links", None, GenericBrowser("links"))
Georg Brandle8f24432005-10-03 14:16:44 +0000501 if _iscommand("elinks"):
502 register("elinks", None, Elinks("elinks"))
503 # The Lynx browser <http://lynx.isc.org/>, <http://lynx.browser.org/>
504 if _iscommand("lynx"):
Georg Brandl23929f22006-01-20 21:03:35 +0000505 register("lynx", None, GenericBrowser("lynx"))
Georg Brandle8f24432005-10-03 14:16:44 +0000506 # The w3m browser <http://w3m.sourceforge.net/>
507 if _iscommand("w3m"):
Georg Brandl23929f22006-01-20 21:03:35 +0000508 register("w3m", None, GenericBrowser("w3m"))
Fred Drake3f8f1642001-07-19 03:46:26 +0000509
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000510#
511# Platform support for Windows
512#
Fred Drakec70b4482000-07-09 16:45:56 +0000513
514if sys.platform[:3] == "win":
Georg Brandle8f24432005-10-03 14:16:44 +0000515 class WindowsDefault(BaseBrowser):
516 def open(self, url, new=0, autoraise=1):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000517 try:
518 os.startfile(url)
519 except WindowsError:
520 # [Error 22] No application is associated with the specified
521 # file for this operation: '<URL>'
522 return False
523 else:
524 return True
Georg Brandle8f24432005-10-03 14:16:44 +0000525
526 _tryorder = []
527 _browsers = {}
Guido van Rossumd8faa362007-04-27 19:54:29 +0000528
529 # First try to use the default Windows browser
530 register("windows-default", WindowsDefault)
531
532 # Detect some common Windows browsers, fallback to IE
533 iexplore = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"),
534 "Internet Explorer\\IEXPLORE.EXE")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000535 for browser in ("firefox", "firebird", "seamonkey", "mozilla",
Guido van Rossumd8faa362007-04-27 19:54:29 +0000536 "netscape", "opera", iexplore):
Georg Brandle8f24432005-10-03 14:16:44 +0000537 if _iscommand(browser):
Georg Brandl23929f22006-01-20 21:03:35 +0000538 register(browser, None, BackgroundBrowser(browser))
Fred Drakec70b4482000-07-09 16:45:56 +0000539
Fred Drakec70b4482000-07-09 16:45:56 +0000540#
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000541# Platform support for MacOS
542#
Fred Drakec70b4482000-07-09 16:45:56 +0000543
544try:
545 import ic
546except ImportError:
547 pass
548else:
Georg Brandle8f24432005-10-03 14:16:44 +0000549 class InternetConfig(BaseBrowser):
550 def open(self, url, new=0, autoraise=1):
551 ic.launchurl(url)
552 return True # Any way to get status?
553
554 register("internet-config", InternetConfig, update_tryorder=-1)
555
556if sys.platform == 'darwin':
557 # Adapted from patch submitted to SourceForge by Steven J. Burr
558 class MacOSX(BaseBrowser):
559 """Launcher class for Aqua browsers on Mac OS X
560
561 Optionally specify a browser name on instantiation. Note that this
562 will not work for Aqua browsers if the user has moved the application
563 package after installation.
564
565 If no browser is specified, the default browser, as specified in the
566 Internet System Preferences panel, will be used.
567 """
568 def __init__(self, name):
569 self.name = name
570
571 def open(self, url, new=0, autoraise=1):
572 assert "'" not in url
Georg Brandl23929f22006-01-20 21:03:35 +0000573 # hack for local urls
574 if not ':' in url:
575 url = 'file:'+url
Tim Peters887c0802006-01-20 23:40:56 +0000576
Georg Brandle8f24432005-10-03 14:16:44 +0000577 # new must be 0 or 1
578 new = int(bool(new))
579 if self.name == "default":
580 # User called open, open_new or get without a browser parameter
Georg Brandl1cb179e2005-11-09 21:42:48 +0000581 script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser
Georg Brandle8f24432005-10-03 14:16:44 +0000582 else:
583 # User called get and chose a browser
584 if self.name == "OmniWeb":
585 toWindow = ""
586 else:
587 # Include toWindow parameter of OpenURL command for browsers
588 # that support it. 0 == new window; -1 == existing
589 toWindow = "toWindow %d" % (new - 1)
Georg Brandl1cb179e2005-11-09 21:42:48 +0000590 cmd = 'OpenURL "%s"' % url.replace('"', '%22')
Georg Brandle8f24432005-10-03 14:16:44 +0000591 script = '''tell application "%s"
592 activate
593 %s %s
594 end tell''' % (self.name, cmd, toWindow)
595 # Open pipe to AppleScript through osascript command
596 osapipe = os.popen("osascript", "w")
597 if osapipe is None:
598 return False
599 # Write script to osascript's stdin
600 osapipe.write(script)
601 rc = osapipe.close()
602 return not rc
603
604 # Don't clear _tryorder or _browsers since OS X can use above Unix support
605 # (but we prefer using the OS X specific stuff)
606 register("MacOSX", None, MacOSX('default'), -1)
607
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000608
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000609#
610# Platform support for OS/2
611#
612
Georg Brandle8f24432005-10-03 14:16:44 +0000613if sys.platform[:3] == "os2" and _iscommand("netscape"):
614 _tryorder = []
615 _browsers = {}
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000616 register("os2netscape", None,
Georg Brandl23929f22006-01-20 21:03:35 +0000617 GenericBrowser(["start", "netscape", "%s"]), -1)
Georg Brandle8f24432005-10-03 14:16:44 +0000618
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000619
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000620# OK, now that we know what the default preference orders for each
621# platform are, allow user to override them with the BROWSER variable.
Raymond Hettinger54f02222002-06-01 14:18:47 +0000622if "BROWSER" in os.environ:
Georg Brandle8f24432005-10-03 14:16:44 +0000623 _userchoices = os.environ["BROWSER"].split(os.pathsep)
624 _userchoices.reverse()
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000625
Georg Brandle8f24432005-10-03 14:16:44 +0000626 # Treat choices in same way as if passed into get() but do register
627 # and prepend to _tryorder
628 for cmdline in _userchoices:
629 if cmdline != '':
630 _synthesize(cmdline, -1)
631 cmdline = None # to make del work if _userchoices was empty
632 del cmdline
633 del _userchoices
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000634
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000635# what to do if _tryorder is now empty?
Georg Brandle8f24432005-10-03 14:16:44 +0000636
637
638def main():
639 import getopt
640 usage = """Usage: %s [-n | -t] url
641 -n: open new window
642 -t: open new tab""" % sys.argv[0]
643 try:
644 opts, args = getopt.getopt(sys.argv[1:], 'ntd')
Guido van Rossumb940e112007-01-10 16:19:56 +0000645 except getopt.error as msg:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000646 print(msg, file=sys.stderr)
647 print(usage, file=sys.stderr)
Georg Brandle8f24432005-10-03 14:16:44 +0000648 sys.exit(1)
649 new_win = 0
650 for o, a in opts:
651 if o == '-n': new_win = 1
652 elif o == '-t': new_win = 2
Guido van Rossumb053cd82006-08-24 03:53:23 +0000653 if len(args) != 1:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000654 print(usage, file=sys.stderr)
Georg Brandle8f24432005-10-03 14:16:44 +0000655 sys.exit(1)
656
657 url = args[0]
658 open(url, new_win)
659
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000660 print("\a")
Georg Brandl23929f22006-01-20 21:03:35 +0000661
Georg Brandle8f24432005-10-03 14:16:44 +0000662if __name__ == "__main__":
663 main()