Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / browser / components / shell / src / nsGNOMEShellService.cpp
blob9391de6cd4adc004afc727c29b3837414a0aec54
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Shell Service.
17 * The Initial Developer of the Original Code is mozilla.org.
18 * Portions created by the Initial Developer are Copyright (C) 2004
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
37 #include "nsCOMPtr.h"
38 #include "nsGNOMEShellService.h"
39 #include "nsShellService.h"
40 #include "nsIServiceManager.h"
41 #include "nsILocalFile.h"
42 #include "nsIProperties.h"
43 #include "nsDirectoryServiceDefs.h"
44 #include "nsIPrefService.h"
45 #include "prenv.h"
46 #include "nsStringAPI.h"
47 #include "nsIGConfService.h"
48 #include "nsIGnomeVFSService.h"
49 #include "nsIStringBundle.h"
50 #include "gfxIImageFrame.h"
51 #include "nsIOutputStream.h"
52 #include "nsIProcess.h"
53 #include "nsNetUtil.h"
54 #include "nsIDOMHTMLImageElement.h"
55 #include "nsIImageLoadingContent.h"
56 #include "imgIRequest.h"
57 #include "imgIContainer.h"
58 #include "nsIImage.h"
59 #include "prprf.h"
60 #ifdef MOZ_WIDGET_GTK2
61 #include "nsIImageToPixbuf.h"
62 #endif
64 #include <glib.h>
65 #include <glib-object.h>
66 #include <gtk/gtkversion.h>
67 #include <gdk/gdk.h>
68 #include <gdk-pixbuf/gdk-pixbuf.h>
69 #include <limits.h>
70 #include <stdlib.h>
72 struct ProtocolAssociation
74 const char *name;
75 PRBool essential;
78 struct MimeTypeAssociation
80 const char *mimeType;
81 const char *extensions;
84 static const ProtocolAssociation appProtocols[] = {
85 { "http", PR_TRUE },
86 { "https", PR_TRUE },
87 { "ftp", PR_FALSE },
88 { "chrome", PR_FALSE }
91 static const MimeTypeAssociation appTypes[] = {
92 { "text/html", "htm html shtml" },
93 { "application/xhtml+xml", "xhtml xht" }
96 static const char kDocumentIconPath[] = "firefox-document.png";
98 // GConf registry key constants
99 #define DG_BACKGROUND "/desktop/gnome/background"
101 static const char kDesktopImageKey[] = DG_BACKGROUND "/picture_filename";
102 static const char kDesktopOptionsKey[] = DG_BACKGROUND "/picture_options";
103 static const char kDesktopDrawBGKey[] = DG_BACKGROUND "/draw_background";
104 static const char kDesktopColorKey[] = DG_BACKGROUND "/primary_color";
106 nsresult
107 nsGNOMEShellService::Init()
109 nsresult rv;
111 // GConf and GnomeVFS _must_ be available, or we do not allow
112 // CreateInstance to succeed.
114 nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
115 nsCOMPtr<nsIGnomeVFSService> vfs =
116 do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
118 if (!gconf || !vfs)
119 return NS_ERROR_NOT_AVAILABLE;
121 // Check G_BROKEN_FILENAMES. If it's set, then filenames in glib use
122 // the locale encoding. If it's not set, they use UTF-8.
123 mUseLocaleFilenames = PR_GetEnv("G_BROKEN_FILENAMES") != nsnull;
125 nsCOMPtr<nsIProperties> dirSvc
126 (do_GetService("@mozilla.org/file/directory_service;1"));
127 NS_ENSURE_TRUE(dirSvc, NS_ERROR_NOT_AVAILABLE);
129 nsCOMPtr<nsILocalFile> appPath;
130 rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR, NS_GET_IID(nsILocalFile),
131 getter_AddRefs(appPath));
132 NS_ENSURE_SUCCESS(rv, rv);
134 rv = appPath->AppendNative(NS_LITERAL_CSTRING(MOZ_APP_NAME));
135 NS_ENSURE_SUCCESS(rv, rv);
137 return appPath->GetNativePath(mAppPath);
140 NS_IMPL_ISUPPORTS1(nsGNOMEShellService, nsIShellService)
142 PRBool
143 nsGNOMEShellService::KeyMatchesAppName(const char *aKeyValue) const
146 gchar *commandPath;
147 if (mUseLocaleFilenames) {
148 gchar *nativePath = g_filename_from_utf8(aKeyValue, -1, NULL, NULL, NULL);
149 if (!nativePath) {
150 NS_ERROR("Error converting path to filesystem encoding");
151 return PR_FALSE;
154 commandPath = g_find_program_in_path(nativePath);
155 g_free(nativePath);
156 } else {
157 commandPath = g_find_program_in_path(aKeyValue);
160 if (!commandPath)
161 return PR_FALSE;
163 PRBool matches = mAppPath.Equals(commandPath);
164 g_free(commandPath);
165 return matches;
168 NS_IMETHODIMP
169 nsGNOMEShellService::IsDefaultBrowser(PRBool aStartupCheck,
170 PRBool* aIsDefaultBrowser)
172 *aIsDefaultBrowser = PR_FALSE;
173 if (aStartupCheck)
174 mCheckedThisSession = PR_TRUE;
176 nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
178 PRBool enabled;
179 nsCAutoString handler;
181 for (unsigned int i = 0; i < NS_ARRAY_LENGTH(appProtocols); ++i) {
182 if (!appProtocols[i].essential)
183 continue;
185 handler.Truncate();
186 gconf->GetAppForProtocol(nsDependentCString(appProtocols[i].name),
187 &enabled, handler);
189 // The string will be something of the form: [/path/to/]browser "%s"
190 // We want to remove all of the parameters and get just the binary name.
192 gint argc;
193 gchar **argv;
195 if (g_shell_parse_argv(handler.get(), &argc, &argv, NULL) && argc > 0) {
196 handler.Assign(argv[0]);
197 g_strfreev(argv);
200 if (!KeyMatchesAppName(handler.get()) || !enabled)
201 return NS_OK; // the handler is disabled or set to another app
204 *aIsDefaultBrowser = PR_TRUE;
206 return NS_OK;
209 NS_IMETHODIMP
210 nsGNOMEShellService::SetDefaultBrowser(PRBool aClaimAllTypes,
211 PRBool aForAllUsers)
213 #ifdef DEBUG
214 if (aForAllUsers)
215 NS_WARNING("Setting the default browser for all users is not yet supported");
216 #endif
218 nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
220 nsCAutoString schemeList;
221 nsCAutoString appKeyValue(mAppPath);
222 appKeyValue.Append(" \"%s\"");
223 unsigned int i;
225 for (i = 0; i < NS_ARRAY_LENGTH(appProtocols); ++i) {
226 schemeList.Append(nsDependentCString(appProtocols[i].name));
227 schemeList.Append(',');
229 if (appProtocols[i].essential || aClaimAllTypes) {
230 gconf->SetAppForProtocol(nsDependentCString(appProtocols[i].name),
231 appKeyValue);
235 if (aClaimAllTypes) {
236 nsCOMPtr<nsIGnomeVFSService> vfs =
237 do_GetService(NS_GNOMEVFSSERVICE_CONTRACTID);
239 nsCOMPtr<nsIStringBundleService> bundleService =
240 do_GetService(NS_STRINGBUNDLE_CONTRACTID);
241 NS_ENSURE_TRUE(bundleService, NS_ERROR_OUT_OF_MEMORY);
243 nsCOMPtr<nsIStringBundle> brandBundle;
244 bundleService->CreateBundle(BRAND_PROPERTIES, getter_AddRefs(brandBundle));
245 NS_ENSURE_TRUE(brandBundle, NS_ERROR_FAILURE);
247 nsString brandShortName, brandFullName;
248 brandBundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(),
249 getter_Copies(brandShortName));
250 brandBundle->GetStringFromName(NS_LITERAL_STRING("brandFullName").get(),
251 getter_Copies(brandFullName));
253 // use brandShortName as the application id.
254 NS_ConvertUTF16toUTF8 id(brandShortName);
256 vfs->SetAppStringKey(id, nsIGnomeVFSService::APP_KEY_COMMAND, mAppPath);
257 vfs->SetAppStringKey(id, nsIGnomeVFSService::APP_KEY_NAME,
258 NS_ConvertUTF16toUTF8(brandFullName));
260 // We don't want to be the default handler for "file:", but we do
261 // want Nautilus to know that we support file: if the MIME type is
262 // one that we can handle.
264 schemeList.Append("file");
266 vfs->SetAppStringKey(id, nsIGnomeVFSService::APP_KEY_SUPPORTED_URI_SCHEMES,
267 schemeList);
269 vfs->SetAppStringKey(id, nsIGnomeVFSService::APP_KEY_EXPECTS_URIS,
270 NS_LITERAL_CSTRING("true"));
272 vfs->SetAppBoolKey(id, nsIGnomeVFSService::APP_KEY_CAN_OPEN_MULTIPLE,
273 PR_FALSE);
275 vfs->SetAppBoolKey(id, nsIGnomeVFSService::APP_KEY_REQUIRES_TERMINAL,
276 PR_FALSE);
278 // Copy icons/document.png to ~/.icons/firefox-document.png
279 nsCAutoString iconFilePath(mAppPath);
280 PRInt32 lastSlash = iconFilePath.RFindChar(PRUnichar('/'));
281 if (lastSlash == -1) {
282 NS_ERROR("no slash in executable path?");
283 } else {
284 iconFilePath.SetLength(lastSlash);
285 nsCOMPtr<nsILocalFile> iconFile;
286 NS_NewNativeLocalFile(iconFilePath, PR_FALSE, getter_AddRefs(iconFile));
287 if (iconFile) {
288 iconFile->AppendRelativeNativePath(NS_LITERAL_CSTRING("icons/document.png"));
290 nsCOMPtr<nsILocalFile> userIconPath;
291 NS_NewNativeLocalFile(nsDependentCString(PR_GetEnv("HOME")), PR_FALSE,
292 getter_AddRefs(userIconPath));
293 if (userIconPath) {
294 userIconPath->AppendNative(NS_LITERAL_CSTRING(".icons"));
295 iconFile->CopyToNative(userIconPath,
296 nsDependentCString(kDocumentIconPath));
301 for (i = 0; i < NS_ARRAY_LENGTH(appTypes); ++i) {
302 vfs->AddMimeType(id, nsDependentCString(appTypes[i].mimeType));
303 vfs->SetMimeExtensions(nsDependentCString(appTypes[i].mimeType),
304 nsDependentCString(appTypes[i].extensions));
305 vfs->SetAppForMimeType(nsDependentCString(appTypes[i].mimeType), id);
306 vfs->SetIconForMimeType(nsDependentCString(appTypes[i].mimeType),
307 NS_LITERAL_CSTRING(kDocumentIconPath));
310 vfs->SyncAppRegistry();
313 return NS_OK;
316 NS_IMETHODIMP
317 nsGNOMEShellService::GetShouldCheckDefaultBrowser(PRBool* aResult)
319 // If we've already checked, the browser has been started and this is a
320 // new window open, and we don't want to check again.
321 if (mCheckedThisSession) {
322 *aResult = PR_FALSE;
323 return NS_OK;
326 nsCOMPtr<nsIPrefBranch> prefs;
327 nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID));
328 if (pserve)
329 pserve->GetBranch("", getter_AddRefs(prefs));
331 if (prefs)
332 prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult);
334 return NS_OK;
337 NS_IMETHODIMP
338 nsGNOMEShellService::SetShouldCheckDefaultBrowser(PRBool aShouldCheck)
340 nsCOMPtr<nsIPrefBranch> prefs;
341 nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID));
342 if (pserve)
343 pserve->GetBranch("", getter_AddRefs(prefs));
345 if (prefs)
346 prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck);
348 return NS_OK;
351 static nsresult
352 WriteImage(const nsCString& aPath, gfxIImageFrame* aImage)
354 nsCOMPtr<nsIImage> img(do_GetInterface(aImage));
355 if (!img)
356 return NS_ERROR_NOT_AVAILABLE;
358 #ifndef MOZ_WIDGET_GTK2
359 return NS_ERROR_NOT_AVAILABLE;
360 #else
361 nsCOMPtr<nsIImageToPixbuf> imgToPixbuf =
362 do_GetService("@mozilla.org/widget/image-to-gdk-pixbuf;1");
363 if (!imgToPixbuf)
364 return NS_ERROR_NOT_AVAILABLE;
366 GdkPixbuf* pixbuf = imgToPixbuf->ConvertImageToPixbuf(img);
367 if (!pixbuf)
368 return NS_ERROR_NOT_AVAILABLE;
370 gboolean res = gdk_pixbuf_save(pixbuf, aPath.get(), "png", NULL, NULL);
372 g_object_unref(pixbuf);
373 return res ? NS_OK : NS_ERROR_FAILURE;
374 #endif
377 NS_IMETHODIMP
378 nsGNOMEShellService::SetDesktopBackground(nsIDOMElement* aElement,
379 PRInt32 aPosition)
381 nsresult rv;
382 nsCOMPtr<gfxIImageFrame> gfxFrame;
384 nsCOMPtr<nsIImageLoadingContent> imageContent = do_QueryInterface(aElement, &rv);
385 if (!imageContent) return rv;
387 // get the image container
388 nsCOMPtr<imgIRequest> request;
389 rv = imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
390 getter_AddRefs(request));
391 if (!request) return rv;
392 nsCOMPtr<imgIContainer> container;
393 rv = request->GetImage(getter_AddRefs(container));
394 if (!container) return rv;
396 // get the current frame, which holds the image data
397 container->GetCurrentFrame(getter_AddRefs(gfxFrame));
399 if (!gfxFrame)
400 return NS_ERROR_FAILURE;
402 // Write the background file to the home directory.
403 nsCAutoString filePath(PR_GetEnv("HOME"));
405 // get the product brand name from localized strings
406 nsString brandName;
407 nsCID bundleCID = NS_STRINGBUNDLESERVICE_CID;
408 nsCOMPtr<nsIStringBundleService> bundleService(do_GetService(bundleCID));
409 if (bundleService) {
410 nsCOMPtr<nsIStringBundle> brandBundle;
411 rv = bundleService->CreateBundle(BRAND_PROPERTIES,
412 getter_AddRefs(brandBundle));
413 if (NS_SUCCEEDED(rv) && brandBundle) {
414 rv = brandBundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(),
415 getter_Copies(brandName));
416 NS_ENSURE_SUCCESS(rv, rv);
420 // build the file name
421 filePath.Append('/');
422 filePath.Append(NS_ConvertUTF16toUTF8(brandName));
423 filePath.Append("_wallpaper.png");
425 // write the image to a file in the home dir
426 rv = WriteImage(filePath, gfxFrame);
428 // if the file was written successfully, set it as the system wallpaper
429 nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
431 nsCAutoString options;
432 if (aPosition == BACKGROUND_TILE)
433 options.Assign("wallpaper");
434 else if (aPosition == BACKGROUND_STRETCH)
435 options.Assign("stretched");
436 else
437 options.Assign("centered");
439 gconf->SetString(NS_LITERAL_CSTRING(kDesktopOptionsKey), options);
441 // Set the image to an empty string first to force a refresh
442 // (since we could be writing a new image on top of an existing
443 // Firefox_wallpaper.png and nautilus doesn't monitor the file for changes)
444 gconf->SetString(NS_LITERAL_CSTRING(kDesktopImageKey),
445 EmptyCString());
447 gconf->SetString(NS_LITERAL_CSTRING(kDesktopImageKey), filePath);
448 gconf->SetBool(NS_LITERAL_CSTRING(kDesktopDrawBGKey), PR_TRUE);
450 return rv;
453 #define COLOR_16_TO_8_BIT(_c) ((_c) >> 8)
454 #define COLOR_8_TO_16_BIT(_c) ((_c) << 8)
456 NS_IMETHODIMP
457 nsGNOMEShellService::GetDesktopBackgroundColor(PRUint32 *aColor)
459 nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
461 nsCAutoString background;
462 gconf->GetString(NS_LITERAL_CSTRING(kDesktopColorKey), background);
464 if (background.IsEmpty()) {
465 *aColor = 0;
466 return NS_OK;
469 GdkColor color;
470 gboolean success = gdk_color_parse(background.get(), &color);
472 NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
474 *aColor = COLOR_16_TO_8_BIT(color.red) << 16 |
475 COLOR_16_TO_8_BIT(color.green) << 8 |
476 COLOR_16_TO_8_BIT(color.blue);
477 return NS_OK;
480 static void
481 ColorToCString(PRUint32 aColor, nsCString& aResult)
483 #if GTK_CHECK_VERSION(2,12,0)
484 GdkColor color;
485 color.red = COLOR_8_TO_16_BIT(aColor >> 16);
486 color.green = COLOR_8_TO_16_BIT((aColor >> 8) & 0xff);
487 color.blue = COLOR_8_TO_16_BIT(aColor & 0xff);
489 gchar *colorString = gdk_color_to_string(&color);
490 aResult.Assign(colorString);
491 g_free(colorString);
493 #else // GTK 2.12.0
495 // The #rrrrggggbbbb format is used to match gdk_color_to_string()
496 char *buf = aResult.BeginWriting(13);
497 if (!buf)
498 return;
500 PRUint8 red = (aColor >> 16);
501 PRUint8 green = (aColor >> 8) & 0xff;
502 PRUint8 blue = aColor & 0xff;
504 PR_snprintf(buf, 14, "#%02x00%02x00%02x00", red, green, blue);
505 #endif // GTK 2.12.0
508 NS_IMETHODIMP
509 nsGNOMEShellService::SetDesktopBackgroundColor(PRUint32 aColor)
511 nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
513 nsCString colorString;
514 ColorToCString(aColor, colorString);
516 gconf->SetString(NS_LITERAL_CSTRING(kDesktopColorKey), colorString);
518 return NS_OK;
521 NS_IMETHODIMP
522 nsGNOMEShellService::OpenApplication(PRInt32 aApplication)
524 nsCAutoString scheme;
525 if (aApplication == APPLICATION_MAIL)
526 scheme.Assign("mailto");
527 else if (aApplication == APPLICATION_NEWS)
528 scheme.Assign("news");
529 else
530 return NS_ERROR_NOT_AVAILABLE;
532 nsCOMPtr<nsIGConfService> gconf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
534 PRBool enabled;
535 nsCAutoString appCommand;
536 gconf->GetAppForProtocol(scheme, &enabled, appCommand);
538 if (!enabled)
539 return NS_ERROR_FAILURE;
541 // XXX we don't currently handle launching a terminal window.
542 // If the handler requires a terminal, bail.
543 PRBool requiresTerminal;
544 gconf->HandlerRequiresTerminal(scheme, &requiresTerminal);
545 if (requiresTerminal)
546 return NS_ERROR_FAILURE;
548 // Perform shell argument expansion
549 int argc;
550 char **argv;
551 if (!g_shell_parse_argv(appCommand.get(), &argc, &argv, NULL))
552 return NS_ERROR_FAILURE;
554 char **newArgv = new char*[argc + 1];
555 int newArgc = 0;
557 // Run through the list of arguments. Copy all of them to the new
558 // argv except for %s, which we skip.
559 for (int i = 0; i < argc; ++i) {
560 if (strcmp(argv[i], "%s") != 0)
561 newArgv[newArgc++] = argv[i];
564 newArgv[newArgc] = nsnull;
566 gboolean err = g_spawn_async(NULL, newArgv, NULL, G_SPAWN_SEARCH_PATH,
567 NULL, NULL, NULL, NULL);
569 g_strfreev(argv);
570 delete[] newArgv;
572 return err ? NS_OK : NS_ERROR_FAILURE;
575 NS_IMETHODIMP
576 nsGNOMEShellService::OpenApplicationWithURI(nsILocalFile* aApplication, const nsACString& aURI)
578 nsresult rv;
579 nsCOMPtr<nsIProcess> process =
580 do_CreateInstance("@mozilla.org/process/util;1", &rv);
581 if (NS_FAILED(rv))
582 return rv;
584 rv = process->Init(aApplication);
585 if (NS_FAILED(rv))
586 return rv;
588 const nsCString spec(aURI);
589 const char* specStr = spec.get();
590 PRUint32 pid;
591 return process->Run(PR_FALSE, &specStr, 1, &pid);
594 NS_IMETHODIMP
595 nsGNOMEShellService::GetDefaultFeedReader(nsILocalFile** _retval)
597 return NS_ERROR_NOT_IMPLEMENTED;