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
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.
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 ***** */
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"
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"
60 #ifdef MOZ_WIDGET_GTK2
61 #include "nsIImageToPixbuf.h"
65 #include <glib-object.h>
66 #include <gtk/gtkversion.h>
68 #include <gdk-pixbuf/gdk-pixbuf.h>
72 struct ProtocolAssociation
78 struct MimeTypeAssociation
81 const char *extensions
;
84 static const ProtocolAssociation appProtocols
[] = {
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";
107 nsGNOMEShellService::Init()
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
);
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
)
143 nsGNOMEShellService::KeyMatchesAppName(const char *aKeyValue
) const
147 if (mUseLocaleFilenames
) {
148 gchar
*nativePath
= g_filename_from_utf8(aKeyValue
, -1, NULL
, NULL
, NULL
);
150 NS_ERROR("Error converting path to filesystem encoding");
154 commandPath
= g_find_program_in_path(nativePath
);
157 commandPath
= g_find_program_in_path(aKeyValue
);
163 PRBool matches
= mAppPath
.Equals(commandPath
);
169 nsGNOMEShellService::IsDefaultBrowser(PRBool aStartupCheck
,
170 PRBool
* aIsDefaultBrowser
)
172 *aIsDefaultBrowser
= PR_FALSE
;
174 mCheckedThisSession
= PR_TRUE
;
176 nsCOMPtr
<nsIGConfService
> gconf
= do_GetService(NS_GCONFSERVICE_CONTRACTID
);
179 nsCAutoString handler
;
181 for (unsigned int i
= 0; i
< NS_ARRAY_LENGTH(appProtocols
); ++i
) {
182 if (!appProtocols
[i
].essential
)
186 gconf
->GetAppForProtocol(nsDependentCString(appProtocols
[i
].name
),
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.
195 if (g_shell_parse_argv(handler
.get(), &argc
, &argv
, NULL
) && argc
> 0) {
196 handler
.Assign(argv
[0]);
200 if (!KeyMatchesAppName(handler
.get()) || !enabled
)
201 return NS_OK
; // the handler is disabled or set to another app
204 *aIsDefaultBrowser
= PR_TRUE
;
210 nsGNOMEShellService::SetDefaultBrowser(PRBool aClaimAllTypes
,
215 NS_WARNING("Setting the default browser for all users is not yet supported");
218 nsCOMPtr
<nsIGConfService
> gconf
= do_GetService(NS_GCONFSERVICE_CONTRACTID
);
220 nsCAutoString schemeList
;
221 nsCAutoString
appKeyValue(mAppPath
);
222 appKeyValue
.Append(" \"%s\"");
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
),
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
,
269 vfs
->SetAppStringKey(id
, nsIGnomeVFSService::APP_KEY_EXPECTS_URIS
,
270 NS_LITERAL_CSTRING("true"));
272 vfs
->SetAppBoolKey(id
, nsIGnomeVFSService::APP_KEY_CAN_OPEN_MULTIPLE
,
275 vfs
->SetAppBoolKey(id
, nsIGnomeVFSService::APP_KEY_REQUIRES_TERMINAL
,
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?");
284 iconFilePath
.SetLength(lastSlash
);
285 nsCOMPtr
<nsILocalFile
> iconFile
;
286 NS_NewNativeLocalFile(iconFilePath
, PR_FALSE
, getter_AddRefs(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
));
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();
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
) {
326 nsCOMPtr
<nsIPrefBranch
> prefs
;
327 nsCOMPtr
<nsIPrefService
> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID
));
329 pserve
->GetBranch("", getter_AddRefs(prefs
));
332 prefs
->GetBoolPref(PREF_CHECKDEFAULTBROWSER
, aResult
);
338 nsGNOMEShellService::SetShouldCheckDefaultBrowser(PRBool aShouldCheck
)
340 nsCOMPtr
<nsIPrefBranch
> prefs
;
341 nsCOMPtr
<nsIPrefService
> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID
));
343 pserve
->GetBranch("", getter_AddRefs(prefs
));
346 prefs
->SetBoolPref(PREF_CHECKDEFAULTBROWSER
, aShouldCheck
);
352 WriteImage(const nsCString
& aPath
, gfxIImageFrame
* aImage
)
354 nsCOMPtr
<nsIImage
> img(do_GetInterface(aImage
));
356 return NS_ERROR_NOT_AVAILABLE
;
358 #ifndef MOZ_WIDGET_GTK2
359 return NS_ERROR_NOT_AVAILABLE
;
361 nsCOMPtr
<nsIImageToPixbuf
> imgToPixbuf
=
362 do_GetService("@mozilla.org/widget/image-to-gdk-pixbuf;1");
364 return NS_ERROR_NOT_AVAILABLE
;
366 GdkPixbuf
* pixbuf
= imgToPixbuf
->ConvertImageToPixbuf(img
);
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
;
378 nsGNOMEShellService::SetDesktopBackground(nsIDOMElement
* aElement
,
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
));
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
407 nsCID bundleCID
= NS_STRINGBUNDLESERVICE_CID
;
408 nsCOMPtr
<nsIStringBundleService
> bundleService(do_GetService(bundleCID
));
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");
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
),
447 gconf
->SetString(NS_LITERAL_CSTRING(kDesktopImageKey
), filePath
);
448 gconf
->SetBool(NS_LITERAL_CSTRING(kDesktopDrawBGKey
), PR_TRUE
);
453 #define COLOR_16_TO_8_BIT(_c) ((_c) >> 8)
454 #define COLOR_8_TO_16_BIT(_c) ((_c) << 8)
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()) {
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
);
481 ColorToCString(PRUint32 aColor
, nsCString
& aResult
)
483 #if GTK_CHECK_VERSION(2,12,0)
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
);
495 // The #rrrrggggbbbb format is used to match gdk_color_to_string()
496 char *buf
= aResult
.BeginWriting(13);
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
);
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
);
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");
530 return NS_ERROR_NOT_AVAILABLE
;
532 nsCOMPtr
<nsIGConfService
> gconf
= do_GetService(NS_GCONFSERVICE_CONTRACTID
);
535 nsCAutoString appCommand
;
536 gconf
->GetAppForProtocol(scheme
, &enabled
, appCommand
);
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
551 if (!g_shell_parse_argv(appCommand
.get(), &argc
, &argv
, NULL
))
552 return NS_ERROR_FAILURE
;
554 char **newArgv
= new char*[argc
+ 1];
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
);
572 return err
? NS_OK
: NS_ERROR_FAILURE
;
576 nsGNOMEShellService::OpenApplicationWithURI(nsILocalFile
* aApplication
, const nsACString
& aURI
)
579 nsCOMPtr
<nsIProcess
> process
=
580 do_CreateInstance("@mozilla.org/process/util;1", &rv
);
584 rv
= process
->Init(aApplication
);
588 const nsCString
spec(aURI
);
589 const char* specStr
= spec
.get();
591 return process
->Run(PR_FALSE
, &specStr
, 1, &pid
);
595 nsGNOMEShellService::GetDefaultFeedReader(nsILocalFile
** _retval
)
597 return NS_ERROR_NOT_IMPLEMENTED
;