diff options
Diffstat (limited to 'comm/suite/components/shell/nsWindowsShellService.cpp')
-rw-r--r-- | comm/suite/components/shell/nsWindowsShellService.cpp | 793 |
1 files changed, 793 insertions, 0 deletions
diff --git a/comm/suite/components/shell/nsWindowsShellService.cpp b/comm/suite/components/shell/nsWindowsShellService.cpp new file mode 100644 index 0000000000..7cdf1cbd88 --- /dev/null +++ b/comm/suite/components/shell/nsWindowsShellService.cpp @@ -0,0 +1,793 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsWindowsShellService.h" + +#include "imgIContainer.h" +#include "imgIRequest.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/RefPtr.h" +#include "nsIContent.h" +#include "nsIImageLoadingContent.h" +#include "nsIOutputStream.h" +#include "nsIPrefService.h" +#include "nsIPrefLocalizedString.h" +#include "nsIServiceManager.h" +#include "nsIStringBundle.h" +#include "nsNetUtil.h" +#include "nsServiceManagerUtils.h" +#include "nsShellService.h" +#include "nsIProcess.h" +#include "nsICategoryManager.h" +#include "nsDirectoryServiceUtils.h" +#include "nsAppDirectoryServiceDefs.h" +#include "nsDirectoryServiceDefs.h" +#include "nsIWindowsRegKey.h" +#include "nsUnicharUtils.h" +#include "nsIURLFormatter.h" +#include "nsXULAppAPI.h" +#include "mozilla/WindowsVersion.h" +#include "mozilla/dom/Element.h" + +#include "windows.h" +#include "shellapi.h" + +#ifdef _WIN32_WINNT +#undef _WIN32_WINNT +#endif +#define _WIN32_WINNT 0x0600 +#define INITGUID +#include <shlobj.h> + +#ifndef MAX_BUF +#define MAX_BUF 4096 +#endif + +#define REG_SUCCEEDED(val) \ + (val == ERROR_SUCCESS) + +#define REG_FAILED(val) \ + (val != ERROR_SUCCESS) + +using namespace mozilla; +using namespace mozilla::gfx; + +NS_IMPL_ISUPPORTS(nsWindowsShellService, nsIShellService) + +static nsresult +OpenKeyForReading(HKEY aKeyRoot, const wchar_t* aKeyName, HKEY* aKey) +{ + DWORD res = ::RegOpenKeyExW(aKeyRoot, aKeyName, 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 SeaMonkey OS integration Registry Settings +// Note: Some settings only exist when using the installer! +// The setting of SeaMonkey as default application is made by a helper +// application since writing those values may require elevation. +// +// Default Browser settings: +// - File Extension Mappings +// ----------------------- +// The following file extensions: +// .htm .html .shtml .xht .xhtml +// are mapped like so: +// +// HKCU\SOFTWARE\Classes\.<ext>\ (default) REG_SZ SeaMonkeyHTML +// +// as aliases to the class: +// +// HKCU\SOFTWARE\Classes\SeaMonkeyHTML\ +// DefaultIcon (default) REG_SZ <appfolder>\chrome\icons\default\html-file.ico +// shell\open\command (default) REG_SZ <apppath> -url "%1" +// +// - Windows Vista Protocol Handler +// +// HKCU\SOFTWARE\Classes\SeaMonkeyURL\(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 SeaMonkey +// \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>,0 +// 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 SeaMonkey +// \Topic (default) REG_SZ WWW_OpenURL +// +// - Windows Start Menu (Win2K SP2, XP SP1, and newer) +// ------------------------------------------------- +// The following keys are set to make SeaMonkey appear in the Start Menu as the +// browser: +// +// HKCU\SOFTWARE\Clients\StartMenuInternet\SEAMONKEY.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> &Preferences +// 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 +// +// +// +// Default Mail&News settings +// +// - File Extension Mappings +// ----------------------- +// The following file extension: +// .eml +// is mapped like this: +// +// HKCU\SOFTWARE\Classes\.eml (default) REG_SZ SeaMonkeyEML +// +// That aliases to this class: +// HKCU\SOFTWARE\Classes\SeaMonkeyEML\ (default) REG_SZ SeaMonkey (Mail) Document +// FriendlyTypeName REG_SZ SeaMonkey (Mail) Document +// DefaultIcon (default) REG_SZ <appfolder>\chrome\icons\default\html-file.ico +// shell\open\command (default) REG_SZ <apppath> "%1" +// +// - Windows Vista Protocol Handler +// +// HKCU\SOFTWARE\Classes\SeaMonkeyCOMPOSE (default) REG_SZ SeaMonkey (Mail) URL +// DefaultIcon REG_SZ <apppath>,0 +// EditFlags REG_DWORD 2 +// shell\open\command (default) REG_SZ <apppath> -osint -compose "%1" +// +// HKCU\SOFTWARE\Classes\SeaMonkeyNEWS (default) REG_SZ SeaMonkey (News) URL +// DefaultIcon REG_SZ <apppath>,0 +// EditFlags REG_DWORD 2 +// shell\open\command (default) REG_SZ <apppath> -osint -news "%1" +// +// +// - Protocol Mappings +// ----------------- +// The following protocol: +// mailto +// is mapped like this: +// +// HKCU\SOFTWARE\Classes\mailto\ (default) REG_SZ SeaMonkey (Mail) URL +// EditFlags REG_DWORD 2 +// URL Protocol REG_SZ +// DefaultIcon (default) REG_SZ <apppath>,0 +// shell\open\command (default) REG_SZ <apppath> -osint -compose "%1" +// +// The following protocols: +// news,nntp,snews +// are mapped like this: +// +// HKCU\SOFTWARE\Classes\<protocol>\ (default) REG_SZ SeaMonkey (News) URL +// EditFlags REG_DWORD 2 +// URL Protocol REG_SZ +// DefaultIcon (default) REG_SZ <appath>,0 +// shell\open\command (default) REG_SZ <appath> -osint -news "%1" +// +// - Windows Start Menu (Win2K SP2, XP SP1, and newer) +// ------------------------------------------------- +// The following keys are set to make SeaMonkey appear in the Start Menu as +// the default mail program: +// +// HKCU\SOFTWARE\Clients\Mail\SeaMonkey +// (default) REG_SZ <appname> +// DLLPath REG_SZ <appfolder>\mozMapi32.dll +// DefaultIcon (default) REG_SZ <apppath>,0 +// InstallInfo HideIconsCommand REG_SZ <uninstpath> /HideShortcuts +// InstallInfo ReinstallCommand REG_SZ <uninstpath> /SetAsDefaultAppGlobal +// InstallInfo ShowIconsCommand REG_SZ <uninstpath> /ShowShortcuts +// shell\open\command (default) REG_SZ <apppath> -mail +// shell\properties (default) REG_SZ <appname> &Preferences +// shell\properties\command (default) REG_SZ <apppath> -preferences +// +// Also set SeaMonkey as News reader (Usenet), though Windows does currently +// not expose a default news reader to UI. Applications like Outlook +// also add themselves to this registry key +// +// HKCU\SOFTWARE\Clients\News\SeaMonkey +// (default) REG_SZ <appname> +// DLLPath REG_SZ <appfolder>\mozMapi32.dll +// DefaultIcon (default) REG_SZ <apppath>,0 +// shell\open\command (default) REG_SZ <apppath> -news +// +/////////////////////////////////////////////////////////////////////////////// + + +typedef enum { + NO_SUBSTITUTION = 0x00, + APP_PATH_SUBSTITUTION = 0x01 +} SettingFlags; + +#define APP_REG_NAME L"SeaMonkey" +// APP_REG_NAME_MAIL and APP_REG_NAME_NEWS should be kept in synch with +// AppRegNameMail and AppRegNameNews in the installer file: defines.nsi.in +#define APP_REG_NAME_MAIL L"SeaMonkey (Mail)" +#define APP_REG_NAME_NEWS L"SeaMonkey (News)" +#define CLS_HTML "SeaMonkeyHTML" +#define CLS_URL "SeaMonkeyURL" +#define CLS_EML "SeaMonkeyEML" +#define CLS_MAILTOURL "SeaMonkeyCOMPOSE" +#define CLS_NEWSURL "SeaMonkeyNEWS" +#define CLS_FEEDURL "SeaMonkeyFEED" +#define SMI "SOFTWARE\\Clients\\StartMenuInternet\\" +#define DI "\\DefaultIcon" +#define II "\\InstallInfo" +#define SOP "\\shell\\open\\command" + +#define VAL_ICON "%APPPATH%,0" +#define VAL_HTML_OPEN "\"%APPPATH%\" -url \"%1\"" +#define VAL_URL_OPEN "\"%APPPATH%\" -requestPending -osint -url \"%1\"" +#define VAL_MAIL_OPEN "\"%APPPATH%\" \"%1\"" + +#define MAKE_KEY_NAME1(PREFIX, MID) \ + PREFIX MID + +// The DefaultIcon registry key value should never be used (e.g. NON_ESSENTIAL) +// when checking if SeaMonkey 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 gBrowserSettings[] = { + // File Extension Class - as of 1.8.1.2 the value for VAL_URL_OPEN is also + // checked for CLS_HTML since SeaMonkey should also own opening local files + // when set as the default browser. + { MAKE_KEY_NAME1(CLS_HTML, SOP), "", VAL_HTML_OPEN, APP_PATH_SUBSTITUTION }, + + // Protocol Handler Class - for Vista and above + { MAKE_KEY_NAME1(CLS_URL, SOP), "", VAL_URL_OPEN, APP_PATH_SUBSTITUTION }, + + // Protocol Handlers + { MAKE_KEY_NAME1("HTTP", DI), "", VAL_ICON, APP_PATH_SUBSTITUTION }, + { MAKE_KEY_NAME1("HTTP", SOP), "", VAL_URL_OPEN, APP_PATH_SUBSTITUTION }, + { MAKE_KEY_NAME1("HTTPS", DI), "", VAL_ICON, APP_PATH_SUBSTITUTION }, + { MAKE_KEY_NAME1("HTTPS", SOP), "", VAL_URL_OPEN, APP_PATH_SUBSTITUTION } + + // These values must be set by hand, since they contain localized strings. + // seamonkey.exe\shell\properties (default) REG_SZ SeaMonkey &Preferences + // seamonkey.exe\shell\safemode (default) REG_SZ SeaMonkey &Safe Mode +}; + + static SETTING gMailSettings[] = { + // File Extension Aliases + { ".eml", "", CLS_EML, NO_SUBSTITUTION }, + // File Extension Class + { MAKE_KEY_NAME1(CLS_EML, SOP), "", VAL_MAIL_OPEN, APP_PATH_SUBSTITUTION}, + + // Protocol Handler Class - for Vista and above + { MAKE_KEY_NAME1(CLS_MAILTOURL, SOP), "", "\"%APPPATH%\" -osint -compose \"%1\"", APP_PATH_SUBSTITUTION }, + + // Protocol Handlers + { MAKE_KEY_NAME1("mailto", SOP), "", "\"%APPPATH%\" -osint -compose \"%1\"", APP_PATH_SUBSTITUTION } + }; + + static SETTING gNewsSettings[] = { + // Protocol Handler Class - for Vista and above + { MAKE_KEY_NAME1(CLS_NEWSURL, SOP), "", "\"%APPPATH%\" -osint -mail \"%1\"", APP_PATH_SUBSTITUTION }, + + // Protocol Handlers + { MAKE_KEY_NAME1("news", SOP), "", "\"%APPPATH%\" -osint -mail \"%1\"", APP_PATH_SUBSTITUTION }, + { MAKE_KEY_NAME1("nntp", SOP), "", "\"%APPPATH%\" -osint -mail \"%1\"", APP_PATH_SUBSTITUTION }, +}; + + static SETTING gFeedSettings[] = { + // Protocol Handler Class - for Vista and above + { MAKE_KEY_NAME1(CLS_FEEDURL, SOP), "", "\"%APPPATH%\" -osint -mail \"%1\"", APP_PATH_SUBSTITUTION }, + + // Protocol Handlers + { MAKE_KEY_NAME1("feed", SOP), "", "\"%APPPATH%\" -osint -mail \"%1\"", APP_PATH_SUBSTITUTION }, +}; + +nsresult +GetHelperPath(nsString& aPath) +{ + nsresult rv; + nsCOMPtr<nsIProperties> directoryService = + do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIFile> appHelper; + rv = directoryService->Get(XRE_EXECUTABLE_FILE, + NS_GET_IID(nsIFile), + getter_AddRefs(appHelper)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = appHelper->SetNativeLeafName("uninstall"_ns); + NS_ENSURE_SUCCESS(rv, rv); + + rv = appHelper->AppendNative("helper.exe"_ns); + NS_ENSURE_SUCCESS(rv, rv); + + rv = appHelper->GetPath(aPath); + + aPath.Insert('"', 0); + aPath.Append('"'); + + return rv; +} + +nsresult +LaunchHelper(const nsString& aPath) +{ + STARTUPINFOW si = {sizeof(si), 0}; + PROCESS_INFORMATION pi = {0}; + + BOOL ok = CreateProcessW(nullptr, (LPWSTR)aPath.get(), nullptr, nullptr, + FALSE, 0, nullptr, nullptr, &si, &pi); + + if (!ok) + return NS_ERROR_FAILURE; + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return NS_OK; +} + +/* helper routine. Iterate over the passed in settings object, + testing each key to see if we are handling it. +*/ +bool +nsWindowsShellService::TestForDefault(SETTING aSettings[], int32_t aSize) +{ + wchar_t currValue[MAX_BUF]; + SETTING* end = aSettings + aSize; + for (SETTING * settings = aSettings; 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) { + int32_t offset = dataLongPath.Find(u"%APPPATH%"); + dataLongPath.Replace(offset, 9, mAppLongPath); + // Remove the quotes around %APPPATH% in VAL_OPEN for short paths + int32_t offsetQuoted = dataShortPath.Find(u"\"%APPPATH%\""); + if (offsetQuoted != -1) + dataShortPath.Replace(offsetQuoted, 11, mAppShortPath); + else + dataShortPath.Replace(offset, 9, mAppShortPath); + } + + ::ZeroMemory(currValue, sizeof(currValue)); + HKEY theKey; + nsresult rv = OpenKeyForReading(HKEY_CLASSES_ROOT, key.get(), &theKey); + if (NS_FAILED(rv)) + // Key does not exist + return false; + + DWORD len = sizeof currValue; + DWORD res = ::RegQueryValueExW(theKey, value.get(), + nullptr, nullptr, (LPBYTE)currValue, &len); + // Close the key we opened. + ::RegCloseKey(theKey); + if (REG_FAILED(res) || + _wcsicmp(dataLongPath.get(), currValue) && + _wcsicmp(dataShortPath.get(), currValue)) { + // Key wasn't set, or was set to something else (something else became the default client) + return false; + } + } + + return true; +} + +nsresult nsWindowsShellService::Init() +{ + wchar_t appPath[MAX_BUF]; + if (!::GetModuleFileNameW(0, appPath, MAX_BUF)) + return NS_ERROR_FAILURE; + + mAppLongPath.Assign(appPath); + + // Support short path to the exe so if it is already set the user is not + // prompted to set the default mail client again. + if (!::GetShortPathNameW(appPath, appPath, MAX_BUF)) + return NS_ERROR_FAILURE; + + mAppShortPath.Assign(appPath); + + return NS_OK; +} + +bool +nsWindowsShellService::IsDefaultClientVista(uint16_t aApps, bool* aIsDefaultClient) +{ + IApplicationAssociationRegistration* pAAR; + + HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration, + nullptr, + CLSCTX_INPROC, + IID_IApplicationAssociationRegistration, + (void**)&pAAR); + + if (SUCCEEDED(hr)) { + BOOL isDefaultBrowser = true; + BOOL isDefaultMail = true; + BOOL isDefaultNews = true; + if (aApps & nsIShellService::BROWSER) + pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE, APP_REG_NAME, &isDefaultBrowser); + if (aApps & nsIShellService::MAIL) + pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE, APP_REG_NAME_MAIL, &isDefaultMail); + if (aApps & nsIShellService::NEWS) + pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE, APP_REG_NAME_NEWS, &isDefaultNews); + + *aIsDefaultClient = isDefaultBrowser && isDefaultNews && isDefaultMail; + + pAAR->Release(); + return true; + } + return false; +} + +NS_IMETHODIMP +nsWindowsShellService::IsDefaultClient(bool aStartupCheck, uint16_t aApps, bool *aIsDefaultClient) +{ + *aIsDefaultClient = true; + + // for each type, check if it is the default app + // browser check needs to be at the top + if (aApps & nsIShellService::BROWSER) { + *aIsDefaultClient &= TestForDefault(gBrowserSettings, sizeof(gBrowserSettings)/sizeof(SETTING)); + // Only check if this app is default on Vista if the previous checks + // indicate that this app is the default. + if (*aIsDefaultClient) + IsDefaultClientVista(nsIShellService::BROWSER, aIsDefaultClient); + } + if (aApps & nsIShellService::MAIL) { + *aIsDefaultClient &= TestForDefault(gMailSettings, sizeof(gMailSettings)/sizeof(SETTING)); + // Only check if this app is default on Vista if the previous checks + // indicate that this app is the default. + if (*aIsDefaultClient) + IsDefaultClientVista(nsIShellService::MAIL, aIsDefaultClient); + } + if (aApps & nsIShellService::NEWS) { + *aIsDefaultClient &= TestForDefault(gNewsSettings, sizeof(gNewsSettings)/sizeof(SETTING)); + // Only check if this app is default on Vista if the previous checks + // indicate that this app is the default. + if (*aIsDefaultClient) + IsDefaultClientVista(nsIShellService::NEWS, aIsDefaultClient); + } + + return NS_OK; +} + + +NS_IMETHODIMP +nsWindowsShellService::SetDefaultClient(bool aForAllUsers, + bool aClaimAllTypes, uint16_t aApps) +{ + nsAutoString appHelperPath; + if (NS_FAILED(GetHelperPath(appHelperPath))) + return NS_ERROR_FAILURE; + + if (aForAllUsers) + appHelperPath.AppendLiteral(" /SetAsDefaultAppGlobal"); + else { + appHelperPath.AppendLiteral(" /SetAsDefaultAppUser"); + if (aApps & nsIShellService::BROWSER) + appHelperPath.AppendLiteral(" Browser"); + + if (aApps & nsIShellService::MAIL) + appHelperPath.AppendLiteral(" Mail"); + + if (aApps & nsIShellService::NEWS) + appHelperPath.AppendLiteral(" News"); + } + + return LaunchHelper(appHelperPath); +} + +static nsresult +WriteBitmap(nsIFile* aFile, imgIContainer* aImage) +{ + nsresult rv; + + RefPtr<SourceSurface> surface = + aImage->GetFrame(imgIContainer::FRAME_CURRENT, + imgIContainer::FLAG_SYNC_DECODE); + NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE); + + // For either of the following formats we want to set the biBitCount member + // of the BITMAPINFOHEADER struct to 32, below. For that value the bitmap + // format defines that the A8/X8 WORDs in the bitmap byte stream be ignored + // for the BI_RGB value we use for the biCompression member. + MOZ_ASSERT(surface->GetFormat() == SurfaceFormat::B8G8R8A8 || + surface->GetFormat() == SurfaceFormat::B8G8R8X8); + + RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface(); + NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE); + + int32_t width = dataSurface->GetSize().width; + int32_t height = dataSurface->GetSize().height; + int32_t bytesPerPixel = 4 * sizeof(uint8_t); + int32_t bytesPerRow = bytesPerPixel * 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)bytesPerPixel*8; + bmi.biCompression = BI_RGB; + bmi.biSizeImage = bytesPerRow * height; + 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; + rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile); + NS_ENSURE_SUCCESS(rv, rv); + + DataSourceSurface::MappedSurface map; + if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) { + return NS_ERROR_FAILURE; + } + + // write the bitmap headers and rgb pixel data to the file + rv = NS_ERROR_FAILURE; + if (stream) { + uint32_t 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 + uint32_t i = map.mStride * height; + rv = NS_OK; + do { + i -= map.mStride; + stream->Write(((const char*)map.mData) + i, bytesPerRow, &written); + if (written != bytesPerRow) { + rv = NS_ERROR_FAILURE; + break; + } + } while (i != 0); + } + } + + stream->Close(); + } + + dataSurface->Unmap(); + + return rv; +} + +NS_IMETHODIMP +nsWindowsShellService::SetDesktopBackground(dom::Element* aElement, + int32_t aPosition, + const nsACString& aImageName) +{ + if (!aElement || !aElement->IsHTMLElement(nsGkAtoms::img)) { + // XXX write background loading stuff! + return NS_ERROR_NOT_AVAILABLE; + } + + nsresult rv; + 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 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" + nsAutoString fileLeafName; + rv = shellBundle->GetStringFromName("desktopBackgroundLeafNameWin", + 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\SeaMonkey\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, container); + + // if the file was written successfully, set it as the system wallpaper + if (NS_SUCCEEDED(rv)) { + nsCOMPtr<nsIWindowsRegKey> key(do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = key->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, + u"Control Panel\\Desktop"_ns, + nsIWindowsRegKey::ACCESS_SET_VALUE); + NS_ENSURE_SUCCESS(rv, rv); + + int style = 0; + switch (aPosition) { + case BACKGROUND_STRETCH: + style = 2; + break; + case BACKGROUND_FILL: + style = 10; + break; + case BACKGROUND_FIT: + style = 6; + break; + } + + nsString value; + value.AppendInt(style); + rv = key->WriteStringValue(u"WallpaperStyle"_ns, value); + NS_ENSURE_SUCCESS(rv, rv); + + value.Assign(aPosition == BACKGROUND_TILE ? '1' : '0'); + rv = key->WriteStringValue(u"TileWallpaper"_ns, value); + NS_ENSURE_SUCCESS(rv, rv); + + rv = key->Close(); + NS_ENSURE_SUCCESS(rv, rv); + + ::SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, (PVOID)path.get(), + SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE); + } + return rv; +} + +NS_IMETHODIMP +nsWindowsShellService::GetDesktopBackgroundColor(uint32_t* aColor) +{ + uint32_t color = ::GetSysColor(COLOR_DESKTOP); + *aColor = (GetRValue(color) << 16) | (GetGValue(color) << 8) | GetBValue(color); + return NS_OK; +} + +NS_IMETHODIMP +nsWindowsShellService::SetDesktopBackgroundColor(uint32_t aColor) +{ + int parameter = COLOR_DESKTOP; + BYTE r = (aColor >> 16); + BYTE g = (aColor << 16) >> 24; + BYTE b = (aColor << 24) >> 24; + COLORREF color = RGB(r,g,b); + + ::SetSysColors(1, ¶meter, &color); + + nsresult rv; + nsCOMPtr<nsIWindowsRegKey> key(do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = key->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, + u"Control Panel\\Colors"_ns, + nsIWindowsRegKey::ACCESS_SET_VALUE); + NS_ENSURE_SUCCESS(rv, rv); + + wchar_t rgb[12]; + _snwprintf(rgb, 12, L"%u %u %u", r, g, b); + rv = key->WriteStringValue(u"Background"_ns, + nsDependentString(rgb)); + NS_ENSURE_SUCCESS(rv, rv); + + return key->Close(); +} + +NS_IMETHODIMP +nsWindowsShellService::OpenApplicationWithURI(nsIFile* 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 = PromiseFlatCString(aURI); + const char* specStr = spec.get(); + return process->Run(false, &specStr, 1); +} + +NS_IMETHODIMP +nsWindowsShellService::GetDefaultFeedReader(nsIFile** _retval) +{ + *_retval = nullptr; + + nsresult rv; + nsCOMPtr<nsIWindowsRegKey> key(do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = key->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, + u"feed\\shell\\open\\command"_ns, + nsIWindowsRegKey::ACCESS_READ); + NS_ENSURE_SUCCESS(rv, rv); + + nsString path; + rv = key->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<nsIFile> defaultReader = + do_CreateInstance("@mozilla.org/file/local;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = defaultReader->InitWithPath(path); + NS_ENSURE_SUCCESS(rv, rv); + + bool exists; + rv = defaultReader->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + if (!exists) + return NS_ERROR_FAILURE; + + NS_ADDREF(*_retval = defaultReader); + return NS_OK; +} |