|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "nsNativeAppSupportBase.h" |
|
7 #include "nsCOMPtr.h" |
|
8 #include "nsXPCOM.h" |
|
9 #include "nsISupportsPrimitives.h" |
|
10 #include "nsIObserverService.h" |
|
11 #include "nsIAppStartup.h" |
|
12 #include "nsServiceManagerUtils.h" |
|
13 #include "prlink.h" |
|
14 #include "nsXREDirProvider.h" |
|
15 #include "nsReadableUtils.h" |
|
16 |
|
17 #include "nsIFile.h" |
|
18 #include "nsDirectoryServiceDefs.h" |
|
19 #include "nsICommandLineRunner.h" |
|
20 #include "nsIWindowMediator.h" |
|
21 #include "nsPIDOMWindow.h" |
|
22 #include "nsIDocShell.h" |
|
23 #include "nsIBaseWindow.h" |
|
24 #include "nsIWidget.h" |
|
25 #include "nsIWritablePropertyBag2.h" |
|
26 #include "nsIPrefService.h" |
|
27 #include "mozilla/Services.h" |
|
28 |
|
29 #include <stdlib.h> |
|
30 #include <glib.h> |
|
31 #include <glib-object.h> |
|
32 #include <gtk/gtk.h> |
|
33 |
|
34 #ifdef MOZ_X11 |
|
35 #include <gdk/gdkx.h> |
|
36 #include <X11/Xatom.h> |
|
37 #endif |
|
38 |
|
39 #ifdef MOZ_ENABLE_DBUS |
|
40 #include <dbus/dbus.h> |
|
41 #endif |
|
42 |
|
43 #define MIN_GTK_MAJOR_VERSION 2 |
|
44 #define MIN_GTK_MINOR_VERSION 10 |
|
45 #define UNSUPPORTED_GTK_MSG "We're sorry, this application requires a version of the GTK+ library that is not installed on your computer.\n\n\ |
|
46 You have GTK+ %d.%d.\nThis application requires GTK+ %d.%d or newer.\n\n\ |
|
47 Please upgrade your GTK+ library if you wish to use this application." |
|
48 |
|
49 typedef struct _GnomeProgram GnomeProgram; |
|
50 typedef struct _GnomeModuleInfo GnomeModuleInfo; |
|
51 typedef struct _GnomeClient GnomeClient; |
|
52 |
|
53 typedef enum { |
|
54 GNOME_SAVE_GLOBAL, |
|
55 GNOME_SAVE_LOCAL, |
|
56 GNOME_SAVE_BOTH |
|
57 } GnomeSaveStyle; |
|
58 |
|
59 typedef enum { |
|
60 GNOME_INTERACT_NONE, |
|
61 GNOME_INTERACT_ERRORS, |
|
62 GNOME_INTERACT_ANY |
|
63 } GnomeInteractStyle; |
|
64 |
|
65 typedef enum { |
|
66 GNOME_DIALOG_ERROR, |
|
67 GNOME_DIALOG_NORMAL |
|
68 } GnomeDialogType; |
|
69 |
|
70 typedef GnomeProgram * (*_gnome_program_init_fn)(const char *, const char *, |
|
71 const GnomeModuleInfo *, int, |
|
72 char **, const char *, ...); |
|
73 typedef GnomeProgram * (*_gnome_program_get_fn)(void); |
|
74 typedef const GnomeModuleInfo * (*_libgnomeui_module_info_get_fn)(); |
|
75 typedef GnomeClient * (*_gnome_master_client_fn)(void); |
|
76 typedef void (*_gnome_client_set_restart_command_fn)(GnomeClient*, gint, gchar*[]); |
|
77 |
|
78 static _gnome_client_set_restart_command_fn gnome_client_set_restart_command; |
|
79 |
|
80 gboolean save_yourself_cb(GnomeClient *client, gint phase, |
|
81 GnomeSaveStyle style, gboolean shutdown, |
|
82 GnomeInteractStyle interact, gboolean fast, |
|
83 gpointer user_data) |
|
84 { |
|
85 nsCOMPtr<nsIObserverService> obsServ = |
|
86 mozilla::services::GetObserverService(); |
|
87 |
|
88 nsCOMPtr<nsISupportsPRBool> didSaveSession = |
|
89 do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID); |
|
90 |
|
91 if (!obsServ || !didSaveSession) |
|
92 return TRUE; // OOM |
|
93 |
|
94 // Notify observers to save the session state |
|
95 didSaveSession->SetData(false); |
|
96 obsServ->NotifyObservers(didSaveSession, "session-save", nullptr); |
|
97 |
|
98 bool status; |
|
99 didSaveSession->GetData(&status); |
|
100 |
|
101 // If there was no session saved and the save_yourself request is |
|
102 // caused by upcoming shutdown we like to prepare for it |
|
103 if (!status && shutdown) { |
|
104 nsCOMPtr<nsISupportsPRBool> cancelQuit = |
|
105 do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID); |
|
106 |
|
107 cancelQuit->SetData(false); |
|
108 obsServ->NotifyObservers(cancelQuit, "quit-application-requested", nullptr); |
|
109 |
|
110 bool abortQuit; |
|
111 cancelQuit->GetData(&abortQuit); |
|
112 } |
|
113 |
|
114 return TRUE; |
|
115 } |
|
116 |
|
117 void die_cb(GnomeClient *client, gpointer user_data) |
|
118 { |
|
119 nsCOMPtr<nsIAppStartup> appService = |
|
120 do_GetService("@mozilla.org/toolkit/app-startup;1"); |
|
121 |
|
122 if (appService) |
|
123 appService->Quit(nsIAppStartup::eForceQuit); |
|
124 } |
|
125 |
|
126 class nsNativeAppSupportUnix : public nsNativeAppSupportBase |
|
127 { |
|
128 public: |
|
129 NS_IMETHOD Start(bool* aRetVal); |
|
130 NS_IMETHOD Stop(bool *aResult); |
|
131 NS_IMETHOD Enable(); |
|
132 |
|
133 private: |
|
134 }; |
|
135 |
|
136 NS_IMETHODIMP |
|
137 nsNativeAppSupportUnix::Start(bool *aRetVal) |
|
138 { |
|
139 NS_ASSERTION(gAppData, "gAppData must not be null."); |
|
140 |
|
141 // The dbus library is used by both nsWifiScannerDBus and BluetoothDBusService, |
|
142 // from diffrent threads. This could lead to race conditions if the dbus is not |
|
143 // initialized before making any other library calls. |
|
144 #ifdef MOZ_ENABLE_DBUS |
|
145 dbus_threads_init_default(); |
|
146 #endif |
|
147 |
|
148 #if (MOZ_WIDGET_GTK == 2) |
|
149 if (gtk_major_version < MIN_GTK_MAJOR_VERSION || |
|
150 (gtk_major_version == MIN_GTK_MAJOR_VERSION && gtk_minor_version < MIN_GTK_MINOR_VERSION)) { |
|
151 GtkWidget* versionErrDialog = gtk_message_dialog_new(nullptr, |
|
152 GtkDialogFlags(GTK_DIALOG_MODAL | |
|
153 GTK_DIALOG_DESTROY_WITH_PARENT), |
|
154 GTK_MESSAGE_ERROR, |
|
155 GTK_BUTTONS_OK, |
|
156 UNSUPPORTED_GTK_MSG, |
|
157 gtk_major_version, |
|
158 gtk_minor_version, |
|
159 MIN_GTK_MAJOR_VERSION, |
|
160 MIN_GTK_MINOR_VERSION); |
|
161 gtk_dialog_run(GTK_DIALOG(versionErrDialog)); |
|
162 gtk_widget_destroy(versionErrDialog); |
|
163 exit(0); |
|
164 } |
|
165 #endif |
|
166 |
|
167 *aRetVal = true; |
|
168 |
|
169 #if defined(MOZ_X11) && (MOZ_WIDGET_GTK == 2) |
|
170 |
|
171 PRLibrary *gnomeuiLib = PR_LoadLibrary("libgnomeui-2.so.0"); |
|
172 if (!gnomeuiLib) |
|
173 return NS_OK; |
|
174 |
|
175 PRLibrary *gnomeLib = PR_LoadLibrary("libgnome-2.so.0"); |
|
176 if (!gnomeLib) { |
|
177 PR_UnloadLibrary(gnomeuiLib); |
|
178 return NS_OK; |
|
179 } |
|
180 |
|
181 _gnome_program_init_fn gnome_program_init = |
|
182 (_gnome_program_init_fn)PR_FindFunctionSymbol(gnomeLib, "gnome_program_init"); |
|
183 _gnome_program_get_fn gnome_program_get = |
|
184 (_gnome_program_get_fn)PR_FindFunctionSymbol(gnomeLib, "gnome_program_get"); |
|
185 _libgnomeui_module_info_get_fn libgnomeui_module_info_get = (_libgnomeui_module_info_get_fn)PR_FindFunctionSymbol(gnomeuiLib, "libgnomeui_module_info_get"); |
|
186 if (!gnome_program_init || !gnome_program_get || !libgnomeui_module_info_get) { |
|
187 PR_UnloadLibrary(gnomeuiLib); |
|
188 PR_UnloadLibrary(gnomeLib); |
|
189 return NS_OK; |
|
190 } |
|
191 |
|
192 #endif /* MOZ_X11 && (MOZ_WIDGET_GTK == 2) */ |
|
193 |
|
194 #ifdef ACCESSIBILITY |
|
195 // We will load gail, atk-bridge by ourself later |
|
196 // We can't run atk-bridge init here, because gail get the control |
|
197 // Set GNOME_ACCESSIBILITY to 0 can avoid this |
|
198 static const char *accEnv = "GNOME_ACCESSIBILITY"; |
|
199 const char *accOldValue = getenv(accEnv); |
|
200 setenv(accEnv, "0", 1); |
|
201 #endif |
|
202 |
|
203 #if defined(MOZ_X11) && (MOZ_WIDGET_GTK == 2) |
|
204 if (!gnome_program_get()) { |
|
205 gnome_program_init("Gecko", "1.0", libgnomeui_module_info_get(), |
|
206 gArgc, gArgv, nullptr); |
|
207 } |
|
208 #endif /* MOZ_X11 && (MOZ_WIDGET_GTK == 2) */ |
|
209 |
|
210 #ifdef ACCESSIBILITY |
|
211 if (accOldValue) { |
|
212 setenv(accEnv, accOldValue, 1); |
|
213 } else { |
|
214 unsetenv(accEnv); |
|
215 } |
|
216 #endif |
|
217 |
|
218 // Careful! These libraries cannot be unloaded after this point because |
|
219 // gnome_program_init causes atexit handlers to be registered. Strange |
|
220 // crashes will occur if these libraries are unloaded. |
|
221 |
|
222 // TODO GTK3 - see Bug 694570 - Stop using libgnome and libgnomeui on Linux |
|
223 #if defined(MOZ_X11) && (MOZ_WIDGET_GTK == 2) |
|
224 gnome_client_set_restart_command = (_gnome_client_set_restart_command_fn) |
|
225 PR_FindFunctionSymbol(gnomeuiLib, "gnome_client_set_restart_command"); |
|
226 |
|
227 _gnome_master_client_fn gnome_master_client = (_gnome_master_client_fn) |
|
228 PR_FindFunctionSymbol(gnomeuiLib, "gnome_master_client"); |
|
229 |
|
230 GnomeClient *client = gnome_master_client(); |
|
231 g_signal_connect(client, "save-yourself", G_CALLBACK(save_yourself_cb), nullptr); |
|
232 g_signal_connect(client, "die", G_CALLBACK(die_cb), nullptr); |
|
233 |
|
234 // Set the correct/requested restart command in any case. |
|
235 |
|
236 // Is there a request to suppress default binary launcher? |
|
237 nsAutoCString path; |
|
238 char* argv1 = getenv("MOZ_APP_LAUNCHER"); |
|
239 |
|
240 if(!argv1) { |
|
241 // Tell the desktop the command for restarting us so that we can be part of XSMP session restore |
|
242 NS_ASSERTION(gDirServiceProvider, "gDirServiceProvider is NULL! This shouldn't happen!"); |
|
243 nsCOMPtr<nsIFile> executablePath; |
|
244 nsresult rv; |
|
245 |
|
246 bool dummy; |
|
247 rv = gDirServiceProvider->GetFile(XRE_EXECUTABLE_FILE, &dummy, getter_AddRefs(executablePath)); |
|
248 |
|
249 if (NS_SUCCEEDED(rv)) { |
|
250 // Strip off the -bin suffix to get the shell script we should run; this is what Breakpad does |
|
251 nsAutoCString leafName; |
|
252 rv = executablePath->GetNativeLeafName(leafName); |
|
253 if (NS_SUCCEEDED(rv) && StringEndsWith(leafName, NS_LITERAL_CSTRING("-bin"))) { |
|
254 leafName.SetLength(leafName.Length() - strlen("-bin")); |
|
255 executablePath->SetNativeLeafName(leafName); |
|
256 } |
|
257 |
|
258 executablePath->GetNativePath(path); |
|
259 argv1 = (char*)(path.get()); |
|
260 } |
|
261 } |
|
262 |
|
263 if (argv1) { |
|
264 gnome_client_set_restart_command(client, 1, &argv1); |
|
265 } |
|
266 #endif /* MOZ_X11 && (MOZ_WIDGET_GTK == 2) */ |
|
267 |
|
268 return NS_OK; |
|
269 } |
|
270 |
|
271 NS_IMETHODIMP |
|
272 nsNativeAppSupportUnix::Stop(bool *aResult) |
|
273 { |
|
274 NS_ENSURE_ARG(aResult); |
|
275 *aResult = true; |
|
276 return NS_OK; |
|
277 } |
|
278 |
|
279 NS_IMETHODIMP |
|
280 nsNativeAppSupportUnix::Enable() |
|
281 { |
|
282 return NS_OK; |
|
283 } |
|
284 |
|
285 nsresult |
|
286 NS_CreateNativeAppSupport(nsINativeAppSupport **aResult) |
|
287 { |
|
288 nsNativeAppSupportBase* native = new nsNativeAppSupportUnix(); |
|
289 if (!native) |
|
290 return NS_ERROR_OUT_OF_MEMORY; |
|
291 |
|
292 *aResult = native; |
|
293 NS_ADDREF(*aResult); |
|
294 |
|
295 return NS_OK; |
|
296 } |