gecko-dev/browser/components/shell/src/nsWindowsShellService.cpp

919 lines
30 KiB
C++
Raw Normal View History

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Shell Service.
*
* The Initial Developer of the Original Code is mozilla.org.
* Portions created by the Initial Developer are Copyright (C) 2004
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Goodger <ben@mozilla.org> (Clients, Mail, New Default Browser)
* Joe Hewitt <hewitt@netscape.com> (Set Background)
* Blake Ross <blake@cs.stanford.edu> (Desktop Color, DDE support)
* Jungshik Shin <jshin@mailaps.org> (I18N)
* Robert Strong <robert.bugzilla@gmail.com>
* Asaf Romano <mano@mozilla.com>
* Ryan Jones <sciguyryan@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "gfxIImageFrame.h"
#include "imgIContainer.h"
#include "imgIRequest.h"
#include "nsIDOMDocument.h"
#include "nsIDOMElement.h"
#include "nsIDOMHTMLImageElement.h"
#include "nsIImageLoadingContent.h"
#include "nsIPrefService.h"
#include "nsIPrefLocalizedString.h"
#include "nsIServiceManager.h"
#include "nsIStringBundle.h"
#include "nsNetUtil.h"
#include "nsShellService.h"
#include "nsWindowsShellService.h"
#include "nsIProcess.h"
#include "nsICategoryManager.h"
#include "nsBrowserCompsCID.h"
#include "nsDirectoryServiceUtils.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceDefs.h"
#include "nsIWindowsRegKey.h"
#include "nsUnicharUtils.h"
#include "windows.h"
#include "shellapi.h"
#include "shlobj.h"
#include <mbstring.h>
#ifndef MAX_BUF
#define MAX_BUF 4096
#endif
#define REG_SUCCEEDED(val) \
(val == ERROR_SUCCESS)
#define REG_FAILED(val) \
(val != ERROR_SUCCESS)
NS_IMPL_ISUPPORTS2(nsWindowsShellService, nsIWindowsShellService, nsIShellService)
static nsresult
OpenKeyForReading(HKEY aKeyRoot, const nsAString& aKeyName, HKEY* aKey)
{
const nsString &flatName = PromiseFlatString(aKeyName);
DWORD res = ::RegOpenKeyExW(aKeyRoot, flatName.get(), 0, KEY_READ, aKey);
switch (res) {
case ERROR_SUCCESS:
break;
case ERROR_ACCESS_DENIED:
return NS_ERROR_FILE_ACCESS_DENIED;
case ERROR_FILE_NOT_FOUND:
return NS_ERROR_NOT_AVAILABLE;
}
return NS_OK;
}
///////////////////////////////////////////////////////////////////////////////
// Default Browser Registry Settings
//
// The setting of these values are made by an external binary since writing
// these values may require elevation.
//
// - File Extension Mappings
// -----------------------
// The following file extensions:
// .htm .html .shtml .xht .xhtml
// are mapped like so:
//
// HKCU\SOFTWARE\Classes\.<ext>\ (default) REG_SZ FirefoxHTML
//
// as aliases to the class:
//
// HKCU\SOFTWARE\Classes\FirefoxHTML\
// DefaultIcon (default) REG_SZ <apppath>,1
// shell\open\command (default) REG_SZ <apppath> -requestPending -osint -url "%1"
// shell\open\ddeexec (default) REG_SZ "%1",,0,0,,,,
// shell\open\ddeexec NoActivateHandler REG_SZ
// \Application (default) REG_SZ Firefox
// \Topic (default) REG_SZ WWW_OpenURL
//
// - Windows Vista Protocol Handler
//
// HKCU\SOFTWARE\Classes\FirefoxURL\ (default) REG_SZ <appname> URL
// EditFlags REG_DWORD 2
// FriendlyTypeName REG_SZ <appname> URL
// DefaultIcon (default) REG_SZ <apppath>,1
// shell\open\command (default) REG_SZ <apppath> -requestPending -osint -url "%1"
// shell\open\ddeexec (default) REG_SZ "%1",,0,0,,,,
// shell\open\ddeexec NoActivateHandler REG_SZ
// \Application (default) REG_SZ Firefox
// \Topic (default) REG_SZ WWW_OpenURL
//
// - Protocol Mappings
// -----------------
// The following protocols:
// HTTP, HTTPS, FTP
// are mapped like so:
//
// HKCU\SOFTWARE\Classes\<protocol>\
// DefaultIcon (default) REG_SZ <apppath>,1
// shell\open\command (default) REG_SZ <apppath> -requestPending -osint -url "%1"
// shell\open\ddeexec (default) REG_SZ "%1",,0,0,,,,
// shell\open\ddeexec NoActivateHandler REG_SZ
// \Application (default) REG_SZ Firefox
// \Topic (default) REG_SZ WWW_OpenURL
//
// - Windows Start Menu (Win2K SP2, XP SP1, and newer)
// -------------------------------------------------
// The following keys are set to make Firefox appear in the Start Menu as the
// browser:
//
// HKCU\SOFTWARE\Clients\StartMenuInternet\FIREFOX.EXE\
// (default) REG_SZ <appname>
// DefaultIcon (default) REG_SZ <apppath>,0
// InstallInfo HideIconsCommand REG_SZ <uninstpath> /HideShortcuts
// InstallInfo IconsVisible REG_DWORD 1
// InstallInfo ReinstallCommand REG_SZ <uninstpath> /SetAsDefaultAppGlobal
// InstallInfo ShowIconsCommand REG_SZ <uninstpath> /ShowShortcuts
// shell\open\command (default) REG_SZ <apppath>
// shell\properties (default) REG_SZ <appname> &Options
// shell\properties\command (default) REG_SZ <apppath> -preferences
// shell\safemode (default) REG_SZ <appname> &Safe Mode
// shell\safemode\command (default) REG_SZ <apppath> -safe-mode
//
typedef enum {
NO_SUBSTITUTION = 0x00,
APP_PATH_SUBSTITUTION = 0x01,
EXE_NAME_SUBSTITUTION = 0x02
} SettingFlags;
typedef struct {
char* keyName;
char* valueName;
char* valueData;
PRInt32 flags;
} SETTING;
#define APP_REG_NAME L"Firefox"
#define DI "\\DefaultIcon"
#define SOP "\\shell\\open\\command"
#define CLS_HTML "FirefoxHTML"
#define CLS_URL "FirefoxURL"
#define VAL_FILE_ICON "%APPPATH%,1"
#define VAL_OPEN "\"%APPPATH%\" -requestPending -osint -url \"%1\""
#define MAKE_KEY_NAME1(PREFIX, MID) \
PREFIX MID
// The DefaultIcon registry key value should never be used when checking if
// Firefox is the default browser since other applications (e.g. MS Office) may
// modify the DefaultIcon registry key value to add Icon Handlers.
// see http://msdn2.microsoft.com/en-us/library/aa969357.aspx for more info.
static SETTING gSettings[] = {
// File Extension Class - as of 1.8.1.2 the value for VAL_OPEN is also checked
// for CLS_HTML since Firefox should also own opeing local files when set as
// the default browser.
{ MAKE_KEY_NAME1(CLS_HTML, SOP), "", VAL_OPEN, APP_PATH_SUBSTITUTION },
// Protocol Handler Class - for Vista and above
{ MAKE_KEY_NAME1(CLS_URL, SOP), "", VAL_OPEN, APP_PATH_SUBSTITUTION },
// Protocol Handlers
{ MAKE_KEY_NAME1("HTTP", DI), "", VAL_FILE_ICON, APP_PATH_SUBSTITUTION },
{ MAKE_KEY_NAME1("HTTP", SOP), "", VAL_OPEN, APP_PATH_SUBSTITUTION },
{ MAKE_KEY_NAME1("HTTPS", DI), "", VAL_FILE_ICON, APP_PATH_SUBSTITUTION },
{ MAKE_KEY_NAME1("HTTPS", SOP), "", VAL_OPEN, APP_PATH_SUBSTITUTION }
};
// Support for versions of shlobj.h that don't include the Vista API's
#if !defined(IApplicationAssociationRegistration)
typedef enum tagASSOCIATIONLEVEL {
AL_MACHINE,
AL_EFFECTIVE,
AL_USER
} ASSOCIATIONLEVEL;
typedef enum tagASSOCIATIONTYPE {
AT_FILEEXTENSION,
AT_URLPROTOCOL,
AT_STARTMENUCLIENT,
AT_MIMETYPE
} ASSOCIATIONTYPE;
MIDL_INTERFACE("4e530b0a-e611-4c77-a3ac-9031d022281b")
IApplicationAssociationRegistration : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE QueryCurrentDefault(LPCWSTR pszQuery,
ASSOCIATIONTYPE atQueryType,
ASSOCIATIONLEVEL alQueryLevel,
LPWSTR *ppszAssociation) = 0;
virtual HRESULT STDMETHODCALLTYPE QueryAppIsDefault(LPCWSTR pszQuery,
ASSOCIATIONTYPE atQueryType,
ASSOCIATIONLEVEL alQueryLevel,
LPCWSTR pszAppRegistryName,
BOOL *pfDefault) = 0;
virtual HRESULT STDMETHODCALLTYPE QueryAppIsDefaultAll(ASSOCIATIONLEVEL alQueryLevel,
LPCWSTR pszAppRegistryName,
BOOL *pfDefault) = 0;
virtual HRESULT STDMETHODCALLTYPE SetAppAsDefault(LPCWSTR pszAppRegistryName,
LPCWSTR pszSet,
ASSOCIATIONTYPE atSetType) = 0;
virtual HRESULT STDMETHODCALLTYPE SetAppAsDefaultAll(LPCWSTR pszAppRegistryName) = 0;
virtual HRESULT STDMETHODCALLTYPE ClearUserAssociations(void) = 0;
};
#endif
static const CLSID CLSID_ApplicationAssociationReg = {0x591209C7,0x767B,0x42B2,{0x9F,0xBA,0x44,0xEE,0x46,0x15,0xF2,0xC7}};
static const IID IID_IApplicationAssociationReg = {0x4e530b0a,0xe611,0x4c77,{0xa3,0xac,0x90,0x31,0xd0,0x22,0x28,0x1b}};
PRBool
nsWindowsShellService::IsDefaultBrowserVista(PRBool aStartupCheck, PRBool* aIsDefaultBrowser)
{
IApplicationAssociationRegistration* pAAR;
HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationReg,
NULL,
CLSCTX_INPROC,
IID_IApplicationAssociationReg,
(void**)&pAAR);
if (SUCCEEDED(hr)) {
hr = pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE,
APP_REG_NAME,
aIsDefaultBrowser);
// If this is the first browser window, maintain internal state that we've
// checked this session (so that subsequent window opens don't show the
// default browser dialog).
if (aStartupCheck)
mCheckedThisSession = PR_TRUE;
pAAR->Release();
return PR_TRUE;
}
return PR_FALSE;
}
NS_IMETHODIMP
nsWindowsShellService::IsDefaultBrowser(PRBool aStartupCheck,
PRBool* aIsDefaultBrowser)
{
// If this is the first browser window, maintain internal state that we've
// checked this session (so that subsequent window opens don't show the
// default browser dialog).
if (aStartupCheck)
mCheckedThisSession = PR_TRUE;
SETTING* settings;
SETTING* end = gSettings + sizeof(gSettings)/sizeof(SETTING);
*aIsDefaultBrowser = PR_TRUE;
PRUnichar exePath[MAX_BUF];
if (!::GetModuleFileNameW(0, exePath, MAX_BUF))
return NS_ERROR_FAILURE;
nsAutoString appLongPath(exePath);
// Support short path to the exe so if it is already set the user is not
// prompted to set the default browser again.
if (!::GetShortPathNameW(exePath, exePath, sizeof(exePath)))
return NS_ERROR_FAILURE;
nsAutoString appShortPath(exePath);
ToUpperCase(appShortPath);
nsCOMPtr<nsILocalFile> lf;
nsresult rv = NS_NewLocalFile(appShortPath, PR_TRUE,
getter_AddRefs(lf));
if (NS_FAILED(rv))
return rv;
nsAutoString exeName;
rv = lf->GetLeafName(exeName);
if (NS_FAILED(rv))
return rv;
ToUpperCase(exeName);
PRUnichar currValue[MAX_BUF];
for (settings = gSettings; settings < end; ++settings) {
NS_ConvertUTF8toUTF16 dataLongPath(settings->valueData);
NS_ConvertUTF8toUTF16 dataShortPath(settings->valueData);
NS_ConvertUTF8toUTF16 key(settings->keyName);
NS_ConvertUTF8toUTF16 value(settings->valueName);
if (settings->flags & APP_PATH_SUBSTITUTION) {
PRInt32 offset = dataLongPath.Find("%APPPATH%");
dataLongPath.Replace(offset, 9, appLongPath);
// Remove the quotes around %APPPATH% in VAL_OPEN for short paths
PRInt32 offsetQuoted = dataShortPath.Find("\"%APPPATH%\"");
if (offsetQuoted != -1)
dataShortPath.Replace(offsetQuoted, 11, appShortPath);
else
dataShortPath.Replace(offset, 9, appShortPath);
}
if (settings->flags & EXE_NAME_SUBSTITUTION) {
PRInt32 offset = key.Find("%APPEXE%");
key.Replace(offset, 8, exeName);
}
::ZeroMemory(currValue, sizeof(currValue));
HKEY theKey;
rv = OpenKeyForReading(HKEY_CLASSES_ROOT, key, &theKey);
if (NS_SUCCEEDED(rv)) {
DWORD len = sizeof currValue;
DWORD res = ::RegQueryValueExW(theKey, PromiseFlatString(value).get(),
NULL, NULL, (LPBYTE)currValue, &len);
// Close the key we opened.
::RegCloseKey(theKey);
if (REG_FAILED(res) ||
!dataLongPath.Equals(currValue, CaseInsensitiveCompare) &&
!dataShortPath.Equals(currValue, CaseInsensitiveCompare)) {
// Key wasn't set, or was set to something else (something else became the default browser)
*aIsDefaultBrowser = PR_FALSE;
return NS_OK;
}
}
}
// Only check if Firefox is the default browser on Vista if the previous
// checks show that Firefox is the default browser.
if (aIsDefaultBrowser)
IsDefaultBrowserVista(aStartupCheck, aIsDefaultBrowser);
return NS_OK;
}
NS_IMETHODIMP
nsWindowsShellService::SetDefaultBrowser(PRBool aClaimAllTypes, PRBool aForAllUsers)
{
nsresult rv;
nsCOMPtr<nsIProperties> directoryService =
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsILocalFile> appHelper;
rv = directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(appHelper));
NS_ENSURE_SUCCESS(rv, rv);
rv = appHelper->AppendNative(NS_LITERAL_CSTRING("uninstall"));
NS_ENSURE_SUCCESS(rv, rv);
rv = appHelper->AppendNative(NS_LITERAL_CSTRING("helper.exe"));
NS_ENSURE_SUCCESS(rv, rv);
nsCAutoString appHelperPath;
rv = appHelper->GetNativePath(appHelperPath);
NS_ENSURE_SUCCESS(rv, rv);
if (aForAllUsers) {
appHelperPath.AppendLiteral(" /SetAsDefaultAppGlobal");
} else {
appHelperPath.AppendLiteral(" /SetAsDefaultAppUser");
}
STARTUPINFO si = {sizeof(si), 0};
PROCESS_INFORMATION pi = {0};
BOOL ok = CreateProcess(NULL, (LPSTR)appHelperPath.get(), NULL, NULL,
FALSE, 0, NULL, NULL, &si, &pi);
if (!ok)
return NS_ERROR_FAILURE;
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return NS_OK;
}
NS_IMETHODIMP
nsWindowsShellService::GetShouldCheckDefaultBrowser(PRBool* aResult)
{
// If we've already checked, the browser has been started and this is a
// new window open, and we don't want to check again.
if (mCheckedThisSession) {
*aResult = PR_FALSE;
return NS_OK;
}
nsCOMPtr<nsIPrefBranch> prefs;
nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID));
if (pserve)
pserve->GetBranch("", getter_AddRefs(prefs));
prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult);
return NS_OK;
}
NS_IMETHODIMP
nsWindowsShellService::SetShouldCheckDefaultBrowser(PRBool aShouldCheck)
{
nsCOMPtr<nsIPrefBranch> prefs;
nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID));
if (pserve)
pserve->GetBranch("", getter_AddRefs(prefs));
prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck);
return NS_OK;
}
static nsresult
WriteBitmap(nsIFile* aFile, gfxIImageFrame* aImage)
{
PRInt32 width, height;
aImage->GetWidth(&width);
aImage->GetHeight(&height);
PRUint8* bits;
PRUint32 length;
aImage->LockImageData();
aImage->GetImageData(&bits, &length);
if (!bits) {
aImage->UnlockImageData();
return NS_ERROR_FAILURE;
}
PRUint32 bpr;
aImage->GetImageBytesPerRow(&bpr);
PRInt32 bitCount = bpr/width;
// initialize these bitmap structs which we will later
// serialize directly to the head of the bitmap file
BITMAPINFOHEADER bmi;
bmi.biSize = sizeof(BITMAPINFOHEADER);
bmi.biWidth = width;
bmi.biHeight = height;
bmi.biPlanes = 1;
bmi.biBitCount = (WORD)bitCount*8;
bmi.biCompression = BI_RGB;
bmi.biSizeImage = length;
bmi.biXPelsPerMeter = 0;
bmi.biYPelsPerMeter = 0;
bmi.biClrUsed = 0;
bmi.biClrImportant = 0;
BITMAPFILEHEADER bf;
bf.bfType = 0x4D42; // 'BM'
bf.bfReserved1 = 0;
bf.bfReserved2 = 0;
bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bf.bfSize = bf.bfOffBits + bmi.biSizeImage;
// get a file output stream
nsCOMPtr<nsIOutputStream> stream;
nsresult rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile);
NS_ENSURE_SUCCESS(rv, rv);
// write the bitmap headers and rgb pixel data to the file
rv = NS_ERROR_FAILURE;
if (stream) {
PRUint32 written;
stream->Write((const char*)&bf, sizeof(BITMAPFILEHEADER), &written);
if (written == sizeof(BITMAPFILEHEADER)) {
stream->Write((const char*)&bmi, sizeof(BITMAPINFOHEADER), &written);
if (written == sizeof(BITMAPINFOHEADER)) {
// write out the image data backwards because the desktop won't
// show bitmaps with negative heights for top-to-bottom
PRUint32 i = length;
do {
i -= bpr;
stream->Write(((const char*)bits) + i, bpr, &written);
if (written == bpr) {
rv = NS_OK;
} else {
rv = NS_ERROR_FAILURE;
break;
}
} while (i != 0);
}
}
stream->Close();
}
aImage->UnlockImageData();
return rv;
}
NS_IMETHODIMP
nsWindowsShellService::SetDesktopBackground(nsIDOMElement* aElement,
PRInt32 aPosition)
{
nsresult rv;
nsCOMPtr<gfxIImageFrame> gfxFrame;
nsCOMPtr<nsIDOMHTMLImageElement> imgElement(do_QueryInterface(aElement));
if (!imgElement) {
// XXX write background loading stuff!
}
else {
nsCOMPtr<nsIImageLoadingContent> imageContent =
do_QueryInterface(aElement, &rv);
if (!imageContent)
return rv;
// get the image container
nsCOMPtr<imgIRequest> request;
rv = imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
getter_AddRefs(request));
if (!request)
return rv;
nsCOMPtr<imgIContainer> container;
rv = request->GetImage(getter_AddRefs(container));
if (!container)
return NS_ERROR_FAILURE;
// get the current frame, which holds the image data
container->GetCurrentFrame(getter_AddRefs(gfxFrame));
}
if (!gfxFrame)
return NS_ERROR_FAILURE;
// get the file name from localized strings
nsCOMPtr<nsIStringBundleService>
bundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIStringBundle> shellBundle;
rv = bundleService->CreateBundle(SHELLSERVICE_PROPERTIES,
getter_AddRefs(shellBundle));
NS_ENSURE_SUCCESS(rv, rv);
// e.g. "Desktop Background.bmp"
nsString fileLeafName;
rv = shellBundle->GetStringFromName
(NS_LITERAL_STRING("desktopBackgroundLeafNameWin").get(),
getter_Copies(fileLeafName));
NS_ENSURE_SUCCESS(rv, rv);
// get the profile root directory
nsCOMPtr<nsIFile> file;
rv = NS_GetSpecialDirectory(NS_APP_APPLICATION_REGISTRY_DIR,
getter_AddRefs(file));
NS_ENSURE_SUCCESS(rv, rv);
// eventually, the path is "%APPDATA%\Mozilla\Firefox\Desktop Background.bmp"
rv = file->Append(fileLeafName);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString path;
rv = file->GetPath(path);
NS_ENSURE_SUCCESS(rv, rv);
// write the bitmap to a file in the profile directory
rv = WriteBitmap(file, gfxFrame);
// if the file was written successfully, set it as the system wallpaper
if (NS_SUCCEEDED(rv)) {
PRBool result = PR_FALSE;
DWORD dwDisp = 0;
HKEY key;
// Try to create/open a subkey under HKLM.
DWORD res = ::RegCreateKeyExW(HKEY_CURRENT_USER,
L"Control Panel\\Desktop",
0, NULL, REG_OPTION_NON_VOLATILE,
KEY_WRITE, NULL, &key, &dwDisp);
if (REG_SUCCEEDED(res)) {
PRUnichar tile[2], style[2];
switch (aPosition) {
case BACKGROUND_TILE:
tile[0] = '1';
style[0] = '1';
break;
case BACKGROUND_CENTER:
tile[0] = '0';
style[0] = '0';
break;
case BACKGROUND_STRETCH:
tile[0] = '0';
style[0] = '2';
break;
}
tile[1] = '\0';
style[1] = '\0';
// The size is always 3 unicode characters.
PRInt32 size = 3 * sizeof(PRUnichar);
::RegSetValueExW(key, L"TileWallpaper",
0, REG_SZ, (const BYTE *)tile, size);
::RegSetValueExW(key, L"WallpaperStyle",
0, REG_SZ, (const BYTE *)style, size);
::SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, (PVOID)path.get(),
SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);
// Close the key we opened.
::RegCloseKey(key);
}
}
return rv;
}
NS_IMETHODIMP
nsWindowsShellService::OpenApplication(PRInt32 aApplication)
{
nsAutoString application;
switch (aApplication) {
case nsIShellService::APPLICATION_MAIL:
application.AssignLiteral("Mail");
break;
case nsIShellService::APPLICATION_NEWS:
application.AssignLiteral("News");
break;
}
// The Default Client section of the Windows Registry looks like this:
//
// Clients\aClient\
// e.g. aClient = "Mail"...
// \Mail\(default) = Client Subkey Name
// \Client Subkey Name
// \Client Subkey Name\shell\open\command\
// \Client Subkey Name\shell\open\command\(default) = path to exe
//
// Find the default application for this class.
HKEY theKey;
nsresult rv = OpenKeyForReading(HKEY_CLASSES_ROOT, application, &theKey);
if (NS_FAILED(rv))
return rv;
PRUnichar buf[MAX_BUF];
DWORD type, len = sizeof buf;
DWORD res = ::RegQueryValueExW(theKey, EmptyString().get(), 0,
&type, (LPBYTE)&buf, &len);
if (REG_FAILED(res) || !*buf)
return NS_OK;
// Close the key we opened.
::RegCloseKey(theKey);
// Find the "open" command
application.AppendLiteral("\\");
application.Append(buf);
application.AppendLiteral("\\shell\\open\\command");
rv = OpenKeyForReading(HKEY_CLASSES_ROOT, application, &theKey);
if (NS_FAILED(rv))
return rv;
::ZeroMemory(buf, sizeof(buf));
len = sizeof buf;
res = ::RegQueryValueExW(theKey, EmptyString().get(), 0,
&type, (LPBYTE)&buf, &len);
if (REG_FAILED(res) || !*buf)
return NS_ERROR_FAILURE;
// Close the key we opened.
::RegCloseKey(theKey);
// Look for any embedded environment variables and substitute their
// values, as |::CreateProcessW| is unable to do this.
nsAutoString path(buf);
PRInt32 end = path.Length();
PRInt32 cursor = 0, temp = 0;
::ZeroMemory(buf, sizeof(buf));
do {
cursor = path.FindChar('%', cursor);
if (cursor < 0)
break;
temp = path.FindChar('%', cursor + 1);
++cursor;
::ZeroMemory(&buf, sizeof(buf));
::GetEnvironmentVariableW(nsAutoString(Substring(path, cursor, temp - cursor)).get(),
buf, sizeof(buf));
// "+ 2" is to subtract the extra characters used to delimit the environment
// variable ('%').
path.Replace((cursor - 1), temp - cursor + 2, nsDependentString(buf));
++cursor;
}
while (cursor < end);
STARTUPINFOW si;
PROCESS_INFORMATION pi;
::ZeroMemory(&si, sizeof(STARTUPINFOW));
::ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
BOOL success = ::CreateProcessW(NULL, (LPWSTR)path.get(), NULL,
NULL, FALSE, 0, NULL, NULL,
&si, &pi);
if (!success)
return NS_ERROR_FAILURE;
return NS_OK;
}
NS_IMETHODIMP
nsWindowsShellService::GetDesktopBackgroundColor(PRUint32* aColor)
{
PRUint32 color = ::GetSysColor(COLOR_DESKTOP);
*aColor = (GetRValue(color) << 16) | (GetGValue(color) << 8) | GetBValue(color);
return NS_OK;
}
NS_IMETHODIMP
nsWindowsShellService::SetDesktopBackgroundColor(PRUint32 aColor)
{
int aParameters[2] = { COLOR_BACKGROUND, COLOR_DESKTOP };
BYTE r = (aColor >> 16);
BYTE g = (aColor << 16) >> 24;
BYTE b = (aColor << 24) >> 24;
COLORREF colors[2] = { RGB(r,g,b), RGB(r,g,b) };
::SetSysColors(sizeof(aParameters) / sizeof(int), aParameters, colors);
PRBool result = PR_FALSE;
DWORD dwDisp = 0;
HKEY key;
// Try to create/open a subkey under HKLM.
DWORD rv = ::RegCreateKeyExW(HKEY_CURRENT_USER,
L"Control Panel\\Colors", 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
&key, &dwDisp);
if (REG_SUCCEEDED(rv)) {
char rgb[12];
sprintf((char*)rgb, "%u %u %u\0", r, g, b);
NS_ConvertUTF8toUTF16 backColor(rgb);
::RegSetValueExW(key, L"Background",
0, REG_SZ, (const BYTE *)backColor.get(),
(backColor.Length() + 1) * sizeof(PRUnichar));
}
// Close the key we opened.
::RegCloseKey(key);
return NS_OK;
}
NS_IMETHODIMP
nsWindowsShellService::GetUnreadMailCount(PRUint32* aCount)
{
*aCount = 0;
HKEY accountKey;
if (GetMailAccountKey(&accountKey)) {
DWORD type, unreadCount;
DWORD len = sizeof unreadCount;
DWORD res = ::RegQueryValueExW(accountKey, L"MessageCount", 0,
&type, (LPBYTE)&unreadCount, &len);
if (REG_SUCCEEDED(res))
*aCount = unreadCount;
// Close the key we opened.
::RegCloseKey(accountKey);
}
return NS_OK;
}
PRBool
nsWindowsShellService::GetMailAccountKey(HKEY* aResult)
{
NS_NAMED_LITERAL_STRING(unread,
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\UnreadMail\\");
HKEY mailKey;
DWORD res = ::RegOpenKeyExW(HKEY_CURRENT_USER, unread.get(), 0,
KEY_ENUMERATE_SUB_KEYS, &mailKey);
PRInt32 i = 0;
do {
PRUnichar subkeyName[MAX_BUF];
DWORD len = sizeof subkeyName;
res = ::RegEnumKeyExW(mailKey, i++, subkeyName, &len, NULL, NULL,
NULL, NULL);
if (REG_SUCCEEDED(res)) {
HKEY accountKey;
res = ::RegOpenKeyExW(mailKey, PromiseFlatString(subkeyName).get(),
0, KEY_READ, &accountKey);
if (REG_SUCCEEDED(res)) {
*aResult = accountKey;
// Close the key we opened.
::RegCloseKey(mailKey);
return PR_TRUE;
}
}
else
break;
}
while (1);
// Close the key we opened.
::RegCloseKey(mailKey);
return PR_FALSE;
}
NS_IMETHODIMP
nsWindowsShellService::OpenApplicationWithURI(nsILocalFile* aApplication,
const nsACString& aURI)
{
nsresult rv;
nsCOMPtr<nsIProcess> process =
do_CreateInstance("@mozilla.org/process/util;1", &rv);
if (NS_FAILED(rv))
return rv;
rv = process->Init(aApplication);
if (NS_FAILED(rv))
return rv;
const nsCString spec(aURI);
const char* specStr = spec.get();
PRUint32 pid;
return process->Run(PR_FALSE, &specStr, 1, &pid);
}
NS_IMETHODIMP
nsWindowsShellService::GetDefaultFeedReader(nsILocalFile** _retval)
{
*_retval = nsnull;
nsresult rv;
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
NS_LITERAL_STRING("feed\\shell\\open\\command"),
nsIWindowsRegKey::ACCESS_READ);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString path;
rv = regKey->ReadStringValue(EmptyString(), path);
NS_ENSURE_SUCCESS(rv, rv);
if (path.IsEmpty())
return NS_ERROR_FAILURE;
if (path.First() == '"') {
// Everything inside the quotes
path = Substring(path, 1, path.FindChar('"', 1) - 1);
}
else {
// Everything up to the first space
path = Substring(path, 0, path.FindChar(' '));
}
nsCOMPtr<nsILocalFile> defaultReader =
do_CreateInstance("@mozilla.org/file/local;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = defaultReader->InitWithPath(path);
NS_ENSURE_SUCCESS(rv, rv);
PRBool exists;
rv = defaultReader->Exists(&exists);
NS_ENSURE_SUCCESS(rv, rv);
if (!exists)
return NS_ERROR_FAILURE;
NS_ADDREF(*_retval = defaultReader);
return NS_OK;
}