17db96d56Sopenharmony_ci#! /usr/bin/env python3 27db96d56Sopenharmony_ci"""Interfaces for launching and remotely controlling web browsers.""" 37db96d56Sopenharmony_ci# Maintained by Georg Brandl. 47db96d56Sopenharmony_ci 57db96d56Sopenharmony_ciimport os 67db96d56Sopenharmony_ciimport shlex 77db96d56Sopenharmony_ciimport shutil 87db96d56Sopenharmony_ciimport sys 97db96d56Sopenharmony_ciimport subprocess 107db96d56Sopenharmony_ciimport threading 117db96d56Sopenharmony_ciimport warnings 127db96d56Sopenharmony_ci 137db96d56Sopenharmony_ci__all__ = ["Error", "open", "open_new", "open_new_tab", "get", "register"] 147db96d56Sopenharmony_ci 157db96d56Sopenharmony_ciclass Error(Exception): 167db96d56Sopenharmony_ci pass 177db96d56Sopenharmony_ci 187db96d56Sopenharmony_ci_lock = threading.RLock() 197db96d56Sopenharmony_ci_browsers = {} # Dictionary of available browser controllers 207db96d56Sopenharmony_ci_tryorder = None # Preference order of available browsers 217db96d56Sopenharmony_ci_os_preferred_browser = None # The preferred browser 227db96d56Sopenharmony_ci 237db96d56Sopenharmony_cidef register(name, klass, instance=None, *, preferred=False): 247db96d56Sopenharmony_ci """Register a browser connector.""" 257db96d56Sopenharmony_ci with _lock: 267db96d56Sopenharmony_ci if _tryorder is None: 277db96d56Sopenharmony_ci register_standard_browsers() 287db96d56Sopenharmony_ci _browsers[name.lower()] = [klass, instance] 297db96d56Sopenharmony_ci 307db96d56Sopenharmony_ci # Preferred browsers go to the front of the list. 317db96d56Sopenharmony_ci # Need to match to the default browser returned by xdg-settings, which 327db96d56Sopenharmony_ci # may be of the form e.g. "firefox.desktop". 337db96d56Sopenharmony_ci if preferred or (_os_preferred_browser and name in _os_preferred_browser): 347db96d56Sopenharmony_ci _tryorder.insert(0, name) 357db96d56Sopenharmony_ci else: 367db96d56Sopenharmony_ci _tryorder.append(name) 377db96d56Sopenharmony_ci 387db96d56Sopenharmony_cidef get(using=None): 397db96d56Sopenharmony_ci """Return a browser launcher instance appropriate for the environment.""" 407db96d56Sopenharmony_ci if _tryorder is None: 417db96d56Sopenharmony_ci with _lock: 427db96d56Sopenharmony_ci if _tryorder is None: 437db96d56Sopenharmony_ci register_standard_browsers() 447db96d56Sopenharmony_ci if using is not None: 457db96d56Sopenharmony_ci alternatives = [using] 467db96d56Sopenharmony_ci else: 477db96d56Sopenharmony_ci alternatives = _tryorder 487db96d56Sopenharmony_ci for browser in alternatives: 497db96d56Sopenharmony_ci if '%s' in browser: 507db96d56Sopenharmony_ci # User gave us a command line, split it into name and args 517db96d56Sopenharmony_ci browser = shlex.split(browser) 527db96d56Sopenharmony_ci if browser[-1] == '&': 537db96d56Sopenharmony_ci return BackgroundBrowser(browser[:-1]) 547db96d56Sopenharmony_ci else: 557db96d56Sopenharmony_ci return GenericBrowser(browser) 567db96d56Sopenharmony_ci else: 577db96d56Sopenharmony_ci # User gave us a browser name or path. 587db96d56Sopenharmony_ci try: 597db96d56Sopenharmony_ci command = _browsers[browser.lower()] 607db96d56Sopenharmony_ci except KeyError: 617db96d56Sopenharmony_ci command = _synthesize(browser) 627db96d56Sopenharmony_ci if command[1] is not None: 637db96d56Sopenharmony_ci return command[1] 647db96d56Sopenharmony_ci elif command[0] is not None: 657db96d56Sopenharmony_ci return command[0]() 667db96d56Sopenharmony_ci raise Error("could not locate runnable browser") 677db96d56Sopenharmony_ci 687db96d56Sopenharmony_ci# Please note: the following definition hides a builtin function. 697db96d56Sopenharmony_ci# It is recommended one does "import webbrowser" and uses webbrowser.open(url) 707db96d56Sopenharmony_ci# instead of "from webbrowser import *". 717db96d56Sopenharmony_ci 727db96d56Sopenharmony_cidef open(url, new=0, autoraise=True): 737db96d56Sopenharmony_ci """Display url using the default browser. 747db96d56Sopenharmony_ci 757db96d56Sopenharmony_ci If possible, open url in a location determined by new. 767db96d56Sopenharmony_ci - 0: the same browser window (the default). 777db96d56Sopenharmony_ci - 1: a new browser window. 787db96d56Sopenharmony_ci - 2: a new browser page ("tab"). 797db96d56Sopenharmony_ci If possible, autoraise raises the window (the default) or not. 807db96d56Sopenharmony_ci """ 817db96d56Sopenharmony_ci if _tryorder is None: 827db96d56Sopenharmony_ci with _lock: 837db96d56Sopenharmony_ci if _tryorder is None: 847db96d56Sopenharmony_ci register_standard_browsers() 857db96d56Sopenharmony_ci for name in _tryorder: 867db96d56Sopenharmony_ci browser = get(name) 877db96d56Sopenharmony_ci if browser.open(url, new, autoraise): 887db96d56Sopenharmony_ci return True 897db96d56Sopenharmony_ci return False 907db96d56Sopenharmony_ci 917db96d56Sopenharmony_cidef open_new(url): 927db96d56Sopenharmony_ci """Open url in a new window of the default browser. 937db96d56Sopenharmony_ci 947db96d56Sopenharmony_ci If not possible, then open url in the only browser window. 957db96d56Sopenharmony_ci """ 967db96d56Sopenharmony_ci return open(url, 1) 977db96d56Sopenharmony_ci 987db96d56Sopenharmony_cidef open_new_tab(url): 997db96d56Sopenharmony_ci """Open url in a new page ("tab") of the default browser. 1007db96d56Sopenharmony_ci 1017db96d56Sopenharmony_ci If not possible, then the behavior becomes equivalent to open_new(). 1027db96d56Sopenharmony_ci """ 1037db96d56Sopenharmony_ci return open(url, 2) 1047db96d56Sopenharmony_ci 1057db96d56Sopenharmony_ci 1067db96d56Sopenharmony_cidef _synthesize(browser, *, preferred=False): 1077db96d56Sopenharmony_ci """Attempt to synthesize a controller based on existing controllers. 1087db96d56Sopenharmony_ci 1097db96d56Sopenharmony_ci This is useful to create a controller when a user specifies a path to 1107db96d56Sopenharmony_ci an entry in the BROWSER environment variable -- we can copy a general 1117db96d56Sopenharmony_ci controller to operate using a specific installation of the desired 1127db96d56Sopenharmony_ci browser in this way. 1137db96d56Sopenharmony_ci 1147db96d56Sopenharmony_ci If we can't create a controller in this way, or if there is no 1157db96d56Sopenharmony_ci executable for the requested browser, return [None, None]. 1167db96d56Sopenharmony_ci 1177db96d56Sopenharmony_ci """ 1187db96d56Sopenharmony_ci cmd = browser.split()[0] 1197db96d56Sopenharmony_ci if not shutil.which(cmd): 1207db96d56Sopenharmony_ci return [None, None] 1217db96d56Sopenharmony_ci name = os.path.basename(cmd) 1227db96d56Sopenharmony_ci try: 1237db96d56Sopenharmony_ci command = _browsers[name.lower()] 1247db96d56Sopenharmony_ci except KeyError: 1257db96d56Sopenharmony_ci return [None, None] 1267db96d56Sopenharmony_ci # now attempt to clone to fit the new name: 1277db96d56Sopenharmony_ci controller = command[1] 1287db96d56Sopenharmony_ci if controller and name.lower() == controller.basename: 1297db96d56Sopenharmony_ci import copy 1307db96d56Sopenharmony_ci controller = copy.copy(controller) 1317db96d56Sopenharmony_ci controller.name = browser 1327db96d56Sopenharmony_ci controller.basename = os.path.basename(browser) 1337db96d56Sopenharmony_ci register(browser, None, instance=controller, preferred=preferred) 1347db96d56Sopenharmony_ci return [None, controller] 1357db96d56Sopenharmony_ci return [None, None] 1367db96d56Sopenharmony_ci 1377db96d56Sopenharmony_ci 1387db96d56Sopenharmony_ci# General parent classes 1397db96d56Sopenharmony_ci 1407db96d56Sopenharmony_ciclass BaseBrowser(object): 1417db96d56Sopenharmony_ci """Parent class for all browsers. Do not use directly.""" 1427db96d56Sopenharmony_ci 1437db96d56Sopenharmony_ci args = ['%s'] 1447db96d56Sopenharmony_ci 1457db96d56Sopenharmony_ci def __init__(self, name=""): 1467db96d56Sopenharmony_ci self.name = name 1477db96d56Sopenharmony_ci self.basename = name 1487db96d56Sopenharmony_ci 1497db96d56Sopenharmony_ci def open(self, url, new=0, autoraise=True): 1507db96d56Sopenharmony_ci raise NotImplementedError 1517db96d56Sopenharmony_ci 1527db96d56Sopenharmony_ci def open_new(self, url): 1537db96d56Sopenharmony_ci return self.open(url, 1) 1547db96d56Sopenharmony_ci 1557db96d56Sopenharmony_ci def open_new_tab(self, url): 1567db96d56Sopenharmony_ci return self.open(url, 2) 1577db96d56Sopenharmony_ci 1587db96d56Sopenharmony_ci 1597db96d56Sopenharmony_ciclass GenericBrowser(BaseBrowser): 1607db96d56Sopenharmony_ci """Class for all browsers started with a command 1617db96d56Sopenharmony_ci and without remote functionality.""" 1627db96d56Sopenharmony_ci 1637db96d56Sopenharmony_ci def __init__(self, name): 1647db96d56Sopenharmony_ci if isinstance(name, str): 1657db96d56Sopenharmony_ci self.name = name 1667db96d56Sopenharmony_ci self.args = ["%s"] 1677db96d56Sopenharmony_ci else: 1687db96d56Sopenharmony_ci # name should be a list with arguments 1697db96d56Sopenharmony_ci self.name = name[0] 1707db96d56Sopenharmony_ci self.args = name[1:] 1717db96d56Sopenharmony_ci self.basename = os.path.basename(self.name) 1727db96d56Sopenharmony_ci 1737db96d56Sopenharmony_ci def open(self, url, new=0, autoraise=True): 1747db96d56Sopenharmony_ci sys.audit("webbrowser.open", url) 1757db96d56Sopenharmony_ci cmdline = [self.name] + [arg.replace("%s", url) 1767db96d56Sopenharmony_ci for arg in self.args] 1777db96d56Sopenharmony_ci try: 1787db96d56Sopenharmony_ci if sys.platform[:3] == 'win': 1797db96d56Sopenharmony_ci p = subprocess.Popen(cmdline) 1807db96d56Sopenharmony_ci else: 1817db96d56Sopenharmony_ci p = subprocess.Popen(cmdline, close_fds=True) 1827db96d56Sopenharmony_ci return not p.wait() 1837db96d56Sopenharmony_ci except OSError: 1847db96d56Sopenharmony_ci return False 1857db96d56Sopenharmony_ci 1867db96d56Sopenharmony_ci 1877db96d56Sopenharmony_ciclass BackgroundBrowser(GenericBrowser): 1887db96d56Sopenharmony_ci """Class for all browsers which are to be started in the 1897db96d56Sopenharmony_ci background.""" 1907db96d56Sopenharmony_ci 1917db96d56Sopenharmony_ci def open(self, url, new=0, autoraise=True): 1927db96d56Sopenharmony_ci cmdline = [self.name] + [arg.replace("%s", url) 1937db96d56Sopenharmony_ci for arg in self.args] 1947db96d56Sopenharmony_ci sys.audit("webbrowser.open", url) 1957db96d56Sopenharmony_ci try: 1967db96d56Sopenharmony_ci if sys.platform[:3] == 'win': 1977db96d56Sopenharmony_ci p = subprocess.Popen(cmdline) 1987db96d56Sopenharmony_ci else: 1997db96d56Sopenharmony_ci p = subprocess.Popen(cmdline, close_fds=True, 2007db96d56Sopenharmony_ci start_new_session=True) 2017db96d56Sopenharmony_ci return (p.poll() is None) 2027db96d56Sopenharmony_ci except OSError: 2037db96d56Sopenharmony_ci return False 2047db96d56Sopenharmony_ci 2057db96d56Sopenharmony_ci 2067db96d56Sopenharmony_ciclass UnixBrowser(BaseBrowser): 2077db96d56Sopenharmony_ci """Parent class for all Unix browsers with remote functionality.""" 2087db96d56Sopenharmony_ci 2097db96d56Sopenharmony_ci raise_opts = None 2107db96d56Sopenharmony_ci background = False 2117db96d56Sopenharmony_ci redirect_stdout = True 2127db96d56Sopenharmony_ci # In remote_args, %s will be replaced with the requested URL. %action will 2137db96d56Sopenharmony_ci # be replaced depending on the value of 'new' passed to open. 2147db96d56Sopenharmony_ci # remote_action is used for new=0 (open). If newwin is not None, it is 2157db96d56Sopenharmony_ci # used for new=1 (open_new). If newtab is not None, it is used for 2167db96d56Sopenharmony_ci # new=3 (open_new_tab). After both substitutions are made, any empty 2177db96d56Sopenharmony_ci # strings in the transformed remote_args list will be removed. 2187db96d56Sopenharmony_ci remote_args = ['%action', '%s'] 2197db96d56Sopenharmony_ci remote_action = None 2207db96d56Sopenharmony_ci remote_action_newwin = None 2217db96d56Sopenharmony_ci remote_action_newtab = None 2227db96d56Sopenharmony_ci 2237db96d56Sopenharmony_ci def _invoke(self, args, remote, autoraise, url=None): 2247db96d56Sopenharmony_ci raise_opt = [] 2257db96d56Sopenharmony_ci if remote and self.raise_opts: 2267db96d56Sopenharmony_ci # use autoraise argument only for remote invocation 2277db96d56Sopenharmony_ci autoraise = int(autoraise) 2287db96d56Sopenharmony_ci opt = self.raise_opts[autoraise] 2297db96d56Sopenharmony_ci if opt: raise_opt = [opt] 2307db96d56Sopenharmony_ci 2317db96d56Sopenharmony_ci cmdline = [self.name] + raise_opt + args 2327db96d56Sopenharmony_ci 2337db96d56Sopenharmony_ci if remote or self.background: 2347db96d56Sopenharmony_ci inout = subprocess.DEVNULL 2357db96d56Sopenharmony_ci else: 2367db96d56Sopenharmony_ci # for TTY browsers, we need stdin/out 2377db96d56Sopenharmony_ci inout = None 2387db96d56Sopenharmony_ci p = subprocess.Popen(cmdline, close_fds=True, stdin=inout, 2397db96d56Sopenharmony_ci stdout=(self.redirect_stdout and inout or None), 2407db96d56Sopenharmony_ci stderr=inout, start_new_session=True) 2417db96d56Sopenharmony_ci if remote: 2427db96d56Sopenharmony_ci # wait at most five seconds. If the subprocess is not finished, the 2437db96d56Sopenharmony_ci # remote invocation has (hopefully) started a new instance. 2447db96d56Sopenharmony_ci try: 2457db96d56Sopenharmony_ci rc = p.wait(5) 2467db96d56Sopenharmony_ci # if remote call failed, open() will try direct invocation 2477db96d56Sopenharmony_ci return not rc 2487db96d56Sopenharmony_ci except subprocess.TimeoutExpired: 2497db96d56Sopenharmony_ci return True 2507db96d56Sopenharmony_ci elif self.background: 2517db96d56Sopenharmony_ci if p.poll() is None: 2527db96d56Sopenharmony_ci return True 2537db96d56Sopenharmony_ci else: 2547db96d56Sopenharmony_ci return False 2557db96d56Sopenharmony_ci else: 2567db96d56Sopenharmony_ci return not p.wait() 2577db96d56Sopenharmony_ci 2587db96d56Sopenharmony_ci def open(self, url, new=0, autoraise=True): 2597db96d56Sopenharmony_ci sys.audit("webbrowser.open", url) 2607db96d56Sopenharmony_ci if new == 0: 2617db96d56Sopenharmony_ci action = self.remote_action 2627db96d56Sopenharmony_ci elif new == 1: 2637db96d56Sopenharmony_ci action = self.remote_action_newwin 2647db96d56Sopenharmony_ci elif new == 2: 2657db96d56Sopenharmony_ci if self.remote_action_newtab is None: 2667db96d56Sopenharmony_ci action = self.remote_action_newwin 2677db96d56Sopenharmony_ci else: 2687db96d56Sopenharmony_ci action = self.remote_action_newtab 2697db96d56Sopenharmony_ci else: 2707db96d56Sopenharmony_ci raise Error("Bad 'new' parameter to open(); " + 2717db96d56Sopenharmony_ci "expected 0, 1, or 2, got %s" % new) 2727db96d56Sopenharmony_ci 2737db96d56Sopenharmony_ci args = [arg.replace("%s", url).replace("%action", action) 2747db96d56Sopenharmony_ci for arg in self.remote_args] 2757db96d56Sopenharmony_ci args = [arg for arg in args if arg] 2767db96d56Sopenharmony_ci success = self._invoke(args, True, autoraise, url) 2777db96d56Sopenharmony_ci if not success: 2787db96d56Sopenharmony_ci # remote invocation failed, try straight way 2797db96d56Sopenharmony_ci args = [arg.replace("%s", url) for arg in self.args] 2807db96d56Sopenharmony_ci return self._invoke(args, False, False) 2817db96d56Sopenharmony_ci else: 2827db96d56Sopenharmony_ci return True 2837db96d56Sopenharmony_ci 2847db96d56Sopenharmony_ci 2857db96d56Sopenharmony_ciclass Mozilla(UnixBrowser): 2867db96d56Sopenharmony_ci """Launcher class for Mozilla browsers.""" 2877db96d56Sopenharmony_ci 2887db96d56Sopenharmony_ci remote_args = ['%action', '%s'] 2897db96d56Sopenharmony_ci remote_action = "" 2907db96d56Sopenharmony_ci remote_action_newwin = "-new-window" 2917db96d56Sopenharmony_ci remote_action_newtab = "-new-tab" 2927db96d56Sopenharmony_ci background = True 2937db96d56Sopenharmony_ci 2947db96d56Sopenharmony_ci 2957db96d56Sopenharmony_ciclass Netscape(UnixBrowser): 2967db96d56Sopenharmony_ci """Launcher class for Netscape browser.""" 2977db96d56Sopenharmony_ci 2987db96d56Sopenharmony_ci raise_opts = ["-noraise", "-raise"] 2997db96d56Sopenharmony_ci remote_args = ['-remote', 'openURL(%s%action)'] 3007db96d56Sopenharmony_ci remote_action = "" 3017db96d56Sopenharmony_ci remote_action_newwin = ",new-window" 3027db96d56Sopenharmony_ci remote_action_newtab = ",new-tab" 3037db96d56Sopenharmony_ci background = True 3047db96d56Sopenharmony_ci 3057db96d56Sopenharmony_ci 3067db96d56Sopenharmony_ciclass Galeon(UnixBrowser): 3077db96d56Sopenharmony_ci """Launcher class for Galeon/Epiphany browsers.""" 3087db96d56Sopenharmony_ci 3097db96d56Sopenharmony_ci raise_opts = ["-noraise", ""] 3107db96d56Sopenharmony_ci remote_args = ['%action', '%s'] 3117db96d56Sopenharmony_ci remote_action = "-n" 3127db96d56Sopenharmony_ci remote_action_newwin = "-w" 3137db96d56Sopenharmony_ci background = True 3147db96d56Sopenharmony_ci 3157db96d56Sopenharmony_ci 3167db96d56Sopenharmony_ciclass Chrome(UnixBrowser): 3177db96d56Sopenharmony_ci "Launcher class for Google Chrome browser." 3187db96d56Sopenharmony_ci 3197db96d56Sopenharmony_ci remote_args = ['%action', '%s'] 3207db96d56Sopenharmony_ci remote_action = "" 3217db96d56Sopenharmony_ci remote_action_newwin = "--new-window" 3227db96d56Sopenharmony_ci remote_action_newtab = "" 3237db96d56Sopenharmony_ci background = True 3247db96d56Sopenharmony_ci 3257db96d56Sopenharmony_ciChromium = Chrome 3267db96d56Sopenharmony_ci 3277db96d56Sopenharmony_ci 3287db96d56Sopenharmony_ciclass Opera(UnixBrowser): 3297db96d56Sopenharmony_ci "Launcher class for Opera browser." 3307db96d56Sopenharmony_ci 3317db96d56Sopenharmony_ci remote_args = ['%action', '%s'] 3327db96d56Sopenharmony_ci remote_action = "" 3337db96d56Sopenharmony_ci remote_action_newwin = "--new-window" 3347db96d56Sopenharmony_ci remote_action_newtab = "" 3357db96d56Sopenharmony_ci background = True 3367db96d56Sopenharmony_ci 3377db96d56Sopenharmony_ci 3387db96d56Sopenharmony_ciclass Elinks(UnixBrowser): 3397db96d56Sopenharmony_ci "Launcher class for Elinks browsers." 3407db96d56Sopenharmony_ci 3417db96d56Sopenharmony_ci remote_args = ['-remote', 'openURL(%s%action)'] 3427db96d56Sopenharmony_ci remote_action = "" 3437db96d56Sopenharmony_ci remote_action_newwin = ",new-window" 3447db96d56Sopenharmony_ci remote_action_newtab = ",new-tab" 3457db96d56Sopenharmony_ci background = False 3467db96d56Sopenharmony_ci 3477db96d56Sopenharmony_ci # elinks doesn't like its stdout to be redirected - 3487db96d56Sopenharmony_ci # it uses redirected stdout as a signal to do -dump 3497db96d56Sopenharmony_ci redirect_stdout = False 3507db96d56Sopenharmony_ci 3517db96d56Sopenharmony_ci 3527db96d56Sopenharmony_ciclass Konqueror(BaseBrowser): 3537db96d56Sopenharmony_ci """Controller for the KDE File Manager (kfm, or Konqueror). 3547db96d56Sopenharmony_ci 3557db96d56Sopenharmony_ci See the output of ``kfmclient --commands`` 3567db96d56Sopenharmony_ci for more information on the Konqueror remote-control interface. 3577db96d56Sopenharmony_ci """ 3587db96d56Sopenharmony_ci 3597db96d56Sopenharmony_ci def open(self, url, new=0, autoraise=True): 3607db96d56Sopenharmony_ci sys.audit("webbrowser.open", url) 3617db96d56Sopenharmony_ci # XXX Currently I know no way to prevent KFM from opening a new win. 3627db96d56Sopenharmony_ci if new == 2: 3637db96d56Sopenharmony_ci action = "newTab" 3647db96d56Sopenharmony_ci else: 3657db96d56Sopenharmony_ci action = "openURL" 3667db96d56Sopenharmony_ci 3677db96d56Sopenharmony_ci devnull = subprocess.DEVNULL 3687db96d56Sopenharmony_ci 3697db96d56Sopenharmony_ci try: 3707db96d56Sopenharmony_ci p = subprocess.Popen(["kfmclient", action, url], 3717db96d56Sopenharmony_ci close_fds=True, stdin=devnull, 3727db96d56Sopenharmony_ci stdout=devnull, stderr=devnull) 3737db96d56Sopenharmony_ci except OSError: 3747db96d56Sopenharmony_ci # fall through to next variant 3757db96d56Sopenharmony_ci pass 3767db96d56Sopenharmony_ci else: 3777db96d56Sopenharmony_ci p.wait() 3787db96d56Sopenharmony_ci # kfmclient's return code unfortunately has no meaning as it seems 3797db96d56Sopenharmony_ci return True 3807db96d56Sopenharmony_ci 3817db96d56Sopenharmony_ci try: 3827db96d56Sopenharmony_ci p = subprocess.Popen(["konqueror", "--silent", url], 3837db96d56Sopenharmony_ci close_fds=True, stdin=devnull, 3847db96d56Sopenharmony_ci stdout=devnull, stderr=devnull, 3857db96d56Sopenharmony_ci start_new_session=True) 3867db96d56Sopenharmony_ci except OSError: 3877db96d56Sopenharmony_ci # fall through to next variant 3887db96d56Sopenharmony_ci pass 3897db96d56Sopenharmony_ci else: 3907db96d56Sopenharmony_ci if p.poll() is None: 3917db96d56Sopenharmony_ci # Should be running now. 3927db96d56Sopenharmony_ci return True 3937db96d56Sopenharmony_ci 3947db96d56Sopenharmony_ci try: 3957db96d56Sopenharmony_ci p = subprocess.Popen(["kfm", "-d", url], 3967db96d56Sopenharmony_ci close_fds=True, stdin=devnull, 3977db96d56Sopenharmony_ci stdout=devnull, stderr=devnull, 3987db96d56Sopenharmony_ci start_new_session=True) 3997db96d56Sopenharmony_ci except OSError: 4007db96d56Sopenharmony_ci return False 4017db96d56Sopenharmony_ci else: 4027db96d56Sopenharmony_ci return (p.poll() is None) 4037db96d56Sopenharmony_ci 4047db96d56Sopenharmony_ci 4057db96d56Sopenharmony_ciclass Grail(BaseBrowser): 4067db96d56Sopenharmony_ci # There should be a way to maintain a connection to Grail, but the 4077db96d56Sopenharmony_ci # Grail remote control protocol doesn't really allow that at this 4087db96d56Sopenharmony_ci # point. It probably never will! 4097db96d56Sopenharmony_ci def _find_grail_rc(self): 4107db96d56Sopenharmony_ci import glob 4117db96d56Sopenharmony_ci import pwd 4127db96d56Sopenharmony_ci import socket 4137db96d56Sopenharmony_ci import tempfile 4147db96d56Sopenharmony_ci tempdir = os.path.join(tempfile.gettempdir(), 4157db96d56Sopenharmony_ci ".grail-unix") 4167db96d56Sopenharmony_ci user = pwd.getpwuid(os.getuid())[0] 4177db96d56Sopenharmony_ci filename = os.path.join(glob.escape(tempdir), glob.escape(user) + "-*") 4187db96d56Sopenharmony_ci maybes = glob.glob(filename) 4197db96d56Sopenharmony_ci if not maybes: 4207db96d56Sopenharmony_ci return None 4217db96d56Sopenharmony_ci s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 4227db96d56Sopenharmony_ci for fn in maybes: 4237db96d56Sopenharmony_ci # need to PING each one until we find one that's live 4247db96d56Sopenharmony_ci try: 4257db96d56Sopenharmony_ci s.connect(fn) 4267db96d56Sopenharmony_ci except OSError: 4277db96d56Sopenharmony_ci # no good; attempt to clean it out, but don't fail: 4287db96d56Sopenharmony_ci try: 4297db96d56Sopenharmony_ci os.unlink(fn) 4307db96d56Sopenharmony_ci except OSError: 4317db96d56Sopenharmony_ci pass 4327db96d56Sopenharmony_ci else: 4337db96d56Sopenharmony_ci return s 4347db96d56Sopenharmony_ci 4357db96d56Sopenharmony_ci def _remote(self, action): 4367db96d56Sopenharmony_ci s = self._find_grail_rc() 4377db96d56Sopenharmony_ci if not s: 4387db96d56Sopenharmony_ci return 0 4397db96d56Sopenharmony_ci s.send(action) 4407db96d56Sopenharmony_ci s.close() 4417db96d56Sopenharmony_ci return 1 4427db96d56Sopenharmony_ci 4437db96d56Sopenharmony_ci def open(self, url, new=0, autoraise=True): 4447db96d56Sopenharmony_ci sys.audit("webbrowser.open", url) 4457db96d56Sopenharmony_ci if new: 4467db96d56Sopenharmony_ci ok = self._remote("LOADNEW " + url) 4477db96d56Sopenharmony_ci else: 4487db96d56Sopenharmony_ci ok = self._remote("LOAD " + url) 4497db96d56Sopenharmony_ci return ok 4507db96d56Sopenharmony_ci 4517db96d56Sopenharmony_ci 4527db96d56Sopenharmony_ci# 4537db96d56Sopenharmony_ci# Platform support for Unix 4547db96d56Sopenharmony_ci# 4557db96d56Sopenharmony_ci 4567db96d56Sopenharmony_ci# These are the right tests because all these Unix browsers require either 4577db96d56Sopenharmony_ci# a console terminal or an X display to run. 4587db96d56Sopenharmony_ci 4597db96d56Sopenharmony_cidef register_X_browsers(): 4607db96d56Sopenharmony_ci 4617db96d56Sopenharmony_ci # use xdg-open if around 4627db96d56Sopenharmony_ci if shutil.which("xdg-open"): 4637db96d56Sopenharmony_ci register("xdg-open", None, BackgroundBrowser("xdg-open")) 4647db96d56Sopenharmony_ci 4657db96d56Sopenharmony_ci # Opens an appropriate browser for the URL scheme according to 4667db96d56Sopenharmony_ci # freedesktop.org settings (GNOME, KDE, XFCE, etc.) 4677db96d56Sopenharmony_ci if shutil.which("gio"): 4687db96d56Sopenharmony_ci register("gio", None, BackgroundBrowser(["gio", "open", "--", "%s"])) 4697db96d56Sopenharmony_ci 4707db96d56Sopenharmony_ci # Equivalent of gio open before 2015 4717db96d56Sopenharmony_ci if "GNOME_DESKTOP_SESSION_ID" in os.environ and shutil.which("gvfs-open"): 4727db96d56Sopenharmony_ci register("gvfs-open", None, BackgroundBrowser("gvfs-open")) 4737db96d56Sopenharmony_ci 4747db96d56Sopenharmony_ci # The default KDE browser 4757db96d56Sopenharmony_ci if "KDE_FULL_SESSION" in os.environ and shutil.which("kfmclient"): 4767db96d56Sopenharmony_ci register("kfmclient", Konqueror, Konqueror("kfmclient")) 4777db96d56Sopenharmony_ci 4787db96d56Sopenharmony_ci if shutil.which("x-www-browser"): 4797db96d56Sopenharmony_ci register("x-www-browser", None, BackgroundBrowser("x-www-browser")) 4807db96d56Sopenharmony_ci 4817db96d56Sopenharmony_ci # The Mozilla browsers 4827db96d56Sopenharmony_ci for browser in ("firefox", "iceweasel", "iceape", "seamonkey"): 4837db96d56Sopenharmony_ci if shutil.which(browser): 4847db96d56Sopenharmony_ci register(browser, None, Mozilla(browser)) 4857db96d56Sopenharmony_ci 4867db96d56Sopenharmony_ci # The Netscape and old Mozilla browsers 4877db96d56Sopenharmony_ci for browser in ("mozilla-firefox", 4887db96d56Sopenharmony_ci "mozilla-firebird", "firebird", 4897db96d56Sopenharmony_ci "mozilla", "netscape"): 4907db96d56Sopenharmony_ci if shutil.which(browser): 4917db96d56Sopenharmony_ci register(browser, None, Netscape(browser)) 4927db96d56Sopenharmony_ci 4937db96d56Sopenharmony_ci # Konqueror/kfm, the KDE browser. 4947db96d56Sopenharmony_ci if shutil.which("kfm"): 4957db96d56Sopenharmony_ci register("kfm", Konqueror, Konqueror("kfm")) 4967db96d56Sopenharmony_ci elif shutil.which("konqueror"): 4977db96d56Sopenharmony_ci register("konqueror", Konqueror, Konqueror("konqueror")) 4987db96d56Sopenharmony_ci 4997db96d56Sopenharmony_ci # Gnome's Galeon and Epiphany 5007db96d56Sopenharmony_ci for browser in ("galeon", "epiphany"): 5017db96d56Sopenharmony_ci if shutil.which(browser): 5027db96d56Sopenharmony_ci register(browser, None, Galeon(browser)) 5037db96d56Sopenharmony_ci 5047db96d56Sopenharmony_ci # Skipstone, another Gtk/Mozilla based browser 5057db96d56Sopenharmony_ci if shutil.which("skipstone"): 5067db96d56Sopenharmony_ci register("skipstone", None, BackgroundBrowser("skipstone")) 5077db96d56Sopenharmony_ci 5087db96d56Sopenharmony_ci # Google Chrome/Chromium browsers 5097db96d56Sopenharmony_ci for browser in ("google-chrome", "chrome", "chromium", "chromium-browser"): 5107db96d56Sopenharmony_ci if shutil.which(browser): 5117db96d56Sopenharmony_ci register(browser, None, Chrome(browser)) 5127db96d56Sopenharmony_ci 5137db96d56Sopenharmony_ci # Opera, quite popular 5147db96d56Sopenharmony_ci if shutil.which("opera"): 5157db96d56Sopenharmony_ci register("opera", None, Opera("opera")) 5167db96d56Sopenharmony_ci 5177db96d56Sopenharmony_ci # Next, Mosaic -- old but still in use. 5187db96d56Sopenharmony_ci if shutil.which("mosaic"): 5197db96d56Sopenharmony_ci register("mosaic", None, BackgroundBrowser("mosaic")) 5207db96d56Sopenharmony_ci 5217db96d56Sopenharmony_ci # Grail, the Python browser. Does anybody still use it? 5227db96d56Sopenharmony_ci if shutil.which("grail"): 5237db96d56Sopenharmony_ci register("grail", Grail, None) 5247db96d56Sopenharmony_ci 5257db96d56Sopenharmony_cidef register_standard_browsers(): 5267db96d56Sopenharmony_ci global _tryorder 5277db96d56Sopenharmony_ci _tryorder = [] 5287db96d56Sopenharmony_ci 5297db96d56Sopenharmony_ci if sys.platform == 'darwin': 5307db96d56Sopenharmony_ci register("MacOSX", None, MacOSXOSAScript('default')) 5317db96d56Sopenharmony_ci register("chrome", None, MacOSXOSAScript('chrome')) 5327db96d56Sopenharmony_ci register("firefox", None, MacOSXOSAScript('firefox')) 5337db96d56Sopenharmony_ci register("safari", None, MacOSXOSAScript('safari')) 5347db96d56Sopenharmony_ci # OS X can use below Unix support (but we prefer using the OS X 5357db96d56Sopenharmony_ci # specific stuff) 5367db96d56Sopenharmony_ci 5377db96d56Sopenharmony_ci if sys.platform == "serenityos": 5387db96d56Sopenharmony_ci # SerenityOS webbrowser, simply called "Browser". 5397db96d56Sopenharmony_ci register("Browser", None, BackgroundBrowser("Browser")) 5407db96d56Sopenharmony_ci 5417db96d56Sopenharmony_ci if sys.platform[:3] == "win": 5427db96d56Sopenharmony_ci # First try to use the default Windows browser 5437db96d56Sopenharmony_ci register("windows-default", WindowsDefault) 5447db96d56Sopenharmony_ci 5457db96d56Sopenharmony_ci # Detect some common Windows browsers, fallback to IE 5467db96d56Sopenharmony_ci iexplore = os.path.join(os.environ.get("PROGRAMFILES", "C:\\Program Files"), 5477db96d56Sopenharmony_ci "Internet Explorer\\IEXPLORE.EXE") 5487db96d56Sopenharmony_ci for browser in ("firefox", "firebird", "seamonkey", "mozilla", 5497db96d56Sopenharmony_ci "netscape", "opera", iexplore): 5507db96d56Sopenharmony_ci if shutil.which(browser): 5517db96d56Sopenharmony_ci register(browser, None, BackgroundBrowser(browser)) 5527db96d56Sopenharmony_ci else: 5537db96d56Sopenharmony_ci # Prefer X browsers if present 5547db96d56Sopenharmony_ci if os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY"): 5557db96d56Sopenharmony_ci try: 5567db96d56Sopenharmony_ci cmd = "xdg-settings get default-web-browser".split() 5577db96d56Sopenharmony_ci raw_result = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) 5587db96d56Sopenharmony_ci result = raw_result.decode().strip() 5597db96d56Sopenharmony_ci except (FileNotFoundError, subprocess.CalledProcessError, PermissionError, NotADirectoryError) : 5607db96d56Sopenharmony_ci pass 5617db96d56Sopenharmony_ci else: 5627db96d56Sopenharmony_ci global _os_preferred_browser 5637db96d56Sopenharmony_ci _os_preferred_browser = result 5647db96d56Sopenharmony_ci 5657db96d56Sopenharmony_ci register_X_browsers() 5667db96d56Sopenharmony_ci 5677db96d56Sopenharmony_ci # Also try console browsers 5687db96d56Sopenharmony_ci if os.environ.get("TERM"): 5697db96d56Sopenharmony_ci if shutil.which("www-browser"): 5707db96d56Sopenharmony_ci register("www-browser", None, GenericBrowser("www-browser")) 5717db96d56Sopenharmony_ci # The Links/elinks browsers <http://artax.karlin.mff.cuni.cz/~mikulas/links/> 5727db96d56Sopenharmony_ci if shutil.which("links"): 5737db96d56Sopenharmony_ci register("links", None, GenericBrowser("links")) 5747db96d56Sopenharmony_ci if shutil.which("elinks"): 5757db96d56Sopenharmony_ci register("elinks", None, Elinks("elinks")) 5767db96d56Sopenharmony_ci # The Lynx browser <http://lynx.isc.org/>, <http://lynx.browser.org/> 5777db96d56Sopenharmony_ci if shutil.which("lynx"): 5787db96d56Sopenharmony_ci register("lynx", None, GenericBrowser("lynx")) 5797db96d56Sopenharmony_ci # The w3m browser <http://w3m.sourceforge.net/> 5807db96d56Sopenharmony_ci if shutil.which("w3m"): 5817db96d56Sopenharmony_ci register("w3m", None, GenericBrowser("w3m")) 5827db96d56Sopenharmony_ci 5837db96d56Sopenharmony_ci # OK, now that we know what the default preference orders for each 5847db96d56Sopenharmony_ci # platform are, allow user to override them with the BROWSER variable. 5857db96d56Sopenharmony_ci if "BROWSER" in os.environ: 5867db96d56Sopenharmony_ci userchoices = os.environ["BROWSER"].split(os.pathsep) 5877db96d56Sopenharmony_ci userchoices.reverse() 5887db96d56Sopenharmony_ci 5897db96d56Sopenharmony_ci # Treat choices in same way as if passed into get() but do register 5907db96d56Sopenharmony_ci # and prepend to _tryorder 5917db96d56Sopenharmony_ci for cmdline in userchoices: 5927db96d56Sopenharmony_ci if cmdline != '': 5937db96d56Sopenharmony_ci cmd = _synthesize(cmdline, preferred=True) 5947db96d56Sopenharmony_ci if cmd[1] is None: 5957db96d56Sopenharmony_ci register(cmdline, None, GenericBrowser(cmdline), preferred=True) 5967db96d56Sopenharmony_ci 5977db96d56Sopenharmony_ci # what to do if _tryorder is now empty? 5987db96d56Sopenharmony_ci 5997db96d56Sopenharmony_ci 6007db96d56Sopenharmony_ci# 6017db96d56Sopenharmony_ci# Platform support for Windows 6027db96d56Sopenharmony_ci# 6037db96d56Sopenharmony_ci 6047db96d56Sopenharmony_ciif sys.platform[:3] == "win": 6057db96d56Sopenharmony_ci class WindowsDefault(BaseBrowser): 6067db96d56Sopenharmony_ci def open(self, url, new=0, autoraise=True): 6077db96d56Sopenharmony_ci sys.audit("webbrowser.open", url) 6087db96d56Sopenharmony_ci try: 6097db96d56Sopenharmony_ci os.startfile(url) 6107db96d56Sopenharmony_ci except OSError: 6117db96d56Sopenharmony_ci # [Error 22] No application is associated with the specified 6127db96d56Sopenharmony_ci # file for this operation: '<URL>' 6137db96d56Sopenharmony_ci return False 6147db96d56Sopenharmony_ci else: 6157db96d56Sopenharmony_ci return True 6167db96d56Sopenharmony_ci 6177db96d56Sopenharmony_ci# 6187db96d56Sopenharmony_ci# Platform support for MacOS 6197db96d56Sopenharmony_ci# 6207db96d56Sopenharmony_ci 6217db96d56Sopenharmony_ciif sys.platform == 'darwin': 6227db96d56Sopenharmony_ci # Adapted from patch submitted to SourceForge by Steven J. Burr 6237db96d56Sopenharmony_ci class MacOSX(BaseBrowser): 6247db96d56Sopenharmony_ci """Launcher class for Aqua browsers on Mac OS X 6257db96d56Sopenharmony_ci 6267db96d56Sopenharmony_ci Optionally specify a browser name on instantiation. Note that this 6277db96d56Sopenharmony_ci will not work for Aqua browsers if the user has moved the application 6287db96d56Sopenharmony_ci package after installation. 6297db96d56Sopenharmony_ci 6307db96d56Sopenharmony_ci If no browser is specified, the default browser, as specified in the 6317db96d56Sopenharmony_ci Internet System Preferences panel, will be used. 6327db96d56Sopenharmony_ci """ 6337db96d56Sopenharmony_ci def __init__(self, name): 6347db96d56Sopenharmony_ci warnings.warn(f'{self.__class__.__name__} is deprecated in 3.11' 6357db96d56Sopenharmony_ci ' use MacOSXOSAScript instead.', DeprecationWarning, stacklevel=2) 6367db96d56Sopenharmony_ci self.name = name 6377db96d56Sopenharmony_ci 6387db96d56Sopenharmony_ci def open(self, url, new=0, autoraise=True): 6397db96d56Sopenharmony_ci sys.audit("webbrowser.open", url) 6407db96d56Sopenharmony_ci assert "'" not in url 6417db96d56Sopenharmony_ci # hack for local urls 6427db96d56Sopenharmony_ci if not ':' in url: 6437db96d56Sopenharmony_ci url = 'file:'+url 6447db96d56Sopenharmony_ci 6457db96d56Sopenharmony_ci # new must be 0 or 1 6467db96d56Sopenharmony_ci new = int(bool(new)) 6477db96d56Sopenharmony_ci if self.name == "default": 6487db96d56Sopenharmony_ci # User called open, open_new or get without a browser parameter 6497db96d56Sopenharmony_ci script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser 6507db96d56Sopenharmony_ci else: 6517db96d56Sopenharmony_ci # User called get and chose a browser 6527db96d56Sopenharmony_ci if self.name == "OmniWeb": 6537db96d56Sopenharmony_ci toWindow = "" 6547db96d56Sopenharmony_ci else: 6557db96d56Sopenharmony_ci # Include toWindow parameter of OpenURL command for browsers 6567db96d56Sopenharmony_ci # that support it. 0 == new window; -1 == existing 6577db96d56Sopenharmony_ci toWindow = "toWindow %d" % (new - 1) 6587db96d56Sopenharmony_ci cmd = 'OpenURL "%s"' % url.replace('"', '%22') 6597db96d56Sopenharmony_ci script = '''tell application "%s" 6607db96d56Sopenharmony_ci activate 6617db96d56Sopenharmony_ci %s %s 6627db96d56Sopenharmony_ci end tell''' % (self.name, cmd, toWindow) 6637db96d56Sopenharmony_ci # Open pipe to AppleScript through osascript command 6647db96d56Sopenharmony_ci osapipe = os.popen("osascript", "w") 6657db96d56Sopenharmony_ci if osapipe is None: 6667db96d56Sopenharmony_ci return False 6677db96d56Sopenharmony_ci # Write script to osascript's stdin 6687db96d56Sopenharmony_ci osapipe.write(script) 6697db96d56Sopenharmony_ci rc = osapipe.close() 6707db96d56Sopenharmony_ci return not rc 6717db96d56Sopenharmony_ci 6727db96d56Sopenharmony_ci class MacOSXOSAScript(BaseBrowser): 6737db96d56Sopenharmony_ci def __init__(self, name='default'): 6747db96d56Sopenharmony_ci super().__init__(name) 6757db96d56Sopenharmony_ci 6767db96d56Sopenharmony_ci @property 6777db96d56Sopenharmony_ci def _name(self): 6787db96d56Sopenharmony_ci warnings.warn(f'{self.__class__.__name__}._name is deprecated in 3.11' 6797db96d56Sopenharmony_ci f' use {self.__class__.__name__}.name instead.', 6807db96d56Sopenharmony_ci DeprecationWarning, stacklevel=2) 6817db96d56Sopenharmony_ci return self.name 6827db96d56Sopenharmony_ci 6837db96d56Sopenharmony_ci @_name.setter 6847db96d56Sopenharmony_ci def _name(self, val): 6857db96d56Sopenharmony_ci warnings.warn(f'{self.__class__.__name__}._name is deprecated in 3.11' 6867db96d56Sopenharmony_ci f' use {self.__class__.__name__}.name instead.', 6877db96d56Sopenharmony_ci DeprecationWarning, stacklevel=2) 6887db96d56Sopenharmony_ci self.name = val 6897db96d56Sopenharmony_ci 6907db96d56Sopenharmony_ci def open(self, url, new=0, autoraise=True): 6917db96d56Sopenharmony_ci if self.name == 'default': 6927db96d56Sopenharmony_ci script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser 6937db96d56Sopenharmony_ci else: 6947db96d56Sopenharmony_ci script = f''' 6957db96d56Sopenharmony_ci tell application "%s" 6967db96d56Sopenharmony_ci activate 6977db96d56Sopenharmony_ci open location "%s" 6987db96d56Sopenharmony_ci end 6997db96d56Sopenharmony_ci '''%(self.name, url.replace('"', '%22')) 7007db96d56Sopenharmony_ci 7017db96d56Sopenharmony_ci osapipe = os.popen("osascript", "w") 7027db96d56Sopenharmony_ci if osapipe is None: 7037db96d56Sopenharmony_ci return False 7047db96d56Sopenharmony_ci 7057db96d56Sopenharmony_ci osapipe.write(script) 7067db96d56Sopenharmony_ci rc = osapipe.close() 7077db96d56Sopenharmony_ci return not rc 7087db96d56Sopenharmony_ci 7097db96d56Sopenharmony_ci 7107db96d56Sopenharmony_cidef main(): 7117db96d56Sopenharmony_ci import getopt 7127db96d56Sopenharmony_ci usage = """Usage: %s [-n | -t] url 7137db96d56Sopenharmony_ci -n: open new window 7147db96d56Sopenharmony_ci -t: open new tab""" % sys.argv[0] 7157db96d56Sopenharmony_ci try: 7167db96d56Sopenharmony_ci opts, args = getopt.getopt(sys.argv[1:], 'ntd') 7177db96d56Sopenharmony_ci except getopt.error as msg: 7187db96d56Sopenharmony_ci print(msg, file=sys.stderr) 7197db96d56Sopenharmony_ci print(usage, file=sys.stderr) 7207db96d56Sopenharmony_ci sys.exit(1) 7217db96d56Sopenharmony_ci new_win = 0 7227db96d56Sopenharmony_ci for o, a in opts: 7237db96d56Sopenharmony_ci if o == '-n': new_win = 1 7247db96d56Sopenharmony_ci elif o == '-t': new_win = 2 7257db96d56Sopenharmony_ci if len(args) != 1: 7267db96d56Sopenharmony_ci print(usage, file=sys.stderr) 7277db96d56Sopenharmony_ci sys.exit(1) 7287db96d56Sopenharmony_ci 7297db96d56Sopenharmony_ci url = args[0] 7307db96d56Sopenharmony_ci open(url, new_win) 7317db96d56Sopenharmony_ci 7327db96d56Sopenharmony_ci print("\a") 7337db96d56Sopenharmony_ci 7347db96d56Sopenharmony_ciif __name__ == "__main__": 7357db96d56Sopenharmony_ci main() 736