blob: 5b441e6ecc067178478c121cf45952f5ed6a2078 [file] [log] [blame]
Benjamin Peterson90f5ba52010-03-11 22:53:45 +00001#! /usr/bin/env python3
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
Fred Drakeee763952008-12-10 06:02:39 +00005import io
Fred Drakec70b4482000-07-09 16:45:56 +00006import os
Guido van Rossumd8faa362007-04-27 19:54:29 +00007import shlex
Fred Drakec70b4482000-07-09 16:45:56 +00008import sys
Georg Brandle8f24432005-10-03 14:16:44 +00009import stat
Georg Brandl23929f22006-01-20 21:03:35 +000010import subprocess
11import time
Fred Drakec70b4482000-07-09 16:45:56 +000012
Georg Brandle8f24432005-10-03 14:16:44 +000013__all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"]
Skip Montanaro40fc1602001-03-01 04:27:19 +000014
Fred Drakec70b4482000-07-09 16:45:56 +000015class Error(Exception):
16 pass
17
Tim Peters658cba62001-02-09 20:06:00 +000018_browsers = {} # Dictionary of available browser controllers
19_tryorder = [] # Preference order of available browsers
Fred Drakec70b4482000-07-09 16:45:56 +000020
Georg Brandle8f24432005-10-03 14:16:44 +000021def register(name, klass, instance=None, update_tryorder=1):
Fred Drakec70b4482000-07-09 16:45:56 +000022 """Register a browser connector and, optionally, connection."""
23 _browsers[name.lower()] = [klass, instance]
Georg Brandle8f24432005-10-03 14:16:44 +000024 if update_tryorder > 0:
25 _tryorder.append(name)
26 elif update_tryorder < 0:
27 _tryorder.insert(0, name)
Fred Drakec70b4482000-07-09 16:45:56 +000028
Eric S. Raymondf7f18512001-01-23 13:16:32 +000029def get(using=None):
30 """Return a browser launcher instance appropriate for the environment."""
Raymond Hettinger10ff7062002-06-02 03:04:52 +000031 if using is not None:
Eric S. Raymondf7f18512001-01-23 13:16:32 +000032 alternatives = [using]
33 else:
34 alternatives = _tryorder
35 for browser in alternatives:
Raymond Hettingerbac788a2004-05-04 09:21:43 +000036 if '%s' in browser:
Georg Brandl23929f22006-01-20 21:03:35 +000037 # User gave us a command line, split it into name and args
Guido van Rossumd8faa362007-04-27 19:54:29 +000038 browser = shlex.split(browser)
39 if browser[-1] == '&':
40 return BackgroundBrowser(browser[:-1])
41 else:
42 return GenericBrowser(browser)
Eric S. Raymondf7f18512001-01-23 13:16:32 +000043 else:
Georg Brandle8f24432005-10-03 14:16:44 +000044 # User gave us a browser name or path.
Fred Drakef4e5bd92001-04-12 22:07:27 +000045 try:
46 command = _browsers[browser.lower()]
47 except KeyError:
48 command = _synthesize(browser)
Georg Brandle8f24432005-10-03 14:16:44 +000049 if command[1] is not None:
Eric S. Raymondf7f18512001-01-23 13:16:32 +000050 return command[1]
Georg Brandle8f24432005-10-03 14:16:44 +000051 elif command[0] is not None:
52 return command[0]()
Eric S. Raymondf7f18512001-01-23 13:16:32 +000053 raise Error("could not locate runnable browser")
Fred Drakec70b4482000-07-09 16:45:56 +000054
55# Please note: the following definition hides a builtin function.
Georg Brandle8f24432005-10-03 14:16:44 +000056# It is recommended one does "import webbrowser" and uses webbrowser.open(url)
57# instead of "from webbrowser import *".
Fred Drakec70b4482000-07-09 16:45:56 +000058
Alexandre Vassalottie223eb82009-07-29 20:12:15 +000059def open(url, new=0, autoraise=True):
Georg Brandle8f24432005-10-03 14:16:44 +000060 for name in _tryorder:
61 browser = get(name)
62 if browser.open(url, new, autoraise):
63 return True
64 return False
Fred Drakec70b4482000-07-09 16:45:56 +000065
Fred Drake3f8f1642001-07-19 03:46:26 +000066def open_new(url):
Georg Brandle8f24432005-10-03 14:16:44 +000067 return open(url, 1)
68
69def open_new_tab(url):
70 return open(url, 2)
Fred Drakec70b4482000-07-09 16:45:56 +000071
Fred Drakef4e5bd92001-04-12 22:07:27 +000072
Georg Brandle8f24432005-10-03 14:16:44 +000073def _synthesize(browser, update_tryorder=1):
Fred Drakef4e5bd92001-04-12 22:07:27 +000074 """Attempt to synthesize a controller base on existing controllers.
75
76 This is useful to create a controller when a user specifies a path to
77 an entry in the BROWSER environment variable -- we can copy a general
78 controller to operate using a specific installation of the desired
79 browser in this way.
80
81 If we can't create a controller in this way, or if there is no
82 executable for the requested browser, return [None, None].
83
84 """
Georg Brandle8f24432005-10-03 14:16:44 +000085 cmd = browser.split()[0]
86 if not _iscommand(cmd):
Fred Drakef4e5bd92001-04-12 22:07:27 +000087 return [None, None]
Georg Brandle8f24432005-10-03 14:16:44 +000088 name = os.path.basename(cmd)
Fred Drakef4e5bd92001-04-12 22:07:27 +000089 try:
90 command = _browsers[name.lower()]
91 except KeyError:
92 return [None, None]
93 # now attempt to clone to fit the new name:
94 controller = command[1]
95 if controller and name.lower() == controller.basename:
96 import copy
97 controller = copy.copy(controller)
98 controller.name = browser
99 controller.basename = os.path.basename(browser)
Georg Brandle8f24432005-10-03 14:16:44 +0000100 register(browser, None, controller, update_tryorder)
Fred Drakef4e5bd92001-04-12 22:07:27 +0000101 return [None, controller]
Andrew M. Kuchling118aa532001-08-13 14:37:23 +0000102 return [None, None]
Fred Drakef4e5bd92001-04-12 22:07:27 +0000103
Fred Drake3f8f1642001-07-19 03:46:26 +0000104
Georg Brandle8f24432005-10-03 14:16:44 +0000105if sys.platform[:3] == "win":
106 def _isexecutable(cmd):
107 cmd = cmd.lower()
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000108 if os.path.isfile(cmd) and cmd.endswith((".exe", ".bat")):
Georg Brandle8f24432005-10-03 14:16:44 +0000109 return True
110 for ext in ".exe", ".bat":
111 if os.path.isfile(cmd + ext):
112 return True
113 return False
114else:
115 def _isexecutable(cmd):
116 if os.path.isfile(cmd):
117 mode = os.stat(cmd)[stat.ST_MODE]
118 if mode & stat.S_IXUSR or mode & stat.S_IXGRP or mode & stat.S_IXOTH:
119 return True
120 return False
121
Fred Drake3f8f1642001-07-19 03:46:26 +0000122def _iscommand(cmd):
Georg Brandle8f24432005-10-03 14:16:44 +0000123 """Return True if cmd is executable or can be found on the executable
124 search path."""
125 if _isexecutable(cmd):
126 return True
Fred Drake3f8f1642001-07-19 03:46:26 +0000127 path = os.environ.get("PATH")
128 if not path:
Tim Petersbc0e9102002-04-04 22:55:58 +0000129 return False
Fred Drake3f8f1642001-07-19 03:46:26 +0000130 for d in path.split(os.pathsep):
131 exe = os.path.join(d, cmd)
Georg Brandle8f24432005-10-03 14:16:44 +0000132 if _isexecutable(exe):
Tim Petersbc0e9102002-04-04 22:55:58 +0000133 return True
134 return False
Fred Drake3f8f1642001-07-19 03:46:26 +0000135
136
Georg Brandle8f24432005-10-03 14:16:44 +0000137# General parent classes
138
139class BaseBrowser(object):
Georg Brandl23929f22006-01-20 21:03:35 +0000140 """Parent class for all browsers. Do not use directly."""
Tim Peters887c0802006-01-20 23:40:56 +0000141
Georg Brandl23929f22006-01-20 21:03:35 +0000142 args = ['%s']
Tim Peters887c0802006-01-20 23:40:56 +0000143
Georg Brandle8f24432005-10-03 14:16:44 +0000144 def __init__(self, name=""):
145 self.name = name
Georg Brandlb9801132005-10-08 20:47:38 +0000146 self.basename = name
Tim Peters536cf992005-12-25 23:18:31 +0000147
Alexandre Vassalottie223eb82009-07-29 20:12:15 +0000148 def open(self, url, new=0, autoraise=True):
Neal Norwitz196f7332005-10-04 03:17:49 +0000149 raise NotImplementedError
150
Georg Brandle8f24432005-10-03 14:16:44 +0000151 def open_new(self, url):
152 return self.open(url, 1)
153
154 def open_new_tab(self, url):
155 return self.open(url, 2)
Fred Drake3f8f1642001-07-19 03:46:26 +0000156
157
Georg Brandle8f24432005-10-03 14:16:44 +0000158class GenericBrowser(BaseBrowser):
159 """Class for all browsers started with a command
160 and without remote functionality."""
161
Georg Brandl23929f22006-01-20 21:03:35 +0000162 def __init__(self, name):
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000163 if isinstance(name, str):
Georg Brandl23929f22006-01-20 21:03:35 +0000164 self.name = name
Guido van Rossum992d4a32007-07-11 13:09:30 +0000165 self.args = ["%s"]
Georg Brandl23929f22006-01-20 21:03:35 +0000166 else:
167 # name should be a list with arguments
168 self.name = name[0]
169 self.args = name[1:]
Georg Brandlb9801132005-10-08 20:47:38 +0000170 self.basename = os.path.basename(self.name)
Fred Drake3f8f1642001-07-19 03:46:26 +0000171
Alexandre Vassalottie223eb82009-07-29 20:12:15 +0000172 def open(self, url, new=0, autoraise=True):
Tim Peters887c0802006-01-20 23:40:56 +0000173 cmdline = [self.name] + [arg.replace("%s", url)
Georg Brandl23929f22006-01-20 21:03:35 +0000174 for arg in self.args]
175 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000176 if sys.platform[:3] == 'win':
177 p = subprocess.Popen(cmdline)
178 else:
179 p = subprocess.Popen(cmdline, close_fds=True)
Georg Brandl23929f22006-01-20 21:03:35 +0000180 return not p.wait()
181 except OSError:
182 return False
183
184
185class BackgroundBrowser(GenericBrowser):
186 """Class for all browsers which are to be started in the
187 background."""
188
Alexandre Vassalottie223eb82009-07-29 20:12:15 +0000189 def open(self, url, new=0, autoraise=True):
Georg Brandl23929f22006-01-20 21:03:35 +0000190 cmdline = [self.name] + [arg.replace("%s", url)
191 for arg in self.args]
Georg Brandl23929f22006-01-20 21:03:35 +0000192 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000193 if sys.platform[:3] == 'win':
194 p = subprocess.Popen(cmdline)
195 else:
196 setsid = getattr(os, 'setsid', None)
197 if not setsid:
198 setsid = getattr(os, 'setpgrp', None)
199 p = subprocess.Popen(cmdline, close_fds=True, preexec_fn=setsid)
Georg Brandl23929f22006-01-20 21:03:35 +0000200 return (p.poll() is None)
201 except OSError:
202 return False
Fred Drake3f8f1642001-07-19 03:46:26 +0000203
204
Georg Brandle8f24432005-10-03 14:16:44 +0000205class UnixBrowser(BaseBrowser):
206 """Parent class for all Unix browsers with remote functionality."""
Fred Drake3f8f1642001-07-19 03:46:26 +0000207
Georg Brandle8f24432005-10-03 14:16:44 +0000208 raise_opts = None
Georg Brandl23929f22006-01-20 21:03:35 +0000209 remote_args = ['%action', '%s']
Georg Brandle8f24432005-10-03 14:16:44 +0000210 remote_action = None
211 remote_action_newwin = None
212 remote_action_newtab = None
Georg Brandl23929f22006-01-20 21:03:35 +0000213 background = False
214 redirect_stdout = True
Georg Brandle8f24432005-10-03 14:16:44 +0000215
Georg Brandl23929f22006-01-20 21:03:35 +0000216 def _invoke(self, args, remote, autoraise):
217 raise_opt = []
218 if remote and self.raise_opts:
219 # use autoraise argument only for remote invocation
Alexandre Vassalottie223eb82009-07-29 20:12:15 +0000220 autoraise = int(autoraise)
Georg Brandl23929f22006-01-20 21:03:35 +0000221 opt = self.raise_opts[autoraise]
222 if opt: raise_opt = [opt]
223
224 cmdline = [self.name] + raise_opt + args
Tim Peters887c0802006-01-20 23:40:56 +0000225
Georg Brandl23929f22006-01-20 21:03:35 +0000226 if remote or self.background:
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
Georg Brandl23929f22006-01-20 21:03:35 +0000231 p = subprocess.Popen(cmdline, close_fds=True, stdin=inout,
232 stdout=(self.redirect_stdout and inout or None),
Gregory P. Smith8f7724f2011-03-15 15:24:43 -0400233 stderr=inout, start_new_session=True)
Georg Brandl23929f22006-01-20 21:03:35 +0000234 if remote:
Ezio Melotti1beea5b2011-07-21 11:16:32 +0300235 # wait five seconds. If the subprocess is not finished, the
Georg Brandl23929f22006-01-20 21:03:35 +0000236 # remote invocation has (hopefully) started a new instance.
237 time.sleep(1)
238 rc = p.poll()
239 if rc is None:
240 time.sleep(4)
241 rc = p.poll()
242 if rc is None:
243 return True
244 # if remote call failed, open() will try direct invocation
245 return not rc
246 elif self.background:
247 if p.poll() is None:
248 return True
249 else:
250 return False
251 else:
252 return not p.wait()
Fred Drake3f8f1642001-07-19 03:46:26 +0000253
Alexandre Vassalottie223eb82009-07-29 20:12:15 +0000254 def open(self, url, new=0, autoraise=True):
Georg Brandle8f24432005-10-03 14:16:44 +0000255 if new == 0:
256 action = self.remote_action
257 elif new == 1:
258 action = self.remote_action_newwin
259 elif new == 2:
260 if self.remote_action_newtab is None:
261 action = self.remote_action_newwin
262 else:
263 action = self.remote_action_newtab
Fred Drake3f8f1642001-07-19 03:46:26 +0000264 else:
Georg Brandl23929f22006-01-20 21:03:35 +0000265 raise Error("Bad 'new' parameter to open(); " +
266 "expected 0, 1, or 2, got %s" % new)
Tim Peters887c0802006-01-20 23:40:56 +0000267
Georg Brandl23929f22006-01-20 21:03:35 +0000268 args = [arg.replace("%s", url).replace("%action", action)
269 for arg in self.remote_args]
270 success = self._invoke(args, True, autoraise)
271 if not success:
272 # remote invocation failed, try straight way
273 args = [arg.replace("%s", url) for arg in self.args]
274 return self._invoke(args, False, False)
275 else:
276 return True
Fred Drake3f8f1642001-07-19 03:46:26 +0000277
278
Georg Brandle8f24432005-10-03 14:16:44 +0000279class Mozilla(UnixBrowser):
280 """Launcher class for Mozilla/Netscape browsers."""
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000281
Georg Brandl23929f22006-01-20 21:03:35 +0000282 raise_opts = ["-noraise", "-raise"]
Georg Brandl23929f22006-01-20 21:03:35 +0000283 remote_args = ['-remote', 'openURL(%s%action)']
284 remote_action = ""
285 remote_action_newwin = ",new-window"
286 remote_action_newtab = ",new-tab"
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 Brandl23929f22006-01-20 21:03:35 +0000299 background = True
Fred Drake3f8f1642001-07-19 03:46:26 +0000300
301
Senthil Kumaranea6b4182011-12-21 22:20:32 +0800302class Chrome(UnixBrowser):
303 "Launcher class for Google Chrome browser."
304
305 remote_args = ['%action', '%s']
306 remote_action = ""
307 remote_action_newwin = "--new-window"
308 remote_action_newtab = ""
309 background = True
310
311Chromium = Chrome
312
313
Georg Brandle8f24432005-10-03 14:16:44 +0000314class Opera(UnixBrowser):
315 "Launcher class for Opera browser."
316
Terry Reedydad532f2010-12-28 19:30:19 +0000317 raise_opts = ["-noraise", ""]
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-page"
322 background = True
Georg Brandle8f24432005-10-03 14:16:44 +0000323
324
325class Elinks(UnixBrowser):
326 "Launcher class for Elinks browsers."
327
Georg Brandl23929f22006-01-20 21:03:35 +0000328 remote_args = ['-remote', 'openURL(%s%action)']
329 remote_action = ""
330 remote_action_newwin = ",new-window"
331 remote_action_newtab = ",new-tab"
332 background = False
Georg Brandle8f24432005-10-03 14:16:44 +0000333
Georg Brandl23929f22006-01-20 21:03:35 +0000334 # elinks doesn't like its stdout to be redirected -
335 # it uses redirected stdout as a signal to do -dump
336 redirect_stdout = False
337
338
339class Konqueror(BaseBrowser):
340 """Controller for the KDE File Manager (kfm, or Konqueror).
341
342 See the output of ``kfmclient --commands``
343 for more information on the Konqueror remote-control interface.
344 """
345
Alexandre Vassalottie223eb82009-07-29 20:12:15 +0000346 def open(self, url, new=0, autoraise=True):
Georg Brandl23929f22006-01-20 21:03:35 +0000347 # XXX Currently I know no way to prevent KFM from opening a new win.
348 if new == 2:
349 action = "newTab"
350 else:
351 action = "openURL"
Tim Peters887c0802006-01-20 23:40:56 +0000352
Amaury Forgeot d'Arc2b2b44d2008-05-12 14:41:00 +0000353 devnull = io.open(os.devnull, "r+")
Georg Brandl23929f22006-01-20 21:03:35 +0000354 # if possible, put browser in separate process group, so
355 # keyboard interrupts don't affect browser as well as Python
356 setsid = getattr(os, 'setsid', None)
357 if not setsid:
358 setsid = getattr(os, 'setpgrp', None)
Tim Peters887c0802006-01-20 23:40:56 +0000359
Georg Brandl23929f22006-01-20 21:03:35 +0000360 try:
361 p = subprocess.Popen(["kfmclient", action, url],
362 close_fds=True, stdin=devnull,
363 stdout=devnull, stderr=devnull)
364 except OSError:
365 # fall through to next variant
366 pass
367 else:
368 p.wait()
369 # kfmclient's return code unfortunately has no meaning as it seems
370 return True
371
372 try:
373 p = subprocess.Popen(["konqueror", "--silent", url],
374 close_fds=True, stdin=devnull,
375 stdout=devnull, stderr=devnull,
376 preexec_fn=setsid)
377 except OSError:
378 # fall through to next variant
379 pass
380 else:
381 if p.poll() is None:
382 # Should be running now.
383 return True
Tim Peters887c0802006-01-20 23:40:56 +0000384
Georg Brandl23929f22006-01-20 21:03:35 +0000385 try:
386 p = subprocess.Popen(["kfm", "-d", url],
387 close_fds=True, stdin=devnull,
388 stdout=devnull, stderr=devnull,
389 preexec_fn=setsid)
390 except OSError:
391 return False
392 else:
393 return (p.poll() is None)
Georg Brandle8f24432005-10-03 14:16:44 +0000394
395
396class Grail(BaseBrowser):
Fred Drake3f8f1642001-07-19 03:46:26 +0000397 # There should be a way to maintain a connection to Grail, but the
398 # Grail remote control protocol doesn't really allow that at this
Georg Brandl23929f22006-01-20 21:03:35 +0000399 # point. It probably never will!
Fred Drake3f8f1642001-07-19 03:46:26 +0000400 def _find_grail_rc(self):
401 import glob
402 import pwd
403 import socket
404 import tempfile
405 tempdir = os.path.join(tempfile.gettempdir(),
406 ".grail-unix")
Fred Drake16623fe2001-10-13 16:00:52 +0000407 user = pwd.getpwuid(os.getuid())[0]
Fred Drake3f8f1642001-07-19 03:46:26 +0000408 filename = os.path.join(tempdir, user + "-*")
409 maybes = glob.glob(filename)
410 if not maybes:
411 return None
412 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
413 for fn in maybes:
414 # need to PING each one until we find one that's live
415 try:
416 s.connect(fn)
417 except socket.error:
418 # no good; attempt to clean it out, but don't fail:
419 try:
420 os.unlink(fn)
421 except IOError:
422 pass
423 else:
424 return s
425
426 def _remote(self, action):
427 s = self._find_grail_rc()
428 if not s:
429 return 0
430 s.send(action)
431 s.close()
432 return 1
433
Alexandre Vassalottie223eb82009-07-29 20:12:15 +0000434 def open(self, url, new=0, autoraise=True):
Fred Drake3f8f1642001-07-19 03:46:26 +0000435 if new:
Georg Brandle8f24432005-10-03 14:16:44 +0000436 ok = self._remote("LOADNEW " + url)
Fred Drake3f8f1642001-07-19 03:46:26 +0000437 else:
Georg Brandle8f24432005-10-03 14:16:44 +0000438 ok = self._remote("LOAD " + url)
439 return ok
Fred Drake3f8f1642001-07-19 03:46:26 +0000440
Fred Drakec70b4482000-07-09 16:45:56 +0000441
Tim Peters658cba62001-02-09 20:06:00 +0000442#
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000443# Platform support for Unix
444#
Fred Drakec70b4482000-07-09 16:45:56 +0000445
Georg Brandle8f24432005-10-03 14:16:44 +0000446# These are the right tests because all these Unix browsers require either
447# a console terminal or an X display to run.
Fred Drakec70b4482000-07-09 16:45:56 +0000448
Neal Norwitz196f7332005-10-04 03:17:49 +0000449def register_X_browsers():
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000450
Matthias Kloseda80b1e2012-04-04 14:19:04 +0200451 # use xdg-open if around
452 if _iscommand("xdg-open"):
453 register("xdg-open", None, BackgroundBrowser("xdg-open"))
454
455 # The default GNOME3 browser
456 if "GNOME_DESKTOP_SESSION_ID" in os.environ and _iscommand("gvfs-open"):
457 register("gvfs-open", None, BackgroundBrowser("gvfs-open"))
458
Guido van Rossumd8faa362007-04-27 19:54:29 +0000459 # The default GNOME browser
460 if "GNOME_DESKTOP_SESSION_ID" in os.environ and _iscommand("gnome-open"):
461 register("gnome-open", None, BackgroundBrowser("gnome-open"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000462
Guido van Rossumd8faa362007-04-27 19:54:29 +0000463 # The default KDE browser
464 if "KDE_FULL_SESSION" in os.environ and _iscommand("kfmclient"):
465 register("kfmclient", Konqueror, Konqueror("kfmclient"))
466
467 # The Mozilla/Netscape browsers
Georg Brandl4a5a9182005-11-22 19:18:01 +0000468 for browser in ("mozilla-firefox", "firefox",
469 "mozilla-firebird", "firebird",
Thomas Wouters477c8d52006-05-27 19:21:47 +0000470 "seamonkey", "mozilla", "netscape"):
Georg Brandl4a5a9182005-11-22 19:18:01 +0000471 if _iscommand(browser):
472 register(browser, None, Mozilla(browser))
473
Georg Brandle8f24432005-10-03 14:16:44 +0000474 # Konqueror/kfm, the KDE browser.
Georg Brandlb9801132005-10-08 20:47:38 +0000475 if _iscommand("kfm"):
476 register("kfm", Konqueror, Konqueror("kfm"))
477 elif _iscommand("konqueror"):
478 register("konqueror", Konqueror, Konqueror("konqueror"))
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000479
Georg Brandle8f24432005-10-03 14:16:44 +0000480 # Gnome's Galeon and Epiphany
481 for browser in ("galeon", "epiphany"):
482 if _iscommand(browser):
483 register(browser, None, Galeon(browser))
Gustavo Niemeyer1456fde2002-11-25 17:25:04 +0000484
Georg Brandle8f24432005-10-03 14:16:44 +0000485 # Skipstone, another Gtk/Mozilla based browser
486 if _iscommand("skipstone"):
Georg Brandl23929f22006-01-20 21:03:35 +0000487 register("skipstone", None, BackgroundBrowser("skipstone"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000488
Senthil Kumaranea6b4182011-12-21 22:20:32 +0800489 # Google Chrome/Chromium browsers
490 for browser in ("google-chrome", "chrome", "chromium", "chromium-browser"):
491 if _iscommand(browser):
492 register(browser, None, Chrome(browser))
493
Georg Brandle8f24432005-10-03 14:16:44 +0000494 # Opera, quite popular
495 if _iscommand("opera"):
496 register("opera", None, Opera("opera"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000497
Georg Brandle8f24432005-10-03 14:16:44 +0000498 # Next, Mosaic -- old but still in use.
499 if _iscommand("mosaic"):
Georg Brandl23929f22006-01-20 21:03:35 +0000500 register("mosaic", None, BackgroundBrowser("mosaic"))
Fred Drake3f8f1642001-07-19 03:46:26 +0000501
Georg Brandle8f24432005-10-03 14:16:44 +0000502 # Grail, the Python browser. Does anybody still use it?
503 if _iscommand("grail"):
504 register("grail", Grail, None)
Fred Drake3f8f1642001-07-19 03:46:26 +0000505
Neal Norwitz196f7332005-10-04 03:17:49 +0000506# Prefer X browsers if present
507if os.environ.get("DISPLAY"):
508 register_X_browsers()
509
Georg Brandle8f24432005-10-03 14:16:44 +0000510# Also try console browsers
511if os.environ.get("TERM"):
512 # The Links/elinks browsers <http://artax.karlin.mff.cuni.cz/~mikulas/links/>
513 if _iscommand("links"):
Georg Brandl23929f22006-01-20 21:03:35 +0000514 register("links", None, GenericBrowser("links"))
Georg Brandle8f24432005-10-03 14:16:44 +0000515 if _iscommand("elinks"):
516 register("elinks", None, Elinks("elinks"))
517 # The Lynx browser <http://lynx.isc.org/>, <http://lynx.browser.org/>
518 if _iscommand("lynx"):
Georg Brandl23929f22006-01-20 21:03:35 +0000519 register("lynx", None, GenericBrowser("lynx"))
Georg Brandle8f24432005-10-03 14:16:44 +0000520 # The w3m browser <http://w3m.sourceforge.net/>
521 if _iscommand("w3m"):
Georg Brandl23929f22006-01-20 21:03:35 +0000522 register("w3m", None, GenericBrowser("w3m"))
Fred Drake3f8f1642001-07-19 03:46:26 +0000523
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000524#
525# Platform support for Windows
526#
Fred Drakec70b4482000-07-09 16:45:56 +0000527
528if sys.platform[:3] == "win":
Georg Brandle8f24432005-10-03 14:16:44 +0000529 class WindowsDefault(BaseBrowser):
Alexandre Vassalottie223eb82009-07-29 20:12:15 +0000530 def open(self, url, new=0, autoraise=True):
Guido van Rossumd8faa362007-04-27 19:54:29 +0000531 try:
532 os.startfile(url)
533 except WindowsError:
534 # [Error 22] No application is associated with the specified
535 # file for this operation: '<URL>'
536 return False
537 else:
538 return True
Georg Brandle8f24432005-10-03 14:16:44 +0000539
540 _tryorder = []
541 _browsers = {}
Guido van Rossumd8faa362007-04-27 19:54:29 +0000542
543 # First try to use the default Windows browser
544 register("windows-default", WindowsDefault)
545
546 # Detect some common Windows browsers, fallback to IE
547 iexplore = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"),
548 "Internet Explorer\\IEXPLORE.EXE")
Thomas Wouters477c8d52006-05-27 19:21:47 +0000549 for browser in ("firefox", "firebird", "seamonkey", "mozilla",
Guido van Rossumd8faa362007-04-27 19:54:29 +0000550 "netscape", "opera", iexplore):
Georg Brandle8f24432005-10-03 14:16:44 +0000551 if _iscommand(browser):
Georg Brandl23929f22006-01-20 21:03:35 +0000552 register(browser, None, BackgroundBrowser(browser))
Fred Drakec70b4482000-07-09 16:45:56 +0000553
Fred Drakec70b4482000-07-09 16:45:56 +0000554#
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000555# Platform support for MacOS
556#
Fred Drakec70b4482000-07-09 16:45:56 +0000557
Georg Brandle8f24432005-10-03 14:16:44 +0000558if sys.platform == 'darwin':
559 # Adapted from patch submitted to SourceForge by Steven J. Burr
560 class MacOSX(BaseBrowser):
561 """Launcher class for Aqua browsers on Mac OS X
562
563 Optionally specify a browser name on instantiation. Note that this
564 will not work for Aqua browsers if the user has moved the application
565 package after installation.
566
567 If no browser is specified, the default browser, as specified in the
568 Internet System Preferences panel, will be used.
569 """
570 def __init__(self, name):
571 self.name = name
572
Alexandre Vassalottie223eb82009-07-29 20:12:15 +0000573 def open(self, url, new=0, autoraise=True):
Georg Brandle8f24432005-10-03 14:16:44 +0000574 assert "'" not in url
Georg Brandl23929f22006-01-20 21:03:35 +0000575 # hack for local urls
576 if not ':' in url:
577 url = 'file:'+url
Tim Peters887c0802006-01-20 23:40:56 +0000578
Georg Brandle8f24432005-10-03 14:16:44 +0000579 # new must be 0 or 1
580 new = int(bool(new))
581 if self.name == "default":
582 # User called open, open_new or get without a browser parameter
Georg Brandl1cb179e2005-11-09 21:42:48 +0000583 script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser
Georg Brandle8f24432005-10-03 14:16:44 +0000584 else:
585 # User called get and chose a browser
586 if self.name == "OmniWeb":
587 toWindow = ""
588 else:
589 # Include toWindow parameter of OpenURL command for browsers
590 # that support it. 0 == new window; -1 == existing
591 toWindow = "toWindow %d" % (new - 1)
Georg Brandl1cb179e2005-11-09 21:42:48 +0000592 cmd = 'OpenURL "%s"' % url.replace('"', '%22')
Georg Brandle8f24432005-10-03 14:16:44 +0000593 script = '''tell application "%s"
594 activate
595 %s %s
596 end tell''' % (self.name, cmd, toWindow)
597 # Open pipe to AppleScript through osascript command
598 osapipe = os.popen("osascript", "w")
599 if osapipe is None:
600 return False
601 # Write script to osascript's stdin
602 osapipe.write(script)
603 rc = osapipe.close()
604 return not rc
605
Ronald Oussoren4d39f6e2010-05-02 09:54:35 +0000606 class MacOSXOSAScript(BaseBrowser):
607 def __init__(self, name):
608 self._name = name
609
610 def open(self, url, new=0, autoraise=True):
611 if self._name == 'default':
612 script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser
613 else:
614 script = '''
615 tell application "%s"
616 activate
617 open location "%s"
618 end
619 '''%(self._name, url.replace('"', '%22'))
620
621 osapipe = os.popen("osascript", "w")
622 if osapipe is None:
623 return False
624
625 osapipe.write(script)
626 rc = osapipe.close()
627 return not rc
628
629
Georg Brandle8f24432005-10-03 14:16:44 +0000630 # Don't clear _tryorder or _browsers since OS X can use above Unix support
631 # (but we prefer using the OS X specific stuff)
Ronald Oussoren4d39f6e2010-05-02 09:54:35 +0000632 register("safari", None, MacOSXOSAScript('safari'), -1)
633 register("firefox", None, MacOSXOSAScript('firefox'), -1)
634 register("MacOSX", None, MacOSXOSAScript('default'), -1)
Georg Brandle8f24432005-10-03 14:16:44 +0000635
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000636
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000637#
638# Platform support for OS/2
639#
640
Georg Brandle8f24432005-10-03 14:16:44 +0000641if sys.platform[:3] == "os2" and _iscommand("netscape"):
642 _tryorder = []
643 _browsers = {}
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000644 register("os2netscape", None,
Georg Brandl23929f22006-01-20 21:03:35 +0000645 GenericBrowser(["start", "netscape", "%s"]), -1)
Georg Brandle8f24432005-10-03 14:16:44 +0000646
Martin v. Löwis3a89b2b2001-11-25 14:35:58 +0000647
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000648# OK, now that we know what the default preference orders for each
649# platform are, allow user to override them with the BROWSER variable.
Raymond Hettinger54f02222002-06-01 14:18:47 +0000650if "BROWSER" in os.environ:
Georg Brandle8f24432005-10-03 14:16:44 +0000651 _userchoices = os.environ["BROWSER"].split(os.pathsep)
652 _userchoices.reverse()
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000653
Georg Brandle8f24432005-10-03 14:16:44 +0000654 # Treat choices in same way as if passed into get() but do register
655 # and prepend to _tryorder
656 for cmdline in _userchoices:
657 if cmdline != '':
Benjamin Peterson8719ad52009-09-11 22:24:02 +0000658 cmd = _synthesize(cmdline, -1)
659 if cmd[1] is None:
660 register(cmdline, None, GenericBrowser(cmdline), -1)
Georg Brandle8f24432005-10-03 14:16:44 +0000661 cmdline = None # to make del work if _userchoices was empty
662 del cmdline
663 del _userchoices
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000664
Skip Montanarocdab3bf2001-07-18 20:03:32 +0000665# what to do if _tryorder is now empty?
Georg Brandle8f24432005-10-03 14:16:44 +0000666
667
668def main():
669 import getopt
670 usage = """Usage: %s [-n | -t] url
671 -n: open new window
672 -t: open new tab""" % sys.argv[0]
673 try:
674 opts, args = getopt.getopt(sys.argv[1:], 'ntd')
Guido van Rossumb940e112007-01-10 16:19:56 +0000675 except getopt.error as msg:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000676 print(msg, file=sys.stderr)
677 print(usage, file=sys.stderr)
Georg Brandle8f24432005-10-03 14:16:44 +0000678 sys.exit(1)
679 new_win = 0
680 for o, a in opts:
681 if o == '-n': new_win = 1
682 elif o == '-t': new_win = 2
Guido van Rossumb053cd82006-08-24 03:53:23 +0000683 if len(args) != 1:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000684 print(usage, file=sys.stderr)
Georg Brandle8f24432005-10-03 14:16:44 +0000685 sys.exit(1)
686
687 url = args[0]
688 open(url, new_win)
689
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000690 print("\a")
Georg Brandl23929f22006-01-20 21:03:35 +0000691
Georg Brandle8f24432005-10-03 14:16:44 +0000692if __name__ == "__main__":
693 main()