blob: 5008a0b6aebbdfe880bc8435354c3a3dd55ee967 [file] [log] [blame]
Eric S. Raymondf7f18512001-01-23 13:16:32 +00001"""Remote-control interfaces to common browsers."""
Fred Drakec70b4482000-07-09 16:45:56 +00002
3import os
4import sys
5
Fred Drakec70b4482000-07-09 16:45:56 +00006class Error(Exception):
7 pass
8
Tim Peters658cba62001-02-09 20:06:00 +00009_browsers = {} # Dictionary of available browser controllers
10_tryorder = [] # Preference order of available browsers
Fred Drakec70b4482000-07-09 16:45:56 +000011
12def register(name, klass, instance=None):
13 """Register a browser connector and, optionally, connection."""
14 _browsers[name.lower()] = [klass, instance]
15
Eric S. Raymondf7f18512001-01-23 13:16:32 +000016def get(using=None):
17 """Return a browser launcher instance appropriate for the environment."""
18 if using:
19 alternatives = [using]
20 else:
21 alternatives = _tryorder
22 for browser in alternatives:
23 if browser.find('%s') > -1:
24 # User gave us a command line, don't mess with it.
25 return browser
26 else:
Tim Peters658cba62001-02-09 20:06:00 +000027 # User gave us a browser name.
Eric S. Raymondf7f18512001-01-23 13:16:32 +000028 command = _browsers[browser.lower()]
29 if command[1] is None:
30 return command[0]()
31 else:
32 return command[1]
33 raise Error("could not locate runnable browser")
Fred Drakec70b4482000-07-09 16:45:56 +000034
35# Please note: the following definition hides a builtin function.
36
Eric S. Raymondf79cb2d2001-01-23 13:49:44 +000037def open(url, new=0, autoraise=1):
38 get().open(url, new, autoraise)
Fred Drakec70b4482000-07-09 16:45:56 +000039
Tim Peters658cba62001-02-09 20:06:00 +000040def open_new(url): # Marked deprecated. May be removed in 2.1.
Eric S. Raymondf7f18512001-01-23 13:16:32 +000041 get().open(url, 1)
Fred Drakec70b4482000-07-09 16:45:56 +000042
Eric S. Raymondf7f18512001-01-23 13:16:32 +000043#
44# Everything after this point initializes _browsers and _tryorder,
45# then disappears. Some class definitions and instances remain
46# live through these globals, but only the minimum set needed to
47# support the user's platform.
48#
Fred Drakec70b4482000-07-09 16:45:56 +000049
Tim Peters658cba62001-02-09 20:06:00 +000050#
Eric S. Raymondf7f18512001-01-23 13:16:32 +000051# Platform support for Unix
52#
Fred Drakec70b4482000-07-09 16:45:56 +000053
Eric S. Raymondf7f18512001-01-23 13:16:32 +000054# This is the right test because all these Unix browsers require either
55# a console terminal of an X display to run. Note that we cannot split
56# the TERM and DISPLAY cases, because we might be running Python from inside
57# an xterm.
58if os.environ.get("TERM") or os.environ.get("DISPLAY"):
59 PROCESS_CREATION_DELAY = 4
Eric S. Raymondf7f18512001-01-23 13:16:32 +000060 _tryorder = ("mozilla","netscape","kfm","grail","links","lynx","w3m")
Fred Drakec70b4482000-07-09 16:45:56 +000061
Eric S. Raymondf7f18512001-01-23 13:16:32 +000062 def _iscommand(cmd):
63 """Return true if cmd can be found on the executable search path."""
64 path = os.environ.get("PATH")
65 if not path:
Fred Drakec70b4482000-07-09 16:45:56 +000066 return 0
Eric S. Raymondf7f18512001-01-23 13:16:32 +000067 for d in path.split(os.pathsep):
68 exe = os.path.join(d, cmd)
69 if os.path.isfile(exe):
70 return 1
71 return 0
Fred Drakec70b4482000-07-09 16:45:56 +000072
Eric S. Raymondf7f18512001-01-23 13:16:32 +000073 class GenericBrowser:
74 def __init__(self, cmd):
75 self.command = cmd
Fred Drakec70b4482000-07-09 16:45:56 +000076
Eric S. Raymondcfa40962001-01-23 15:49:34 +000077 def open(self, url, new=0, autoraise=1):
Eric S. Raymondf7f18512001-01-23 13:16:32 +000078 os.system(self.command % url)
Fred Drakec70b4482000-07-09 16:45:56 +000079
Tim Peters658cba62001-02-09 20:06:00 +000080 def open_new(self, url): # Deprecated. May be removed in 2.1.
Eric S. Raymondf7f18512001-01-23 13:16:32 +000081 self.open(url)
Fred Drakec70b4482000-07-09 16:45:56 +000082
Eric S. Raymondf7f18512001-01-23 13:16:32 +000083 # Easy cases first -- register console browsers if we have them.
84 if os.environ.get("TERM"):
85 # The Links browser <http://artax.karlin.mff.cuni.cz/~mikulas/links/>
86 if _iscommand("links"):
87 register("links", None, GenericBrowser("links %s"))
88 # The Lynx browser <http://lynx.browser.org/>
89 if _iscommand("lynx"):
90 register("lynx", None, GenericBrowser("lynx %s"))
91 # The w3m browser <http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/eng/>
92 if _iscommand("w3m"):
93 register("w3m", None, GenericBrowser("w3m %s"))
Fred Drakec70b4482000-07-09 16:45:56 +000094
Eric S. Raymondf7f18512001-01-23 13:16:32 +000095 # X browsers have mre in the way of options
96 if os.environ.get("DISPLAY"):
97 # First, the Netscape series
98 if _iscommand("netscape") or _iscommand("mozilla"):
99 class Netscape:
100 "Launcher class for Netscape browsers."
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000101 def __init__(self, name):
102 self.name = name
Fred Drakec70b4482000-07-09 16:45:56 +0000103
Eric S. Raymondf79cb2d2001-01-23 13:49:44 +0000104 def _remote(self, action, autoraise):
105 raise_opt = ("-noraise", "-raise")[autoraise]
Jeremy Hylton8016a4b2001-02-27 18:44:14 +0000106 cmd = "%s %s -remote '%s' >/dev/null 2>&1" % (self.name,
107 raise_opt,
108 action)
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000109 rc = os.system(cmd)
110 if rc:
111 import time
112 os.system("%s -no-about-splash &" % self.name)
113 time.sleep(PROCESS_CREATION_DELAY)
114 rc = os.system(cmd)
115 return not rc
Fred Drakec70b4482000-07-09 16:45:56 +0000116
Eric S. Raymondf79cb2d2001-01-23 13:49:44 +0000117 def open(self, url, new=0, autoraise=1):
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000118 if new:
Eric S. Raymondf79cb2d2001-01-23 13:49:44 +0000119 self._remote("openURL(%s, new-window)"%url, autoraise)
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000120 else:
Eric S. Raymondf79cb2d2001-01-23 13:49:44 +0000121 self._remote("openURL(%s)" % url, autoraise)
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000122
123 # Deprecated. May be removed in 2.1.
124 def open_new(self, url):
125 self.open(url, 1)
126
127 if _iscommand("mozilla"):
128 register("mozilla", None, Netscape("mozilla"))
129 if _iscommand("netscape"):
130 register("netscape", None, Netscape("netscape"))
131
132 # Next, Mosaic -- old but still in use.
133 if _iscommand("mosaic"):
134 register("mosaic", None, GenericBrowser("mosaic %s >/dev/null &"))
135
136 # Konqueror/kfm, the KDE browser.
137 if _iscommand("kfm"):
138 class Konqueror:
139 """Controller for the KDE File Manager (kfm, or Konqueror).
140
141 See http://developer.kde.org/documentation/other/kfmclient.html
142 for more information on the Konqueror remote-control interface.
143
144 """
145 def _remote(self, action):
146 cmd = "kfmclient %s >/dev/null 2>&1" % action
147 rc = os.system(cmd)
148 if rc:
149 import time
150 os.system("kfm -d &")
151 time.sleep(PROCESS_CREATION_DELAY)
152 rc = os.system(cmd)
153 return not rc
154
Eric S. Raymondcfa40962001-01-23 15:49:34 +0000155 def open(self, url, new=1, autoraise=1):
Jeremy Hylton8016a4b2001-02-27 18:44:14 +0000156 # XXX Currently I know no way to prevent KFM from
Tim Peters85ba6732001-02-28 08:26:44 +0000157 # opening a new win.
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000158 self._remote("openURL %s" % url)
159
160 # Deprecated. May be removed in 2.1.
161 open_new = open
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000162
163 register("kfm", Konqueror, None)
164
165 # Grail, the Python browser.
166 if _iscommand("grail"):
167 class Grail:
168 # There should be a way to maintain a connection to
169 # Grail, but the Grail remote control protocol doesn't
170 # really allow that at this point. It probably neverwill!
171 def _find_grail_rc(self):
172 import glob
173 import pwd
174 import socket
175 import tempfile
Jeremy Hylton8016a4b2001-02-27 18:44:14 +0000176 tempdir = os.path.join(tempfile.gettempdir(),
177 ".grail-unix")
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000178 user = pwd.getpwuid(_os.getuid())[0]
179 filename = os.path.join(tempdir, user + "-*")
180 maybes = glob.glob(filename)
181 if not maybes:
182 return None
183 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
184 for fn in maybes:
185 # need to PING each one until we find one that's live
186 try:
187 s.connect(fn)
188 except socket.error:
189 # no good; attempt to clean it out, but don't fail:
190 try:
191 os.unlink(fn)
192 except IOError:
193 pass
194 else:
195 return s
196
197 def _remote(self, action):
198 s = self._find_grail_rc()
199 if not s:
200 return 0
201 s.send(action)
202 s.close()
203 return 1
204
Eric S. Raymondcfa40962001-01-23 15:49:34 +0000205 def open(self, url, new=0, autoraise=1):
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000206 if new:
207 self._remote("LOADNEW " + url)
208 else:
209 self._remote("LOAD " + url)
210
211 # Deprecated. May be removed in 2.1.
212 def open_new(self, url):
213 self.open(url, 1)
214
215 register("grail", Grail, None)
216
217#
218# Platform support for Windows
219#
Fred Drakec70b4482000-07-09 16:45:56 +0000220
221if sys.platform[:3] == "win":
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000222 _tryorder = ("netscape", "windows-default")
223
224 class WindowsDefault:
Eric S. Raymondcfa40962001-01-23 15:49:34 +0000225 def open(self, url, new=0, autoraise=1):
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000226 os.startfile(url)
227
228 def open_new(self, url): # Deprecated. May be removed in 2.1.
229 self.open(url)
230
Fred Drakec70b4482000-07-09 16:45:56 +0000231 register("windows-default", WindowsDefault)
Fred Drakec70b4482000-07-09 16:45:56 +0000232
Fred Drakec70b4482000-07-09 16:45:56 +0000233#
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000234# Platform support for MacOS
235#
Fred Drakec70b4482000-07-09 16:45:56 +0000236
237try:
238 import ic
239except ImportError:
240 pass
241else:
242 class InternetConfig:
Eric S. Raymondcfa40962001-01-23 15:49:34 +0000243 def open(self, url, new=0, autoraise=1):
Guido van Rossum2595a832000-11-13 20:30:57 +0000244 ic.launchurl(url)
Fred Drakec70b4482000-07-09 16:45:56 +0000245
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000246 def open_new(self, url): # Deprecated. May be removed in 2.1.
Fred Drakec70b4482000-07-09 16:45:56 +0000247 self.open(url)
248
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000249 # internet-config is the only supported controller on MacOS,
250 # so don't mess with the default!
251 _tryorder = ("internet-config")
Fred Drakec70b4482000-07-09 16:45:56 +0000252 register("internet-config", InternetConfig)
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000253
254# OK, now that we know what the default preference orders for each
255# platform are, allow user to override them with the BROWSER variable.
256#
257if os.environ.has_key("BROWSER"):
258 # It's the user's responsibility to register handlers for any unknown
259 # browser referenced by this value, before calling open().
260 _tryorder = os.environ["BROWSER"].split(":")
261else:
262 # Optimization: filter out alternatives that aren't available, so we can
263 # avoid has_key() tests at runtime. (This may also allow some unused
264 # classes and class-instance storage to be garbage-collected.)
Jeremy Hylton8016a4b2001-02-27 18:44:14 +0000265 _tryorder = filter(lambda x: _browsers.has_key(x.lower())
266 or x.find("%s") > -1, _tryorder)
Eric S. Raymondf7f18512001-01-23 13:16:32 +0000267
268# end