1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 "mozilla/ArrayUtils.h"
7
8 #include "nsCOMPtr.h"
9 #include "nsGNOMEShellService.h"
10 #include "nsShellService.h"
11 #include "nsIServiceManager.h"
12 #include "nsIFile.h"
13 #include "nsIProperties.h"
14 #include "nsDirectoryServiceDefs.h"
15 #include "nsIPrefService.h"
16 #include "prenv.h"
17 #include "nsString.h"
18 #include "nsIGConfService.h"
19 #include "nsIGIOService.h"
20 #include "nsIGSettingsService.h"
21 #include "nsIStringBundle.h"
22 #include "nsIOutputStream.h"
23 #include "nsIProcess.h"
24 #include "nsServiceManagerUtils.h"
25 #include "nsComponentManagerUtils.h"
26 #include "nsIDOMElement.h"
27 #include "nsIImageLoadingContent.h"
28 #include "imgIRequest.h"
29 #include "imgIContainer.h"
30 #include "mozilla/Sprintf.h"
31 #if defined(MOZ_WIDGET_GTK)
32 #include "nsIImageToPixbuf.h"
33 #endif
34 #include "nsXULAppAPI.h"
35 #include "gfxPlatform.h"
36
37 #include <glib.h>
38 #include <glib-object.h>
39 #include <gtk/gtk.h>
40 #include <gdk/gdk.h>
41 #include <gdk-pixbuf/gdk-pixbuf.h>
42 #include <limits.h>
43 #include <stdlib.h>
44
45 using namespace mozilla;
46
47 struct ProtocolAssociation {
48 const char *name;
49 bool essential;
50 };
51
52 struct MimeTypeAssociation {
53 const char *mimeType;
54 const char *extensions;
55 };
56
57 static const ProtocolAssociation appProtocols[] = {
58 // clang-format off
59 { "http", true },
60 { "https", true },
61 { "ftp", false },
62 { "chrome", false }
63 // clang-format on
64 };
65
66 static const MimeTypeAssociation appTypes[] = {
67 // clang-format off
68 { "text/html", "htm html shtml" },
69 { "application/xhtml+xml", "xhtml xht" }
70 // clang-format on
71 };
72
73 // GConf registry key constants
74 #define DG_BACKGROUND "/desktop/gnome/background"
75
76 #define kDesktopImageKey DG_BACKGROUND "/picture_filename"
77 #define kDesktopOptionsKey DG_BACKGROUND "/picture_options"
78 #define kDesktopDrawBGKey DG_BACKGROUND "/draw_background"
79 #define kDesktopColorKey DG_BACKGROUND "/primary_color"
80
81 #define kDesktopBGSchema "org.gnome.desktop.background"
82 #define kDesktopImageGSKey "picture-uri"
83 #define kDesktopOptionGSKey "picture-options"
84 #define kDesktopDrawBGGSKey "draw-background"
85 #define kDesktopColorGSKey "primary-color"
86
IsRunningAsASnap()87 static bool IsRunningAsASnap() { return (PR_GetEnv("SNAP") != nullptr); }
88
Init()89 nsresult nsGNOMEShellService::Init() {
90 nsresult rv;
91
92 if (gfxPlatform::IsHeadless()) {
93 return NS_ERROR_NOT_AVAILABLE;
94 }
95
96 // GConf, GSettings or GIO _must_ be available, or we do not allow
97 // CreateInstance to succeed.
98
99 nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
100 nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
101 nsCOMPtr<nsIGSettingsService> gsettings =
102 do_GetService(NS_GSETTINGSSERVICE_CONTRACTID);
103
104 if (!gconf && !giovfs && !gsettings) return NS_ERROR_NOT_AVAILABLE;
105
106 // Check G_BROKEN_FILENAMES. If it's set, then filenames in glib use
107 // the locale encoding. If it's not set, they use UTF-8.
108 mUseLocaleFilenames = PR_GetEnv("G_BROKEN_FILENAMES") != nullptr;
109
110 if (GetAppPathFromLauncher()) return NS_OK;
111
112 nsCOMPtr<nsIProperties> dirSvc(
113 do_GetService("@mozilla.org/file/directory_service;1"));
114 NS_ENSURE_TRUE(dirSvc, NS_ERROR_NOT_AVAILABLE);
115
116 nsCOMPtr<nsIFile> appPath;
117 rv = dirSvc->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile),
118 getter_AddRefs(appPath));
119 NS_ENSURE_SUCCESS(rv, rv);
120
121 return appPath->GetNativePath(mAppPath);
122 }
123
NS_IMPL_ISUPPORTS(nsGNOMEShellService,nsIGNOMEShellService,nsIShellService)124 NS_IMPL_ISUPPORTS(nsGNOMEShellService, nsIGNOMEShellService, nsIShellService)
125
126 bool nsGNOMEShellService::GetAppPathFromLauncher() {
127 gchar *tmp;
128
129 const char *launcher = PR_GetEnv("MOZ_APP_LAUNCHER");
130 if (!launcher) return false;
131
132 if (g_path_is_absolute(launcher)) {
133 mAppPath = launcher;
134 tmp = g_path_get_basename(launcher);
135 gchar *fullpath = g_find_program_in_path(tmp);
136 if (fullpath && mAppPath.Equals(fullpath)) mAppIsInPath = true;
137 g_free(fullpath);
138 } else {
139 tmp = g_find_program_in_path(launcher);
140 if (!tmp) return false;
141 mAppPath = tmp;
142 mAppIsInPath = true;
143 }
144
145 g_free(tmp);
146 return true;
147 }
148
KeyMatchesAppName(const char * aKeyValue) const149 bool nsGNOMEShellService::KeyMatchesAppName(const char *aKeyValue) const {
150 gchar *commandPath;
151 if (mUseLocaleFilenames) {
152 gchar *nativePath =
153 g_filename_from_utf8(aKeyValue, -1, nullptr, nullptr, nullptr);
154 if (!nativePath) {
155 NS_ERROR("Error converting path to filesystem encoding");
156 return false;
157 }
158
159 commandPath = g_find_program_in_path(nativePath);
160 g_free(nativePath);
161 } else {
162 commandPath = g_find_program_in_path(aKeyValue);
163 }
164
165 if (!commandPath) return false;
166
167 bool matches = mAppPath.Equals(commandPath);
168 g_free(commandPath);
169 return matches;
170 }
171
CheckHandlerMatchesAppName(const nsACString & handler) const172 bool nsGNOMEShellService::CheckHandlerMatchesAppName(
173 const nsACString &handler) const {
174 gint argc;
175 gchar **argv;
176 nsAutoCString command(handler);
177
178 // The string will be something of the form: [/path/to/]browser "%s"
179 // We want to remove all of the parameters and get just the binary name.
180
181 if (g_shell_parse_argv(command.get(), &argc, &argv, nullptr) && argc > 0) {
182 command.Assign(argv[0]);
183 g_strfreev(argv);
184 }
185
186 if (!KeyMatchesAppName(command.get()))
187 return false; // the handler is set to another app
188
189 return true;
190 }
191
192 NS_IMETHODIMP
IsDefaultBrowser(bool aStartupCheck,bool aForAllTypes,bool * aIsDefaultBrowser)193 nsGNOMEShellService::IsDefaultBrowser(bool aStartupCheck, bool aForAllTypes,
194 bool *aIsDefaultBrowser) {
195 *aIsDefaultBrowser = false;
196
197 if (IsRunningAsASnap()) {
198 const gchar *argv[] = {"xdg-settings", "check", "default-web-browser",
199 "firefox.desktop", nullptr};
200 GSpawnFlags flags = static_cast<GSpawnFlags>(G_SPAWN_SEARCH_PATH |
201 G_SPAWN_STDERR_TO_DEV_NULL);
202 gchar *output = nullptr;
203 gint exit_status = 0;
204 if (!g_spawn_sync(nullptr, (gchar **)argv, nullptr, flags, nullptr, nullptr,
205 &output, nullptr, &exit_status, nullptr)) {
206 return NS_OK;
207 }
208 if (exit_status != 0) {
209 g_free(output);
210 return NS_OK;
211 }
212 if (strcmp(output, "yes\n") == 0) {
213 *aIsDefaultBrowser = true;
214 }
215 g_free(output);
216 return NS_OK;
217 }
218
219 nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
220 nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
221
222 bool enabled;
223 nsAutoCString handler;
224 nsCOMPtr<nsIGIOMimeApp> gioApp;
225
226 for (unsigned int i = 0; i < ArrayLength(appProtocols); ++i) {
227 if (!appProtocols[i].essential) continue;
228
229 if (gconf) {
230 handler.Truncate();
231 gconf->GetAppForProtocol(nsDependentCString(appProtocols[i].name),
232 &enabled, handler);
233
234 if (!CheckHandlerMatchesAppName(handler) || !enabled)
235 return NS_OK; // the handler is disabled or set to another app
236 }
237
238 if (giovfs) {
239 handler.Truncate();
240 nsCOMPtr<nsIHandlerApp> handlerApp;
241 giovfs->GetAppForURIScheme(nsDependentCString(appProtocols[i].name),
242 getter_AddRefs(handlerApp));
243 gioApp = do_QueryInterface(handlerApp);
244 if (!gioApp) return NS_OK;
245
246 gioApp->GetCommand(handler);
247
248 if (!CheckHandlerMatchesAppName(handler))
249 return NS_OK; // the handler is set to another app
250 }
251 }
252
253 *aIsDefaultBrowser = true;
254
255 return NS_OK;
256 }
257
258 NS_IMETHODIMP
SetDefaultBrowser(bool aClaimAllTypes,bool aForAllUsers)259 nsGNOMEShellService::SetDefaultBrowser(bool aClaimAllTypes, bool aForAllUsers) {
260 #ifdef DEBUG
261 if (aForAllUsers)
262 NS_WARNING(
263 "Setting the default browser for all users is not yet supported");
264 #endif
265
266 if (IsRunningAsASnap()) {
267 const gchar *argv[] = {"xdg-settings", "set", "default-web-browser",
268 "firefox.desktop", nullptr};
269 GSpawnFlags flags = static_cast<GSpawnFlags>(G_SPAWN_SEARCH_PATH |
270 G_SPAWN_STDOUT_TO_DEV_NULL |
271 G_SPAWN_STDERR_TO_DEV_NULL);
272 g_spawn_sync(nullptr, (gchar **)argv, nullptr, flags, nullptr, nullptr,
273 nullptr, nullptr, nullptr, nullptr);
274 return NS_OK;
275 }
276
277 nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
278 nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
279 if (gconf) {
280 nsAutoCString appKeyValue;
281 if (mAppIsInPath) {
282 // mAppPath is in the users path, so use only the basename as the launcher
283 gchar *tmp = g_path_get_basename(mAppPath.get());
284 appKeyValue = tmp;
285 g_free(tmp);
286 } else {
287 appKeyValue = mAppPath;
288 }
289
290 appKeyValue.AppendLiteral(" %s");
291
292 for (unsigned int i = 0; i < ArrayLength(appProtocols); ++i) {
293 if (appProtocols[i].essential || aClaimAllTypes) {
294 gconf->SetAppForProtocol(nsDependentCString(appProtocols[i].name),
295 appKeyValue);
296 }
297 }
298 }
299
300 if (giovfs) {
301 nsresult rv;
302 nsCOMPtr<nsIStringBundleService> bundleService =
303 do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
304 NS_ENSURE_SUCCESS(rv, rv);
305
306 nsCOMPtr<nsIStringBundle> brandBundle;
307 rv = bundleService->CreateBundle(BRAND_PROPERTIES,
308 getter_AddRefs(brandBundle));
309 NS_ENSURE_SUCCESS(rv, rv);
310
311 nsAutoString brandShortName;
312 brandBundle->GetStringFromName("brandShortName", brandShortName);
313
314 // use brandShortName as the application id.
315 NS_ConvertUTF16toUTF8 id(brandShortName);
316 nsCOMPtr<nsIGIOMimeApp> appInfo;
317 rv = giovfs->FindAppFromCommand(mAppPath, getter_AddRefs(appInfo));
318 if (NS_FAILED(rv)) {
319 // Application was not found in the list of installed applications
320 // provided by OS. Fallback to create appInfo from command and name.
321 rv = giovfs->CreateAppFromCommand(mAppPath, id, getter_AddRefs(appInfo));
322 NS_ENSURE_SUCCESS(rv, rv);
323 }
324
325 // set handler for the protocols
326 for (unsigned int i = 0; i < ArrayLength(appProtocols); ++i) {
327 if (appProtocols[i].essential || aClaimAllTypes) {
328 appInfo->SetAsDefaultForURIScheme(
329 nsDependentCString(appProtocols[i].name));
330 }
331 }
332
333 // set handler for .html and xhtml files and MIME types:
334 if (aClaimAllTypes) {
335 // Add mime types for html, xhtml extension and set app to just created
336 // appinfo.
337 for (unsigned int i = 0; i < ArrayLength(appTypes); ++i) {
338 appInfo->SetAsDefaultForMimeType(
339 nsDependentCString(appTypes[i].mimeType));
340 appInfo->SetAsDefaultForFileExtensions(
341 nsDependentCString(appTypes[i].extensions));
342 }
343 }
344 }
345
346 nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
347 if (prefs) {
348 (void)prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, true);
349 // Reset the number of times the dialog should be shown
350 // before it is silenced.
351 (void)prefs->SetIntPref(PREF_DEFAULTBROWSERCHECKCOUNT, 0);
352 }
353
354 return NS_OK;
355 }
356
357 NS_IMETHODIMP
GetCanSetDesktopBackground(bool * aResult)358 nsGNOMEShellService::GetCanSetDesktopBackground(bool *aResult) {
359 // setting desktop background is currently only supported
360 // for Gnome or desktops using the same GSettings and GConf keys
361 const char *gnomeSession = getenv("GNOME_DESKTOP_SESSION_ID");
362 if (gnomeSession) {
363 *aResult = true;
364 } else {
365 *aResult = false;
366 }
367
368 return NS_OK;
369 }
370
WriteImage(const nsCString & aPath,imgIContainer * aImage)371 static nsresult WriteImage(const nsCString &aPath, imgIContainer *aImage) {
372 #if !defined(MOZ_WIDGET_GTK)
373 return NS_ERROR_NOT_AVAILABLE;
374 #else
375 nsCOMPtr<nsIImageToPixbuf> imgToPixbuf =
376 do_GetService("@mozilla.org/widget/image-to-gdk-pixbuf;1");
377 if (!imgToPixbuf) return NS_ERROR_NOT_AVAILABLE;
378
379 GdkPixbuf *pixbuf = imgToPixbuf->ConvertImageToPixbuf(aImage);
380 if (!pixbuf) return NS_ERROR_NOT_AVAILABLE;
381
382 gboolean res = gdk_pixbuf_save(pixbuf, aPath.get(), "png", nullptr, nullptr);
383
384 g_object_unref(pixbuf);
385 return res ? NS_OK : NS_ERROR_FAILURE;
386 #endif
387 }
388
389 NS_IMETHODIMP
SetDesktopBackground(nsIDOMElement * aElement,int32_t aPosition,const nsACString & aImageName)390 nsGNOMEShellService::SetDesktopBackground(nsIDOMElement *aElement,
391 int32_t aPosition,
392 const nsACString &aImageName) {
393 nsresult rv;
394 nsCOMPtr<nsIImageLoadingContent> imageContent =
395 do_QueryInterface(aElement, &rv);
396 if (!imageContent) return rv;
397
398 // get the image container
399 nsCOMPtr<imgIRequest> request;
400 rv = imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
401 getter_AddRefs(request));
402 if (!request) return rv;
403 nsCOMPtr<imgIContainer> container;
404 rv = request->GetImage(getter_AddRefs(container));
405 if (!container) return rv;
406
407 // Set desktop wallpaper filling style
408 nsAutoCString options;
409 if (aPosition == BACKGROUND_TILE)
410 options.AssignLiteral("wallpaper");
411 else if (aPosition == BACKGROUND_STRETCH)
412 options.AssignLiteral("stretched");
413 else if (aPosition == BACKGROUND_FILL)
414 options.AssignLiteral("zoom");
415 else if (aPosition == BACKGROUND_FIT)
416 options.AssignLiteral("scaled");
417 else
418 options.AssignLiteral("centered");
419
420 // Write the background file to the home directory.
421 nsAutoCString filePath(PR_GetEnv("HOME"));
422
423 // get the product brand name from localized strings
424 nsAutoString brandName;
425 nsCID bundleCID = NS_STRINGBUNDLESERVICE_CID;
426 nsCOMPtr<nsIStringBundleService> bundleService(do_GetService(bundleCID));
427 if (bundleService) {
428 nsCOMPtr<nsIStringBundle> brandBundle;
429 rv = bundleService->CreateBundle(BRAND_PROPERTIES,
430 getter_AddRefs(brandBundle));
431 if (NS_SUCCEEDED(rv) && brandBundle) {
432 rv = brandBundle->GetStringFromName("brandShortName", brandName);
433 NS_ENSURE_SUCCESS(rv, rv);
434 }
435 }
436
437 // build the file name
438 filePath.Append('/');
439 filePath.Append(NS_ConvertUTF16toUTF8(brandName));
440 filePath.AppendLiteral("_wallpaper.png");
441
442 // write the image to a file in the home dir
443 rv = WriteImage(filePath, container);
444 NS_ENSURE_SUCCESS(rv, rv);
445
446 // Try GSettings first. If we don't have GSettings or the right schema, fall
447 // back to using GConf instead. Note that if GSettings works ok, the changes
448 // get mirrored to GConf by the gsettings->gconf bridge in
449 // gnome-settings-daemon
450 nsCOMPtr<nsIGSettingsService> gsettings =
451 do_GetService(NS_GSETTINGSSERVICE_CONTRACTID);
452 if (gsettings) {
453 nsCOMPtr<nsIGSettingsCollection> background_settings;
454 gsettings->GetCollectionForSchema(NS_LITERAL_CSTRING(kDesktopBGSchema),
455 getter_AddRefs(background_settings));
456 if (background_settings) {
457 gchar *file_uri = g_filename_to_uri(filePath.get(), nullptr, nullptr);
458 if (!file_uri) return NS_ERROR_FAILURE;
459
460 background_settings->SetString(NS_LITERAL_CSTRING(kDesktopOptionGSKey),
461 options);
462
463 background_settings->SetString(NS_LITERAL_CSTRING(kDesktopImageGSKey),
464 nsDependentCString(file_uri));
465 g_free(file_uri);
466 background_settings->SetBoolean(NS_LITERAL_CSTRING(kDesktopDrawBGGSKey),
467 true);
468 return rv;
469 }
470 }
471
472 // if the file was written successfully, set it as the system wallpaper
473 nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
474
475 if (gconf) {
476 gconf->SetString(NS_LITERAL_CSTRING(kDesktopOptionsKey), options);
477
478 // Set the image to an empty string first to force a refresh
479 // (since we could be writing a new image on top of an existing
480 // Firefox_wallpaper.png and nautilus doesn't monitor the file for changes)
481 gconf->SetString(NS_LITERAL_CSTRING(kDesktopImageKey), EmptyCString());
482
483 gconf->SetString(NS_LITERAL_CSTRING(kDesktopImageKey), filePath);
484 gconf->SetBool(NS_LITERAL_CSTRING(kDesktopDrawBGKey), true);
485 }
486
487 return rv;
488 }
489
490 #define COLOR_16_TO_8_BIT(_c) ((_c) >> 8)
491 #define COLOR_8_TO_16_BIT(_c) ((_c) << 8 | (_c))
492
493 NS_IMETHODIMP
GetDesktopBackgroundColor(uint32_t * aColor)494 nsGNOMEShellService::GetDesktopBackgroundColor(uint32_t *aColor) {
495 nsCOMPtr<nsIGSettingsService> gsettings =
496 do_GetService(NS_GSETTINGSSERVICE_CONTRACTID);
497 nsCOMPtr<nsIGSettingsCollection> background_settings;
498 nsAutoCString background;
499
500 if (gsettings) {
501 gsettings->GetCollectionForSchema(NS_LITERAL_CSTRING(kDesktopBGSchema),
502 getter_AddRefs(background_settings));
503 if (background_settings) {
504 background_settings->GetString(NS_LITERAL_CSTRING(kDesktopColorGSKey),
505 background);
506 }
507 }
508
509 if (!background_settings) {
510 nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
511 if (gconf)
512 gconf->GetString(NS_LITERAL_CSTRING(kDesktopColorKey), background);
513 }
514
515 if (background.IsEmpty()) {
516 *aColor = 0;
517 return NS_OK;
518 }
519
520 GdkColor color;
521 gboolean success = gdk_color_parse(background.get(), &color);
522
523 NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
524
525 *aColor = COLOR_16_TO_8_BIT(color.red) << 16 |
526 COLOR_16_TO_8_BIT(color.green) << 8 | COLOR_16_TO_8_BIT(color.blue);
527 return NS_OK;
528 }
529
ColorToCString(uint32_t aColor,nsCString & aResult)530 static void ColorToCString(uint32_t aColor, nsCString &aResult) {
531 // The #rrrrggggbbbb format is used to match gdk_color_to_string()
532 aResult.SetLength(13);
533 char *buf = aResult.BeginWriting();
534 if (!buf) return;
535
536 uint16_t red = COLOR_8_TO_16_BIT((aColor >> 16) & 0xff);
537 uint16_t green = COLOR_8_TO_16_BIT((aColor >> 8) & 0xff);
538 uint16_t blue = COLOR_8_TO_16_BIT(aColor & 0xff);
539
540 snprintf(buf, 14, "#%04x%04x%04x", red, green, blue);
541 }
542
543 NS_IMETHODIMP
SetDesktopBackgroundColor(uint32_t aColor)544 nsGNOMEShellService::SetDesktopBackgroundColor(uint32_t aColor) {
545 NS_ASSERTION(aColor <= 0xffffff, "aColor has extra bits");
546 nsAutoCString colorString;
547 ColorToCString(aColor, colorString);
548
549 nsCOMPtr<nsIGSettingsService> gsettings =
550 do_GetService(NS_GSETTINGSSERVICE_CONTRACTID);
551 if (gsettings) {
552 nsCOMPtr<nsIGSettingsCollection> background_settings;
553 gsettings->GetCollectionForSchema(NS_LITERAL_CSTRING(kDesktopBGSchema),
554 getter_AddRefs(background_settings));
555 if (background_settings) {
556 background_settings->SetString(NS_LITERAL_CSTRING(kDesktopColorGSKey),
557 colorString);
558 return NS_OK;
559 }
560 }
561
562 nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
563
564 if (gconf) {
565 gconf->SetString(NS_LITERAL_CSTRING(kDesktopColorKey), colorString);
566 }
567
568 return NS_OK;
569 }
570
571 NS_IMETHODIMP
OpenApplication(int32_t aApplication)572 nsGNOMEShellService::OpenApplication(int32_t aApplication) {
573 nsAutoCString scheme;
574 if (aApplication == APPLICATION_MAIL)
575 scheme.AssignLiteral("mailto");
576 else if (aApplication == APPLICATION_NEWS)
577 scheme.AssignLiteral("news");
578 else
579 return NS_ERROR_NOT_AVAILABLE;
580
581 nsCOMPtr<nsIGIOService> giovfs = do_GetService(NS_GIOSERVICE_CONTRACTID);
582 if (giovfs) {
583 nsCOMPtr<nsIHandlerApp> handlerApp;
584 giovfs->GetAppForURIScheme(scheme, getter_AddRefs(handlerApp));
585 if (handlerApp) return handlerApp->LaunchWithURI(nullptr, nullptr);
586 }
587
588 nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
589 if (!gconf) return NS_ERROR_FAILURE;
590
591 bool enabled;
592 nsAutoCString appCommand;
593 gconf->GetAppForProtocol(scheme, &enabled, appCommand);
594
595 if (!enabled) return NS_ERROR_FAILURE;
596
597 // XXX we don't currently handle launching a terminal window.
598 // If the handler requires a terminal, bail.
599 bool requiresTerminal;
600 gconf->HandlerRequiresTerminal(scheme, &requiresTerminal);
601 if (requiresTerminal) return NS_ERROR_FAILURE;
602
603 // Perform shell argument expansion
604 int argc;
605 char **argv;
606 if (!g_shell_parse_argv(appCommand.get(), &argc, &argv, nullptr))
607 return NS_ERROR_FAILURE;
608
609 char **newArgv = new char *[argc + 1];
610 int newArgc = 0;
611
612 // Run through the list of arguments. Copy all of them to the new
613 // argv except for %s, which we skip.
614 for (int i = 0; i < argc; ++i) {
615 if (strcmp(argv[i], "%s") != 0) newArgv[newArgc++] = argv[i];
616 }
617
618 newArgv[newArgc] = nullptr;
619
620 gboolean err = g_spawn_async(nullptr, newArgv, nullptr, G_SPAWN_SEARCH_PATH,
621 nullptr, nullptr, nullptr, nullptr);
622
623 g_strfreev(argv);
624 delete[] newArgv;
625
626 return err ? NS_OK : NS_ERROR_FAILURE;
627 }
628
629 NS_IMETHODIMP
OpenApplicationWithURI(nsIFile * aApplication,const nsACString & aURI)630 nsGNOMEShellService::OpenApplicationWithURI(nsIFile *aApplication,
631 const nsACString &aURI) {
632 nsresult rv;
633 nsCOMPtr<nsIProcess> process =
634 do_CreateInstance("@mozilla.org/process/util;1", &rv);
635 if (NS_FAILED(rv)) return rv;
636
637 rv = process->Init(aApplication);
638 if (NS_FAILED(rv)) return rv;
639
640 const nsCString spec(aURI);
641 const char *specStr = spec.get();
642 return process->Run(false, &specStr, 1);
643 }
644
645 NS_IMETHODIMP
GetDefaultFeedReader(nsIFile ** _retval)646 nsGNOMEShellService::GetDefaultFeedReader(nsIFile **_retval) {
647 return NS_ERROR_NOT_IMPLEMENTED;
648 }
649