blob: 64573d0d90fc1fe66e9e35a03d5d5731c82d0a11 [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()
Tim Peters536cf992005-12-25 23:18:31 +0000101 if os.path.isfile(cmd) and (cmd.endswith(".exe") or
Georg Brandle8f24432005-10-03 14:16:44 +0000102 cmd.endswith(".bat")):
103 return True
104 for ext in ".exe", ".bat":
105 if os.path.isfile(cmd + ext):
106 return True
107 return False
108else:
109 def _isexecutable(cmd):
110 if os.path.isfile(cmd):
111 mode = os.stat(cmd)[stat.ST_MODE]
112 if mode & stat.S_IXUSR or mode & stat.S_IXGRP or mode & stat.S_IXOTH:
113 return True
114 return False
115
Fred Drake3f8f1642001-07-19 03:46:26 +0000116def _iscommand(cmd):
Georg Brandle8f24432005-10-03 14:16:44 +0000117 """Return True if cmd is executable or can be found on the executable
118 search path."""
119 if _isexecutable(cmd):
120 return True
Fred Drake3f8f1642001-07-19 03:46:26 +0000121 path = os.environ.get("PATH")
122 if not path:
Tim Petersbc0e9102002-04-04 22:55:58 +0000123 return False
Fred Drake3f8f1642001-07-19 03:46:26 +0000124 for d in path.split(os.pathsep):
125 exe = os.path.join(d, cmd)
Georg Brandle8f24432005-10-03 14:16:44 +0000126 if _isexecutable(exe):
Tim Petersbc0e9102002-04-04 22:55:58 +0000127 return True
128 return False
Fred Drake3f8f1642001-07-19 03:46:26 +0000129
130
Georg Brandle8f24432005-10-03 14:16:44 +0000131# General parent classes
132
133class BaseBrowser(object):
Georg Brandl23929f22006-01-20 21:03:35 +0000134 """Parent class for all browsers. Do not use directly."""
135
136 args = ['%s']
137
Georg Brandle8f24432005-10-03 14:16:44 +0000138 def __init__(self, name=""):
139 self.name = name
Georg Brandlb9801132005-10-08 20:47:38 +0000140 self.basename = name
Tim Peters536cf992005-12-25 23:18:31 +0000141
Neal Norwitz196f7332005-10-04 03:17:49 +0000142 def open(self, url, new=0, autoraise=1):
143 raise NotImplementedError
144
Georg Brandle8f24432005-10-03 14:16:44 +0000145 def open_new(self, url):
146 return self.open(url, 1)
147
148 def open_new_tab(self, url):
149 return self.open(url, 2)
Fred Drake3f8f1642001-07-19 03:46:26 +0000150
151
Georg Brandle8f24432005-10-03 14:16:44 +0000152class GenericBrowser(BaseBrowser):
153 """Class for all browsers started with a command
154 and without remote functionality."""
155
Georg Brandl23929f22006-01-20 21:03:35 +0000156 def __init__(self, name):
157 if isinstance(name, basestring):
158 self.name = name
159 else:
160 # name should be a list with arguments
161 self.name = name[0]
162 self.args = name[1:]
Georg Brandlb9801132005-10-08 20:47:38 +0000163 self.basename = os.path.basename(self.name)
Fred Drake3f8f1642001-07-19 03:46:26 +0000164
165 def open(self, url, new=0, autoraise=1):
Georg Brandl23929f22006-01-20 21:03:35 +0000166 cmdline = [self.name] + [arg.replace("%s", url)
167 for arg in self.args]
168 try:
169 p = subprocess.Popen(cmdline, close_fds=True)
170 return not p.wait()
171 except OSError:
172 return False
173
174
175class BackgroundBrowser(GenericBrowser):
176 """Class for all browsers which are to be started in the
177 background."""
178
179 def open(self, url, new=0, autoraise=1):
180 cmdline = [self.name] + [arg.replace("%s", url)
181 for arg in self.args]
182 setsid = getattr(os, 'setsid', None)
183 if not setsid:
184 setsid = getattr(os, 'setpgrp', None)
185 try:
186 p = subprocess.Popen(cmdline, close_fds=True, preexec_fn=setsid)
187 return (p.poll() is None)
188 except OSError:
189 return False
Fred Drake3f8f1642001-07-19 03:46:26 +0000190
191
Georg Brandle8f24432005-10-03 14:16:44 +0000192class UnixBrowser(BaseBrowser):
193 """Parent class for all Unix browsers with remote functionality."""
Fred Drake3f8f1642001-07-19 03:46:26 +0000194
Georg Brandle8f24432005-10-03 14:16:44 +0000195 raise_opts = None
Georg Brandl23929f22006-01-20 21:03:35 +0000196 remote_args = ['%action', '%s']
Georg Brandle8f24432005-10-03 14:16:44 +0000197 remote_action = None
198 remote_action_newwin = None
199 remote_action_newtab = None
Georg Brandl23929f22006-01-20 21:03:35 +0000200 background = False
201 redirect_stdout = True
Georg Brandle8f24432005-10-03 14:16:44 +0000202
Georg Brandl23929f22006-01-20 21:03:35 +0000203 def _invoke(self, args, remote, autoraise):
204 raise_opt = []
205 if remote and self.raise_opts:
206 # use autoraise argument only for remote invocation
207 autoraise = int(bool(autoraise))
208 opt = self.raise_opts[autoraise]
209 if opt: raise_opt = [opt]
210
211 cmdline = [self.name] + raise_opt + args
212
213 if remote or self.background:
214 inout = file(os.devnull, "r+")
215 else:
216 # for TTY browsers, we need stdin/out
217 inout = None
218 # if possible, put browser in separate process group, so
219 # keyboard interrupts don't affect browser as well as Python
220 setsid = getattr(os, 'setsid', None)
221 if not setsid:
222 setsid = getattr(os, 'setpgrp', None)
223
224 p = subprocess.Popen(cmdline, close_fds=True, stdin=inout,
225 stdout=(self.redirect_stdout and inout or None),
226 stderr=inout, preexec_fn=setsid)
227 if remote:
228 # wait five secons. If the subprocess is not finished, the
229 # remote invocation has (hopefully) started a new instance.
230 time.sleep(1)
231 rc = p.poll()
232 if rc is None:
233 time.sleep(4)
234 rc = p.poll()
235 if rc is None:
236 return True
237 # if remote call failed, open() will try direct invocation
238 return not rc
239 elif self.background:
240 if p.poll() is None:
241 return True
242 else:
243 return False
244 else:
245 return not p.wait()
Fred Drake3f8f1642001-07-19 03:46:26 +0000246
247 def open(self, url, new=0, autoraise=1):
Georg Brandle8f24432005-10-03 14:16:44 +0000248 if new == 0:
249 action = self.remote_action
250 elif new == 1:
251 action = self.remote_action_newwin
252 elif new == 2:
253 if self.remote_action_newtab is None:
254 action = self.remote_action_newwin
255 else:
256 action = self.remote_action_newtab
Fred Drake3f8f1642001-07-19 03:46:26 +0000257 else:
Georg Brandl23929f22006-01-20 21:03:35 +0000258 raise Error("Bad 'new' parameter to open(); " +
259 "expected 0, 1, or 2, got %s" % new)
260
261 args = [arg.replace("%s", url).replace("%action", action)
262 for arg in self.remote_args]
263 success = self._invoke(args, True, autoraise)
264 if not success:
265 # remote invocation failed, try straight way
266 args = [arg.replace("%s", url) for arg in self.args]
267 return self._invoke(args, False, False)
268 else:
269 return True
Fred Drake3f8f1642001-07-19 03:46:26 +0000270
271
Georg Brandle8f24432005-10-03 14:16:44 +0000272class Mozilla(UnixBrowser):
273 """Launcher class for Mozilla/Netscape browsers."""
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000274
Georg Brandl23929f22006-01-20 21:03:35 +0000275 raise_opts = ["-noraise", "-raise"]
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000276
Georg Brandl23929f22006-01-20 21:03:35 +0000277 remote_args = ['-remote', 'openURL(%s%action)']
278 remote_action = ""
279 remote_action_newwin = ",new-window"
280 remote_action_newtab = ",new-tab"
281
282 background = True
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000283
Georg Brandle8f24432005-10-03 14:16:44 +0000284Netscape = Mozilla
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000285
286
Georg Brandle8f24432005-10-03 14:16:44 +0000287class Galeon(UnixBrowser):
288 """Launcher class for Galeon/Epiphany browsers."""
289
Georg Brandl23929f22006-01-20 21:03:35 +0000290 raise_opts = ["-noraise", ""]
291 remote_args = ['%action', '%s']
292 remote_action = "-n"
293 remote_action_newwin = "-w"
Georg Brandle8f24432005-10-03 14:16:44 +0000294
Georg Brandl23929f22006-01-20 21:03:35 +0000295 background = True
Fred Drake3f8f1642001-07-19 03:46:26 +0000296
297
Georg Brandle8f24432005-10-03 14:16:44 +0000298class Opera(UnixBrowser):
299 "Launcher class for Opera browser."
300
Georg Brandl23929f22006-01-20 21:03:35 +0000301 raise_opts = ["", "-raise"]
Georg Brandle8f24432005-10-03 14:16:44 +0000302
Georg Brandl23929f22006-01-20 21:03:35 +0000303 remote_args = ['-remote', 'openURL(%s%action)']
304 remote_action = ""
305 remote_action_newwin = ",new-window"
306 remote_action_newtab = ",new-page"
307 background = True
Georg Brandle8f24432005-10-03 14:16:44 +0000308
309
310class Elinks(UnixBrowser):
311 "Launcher class for Elinks browsers."
312
Georg Brandl23929f22006-01-20 21:03:35 +0000313 remote_args = ['-remote', 'openURL(%s%action)']
314 remote_action = ""
315 remote_action_newwin = ",new-window"
316 remote_action_newtab = ",new-tab"
317 background = False
Georg Brandle8f24432005-10-03 14:16:44 +0000318
Georg Brandl23929f22006-01-20 21:03:35 +0000319 # elinks doesn't like its stdout to be redirected -
320 # it uses redirected stdout as a signal to do -dump
321 redirect_stdout = False
322
323
324class Konqueror(BaseBrowser):
325 """Controller for the KDE File Manager (kfm, or Konqueror).
326
327 See the output of ``kfmclient --commands``
328 for more information on the Konqueror remote-control interface.
329 """
330
331 def open(self, url, new=0, autoraise=1):
332 # XXX Currently I know no way to prevent KFM from opening a new win.
333 if new == 2:
334 action = "newTab"
335 else:
336 action = "openURL"
337
338 devnull = file(os.devnull, "r+")
339 # if possible, put browser in separate process group, so
340 # keyboard interrupts don't affect browser as well as Python
341 setsid = getattr(os, 'setsid', None)
342 if not setsid:
343 setsid = getattr(os, 'setpgrp', None)
344
345 try:
346 p = subprocess.Popen(["kfmclient", action, url],
347 close_fds=True, stdin=devnull,
348 stdout=devnull, stderr=devnull)
349 except OSError:
350 # fall through to next variant
351 pass
352 else:
353 p.wait()
354 # kfmclient's return code unfortunately has no meaning as it seems
355 return True
356
357 try:
358 p = subprocess.Popen(["konqueror", "--silent", url],
359 close_fds=True, stdin=devnull,
360 stdout=devnull, stderr=devnull,
361 preexec_fn=setsid)
362 except OSError:
363 # fall through to next variant
364 pass
365 else:
366 if p.poll() is None:
367 # Should be running now.
368 return True
369
370 try:
371 p = subprocess.Popen(["kfm", "-d", url],
372 close_fds=True, stdin=devnull,
373 stdout=devnull, stderr=devnull,
374 preexec_fn=setsid)
375 except OSError:
376 return False
377 else:
378 return (p.poll() is None)
Georg Brandle8f24432005-10-03 14:16:44 +0000379
380
381class Grail(BaseBrowser):
Fred Drake3f8f1642001-07-19 03:46:26 +0000382 # There should be a way to maintain a connection to Grail, but the
383 # Grail remote control protocol doesn't really allow that at this
Georg Brandl23929f22006-01-20 21:03:35 +0000384 # point. It probably never will!
Fred Drake3f8f1642001-07-19 03:46:26 +0000385 def _find_grail_rc(self):
386 import glob
387 import pwd
388 import socket
389 import tempfile
390 tempdir = os.path.join(tempfile.gettempdir(),
391 ".grail-unix")
Fred Drake16623fe2001-10-13 16:00:52 +0000392 user = pwd.getpwuid(os.getuid())[0]
Fred Drake3f8f1642001-07-19 03:46:26 +0000393 filename = os.path.join(tempdir, user + "-*")
394 maybes = glob.glob(filename)
395 if not maybes:
396 return None
397 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
398 for fn in maybes:
399 # need to PING each one until we find one that's live
400 try:
401 s.connect(fn)
402 except socket.error:
403 # no good; attempt to clean it out, but don't fail:
404 try:
405 os.unlink(fn)
406 except IOError:
407 pass
408 else:
409 return s
410
411 def _remote(self, action):
412 s = self._find_grail_rc()
413 if not s:
414 return 0
415 s.send(action)
416 s.close()
417 return 1
418
419 def open(self, url, new=0, autoraise=1):
420 if new:
Georg Brandle8f24432005-10-03 14:16:44 +0000421 ok = self._remote("LOADNEW " + url)
Fred Drake3f8f1642001-07-19 03:46:26 +0000422 else:
Georg Brandle8f24432005-10-03 14:16:44 +0000423 ok = self._remote("LOAD " + url)
424 return ok
Fred Drake3f8f1642001-07-19 03:46:26 +0000425
Fred Drakec70b4482000-07-09 16:45:56 +0000426
Tim Peters658cba62001-02-09 20:06:00 +0000427#
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000428# Platform support for Unix
429#
Fred Drakec70b4482000-07-09 16:45:56 +0000430
Georg Brandle8f24432005-10-03 14:16:44 +0000431# These are the right tests because all these Unix browsers require either
432# a console terminal or an X display to run.
Fred Drakec70b4482000-07-09 16:45:56 +0000433
Neal Norwitz196f7332005-10-04 03:17:49 +0000434def register_X_browsers():
Georg Brandle8f24432005-10-03 14:16:44 +0000435 # The default Gnome browser
436 if _iscommand("gconftool-2"):
437 # get the web browser string from gconftool
438 gc = 'gconftool-2 -g /desktop/gnome/url-handlers/http/command'
439 out = os.popen(gc)
440 commd = out.read().strip()
441 retncode = out.close()
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000442
Georg Brandle8f24432005-10-03 14:16:44 +0000443 # if successful, register it
444 if retncode == None and len(commd) != 0:
Georg Brandl23929f22006-01-20 21:03:35 +0000445 register("gnome", None, BackgroundBrowser(commd))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000446
Georg Brandl23929f22006-01-20 21:03:35 +0000447 # First, the Mozilla/Netscape browsers
Georg Brandl4a5a9182005-11-22 19:18:01 +0000448 for browser in ("mozilla-firefox", "firefox",
449 "mozilla-firebird", "firebird",
450 "mozilla", "netscape"):
451 if _iscommand(browser):
452 register(browser, None, Mozilla(browser))
453
Georg Brandle8f24432005-10-03 14:16:44 +0000454 # Konqueror/kfm, the KDE browser.
Georg Brandlb9801132005-10-08 20:47:38 +0000455 if _iscommand("kfm"):
456 register("kfm", Konqueror, Konqueror("kfm"))
457 elif _iscommand("konqueror"):
458 register("konqueror", Konqueror, Konqueror("konqueror"))
Neal Norwitz8dd28eb2002-10-10 22:49:29 +0000459
Georg Brandle8f24432005-10-03 14:16:44 +0000460 # Gnome's Galeon and Epiphany
461 for browser in ("galeon", "epiphany"):
462 if _iscommand(browser):
463 register(browser, None, Galeon(browser))
Gustavo Niemeyer1456fde2002-11-25 17:25:04 +0000464
Georg Brandle8f24432005-10-03 14:16:44 +0000465 # Skipstone, another Gtk/Mozilla based browser
466 if _iscommand("skipstone"):
Georg Brandl23929f22006-01-20 21:03:35 +0000467 register("skipstone", None, BackgroundBrowser("skipstone"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000468
Georg Brandle8f24432005-10-03 14:16:44 +0000469 # Opera, quite popular
470 if _iscommand("opera"):
471 register("opera", None, Opera("opera"))
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000472
Georg Brandle8f24432005-10-03 14:16:44 +0000473 # Next, Mosaic -- old but still in use.
474 if _iscommand("mosaic"):
Georg Brandl23929f22006-01-20 21:03:35 +0000475 register("mosaic", None, BackgroundBrowser("mosaic"))
Fred Drake3f8f1642001-07-19 03:46:26 +0000476
Georg Brandle8f24432005-10-03 14:16:44 +0000477 # Grail, the Python browser. Does anybody still use it?
478 if _iscommand("grail"):
479 register("grail", Grail, None)
Fred Drake3f8f1642001-07-19 03:46:26 +0000480
Neal Norwitz196f7332005-10-04 03:17:49 +0000481# Prefer X browsers if present
482if os.environ.get("DISPLAY"):
483 register_X_browsers()
484
Georg Brandle8f24432005-10-03 14:16:44 +0000485# Also try console browsers
486if os.environ.get("TERM"):
487 # The Links/elinks browsers <http://artax.karlin.mff.cuni.cz/~mikulas/links/>
488 if _iscommand("links"):
Georg Brandl23929f22006-01-20 21:03:35 +0000489 register("links", None, GenericBrowser("links"))
Georg Brandle8f24432005-10-03 14:16:44 +0000490 if _iscommand("elinks"):
491 register("elinks", None, Elinks("elinks"))
492 # The Lynx browser <http://lynx.isc.org/>, <http://lynx.browser.org/>
493 if _iscommand("lynx"):
Georg Brandl23929f22006-01-20 21:03:35 +0000494 register("lynx", None, GenericBrowser("lynx"))
Georg Brandle8f24432005-10-03 14:16:44 +0000495 # The w3m browser <http://w3m.sourceforge.net/>
496 if _iscommand("w3m"):
Georg Brandl23929f22006-01-20 21:03:35 +0000497 register("w3m", None, GenericBrowser("w3m"))
Fred Drake3f8f1642001-07-19 03:46:26 +0000498
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000499#
500# Platform support for Windows
501#
Fred Drakec70b4482000-07-09 16:45:56 +0000502
503if sys.platform[:3] == "win":
Georg Brandle8f24432005-10-03 14:16:44 +0000504 class WindowsDefault(BaseBrowser):
505 def open(self, url, new=0, autoraise=1):
506 os.startfile(url)
507 return True # Oh, my...
508
509 _tryorder = []
510 _browsers = {}
511 # Prefer mozilla/netscape/opera if present
512 for browser in ("firefox", "firebird", "mozilla", "netscape", "opera"):
513 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
553
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()