1 | #! /usr/bin/env python
|
---|
2 | """Interfaces for launching and remotely controlling Web browsers."""
|
---|
3 | # Maintained by Georg Brandl.
|
---|
4 |
|
---|
5 | import os
|
---|
6 | import shlex
|
---|
7 | import sys
|
---|
8 | import stat
|
---|
9 | import subprocess
|
---|
10 | import time
|
---|
11 |
|
---|
12 | __all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"]
|
---|
13 |
|
---|
14 | class Error(Exception):
|
---|
15 | pass
|
---|
16 |
|
---|
17 | _browsers = {} # Dictionary of available browser controllers
|
---|
18 | _tryorder = [] # Preference order of available browsers
|
---|
19 |
|
---|
20 | def register(name, klass, instance=None, update_tryorder=1):
|
---|
21 | """Register a browser connector and, optionally, connection."""
|
---|
22 | _browsers[name.lower()] = [klass, instance]
|
---|
23 | if update_tryorder > 0:
|
---|
24 | _tryorder.append(name)
|
---|
25 | elif update_tryorder < 0:
|
---|
26 | _tryorder.insert(0, name)
|
---|
27 |
|
---|
28 | def get(using=None):
|
---|
29 | """Return a browser launcher instance appropriate for the environment."""
|
---|
30 | if using is not None:
|
---|
31 | alternatives = [using]
|
---|
32 | else:
|
---|
33 | alternatives = _tryorder
|
---|
34 | for browser in alternatives:
|
---|
35 | if '%s' in browser:
|
---|
36 | # User gave us a command line, split it into name and args
|
---|
37 | browser = shlex.split(browser)
|
---|
38 | if browser[-1] == '&':
|
---|
39 | return BackgroundBrowser(browser[:-1])
|
---|
40 | else:
|
---|
41 | return GenericBrowser(browser)
|
---|
42 | else:
|
---|
43 | # User gave us a browser name or path.
|
---|
44 | try:
|
---|
45 | command = _browsers[browser.lower()]
|
---|
46 | except KeyError:
|
---|
47 | command = _synthesize(browser)
|
---|
48 | if command[1] is not None:
|
---|
49 | return command[1]
|
---|
50 | elif command[0] is not None:
|
---|
51 | return command[0]()
|
---|
52 | raise Error("could not locate runnable browser")
|
---|
53 |
|
---|
54 | # Please note: the following definition hides a builtin function.
|
---|
55 | # It is recommended one does "import webbrowser" and uses webbrowser.open(url)
|
---|
56 | # instead of "from webbrowser import *".
|
---|
57 |
|
---|
58 | def open(url, new=0, autoraise=True):
|
---|
59 | for name in _tryorder:
|
---|
60 | browser = get(name)
|
---|
61 | if browser.open(url, new, autoraise):
|
---|
62 | return True
|
---|
63 | return False
|
---|
64 |
|
---|
65 | def open_new(url):
|
---|
66 | return open(url, 1)
|
---|
67 |
|
---|
68 | def open_new_tab(url):
|
---|
69 | return open(url, 2)
|
---|
70 |
|
---|
71 |
|
---|
72 | def _synthesize(browser, update_tryorder=1):
|
---|
73 | """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 | """
|
---|
84 | cmd = browser.split()[0]
|
---|
85 | if not _iscommand(cmd):
|
---|
86 | return [None, None]
|
---|
87 | name = os.path.basename(cmd)
|
---|
88 | 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)
|
---|
99 | register(browser, None, controller, update_tryorder)
|
---|
100 | return [None, controller]
|
---|
101 | return [None, None]
|
---|
102 |
|
---|
103 |
|
---|
104 | if sys.platform[:3] == "win":
|
---|
105 | def _isexecutable(cmd):
|
---|
106 | cmd = cmd.lower()
|
---|
107 | if os.path.isfile(cmd) and cmd.endswith((".exe", ".bat")):
|
---|
108 | return True
|
---|
109 | for ext in ".exe", ".bat":
|
---|
110 | if os.path.isfile(cmd + ext):
|
---|
111 | return True
|
---|
112 | return False
|
---|
113 | else:
|
---|
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 |
|
---|
121 | def _iscommand(cmd):
|
---|
122 | """Return True if cmd is executable or can be found on the executable
|
---|
123 | search path."""
|
---|
124 | if _isexecutable(cmd):
|
---|
125 | return True
|
---|
126 | path = os.environ.get("PATH")
|
---|
127 | if not path:
|
---|
128 | return False
|
---|
129 | for d in path.split(os.pathsep):
|
---|
130 | exe = os.path.join(d, cmd)
|
---|
131 | if _isexecutable(exe):
|
---|
132 | return True
|
---|
133 | return False
|
---|
134 |
|
---|
135 |
|
---|
136 | # General parent classes
|
---|
137 |
|
---|
138 | class BaseBrowser(object):
|
---|
139 | """Parent class for all browsers. Do not use directly."""
|
---|
140 |
|
---|
141 | args = ['%s']
|
---|
142 |
|
---|
143 | def __init__(self, name=""):
|
---|
144 | self.name = name
|
---|
145 | self.basename = name
|
---|
146 |
|
---|
147 | def open(self, url, new=0, autoraise=True):
|
---|
148 | raise NotImplementedError
|
---|
149 |
|
---|
150 | 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)
|
---|
155 |
|
---|
156 |
|
---|
157 | class GenericBrowser(BaseBrowser):
|
---|
158 | """Class for all browsers started with a command
|
---|
159 | and without remote functionality."""
|
---|
160 |
|
---|
161 | def __init__(self, name):
|
---|
162 | if isinstance(name, basestring):
|
---|
163 | self.name = name
|
---|
164 | self.args = ["%s"]
|
---|
165 | else:
|
---|
166 | # name should be a list with arguments
|
---|
167 | self.name = name[0]
|
---|
168 | self.args = name[1:]
|
---|
169 | self.basename = os.path.basename(self.name)
|
---|
170 |
|
---|
171 | def open(self, url, new=0, autoraise=True):
|
---|
172 | cmdline = [self.name] + [arg.replace("%s", url)
|
---|
173 | for arg in self.args]
|
---|
174 | try:
|
---|
175 | if sys.platform[:3] == 'win':
|
---|
176 | p = subprocess.Popen(cmdline)
|
---|
177 | else:
|
---|
178 | p = subprocess.Popen(cmdline, close_fds=True)
|
---|
179 | return not p.wait()
|
---|
180 | except OSError:
|
---|
181 | return False
|
---|
182 |
|
---|
183 |
|
---|
184 | class 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=True):
|
---|
189 | cmdline = [self.name] + [arg.replace("%s", url)
|
---|
190 | for arg in self.args]
|
---|
191 | try:
|
---|
192 | 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)
|
---|
199 | return (p.poll() is None)
|
---|
200 | except OSError:
|
---|
201 | return False
|
---|
202 |
|
---|
203 |
|
---|
204 | class UnixBrowser(BaseBrowser):
|
---|
205 | """Parent class for all Unix browsers with remote functionality."""
|
---|
206 |
|
---|
207 | raise_opts = None
|
---|
208 | remote_args = ['%action', '%s']
|
---|
209 | remote_action = None
|
---|
210 | remote_action_newwin = None
|
---|
211 | remote_action_newtab = None
|
---|
212 | background = False
|
---|
213 | redirect_stdout = True
|
---|
214 |
|
---|
215 | 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(autoraise)
|
---|
220 | opt = self.raise_opts[autoraise]
|
---|
221 | if opt: raise_opt = [opt]
|
---|
222 |
|
---|
223 | cmdline = [self.name] + raise_opt + args
|
---|
224 |
|
---|
225 | if remote or self.background:
|
---|
226 | inout = file(os.devnull, "r+")
|
---|
227 | else:
|
---|
228 | # for TTY browsers, we need stdin/out
|
---|
229 | inout = None
|
---|
230 | # if possible, put browser in separate process group, so
|
---|
231 | # keyboard interrupts don't affect browser as well as Python
|
---|
232 | setsid = getattr(os, 'setsid', None)
|
---|
233 | if not setsid:
|
---|
234 | setsid = getattr(os, 'setpgrp', None)
|
---|
235 |
|
---|
236 | p = subprocess.Popen(cmdline, close_fds=True, stdin=inout,
|
---|
237 | stdout=(self.redirect_stdout and inout or None),
|
---|
238 | stderr=inout, preexec_fn=setsid)
|
---|
239 | if remote:
|
---|
240 | # wait five seconds. If the subprocess is not finished, the
|
---|
241 | # remote invocation has (hopefully) started a new instance.
|
---|
242 | time.sleep(1)
|
---|
243 | rc = p.poll()
|
---|
244 | if rc is None:
|
---|
245 | time.sleep(4)
|
---|
246 | rc = p.poll()
|
---|
247 | if rc is None:
|
---|
248 | return True
|
---|
249 | # if remote call failed, open() will try direct invocation
|
---|
250 | return not rc
|
---|
251 | elif self.background:
|
---|
252 | if p.poll() is None:
|
---|
253 | return True
|
---|
254 | else:
|
---|
255 | return False
|
---|
256 | else:
|
---|
257 | return not p.wait()
|
---|
258 |
|
---|
259 | def open(self, url, new=0, autoraise=True):
|
---|
260 | if new == 0:
|
---|
261 | action = self.remote_action
|
---|
262 | elif new == 1:
|
---|
263 | action = self.remote_action_newwin
|
---|
264 | elif new == 2:
|
---|
265 | if self.remote_action_newtab is None:
|
---|
266 | action = self.remote_action_newwin
|
---|
267 | else:
|
---|
268 | action = self.remote_action_newtab
|
---|
269 | else:
|
---|
270 | raise Error("Bad 'new' parameter to open(); " +
|
---|
271 | "expected 0, 1, or 2, got %s" % new)
|
---|
272 |
|
---|
273 | args = [arg.replace("%s", url).replace("%action", action)
|
---|
274 | for arg in self.remote_args]
|
---|
275 | success = self._invoke(args, True, autoraise)
|
---|
276 | if not success:
|
---|
277 | # remote invocation failed, try straight way
|
---|
278 | args = [arg.replace("%s", url) for arg in self.args]
|
---|
279 | return self._invoke(args, False, False)
|
---|
280 | else:
|
---|
281 | return True
|
---|
282 |
|
---|
283 |
|
---|
284 | class Mozilla(UnixBrowser):
|
---|
285 | """Launcher class for Mozilla/Netscape browsers."""
|
---|
286 |
|
---|
287 | raise_opts = ["-noraise", "-raise"]
|
---|
288 | remote_args = ['-remote', 'openURL(%s%action)']
|
---|
289 | remote_action = ""
|
---|
290 | remote_action_newwin = ",new-window"
|
---|
291 | remote_action_newtab = ",new-tab"
|
---|
292 | background = True
|
---|
293 |
|
---|
294 | Netscape = Mozilla
|
---|
295 |
|
---|
296 |
|
---|
297 | class Galeon(UnixBrowser):
|
---|
298 | """Launcher class for Galeon/Epiphany browsers."""
|
---|
299 |
|
---|
300 | raise_opts = ["-noraise", ""]
|
---|
301 | remote_args = ['%action', '%s']
|
---|
302 | remote_action = "-n"
|
---|
303 | remote_action_newwin = "-w"
|
---|
304 | background = True
|
---|
305 |
|
---|
306 |
|
---|
307 | class Chrome(UnixBrowser):
|
---|
308 | "Launcher class for Google Chrome browser."
|
---|
309 |
|
---|
310 | remote_args = ['%action', '%s']
|
---|
311 | remote_action = ""
|
---|
312 | remote_action_newwin = "--new-window"
|
---|
313 | remote_action_newtab = ""
|
---|
314 | background = True
|
---|
315 |
|
---|
316 | Chromium = Chrome
|
---|
317 |
|
---|
318 |
|
---|
319 | class Opera(UnixBrowser):
|
---|
320 | "Launcher class for Opera browser."
|
---|
321 |
|
---|
322 | raise_opts = ["-noraise", ""]
|
---|
323 | remote_args = ['-remote', 'openURL(%s%action)']
|
---|
324 | remote_action = ""
|
---|
325 | remote_action_newwin = ",new-window"
|
---|
326 | remote_action_newtab = ",new-page"
|
---|
327 | background = True
|
---|
328 |
|
---|
329 |
|
---|
330 | class Elinks(UnixBrowser):
|
---|
331 | "Launcher class for Elinks browsers."
|
---|
332 |
|
---|
333 | remote_args = ['-remote', 'openURL(%s%action)']
|
---|
334 | remote_action = ""
|
---|
335 | remote_action_newwin = ",new-window"
|
---|
336 | remote_action_newtab = ",new-tab"
|
---|
337 | background = False
|
---|
338 |
|
---|
339 | # elinks doesn't like its stdout to be redirected -
|
---|
340 | # it uses redirected stdout as a signal to do -dump
|
---|
341 | redirect_stdout = False
|
---|
342 |
|
---|
343 |
|
---|
344 | class Konqueror(BaseBrowser):
|
---|
345 | """Controller for the KDE File Manager (kfm, or Konqueror).
|
---|
346 |
|
---|
347 | See the output of ``kfmclient --commands``
|
---|
348 | for more information on the Konqueror remote-control interface.
|
---|
349 | """
|
---|
350 |
|
---|
351 | def open(self, url, new=0, autoraise=True):
|
---|
352 | # XXX Currently I know no way to prevent KFM from opening a new win.
|
---|
353 | if new == 2:
|
---|
354 | action = "newTab"
|
---|
355 | else:
|
---|
356 | action = "openURL"
|
---|
357 |
|
---|
358 | devnull = file(os.devnull, "r+")
|
---|
359 | # if possible, put browser in separate process group, so
|
---|
360 | # keyboard interrupts don't affect browser as well as Python
|
---|
361 | setsid = getattr(os, 'setsid', None)
|
---|
362 | if not setsid:
|
---|
363 | setsid = getattr(os, 'setpgrp', None)
|
---|
364 |
|
---|
365 | try:
|
---|
366 | p = subprocess.Popen(["kfmclient", action, url],
|
---|
367 | close_fds=True, stdin=devnull,
|
---|
368 | stdout=devnull, stderr=devnull)
|
---|
369 | except OSError:
|
---|
370 | # fall through to next variant
|
---|
371 | pass
|
---|
372 | else:
|
---|
373 | p.wait()
|
---|
374 | # kfmclient's return code unfortunately has no meaning as it seems
|
---|
375 | return True
|
---|
376 |
|
---|
377 | try:
|
---|
378 | p = subprocess.Popen(["konqueror", "--silent", url],
|
---|
379 | close_fds=True, stdin=devnull,
|
---|
380 | stdout=devnull, stderr=devnull,
|
---|
381 | preexec_fn=setsid)
|
---|
382 | except OSError:
|
---|
383 | # fall through to next variant
|
---|
384 | pass
|
---|
385 | else:
|
---|
386 | if p.poll() is None:
|
---|
387 | # Should be running now.
|
---|
388 | return True
|
---|
389 |
|
---|
390 | try:
|
---|
391 | p = subprocess.Popen(["kfm", "-d", url],
|
---|
392 | close_fds=True, stdin=devnull,
|
---|
393 | stdout=devnull, stderr=devnull,
|
---|
394 | preexec_fn=setsid)
|
---|
395 | except OSError:
|
---|
396 | return False
|
---|
397 | else:
|
---|
398 | return (p.poll() is None)
|
---|
399 |
|
---|
400 |
|
---|
401 | class Grail(BaseBrowser):
|
---|
402 | # There should be a way to maintain a connection to Grail, but the
|
---|
403 | # Grail remote control protocol doesn't really allow that at this
|
---|
404 | # point. It probably never will!
|
---|
405 | def _find_grail_rc(self):
|
---|
406 | import glob
|
---|
407 | import pwd
|
---|
408 | import socket
|
---|
409 | import tempfile
|
---|
410 | tempdir = os.path.join(tempfile.gettempdir(),
|
---|
411 | ".grail-unix")
|
---|
412 | user = pwd.getpwuid(os.getuid())[0]
|
---|
413 | filename = os.path.join(tempdir, user + "-*")
|
---|
414 | maybes = glob.glob(filename)
|
---|
415 | if not maybes:
|
---|
416 | return None
|
---|
417 | s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
---|
418 | for fn in maybes:
|
---|
419 | # need to PING each one until we find one that's live
|
---|
420 | try:
|
---|
421 | s.connect(fn)
|
---|
422 | except socket.error:
|
---|
423 | # no good; attempt to clean it out, but don't fail:
|
---|
424 | try:
|
---|
425 | os.unlink(fn)
|
---|
426 | except IOError:
|
---|
427 | pass
|
---|
428 | else:
|
---|
429 | return s
|
---|
430 |
|
---|
431 | def _remote(self, action):
|
---|
432 | s = self._find_grail_rc()
|
---|
433 | if not s:
|
---|
434 | return 0
|
---|
435 | s.send(action)
|
---|
436 | s.close()
|
---|
437 | return 1
|
---|
438 |
|
---|
439 | def open(self, url, new=0, autoraise=True):
|
---|
440 | if new:
|
---|
441 | ok = self._remote("LOADNEW " + url)
|
---|
442 | else:
|
---|
443 | ok = self._remote("LOAD " + url)
|
---|
444 | return ok
|
---|
445 |
|
---|
446 |
|
---|
447 | #
|
---|
448 | # Platform support for Unix
|
---|
449 | #
|
---|
450 |
|
---|
451 | # These are the right tests because all these Unix browsers require either
|
---|
452 | # a console terminal or an X display to run.
|
---|
453 |
|
---|
454 | def register_X_browsers():
|
---|
455 |
|
---|
456 | # use xdg-open if around
|
---|
457 | if _iscommand("xdg-open"):
|
---|
458 | register("xdg-open", None, BackgroundBrowser("xdg-open"))
|
---|
459 |
|
---|
460 | # The default GNOME3 browser
|
---|
461 | if "GNOME_DESKTOP_SESSION_ID" in os.environ and _iscommand("gvfs-open"):
|
---|
462 | register("gvfs-open", None, BackgroundBrowser("gvfs-open"))
|
---|
463 |
|
---|
464 | # The default GNOME browser
|
---|
465 | if "GNOME_DESKTOP_SESSION_ID" in os.environ and _iscommand("gnome-open"):
|
---|
466 | register("gnome-open", None, BackgroundBrowser("gnome-open"))
|
---|
467 |
|
---|
468 | # The default KDE browser
|
---|
469 | if "KDE_FULL_SESSION" in os.environ and _iscommand("kfmclient"):
|
---|
470 | register("kfmclient", Konqueror, Konqueror("kfmclient"))
|
---|
471 |
|
---|
472 | if _iscommand("x-www-browser"):
|
---|
473 | register("x-www-browser", None, BackgroundBrowser("x-www-browser"))
|
---|
474 |
|
---|
475 | # The Mozilla/Netscape browsers
|
---|
476 | for browser in ("mozilla-firefox", "firefox",
|
---|
477 | "mozilla-firebird", "firebird",
|
---|
478 | "iceweasel", "iceape",
|
---|
479 | "seamonkey", "mozilla", "netscape"):
|
---|
480 | if _iscommand(browser):
|
---|
481 | register(browser, None, Mozilla(browser))
|
---|
482 |
|
---|
483 | # Konqueror/kfm, the KDE browser.
|
---|
484 | if _iscommand("kfm"):
|
---|
485 | register("kfm", Konqueror, Konqueror("kfm"))
|
---|
486 | elif _iscommand("konqueror"):
|
---|
487 | register("konqueror", Konqueror, Konqueror("konqueror"))
|
---|
488 |
|
---|
489 | # Gnome's Galeon and Epiphany
|
---|
490 | for browser in ("galeon", "epiphany"):
|
---|
491 | if _iscommand(browser):
|
---|
492 | register(browser, None, Galeon(browser))
|
---|
493 |
|
---|
494 | # Skipstone, another Gtk/Mozilla based browser
|
---|
495 | if _iscommand("skipstone"):
|
---|
496 | register("skipstone", None, BackgroundBrowser("skipstone"))
|
---|
497 |
|
---|
498 | # Google Chrome/Chromium browsers
|
---|
499 | for browser in ("google-chrome", "chrome", "chromium", "chromium-browser"):
|
---|
500 | if _iscommand(browser):
|
---|
501 | register(browser, None, Chrome(browser))
|
---|
502 |
|
---|
503 | # Opera, quite popular
|
---|
504 | if _iscommand("opera"):
|
---|
505 | register("opera", None, Opera("opera"))
|
---|
506 |
|
---|
507 | # Next, Mosaic -- old but still in use.
|
---|
508 | if _iscommand("mosaic"):
|
---|
509 | register("mosaic", None, BackgroundBrowser("mosaic"))
|
---|
510 |
|
---|
511 | # Grail, the Python browser. Does anybody still use it?
|
---|
512 | if _iscommand("grail"):
|
---|
513 | register("grail", Grail, None)
|
---|
514 |
|
---|
515 | # Prefer X browsers if present
|
---|
516 | if os.environ.get("DISPLAY"):
|
---|
517 | register_X_browsers()
|
---|
518 |
|
---|
519 | # Also try console browsers
|
---|
520 | if os.environ.get("TERM"):
|
---|
521 | if _iscommand("www-browser"):
|
---|
522 | register("www-browser", None, GenericBrowser("www-browser"))
|
---|
523 | # The Links/elinks browsers <http://artax.karlin.mff.cuni.cz/~mikulas/links/>
|
---|
524 | if _iscommand("links"):
|
---|
525 | register("links", None, GenericBrowser("links"))
|
---|
526 | if _iscommand("elinks"):
|
---|
527 | register("elinks", None, Elinks("elinks"))
|
---|
528 | # The Lynx browser <http://lynx.isc.org/>, <http://lynx.browser.org/>
|
---|
529 | if _iscommand("lynx"):
|
---|
530 | register("lynx", None, GenericBrowser("lynx"))
|
---|
531 | # The w3m browser <http://w3m.sourceforge.net/>
|
---|
532 | if _iscommand("w3m"):
|
---|
533 | register("w3m", None, GenericBrowser("w3m"))
|
---|
534 |
|
---|
535 | #
|
---|
536 | # Platform support for Windows
|
---|
537 | #
|
---|
538 |
|
---|
539 | if sys.platform[:3] == "win":
|
---|
540 | class WindowsDefault(BaseBrowser):
|
---|
541 | def open(self, url, new=0, autoraise=True):
|
---|
542 | try:
|
---|
543 | os.startfile(url)
|
---|
544 | except WindowsError:
|
---|
545 | # [Error 22] No application is associated with the specified
|
---|
546 | # file for this operation: '<URL>'
|
---|
547 | return False
|
---|
548 | else:
|
---|
549 | return True
|
---|
550 |
|
---|
551 | _tryorder = []
|
---|
552 | _browsers = {}
|
---|
553 |
|
---|
554 | # First try to use the default Windows browser
|
---|
555 | register("windows-default", WindowsDefault)
|
---|
556 |
|
---|
557 | # Detect some common Windows browsers, fallback to IE
|
---|
558 | iexplore = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"),
|
---|
559 | "Internet Explorer\\IEXPLORE.EXE")
|
---|
560 | for browser in ("firefox", "firebird", "seamonkey", "mozilla",
|
---|
561 | "netscape", "opera", iexplore):
|
---|
562 | if _iscommand(browser):
|
---|
563 | register(browser, None, BackgroundBrowser(browser))
|
---|
564 |
|
---|
565 | #
|
---|
566 | # Platform support for MacOS
|
---|
567 | #
|
---|
568 |
|
---|
569 | if sys.platform == 'darwin':
|
---|
570 | # Adapted from patch submitted to SourceForge by Steven J. Burr
|
---|
571 | class MacOSX(BaseBrowser):
|
---|
572 | """Launcher class for Aqua browsers on Mac OS X
|
---|
573 |
|
---|
574 | Optionally specify a browser name on instantiation. Note that this
|
---|
575 | will not work for Aqua browsers if the user has moved the application
|
---|
576 | package after installation.
|
---|
577 |
|
---|
578 | If no browser is specified, the default browser, as specified in the
|
---|
579 | Internet System Preferences panel, will be used.
|
---|
580 | """
|
---|
581 | def __init__(self, name):
|
---|
582 | self.name = name
|
---|
583 |
|
---|
584 | def open(self, url, new=0, autoraise=True):
|
---|
585 | assert "'" not in url
|
---|
586 | # hack for local urls
|
---|
587 | if not ':' in url:
|
---|
588 | url = 'file:'+url
|
---|
589 |
|
---|
590 | # new must be 0 or 1
|
---|
591 | new = int(bool(new))
|
---|
592 | if self.name == "default":
|
---|
593 | # User called open, open_new or get without a browser parameter
|
---|
594 | script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser
|
---|
595 | else:
|
---|
596 | # User called get and chose a browser
|
---|
597 | if self.name == "OmniWeb":
|
---|
598 | toWindow = ""
|
---|
599 | else:
|
---|
600 | # Include toWindow parameter of OpenURL command for browsers
|
---|
601 | # that support it. 0 == new window; -1 == existing
|
---|
602 | toWindow = "toWindow %d" % (new - 1)
|
---|
603 | cmd = 'OpenURL "%s"' % url.replace('"', '%22')
|
---|
604 | script = '''tell application "%s"
|
---|
605 | activate
|
---|
606 | %s %s
|
---|
607 | end tell''' % (self.name, cmd, toWindow)
|
---|
608 | # Open pipe to AppleScript through osascript command
|
---|
609 | osapipe = os.popen("osascript", "w")
|
---|
610 | if osapipe is None:
|
---|
611 | return False
|
---|
612 | # Write script to osascript's stdin
|
---|
613 | osapipe.write(script)
|
---|
614 | rc = osapipe.close()
|
---|
615 | return not rc
|
---|
616 |
|
---|
617 | class MacOSXOSAScript(BaseBrowser):
|
---|
618 | def __init__(self, name):
|
---|
619 | self._name = name
|
---|
620 |
|
---|
621 | def open(self, url, new=0, autoraise=True):
|
---|
622 | if self._name == 'default':
|
---|
623 | script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser
|
---|
624 | else:
|
---|
625 | script = '''
|
---|
626 | tell application "%s"
|
---|
627 | activate
|
---|
628 | open location "%s"
|
---|
629 | end
|
---|
630 | '''%(self._name, url.replace('"', '%22'))
|
---|
631 |
|
---|
632 | osapipe = os.popen("osascript", "w")
|
---|
633 | if osapipe is None:
|
---|
634 | return False
|
---|
635 |
|
---|
636 | osapipe.write(script)
|
---|
637 | rc = osapipe.close()
|
---|
638 | return not rc
|
---|
639 |
|
---|
640 |
|
---|
641 | # Don't clear _tryorder or _browsers since OS X can use above Unix support
|
---|
642 | # (but we prefer using the OS X specific stuff)
|
---|
643 | register("safari", None, MacOSXOSAScript('safari'), -1)
|
---|
644 | register("firefox", None, MacOSXOSAScript('firefox'), -1)
|
---|
645 | register("MacOSX", None, MacOSXOSAScript('default'), -1)
|
---|
646 |
|
---|
647 |
|
---|
648 | #
|
---|
649 | # Platform support for OS/2
|
---|
650 | #
|
---|
651 |
|
---|
652 | if sys.platform[:3] == "os2" and _iscommand("netscape"):
|
---|
653 | _tryorder = []
|
---|
654 | _browsers = {}
|
---|
655 | register("os2netscape", None,
|
---|
656 | GenericBrowser(["start", "netscape", "%s"]), -1)
|
---|
657 |
|
---|
658 |
|
---|
659 | # OK, now that we know what the default preference orders for each
|
---|
660 | # platform are, allow user to override them with the BROWSER variable.
|
---|
661 | if "BROWSER" in os.environ:
|
---|
662 | _userchoices = os.environ["BROWSER"].split(os.pathsep)
|
---|
663 | _userchoices.reverse()
|
---|
664 |
|
---|
665 | # Treat choices in same way as if passed into get() but do register
|
---|
666 | # and prepend to _tryorder
|
---|
667 | for cmdline in _userchoices:
|
---|
668 | if cmdline != '':
|
---|
669 | cmd = _synthesize(cmdline, -1)
|
---|
670 | if cmd[1] is None:
|
---|
671 | register(cmdline, None, GenericBrowser(cmdline), -1)
|
---|
672 | cmdline = None # to make del work if _userchoices was empty
|
---|
673 | del cmdline
|
---|
674 | del _userchoices
|
---|
675 |
|
---|
676 | # what to do if _tryorder is now empty?
|
---|
677 |
|
---|
678 |
|
---|
679 | def main():
|
---|
680 | import getopt
|
---|
681 | usage = """Usage: %s [-n | -t] url
|
---|
682 | -n: open new window
|
---|
683 | -t: open new tab""" % sys.argv[0]
|
---|
684 | try:
|
---|
685 | opts, args = getopt.getopt(sys.argv[1:], 'ntd')
|
---|
686 | except getopt.error, msg:
|
---|
687 | print >>sys.stderr, msg
|
---|
688 | print >>sys.stderr, usage
|
---|
689 | sys.exit(1)
|
---|
690 | new_win = 0
|
---|
691 | for o, a in opts:
|
---|
692 | if o == '-n': new_win = 1
|
---|
693 | elif o == '-t': new_win = 2
|
---|
694 | if len(args) != 1:
|
---|
695 | print >>sys.stderr, usage
|
---|
696 | sys.exit(1)
|
---|
697 |
|
---|
698 | url = args[0]
|
---|
699 | open(url, new_win)
|
---|
700 |
|
---|
701 | print "\a"
|
---|
702 |
|
---|
703 | if __name__ == "__main__":
|
---|
704 | main()
|
---|