Don't execute hildon_program_get_instance() before gtk_init()
[browser-switch] / browser-switchboard
1 #!/usr/bin/python
2 # browser-switchboard
3 # version 2.2 by steven676 and xiojason
4
5 # simple python-dbus service that proxies osso_browser events to Tear
6 # based on code from http://paste.lisp.org/display/45824
7
8 # Copyright (C) 2009 Jason Simpson
9 # Copyright (C) 2009 Steven Luo
10
11 # This program is free software; you can redistribute it and/or
12 # modify it under the terms of the GNU General Public License
13 # as published by the Free Software Foundation; either version 2
14 # of the License, or (at your option) any later version.
15
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 # GNU General Public License for more details.
20
21 # You should have received a copy of the GNU General Public License
22 # along with this program; if not, write to the Free Software
23 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24
25 # On Maemo systems, a copy of the GPL can be found in
26 # /usr/share/common-licenses/GPL .
27
28
29 import os
30 import gobject
31 import dbus
32 import dbus.service
33 import dbus.glib
34
35 # Set default configuration values
36 # These can be overriden by the config file $HOME/.config/browser-switchboard
37 def setconfigdefaults():
38     global continuous_mode, default_browser, other_browser_cmd
39
40     # continuous_mode: 0 -- close after handling a request; 1 -- run
41     # continuously in the background
42     continuous_mode = 0
43     # default_browser: "tear", "microb", "fennec", "midori", or "other"
44     # empty string is handled specially -- see below
45     default_browser = ""
46     # If default browser is "other", what program to run (%s will be replaced
47     # by URI)
48     other_browser_cmd = ""
49
50 class BrowserLauncher:
51     def LaunchTear(self, uri):
52         # We should be able to just call the D-Bus service to open Tear ...
53         # but if Tear's not open, that causes D-Bus to start Tear and then
54         # pass it the OpenAddress call, which results in two browser windows.
55         # Properly fixing this probably requires Tear to provide a D-Bus method
56         # that opens an address in an existing window, but for now work around
57         # by just opening Tear via the command line if it's not running.
58         if os.system("pidof tear > /dev/null") == 0:
59             dbus.SessionBus().get_object('com.nokia.tear', '/com/nokia/tear').OpenAddress(uri, dbus_interface='com.nokia.Tear')
60             if continuous_mode == 0:
61                 quit()
62         else:
63             if continuous_mode:
64                 if os.fork() != 0:
65                     # parent process doesn't need to do anything
66                     return
67                 # child process
68                 os.setsid()
69             os.execl('/usr/bin/tear', '/usr/bin/tear', uri)
70
71     def LaunchMicroB(self, uri):
72         if os.system("pidof /usr/sbin/browserd > /dev/null") != 0:
73             kill_browserd = 1
74             os.system("/usr/sbin/browserd -d")
75         else:
76             kill_browserd = 0
77         dbus.SessionBus().release_name("com.nokia.osso_browser")
78         if uri == "new_window":
79             # exec maemo-invoker directly instead of relying on the
80             # /usr/bin/browser symlink, since /usr/bin/browser may have been
81             # replaced with a shell script calling us via D-Bus
82             os.spawnl(os.P_WAIT, '/usr/bin/maemo-invoker', '/usr/bin/browser')
83         else:
84             os.spawnl(os.P_WAIT, '/usr/bin/maemo-invoker', '/usr/bin/browser', '--url', uri)
85         if kill_browserd:
86             os.system("kill `pidof /usr/sbin/browserd`")
87         if continuous_mode:
88             dbus.SessionBus().request_name("com.nokia.osso_browser")
89         else:
90             quit()
91
92     def LaunchOtherBrowser(self, uri):
93         if uri == "new_window":
94             uri = ""
95         try:
96             # URI must be quoted and quotes in the URI must be URL-escaped
97             # to prevent the shell from interpreting any part of the URI
98             command = other_browser_cmd % "'%s'" % uri.replace("'", "%27")
99         except TypeError:
100             # Couldn't substitute URI in, just launch the browser
101             print "other_browser_cmd should have a %s placeholder for the URI"
102             command = other_browser_cmd
103         import subprocess
104         subprocess.Popen(command, shell=True)
105         if continuous_mode == 0:
106             quit()
107
108     def UpdateDefaultBrowser(self):
109         global other_browser_cmd
110         if default_browser == "tear":
111             self.LaunchBrowser = self.LaunchTear
112         elif default_browser == "microb":
113             self.LaunchBrowser = self.LaunchMicroB
114         elif default_browser == "fennec":
115             # Cheat and reuse LaunchOtherBrowser, since we don't appear to
116             # need to do anything special
117             # TODO: does Fennec have a D-Bus API or signaling mechanism?
118             # Invoking the fennec binary appears to be somewhat expensive
119             other_browser_cmd = "fennec '%s'"
120             self.LaunchBrowser = self.LaunchOtherBrowser
121         elif default_browser == "midori":
122             other_browser_cmd = "midori '%s'"
123             self.LaunchBrowser = self.LaunchOtherBrowser
124         elif default_browser == "other":
125             if other_browser_cmd != "":
126                 self.LaunchBrowser = self.LaunchOtherBrowser
127             else:
128                 print "default_browser is 'other', but no other_browser_cmd set -- using default"
129                 self.LaunchBrowser = self.LaunchTear
130         elif default_browser == "":
131             # If default_browser is empty, use Tear as the default if
132             # installed, otherwise use MicroB
133             if os.access("/usr/bin/tear", os.X_OK):
134                 self.LaunchBrowser = self.LaunchTear
135             else:
136                 self.LaunchBrowser = self.LaunchMicroB
137         else:
138             print "Unknown default_browser %s, using default" % default_browser
139             self.LaunchBrowser = self.LaunchTear
140
141     def __init__(self):
142         self.UpdateDefaultBrowser()
143
144 class ProxyBrowserService(dbus.service.Object):
145     def __init__(self):
146         bus_name = dbus.service.BusName('com.nokia.osso_browser', bus=dbus.SessionBus())
147         dbus.service.Object.__init__(self, bus_name, '/com/nokia/osso_browser')
148         dbus.service.Object.__init__(self, bus_name, '/com/nokia/osso_browser/request')
149
150     def OpenAddress(self, uri):
151         print uri
152
153         if uri[0] == '/':
154             print "prefixing apparent local path with file://"
155             uri = "file://" + uri
156
157         launcher.LaunchBrowser(uri)
158
159     @dbus.service.method(dbus_interface='com.nokia.osso_browser', in_signature='s')
160     def load_url(self, uri):
161         print "load_url"
162         self.OpenAddress(uri)
163
164     @dbus.service.method(dbus_interface='com.nokia.osso_browser', in_signature='s')
165     def mime_open(self, uri):
166         print "mime_open"
167         self.OpenAddress(uri)
168
169     @dbus.service.method(dbus_interface='com.nokia.osso_browser', in_signature='s')
170     def open_new_window(self, uri):
171         print "open_new_window"
172         self.OpenAddress(uri)
173
174     @dbus.service.method(dbus_interface='com.nokia.osso_browser')
175     def top_application(self):
176         print "top_application"
177         launcher.LaunchMicroB("new_window")
178
179     # This method is an "undocumented", non-standard extension to the
180     # osso_browser D-Bus API, intended solely to allow a wrapper script
181     # replacing /usr/bin/browser to implement --url
182     @dbus.service.method(dbus_interface='com.nokia.osso_browser')
183     def switchboard_launch_microb(self, uri="new_window"):
184         print "switchboard_launch_microb"
185         launcher.LaunchMicroB(uri)
186
187 def readconfigfile(signalnum=None, frame=None):
188     # reset configuration to defaults
189     setconfigdefaults()
190
191     # read configuration from the config file, if available
192     try:
193         execfile(os.getenv("HOME", "/home/user") + "/.config/browser-switchboard", globals())
194     except:
195         # Try the legacy config file location
196         try:
197             execfile(os.getenv("HOME", "/home/user") + "/.config/browser-proxy", globals())
198         except:
199             # No valid config file available
200             pass
201     launcher.UpdateDefaultBrowser()
202
203 setconfigdefaults()
204
205 launcher = BrowserLauncher()
206 pbrowser = ProxyBrowserService()
207
208 readconfigfile()
209
210 if continuous_mode:
211     import signal
212     def waitforzombies(signalnum, frame):
213         try:
214             while os.waitpid(-1, os.WNOHANG) != (0, 0):
215                 continue
216         except OSError:
217             pass
218     signal.signal(signal.SIGCHLD, waitforzombies)
219     signal.signal(signal.SIGHUP, readconfigfile)
220
221 loop = gobject.MainLoop()
222 loop.run()