Files
palemoon27/browser/components/shell/nsWindowsShellService.cpp
T
roytam1 bfe2ea5afa import changes from `dev' branch of rmottola/Arctic-Fox:
- reapply  Bug 1574573 - Disambiguate a use of Handle in XPCShellEnvironment.cpp r=Ehsan (a674c4b006)
- re-create --disable-applemedia option (needed for pre-10.7 Mac) in new configure ssytem (28e2fd5fee)
- Bug 1255707 - Part 2. Remove ScreenSizeChanged. r=snorp (3a93e4e768)
- Bug 1250418 - Remove the assertion check of mCanSend in CompositorCh ld::ActorDestroy, r=nical (14bb402a1d)
- Bug 1250718 - Improve layer logging for preserve-3d layers. r=thinker (f373a50040)
- Bug 1232042 - Addendum: Add comment for mLayerManager check. r=jrmuizel (2b69aa784a)
- Bug 1239861. Skip composite if vsync time is before force composite time. r=kats (5ee4038157)
- Bug 1241678 - Fix low-volume null-deref crash. r=BenWa (b28d944615)
- Rename PCompositor to PCompositorBridge. (bug 1258479 part 2, r=mattwoodrow) (dd535a9bdd)
- Bug 1238160 - Set frame type on TabContext. r=billm,mayhemer (54424d792b)
- bits of 1240471 (fb34a97806)
- Bug 1238160 - Test mozbrowser APIs to ensure no content exposure. r=bz (17a0ac611c)
- Bug 1238160 - Enable mozbrowser frames on desktop. r=bz (5b42b5403d)
- Bug 1197461 - Implement Permissions.revoke. r=poiru,baku (306712f9ca)
- Bug 1197461 - e10s support for Permissions.revoke. r=poiru (bbda5df25d)
- Bug 1220184 - Eliminate Gingerbread compatibility. r=froydnj, r=nalexander (dce9e4f9e8)
- Bug 1238160 - Set docshell isolation mode. r=smaug (6ac2c21d1d)
- fix of Bug 1238160 (786a554a6a)
- Bug 1237364 - nsFrameLoader.cpp can set the userContextId directly using nsIDocShell, r=smaug (fea166caa3)
- Bug 1227861 - Add OriginAttributes getter/setter into nsIDocShell. r=smaug, sicking (4f8ac4f8ce)
- Bug 1246956 - Add originURI to Sessionstore. r=bz,Yoric,mfinkle (a1dcd44194)
- Bug 1246956 - Add loadReplace to session history. r=yoric r=mfinkle (662e66ad7e)
- Bug 1252811 - remove mIsInIsolatedMozBrowser from nsDocShell. r=smaug (b5927775b7)
- Bug 1250917 - Remove NS_SUCCESS_I_DID_SOMETHING; r=bholley (9dd6fe351b)
- Bug 1225053 - use null principal in gfxSVGGlyphs.cpp r=edwin (597d10fd4e)
- Bug 1226120 - add test for unknownContentType.xul and file types with a default handler but always ask set, r=mconley (dd95e18fd9)
- Bug 1254118 - Web protocol handlers need more testing coverage, r=felipe. (4fb6949d10)
- Bug 1253307 - Use a better function to load web handler apps in e10s. r=billm/mconley (28695be154)
- Bug 1155241: Check mInstanceOwner for nullptr in nsObjectLoadingContent::PluginDestroyed; r=smaug (ad60991e3e)
- Bug 1229220 - Update the scrollbar visibility prefs when initializing a TabChild; r=smaug (28997e0a6d)
- Bug 1252262 - Don't combine the client offset into the outer rect for the child process. r=jimm (f415c0418e)
- Bug 1249943 - Make test_basic_pan work on Fennec and Linux as well. r=botond (657c940be1)
- bit of bug 1245765 part 5 (82463f7eaa)
- Bug 1207512 - Remove the JS_IsRunning call in nsObjectLoadingContent::ScriptRequestPluginInstance; r=bholley (76047284a6)
- minor cleanup and Telemetry pieces (2765a1408d)
- Bug 1239463 - Do not assert when notifying an inactive document about changed content from the plugin crash notification. r=bz (03bf38a683)
- Bug 1192450 - Remove PlayPreview registration from Shumway. r=jet (9b6e131876)
- Bug 1200602 - Use the alternate content for <applet>. r=kmachulis (843fccf0aa)
- Bug 1236900 - Remove useless null check since mOwnerContent cannot be null. r=jst (a7e2d4b95c)
- bug 1257287 - add nsIDocShell::GetEdItingSession() (abd9022426)
- bug 1257287 - add nsIDocShell.tabChild r=smaug (6fe910c2b8)
- Bug 1256626: P1. Add NotifyBenchmarkResult ipc methods. r=jimm (02ab9cb0f6)
- Bug 1256626: P2. Use NotifyBenchmarkResult to save VP9 result. r=jimm (877277768c)
- Bug 1256626. Workaround Microsoft macro silliness. r=me (070e5a8cf0)
- Bug 1183915 - Put images dragged from content processes in the drag data in the parent. r=smaug (72906fab07)
- Bug 1228652 - Check for window.closed after flushing messages in navigateAndRestore. r=mossop (fd9d2b7d42)
- Bug 1217517 - nullcheck consumers for gKeywordURIFixup, r=jaws (adf6388856)
- Bug 951695 - Rename 'Character Encoding' to 'Text Encoding'. r=jaws (046e9644c2)
- Bug 1088710 - part 2: make it work on e10s, r=mconley (479366cba8)
- Bug 1147720 - Fix intermittent waitForDocLoadAndStopIt() timeouts by keeping a strong reference to the progress listener r=mak (4e9fa3f8c2)
- Bug 1228754, r=mak,bz (a33bc4da9c)
- Bug 1250482 - r=mak (0f69dd5f69)
- Bug 798249 - track when we're 'inside' a loadURI call when dealing with Stop() calls resulting from the same, r=mconley (e06b5b44e9)
- Bug 1254657 - change how we send Content:LoadURIResult to avoid upsetting RemoteWebProgress.jsm, r=mconley (915e7a1be6)
- 1253584 - Fix and enable browser_NetworkPrioritizer.js for e10s. r=mconley (9757c53ab6)
- Bug 1254522 - Make remote-browser.xml and browser.xml not depend on Firefox's browser.js. r=mconley (0a15acf7cc)
- Bug 621158 - make appcache use messaging for quota management, r=mayhemer,jaws (7e69707327)
- Bug 1255511 - Skip beforeunload prompts once nsIAppStartup shuttingDown returns true. r=Gijs (66bb2ce055)
- Bug 884355 - Warn about closing multiple tabs when "Never remember history" is enabled" r=gavin (7e61149b88)
- Bug 1228754 - bustage followup, rs=me,bustage Bug 1228754 - correct comments from bustage fix, rs=me, DONTBUILD (ef4d053a24)
- Bug 985777 - add a whitelist for URLs that we can switch to/from private browsing windows, r=ehsan (dc799a2213)
- Bug 963945 - Add about:addons URI as whitelisted so that it can be refocused instead of opening duplicate tabs in private browsing. r=jdm (8d5770a31c)
- reshuffle misspatch (8d2f7c2924)
- Bug 1236126 - fix oversight in principal handling for dialogs with host-less principals, r=aryx (1827672cae)
- align some stuff (256ffe50fa)
- Bug 1252239: Trap errors thrown by speculativeConnect. r=ttaubert (726d890a7b)
- Bug 1093153: enabled and re-factored browser_aboutHome.js to work correctly in e10s mode. r=mak (90518a9742)
- Bug 1239671 - Don't let session store override persistent cookies, r=ehsan (ee5b04fc97)
- Bug 1255685 - SessionStorage.jsm should use origin attributes from docshell. r=sicking (1f055f6ab2)
- Bug 1234021 - Catch exceptions raised by storage.length in SessionStorage.jsm, r=mconley (85ed6cf819)
- Bug 1192394 - Force an image load whenever the thumbnail file changes. r=adw (c194d868f9)
- let-var (f5e9a53170)
- Bug 529899 - Purge cookies on clean shutdown with "Keep cookies until I close Firefox" r=Yoric (1a15abd5f3)
- Bug 1071104 - Remove legacy "hasWrittenState" flag from SessionWorker r=yoric (484e1ac5b1)
- Bug 1198898 - Determining number of tabs/windows restored by Session Restore;r=mconley (5bda3ecc88)
- Bug 1245891 - Changing Session Restore Talos tests to include the time to restore actual tabs;r=mconley (d7c5957192)
- Bug 1210940 - New Browser Component: Newtab r=Mardak (a59fd6c88e)
- Bug 1219454 - Replace 'show' with 'receive' in about:permissions and control center for consistent messaging around notifications and to account for new Push permission. r=MattN (60cc4211ec)
- align (b7bc52abef)
- Bug 868711 - Remove confusing expression in _trackSlowStartup. r=gavin (b2d5609496)
- Bug 1231112 - work around tab groups migration issue in safe mode, r=ttaubert (e71ceebd3f)
- Bug 1249608 - Don't run UI migration steps with new profiles. r=gijs (b6f92e45ba)
- Bug 910431 - Electrolysis: Permission code followup. f=felipe (1bf556eeea)
- align some e10s (05aa76a560)
- Bug 1250109 - Change DOMEventTargetHelper subclasses to not assume that GetOwner() is non-null, since it can be nulled out by navigation. r=bzbarsky (deb440c2a4)
- Bug 908277 - Prevent permission UI errors when PopupNotifications is not available. r=dolske (dcefc44921)
- Bug 1199805 - Fix displayURI typo on site permission prompts. r=bgrins (78dbd44c98)
- Bug 1246028 Implement chrome.commands.getAll. r=kmag (b263fbe137)
- Bug 1242557 - Import missing commands API schema file. r=kmag (b930a95b2f)
- Bug 1249689 - replace bootstrapScope with an activeAddons Map() that contains it r=mossop (00bef48286)
- Bug 1249689 - generate and provide a Symbol for each add-on on startup r=mossop (bd7b936bcb)
- Bug 685155: Treat symbolic links the same as proxy files in the Add-on Manager. r=Mossop (4f458140df)
- Bug 1244248: Test that the certificate database is cached. r=rhelmer (a5cc6e8d75)
- Bug 1250784: Part 1 - [webext] Add support for options_ui via inline browsers in the Add-on Manager. r=Mossop (62b6efe654)
- Bug 1250784: Part 2 - [webext] Fix some issues that cause noisy tests. r=Mossop (4ada262435)
- Bug 1252250 - Implement browser.bookmarks.removeTree(), r=kmag r=mak (7e67b5ccf1)
- Bug 1253652 - Fix browser.bookmarks.move() and add tests for it. r=kmag, r=mak (151a8bf09a)
- Bug 1250784: Follow-up: Fix merge conflict in bookmarks API. r=me (3db6ea04ea)
- Bug 1251269 - Implement browser.bookmarks.getRecent(), r=kmag r=mak (d4564d6aee)
- Bug 1246614 - Check if system add-ons directory exists before trying to clean it. r=mossop (c2d59f34a8)
- Bug 1041514 - Don't show default browser prompt if a user opts out in the installer. r=jimm (b2ccf151f7)
- use ArcticFoxHTML and ArcticFoxURL keys, not to mix with Firefox (e6d36fdd78)
- Bug 1207784 - skip permission hooks in createOffer when called from hiddenWindow (add-ons). r=mt (b90a4c940f)
- Bug 1209766 - Update validation message before showing notification. r=khuey (8c9a865969)
- Bug 1206560 - Show the site favicon in XUL notifications. r=jaws (e5bad6ea20)
- Bug 1248497 창 Add promise support to the sendMessage APIs. r=billm (e26a805c92)
- Bug 911216 - Part 1: Add tests directly testing content Promise constructor resolved with chrome Promise. r=bz (1b2f1ec6b8)
- Bug 911216 - Part 2: Add self-hosting intrinsic for calling wrapped functions without wrapper security checks. r=efaust,bholley (de086e8422)
- Bug 911216 - Part 3: Allow wrapped self-hosted functions and intrinsics in the callFunction debug check. r=efaust (c02e6337fe)
- Bug 1251921 - Do not call debugger hooks with half-initialized frame if InterpeterFrame::prologue fails. (r=jorendorff) (9873720345)
- Bug 1256342. Fix typed array iteration to work correctly over Xrays. r=till (6a7f5c12c6)
- Bug 1256376. Fix forEach on typed arrays to work over Xrays from web extension sandboxes. r=till (ab19703ab5)
- Bug 1253436 - Add __repr__ to BaseLibrary and BaseProgram; r=glandium (a04a16f2ac)
- Bug 1238064 - Update docs to reflect new workflow. r=mossop (c7670ac086)
- Bug 911216 - Part 4: Add self-hosting intrinsic for creating arrays in other compartments. r=efaust (37b14521fb)
- Bug 1233497 - Temporarily allow unsafe CPOWs in Promise-backend.js and Task.jsm. r=billm (d2672a456a)
- Bug 1225041 - Implement ES6 Annex B.3.5 for direct eval. (r=jorendorff) (daf24f0e34)
- Bug 1254185 - Deal with missing arguments assigned to block bindings. (r=jimb) (3ce53dcd06)
- Bug 1250506 - check if node is acceptable as a child before creating an accessible for it, r=davidb (5960ba726d)
- Bug 1251941 - aria::GetRoleMap should take element, r=davidb (e9ee4e20ea)
- Bug 1251944 - get rid of nsCoreUtils::GetRoleContent, r=davidb (a2bf199bb4)
- Bug 1257030 - Add support for supplying preexisting stack instead of capturing one for use as the async parent stack of CallbackObject. r=bz,tromey (a4ddb41fac)
- Bug 1232291 - Non-used header in MessagePortService.*, r=smaug (1e2398e314)
- Bug 1255655 - Const-ify sWAIRoleMaps. r=tbsaunde. (09653e44af)
- align (24667f7952)
- Bug 1253438 - Expose Push observer notification topics. r=markh (b62a068d4b)
2024-03-14 23:40:15 +08:00

1383 lines
44 KiB
C++

/* -*- 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 "nsIDOMElement.h"
#include "nsIDOMHTMLImageElement.h"
#include "nsIImageLoadingContent.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 "nsBrowserCompsCID.h"
#include "nsDirectoryServiceUtils.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceDefs.h"
#include "nsIWindowsRegKey.h"
#include "nsUnicharUtils.h"
#include "nsIWinTaskbar.h"
#include "nsISupportsPrimitives.h"
#include "nsIURLFormatter.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#include "mozilla/WindowsVersion.h"
#include "windows.h"
#include "shellapi.h"
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0600
#define INITGUID
#undef NTDDI_VERSION
#define NTDDI_VERSION NTDDI_WIN8
// Needed for access to IApplicationActivationManager
#include <shlobj.h>
#include <mbstring.h>
#include <shlwapi.h>
#ifndef MAX_BUF
#define MAX_BUF 4096
#endif
#define REG_SUCCEEDED(val) \
(val == ERROR_SUCCESS)
#define REG_FAILED(val) \
(val != ERROR_SUCCESS)
#define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
using mozilla::IsWin8OrLater;
using namespace mozilla;
using namespace mozilla::gfx;
NS_IMPL_ISUPPORTS(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;
}
static bool
GetPrefString(const nsCString& aPrefName, nsAString& aValue)
{
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
if (!prefs) {
return false;
}
nsAutoCString prefCStr;
nsresult rv = prefs->GetCharPref(aPrefName.get(),
getter_Copies(prefCStr));
if (NS_FAILED(rv)) {
return false;
}
CopyUTF8toUTF16(prefCStr, aValue);
return true;
}
static bool
SetPrefString(const nsCString& aPrefName, const nsString& aValue)
{
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
if (!prefs) {
return false;
}
nsresult rv = prefs->SetCharPref(aPrefName.get(),
NS_ConvertUTF16toUTF8(aValue).get());
return NS_SUCCEEDED(rv);
}
///////////////////////////////////////////////////////////////////////////////
// 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 PaleMoonHTML
//
// as aliases to the class:
//
// HKCU\SOFTWARE\Classes\PaleMoonHTML\
// DefaultIcon (default) REG_SZ <apppath>,1
// shell\open\command (default) REG_SZ <apppath> -osint -url "%1"
// shell\open\ddeexec (default) REG_SZ <empty string>
//
// - Windows Vista and above Protocol Handler
//
// HKCU\SOFTWARE\Classes\PaleMoonURL\ (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> -osint -url "%1"
// shell\open\ddeexec (default) REG_SZ <empty string>
//
// - 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> -osint -url "%1"
// shell\open\ddeexec (default) REG_SZ <empty string>
//
// - Windows Start Menu (XP SP1 and newer)
// -------------------------------------------------
// The following keys are set to make PaleMoon 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
//
// The values checked are all default values so the value name is not needed.
typedef struct {
const char* keyName;
const char* valueData;
const char* oldValueData;
} SETTING;
#define APP_REG_NAME L"Pale Moon"
#define VAL_FILE_ICON "%APPPATH%,1"
#define VAL_OPEN "\"%APPPATH%\" -osint -url \"%1\""
#define OLD_VAL_OPEN "\"%APPPATH%\" -requestPending -osint -url \"%1\""
#define DI "\\DefaultIcon"
#define SOC "\\shell\\open\\command"
#define SOD "\\shell\\open\\ddeexec"
// Used for updating the FTP protocol handler's shell open command under HKCU.
#define FTP_SOC L"Software\\Classes\\ftp\\shell\\open\\command"
#define MAKE_KEY_NAME1(PREFIX, MID) \
PREFIX MID
// The DefaultIcon registry key value should never be used when checking if
// PaleMoon is the default browser for file handlers 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. The FTP protocol is not checked so advanced users can set the FTP
// handler to another application and still have PaleMoon check if it is the
// default HTTP and HTTPS handler.
// *** Do not add additional checks here unless you skip them when aForAllTypes
// is false below***.
static SETTING gSettings[] = {
// File Handler Class
// ***keep this as the first entry because when aForAllTypes is not set below
// it will skip over this check.***
{ MAKE_KEY_NAME1("PaleMoonHTML", SOC), VAL_OPEN, OLD_VAL_OPEN },
// Protocol Handler Class - for Vista and above
{ MAKE_KEY_NAME1("PaleMoonURL", SOC), VAL_OPEN, OLD_VAL_OPEN },
// Protocol Handlers
{ MAKE_KEY_NAME1("HTTP", DI), VAL_FILE_ICON },
{ MAKE_KEY_NAME1("HTTP", SOC), VAL_OPEN, OLD_VAL_OPEN },
{ MAKE_KEY_NAME1("HTTPS", DI), VAL_FILE_ICON },
{ MAKE_KEY_NAME1("HTTPS", SOC), VAL_OPEN, OLD_VAL_OPEN }
};
// The settings to disable DDE are separate from the default browser settings
// since they are only checked when PaleMoon is the default browser and if they
// are incorrect they are fixed without notifying the user.
static SETTING gDDESettings[] = {
// File Handler Class
{ MAKE_KEY_NAME1("Software\\Classes\\PaleMoonHTML", SOD) },
// Protocol Handler Class - for Vista and above
{ MAKE_KEY_NAME1("Software\\Classes\\PaleMoonURL", SOD) },
// Protocol Handlers
{ MAKE_KEY_NAME1("Software\\Classes\\FTP", SOD) },
{ MAKE_KEY_NAME1("Software\\Classes\\HTTP", SOD) },
{ MAKE_KEY_NAME1("Software\\Classes\\HTTPS", SOD) }
};
nsresult
GetHelperPath(nsAutoString& 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(NS_LITERAL_CSTRING("uninstall"));
NS_ENSURE_SUCCESS(rv, rv);
rv = appHelper->AppendNative(NS_LITERAL_CSTRING("helper.exe"));
NS_ENSURE_SUCCESS(rv, rv);
rv = appHelper->GetPath(aPath);
aPath.Insert(L'"', 0);
aPath.Append(L'"');
return rv;
}
nsresult
LaunchHelper(nsAutoString& aPath)
{
STARTUPINFOW si = {sizeof(si), 0};
PROCESS_INFORMATION pi = {0};
if (!CreateProcessW(nullptr, (LPWSTR)aPath.get(), nullptr, nullptr, FALSE,
0, nullptr, nullptr, &si, &pi)) {
return NS_ERROR_FAILURE;
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return NS_OK;
}
NS_IMETHODIMP
nsWindowsShellService::ShortcutMaintenance()
{
nsresult rv;
// XXX App ids were updated to a constant install path hash,
// XXX this code can be removed after a few upgrade cycles.
// Launch helper.exe so it can update the application user model ids on
// shortcuts in the user's taskbar and start menu. This keeps older pinned
// shortcuts grouped correctly after major updates. Note, we also do this
// through the upgrade installer script, however, this is the only place we
// have a chance to trap links created by users who do control the install/
// update process of the browser.
nsCOMPtr<nsIWinTaskbar> taskbarInfo =
do_GetService(NS_TASKBAR_CONTRACTID);
if (!taskbarInfo) // If we haven't built with win7 sdk features, this fails.
return NS_OK;
// Avoid if this isn't Win7+
bool isSupported = false;
taskbarInfo->GetAvailable(&isSupported);
if (!isSupported)
return NS_OK;
nsAutoString appId;
if (NS_FAILED(taskbarInfo->GetDefaultGroupId(appId)))
return NS_ERROR_UNEXPECTED;
NS_NAMED_LITERAL_CSTRING(prefName, "browser.taskbar.lastgroupid");
nsCOMPtr<nsIPrefBranch> prefs =
do_GetService(NS_PREFSERVICE_CONTRACTID);
if (!prefs)
return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsISupportsString> prefString;
rv = prefs->GetComplexValue(prefName.get(),
NS_GET_IID(nsISupportsString),
getter_AddRefs(prefString));
if (NS_SUCCEEDED(rv)) {
nsAutoString version;
prefString->GetData(version);
if (!version.IsEmpty() && version.Equals(appId)) {
// We're all good, get out of here.
return NS_OK;
}
}
// Update the version in prefs
prefString =
do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
if (NS_FAILED(rv))
return rv;
prefString->SetData(appId);
rv = prefs->SetComplexValue(prefName.get(),
NS_GET_IID(nsISupportsString),
prefString);
if (NS_FAILED(rv)) {
NS_WARNING("Couldn't set last user model id!");
return NS_ERROR_UNEXPECTED;
}
nsAutoString appHelperPath;
if (NS_FAILED(GetHelperPath(appHelperPath)))
return NS_ERROR_UNEXPECTED;
appHelperPath.AppendLiteral(" /UpdateShortcutAppUserModelIds");
return LaunchHelper(appHelperPath);
}
static bool
IsAARDefault(const RefPtr<IApplicationAssociationRegistration>& pAAR,
LPCWSTR aClassName)
{
// Make sure the Prog ID matches what we have
LPWSTR registeredApp;
bool isProtocol = *aClassName != L'.';
ASSOCIATIONTYPE queryType = isProtocol ? AT_URLPROTOCOL : AT_FILEEXTENSION;
HRESULT hr = pAAR->QueryCurrentDefault(aClassName, queryType, AL_EFFECTIVE,
&registeredApp);
if (FAILED(hr)) {
return false;
}
LPCWSTR progID = isProtocol ? L"ArcticFoxURL" : L"ArcticFoxHTML";
bool isDefault = !wcsicmp(registeredApp, progID);
CoTaskMemFree(registeredApp);
return isDefault;
}
static void
GetUserChoiceKeyName(LPCWSTR aClassName, bool aIsProtocol,
nsAString& aKeyName)
{
aKeyName.AssignLiteral(aIsProtocol
? "Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\"
: "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\");
aKeyName.Append(aClassName);
aKeyName.AppendLiteral("\\UserChoice");
}
static void
GetHashPrefName(LPCWSTR aClassName, nsACString& aPrefName)
{
aPrefName.AssignLiteral("browser.shell.associationHash.");
aPrefName.Append(NS_ConvertUTF16toUTF8(*aClassName == L'.' ? aClassName + 1
: aClassName));
}
static bool
SaveWin8RegistryHash(const RefPtr<IApplicationAssociationRegistration>& pAAR,
LPCWSTR aClassName)
{
bool isProtocol = *aClassName != L'.';
bool isDefault = IsAARDefault(pAAR, aClassName);
// We can save the value only if ArctiFox is the default.
if (!isDefault) {
return isDefault;
}
nsAutoString keyName;
GetUserChoiceKeyName(aClassName, isProtocol, keyName);
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1");
if (!regKey) {
return isDefault;
}
nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
keyName, nsIWindowsRegKey::ACCESS_READ);
if (NS_FAILED(rv)) {
return isDefault;
}
nsAutoString hash;
rv = regKey->ReadStringValue(NS_LITERAL_STRING("Hash"), hash);
if (NS_FAILED(rv)) {
return isDefault;
}
nsAutoCString prefName;
GetHashPrefName(aClassName, prefName);
SetPrefString(prefName, hash);
return isDefault;
}
static bool
RestoreWin8RegistryHash(const RefPtr<IApplicationAssociationRegistration>& pAAR,
LPCWSTR aClassName)
{
nsAutoCString prefName;
GetHashPrefName(aClassName, prefName);
nsAutoString hash;
if (!GetPrefString(prefName, hash)) {
return false;
}
bool isProtocol = *aClassName != L'.';
nsString progId = isProtocol ? NS_LITERAL_STRING("ArcticFoxURL")
: NS_LITERAL_STRING("ArcticFoxHTML");
nsAutoString keyName;
GetUserChoiceKeyName(aClassName, isProtocol, keyName);
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1");
if (!regKey) {
return false;
}
nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
keyName, nsIWindowsRegKey::ACCESS_READ);
if (NS_SUCCEEDED(rv)) {
nsAutoString currValue;
if (NS_SUCCEEDED(regKey->ReadStringValue(NS_LITERAL_STRING("Hash"),
currValue)) &&
currValue.Equals(hash) &&
NS_SUCCEEDED(regKey->ReadStringValue(NS_LITERAL_STRING("ProgId"),
currValue)) &&
currValue.Equals(progId)) {
// The value is already set.
return true;
}
// We need to close this explicitly because nsIWindowsRegKey::SetKey
// does not close the old key.
regKey->Close();
}
// We have to use the registry function directly because
// nsIWindowsRegKey::Create will only return NS_ERROR_FAILURE
// on failure.
HKEY theKey;
DWORD res = ::RegOpenKeyExW(HKEY_CURRENT_USER, keyName.get(), 0,
KEY_READ | KEY_SET_VALUE, &theKey);
if (REG_FAILED(res)) {
if (res != ERROR_ACCESS_DENIED && res != ERROR_FILE_NOT_FOUND) {
return false;
}
if (res == ERROR_ACCESS_DENIED) {
res = ::RegDeleteKeyW(HKEY_CURRENT_USER, keyName.get());
if (REG_FAILED(res)) {
return false;
}
}
res = ::RegCreateKeyExW(HKEY_CURRENT_USER, keyName.get(), 0,
nullptr, 0, KEY_READ | KEY_SET_VALUE,
nullptr, &theKey, nullptr);
if (REG_FAILED(res)) {
return false;
}
}
regKey->SetKey(theKey);
rv = regKey->WriteStringValue(NS_LITERAL_STRING("Hash"), hash);
if (NS_FAILED(rv)) {
return false;
}
rv = regKey->WriteStringValue(NS_LITERAL_STRING("ProgId"), progId);
if (NS_FAILED(rv)) {
return false;
}
return IsAARDefault(pAAR, aClassName);
}
static void
SaveWin8RegistryHashes(bool aCheckAllTypes, bool* aIsDefaultBrowser)
{
RefPtr<IApplicationAssociationRegistration> pAAR;
HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
nullptr,
CLSCTX_INPROC,
IID_IApplicationAssociationRegistration,
getter_AddRefs(pAAR));
if (FAILED(hr)) {
return;
}
bool res = SaveWin8RegistryHash(pAAR, L"http");
if (*aIsDefaultBrowser) {
*aIsDefaultBrowser = res;
}
SaveWin8RegistryHash(pAAR, L"https");
SaveWin8RegistryHash(pAAR, L"ftp");
res = SaveWin8RegistryHash(pAAR, L".html");
if (*aIsDefaultBrowser && aCheckAllTypes) {
*aIsDefaultBrowser = res;
}
SaveWin8RegistryHash(pAAR, L".htm");
SaveWin8RegistryHash(pAAR, L".shtml");
SaveWin8RegistryHash(pAAR, L".xhtml");
SaveWin8RegistryHash(pAAR, L".xht");
}
static bool
RestoreWin8RegistryHashes(bool aClaimAllTypes)
{
RefPtr<IApplicationAssociationRegistration> pAAR;
HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
nullptr,
CLSCTX_INPROC,
IID_IApplicationAssociationRegistration,
getter_AddRefs(pAAR));
if (FAILED(hr)) {
return false;
}
bool res = RestoreWin8RegistryHash(pAAR, L"http");
res = RestoreWin8RegistryHash(pAAR, L"https") && res;
RestoreWin8RegistryHash(pAAR, L"ftp");
bool res2 = RestoreWin8RegistryHash(pAAR, L".html");
res2 = RestoreWin8RegistryHash(pAAR, L".htm") && res2;
if (aClaimAllTypes) {
res = res && res2;
}
RestoreWin8RegistryHash(pAAR, L".shtml");
RestoreWin8RegistryHash(pAAR, L".xhtml");
RestoreWin8RegistryHash(pAAR, L".xht");
return res;
}
/*
* Query's the AAR for the default status.
* This only checks for PaleMoonURL and if aCheckAllTypes is set, then
* it also checks for PaleMoonHTML. Note that those ProgIDs are shared
* by all PaleMoon browsers.
*/
bool
nsWindowsShellService::IsDefaultBrowserVista(bool aCheckAllTypes,
bool* aIsDefaultBrowser)
{
RefPtr<IApplicationAssociationRegistration> pAAR;
HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
nullptr,
CLSCTX_INPROC,
IID_IApplicationAssociationRegistration,
getter_AddRefs(pAAR));
if (FAILED(hr)) {
return false;
}
if (aCheckAllTypes) {
BOOL res;
hr = pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE,
APP_REG_NAME,
&res);
*aIsDefaultBrowser = res;
} else if (!IsWin8OrLater()) {
*aIsDefaultBrowser = IsAARDefault(pAAR, L"http");
}
return true;
}
NS_IMETHODIMP
nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
bool aForAllTypes,
bool* aIsDefaultBrowser)
{
// Assume we're the default unless one of the several checks below tell us
// otherwise.
*aIsDefaultBrowser = true;
wchar_t exePath[MAX_BUF];
if (!::GetModuleFileNameW(0, exePath, MAX_BUF))
return NS_ERROR_FAILURE;
// Convert the path to a long path since GetModuleFileNameW returns the path
// that was used to launch PaleMoon which is not necessarily a long path.
if (!::GetLongPathNameW(exePath, exePath, MAX_BUF))
return NS_ERROR_FAILURE;
nsAutoString appLongPath(exePath);
HKEY theKey;
DWORD res;
nsresult rv;
wchar_t currValue[MAX_BUF];
SETTING* settings = gSettings;
if (!aForAllTypes && IsWin8OrLater()) {
// Skip over the file handler check
settings++;
}
SETTING* end = gSettings + sizeof(gSettings) / sizeof(SETTING);
for (; settings < end; ++settings) {
NS_ConvertUTF8toUTF16 keyName(settings->keyName);
NS_ConvertUTF8toUTF16 valueData(settings->valueData);
int32_t offset = valueData.Find("%APPPATH%");
valueData.Replace(offset, 9, appLongPath);
rv = OpenKeyForReading(HKEY_CLASSES_ROOT, keyName, &theKey);
if (NS_FAILED(rv)) {
*aIsDefaultBrowser = false;
return NS_OK;
}
::ZeroMemory(currValue, sizeof(currValue));
DWORD len = sizeof currValue;
res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr,
(LPBYTE)currValue, &len);
// Close the key that was opened.
::RegCloseKey(theKey);
if (REG_FAILED(res) ||
_wcsicmp(valueData.get(), currValue)) {
// Key wasn't set or was set to something other than our registry entry.
NS_ConvertUTF8toUTF16 oldValueData(settings->oldValueData);
offset = oldValueData.Find("%APPPATH%");
oldValueData.Replace(offset, 9, appLongPath);
// The current registry value doesn't match the current or the old format.
if (_wcsicmp(oldValueData.get(), currValue)) {
*aIsDefaultBrowser = false;
return NS_OK;
}
res = ::RegOpenKeyExW(HKEY_CLASSES_ROOT, PromiseFlatString(keyName).get(),
0, KEY_SET_VALUE, &theKey);
if (REG_FAILED(res)) {
// If updating the open command fails try to update it using the helper
// application when setting PaleMoon as the default browser.
*aIsDefaultBrowser = false;
return NS_OK;
}
const nsString &flatValue = PromiseFlatString(valueData);
res = ::RegSetValueExW(theKey, L"", 0, REG_SZ,
(const BYTE *) flatValue.get(),
(flatValue.Length() + 1) * sizeof(char16_t));
// Close the key that was created.
::RegCloseKey(theKey);
if (REG_FAILED(res)) {
// If updating the open command fails try to update it using the helper
// application when setting PaleMoon as the default browser.
*aIsDefaultBrowser = false;
return NS_OK;
}
}
}
// Only check if PaleMoon is the default browser on Vista and above if the
// previous checks show that PaleMoon is the default browser.
if (*aIsDefaultBrowser) {
if (IsWin8OrLater()) {
SaveWin8RegistryHashes(aForAllTypes, aIsDefaultBrowser);
}
}
// To handle the case where DDE isn't disabled due for a user because there
// account didn't perform a PaleMoon update this will check if PaleMoon is the
// default browser and if dde is disabled for each handler
// and if it isn't disable it. When PaleMoon is not the default browser the
// helper application will disable dde for each handler.
if (*aIsDefaultBrowser && aForAllTypes) {
// Check ftp settings
end = gDDESettings + sizeof(gDDESettings) / sizeof(SETTING);
for (settings = gDDESettings; settings < end; ++settings) {
NS_ConvertUTF8toUTF16 keyName(settings->keyName);
rv = OpenKeyForReading(HKEY_CURRENT_USER, keyName, &theKey);
if (NS_FAILED(rv)) {
::RegCloseKey(theKey);
// If disabling DDE fails try to disable it using the helper
// application when setting PaleMoon as the default browser.
*aIsDefaultBrowser = false;
return NS_OK;
}
::ZeroMemory(currValue, sizeof(currValue));
DWORD len = sizeof currValue;
res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr,
(LPBYTE)currValue, &len);
// Close the key that was opened.
::RegCloseKey(theKey);
if (REG_FAILED(res) || char16_t('\0') != *currValue) {
// Key wasn't set or was set to something other than our registry entry.
// Delete the key along with all of its childrean and then recreate it.
const nsString &flatName = PromiseFlatString(keyName);
::SHDeleteKeyW(HKEY_CURRENT_USER, flatName.get());
res = ::RegCreateKeyExW(HKEY_CURRENT_USER, flatName.get(), 0, nullptr,
REG_OPTION_NON_VOLATILE, KEY_SET_VALUE,
nullptr, &theKey, nullptr);
if (REG_FAILED(res)) {
// If disabling DDE fails try to disable it using the helper
// application when setting PaleMoon as the default browser.
*aIsDefaultBrowser = false;
return NS_OK;
}
res = ::RegSetValueExW(theKey, L"", 0, REG_SZ, (const BYTE *) L"",
sizeof(char16_t));
// Close the key that was created.
::RegCloseKey(theKey);
if (REG_FAILED(res)) {
// If disabling DDE fails try to disable it using the helper
// application when setting PaleMoon as the default browser.
*aIsDefaultBrowser = false;
return NS_OK;
}
}
}
// Update the FTP protocol handler's shell open command if it is the old
// format.
res = ::RegOpenKeyExW(HKEY_CURRENT_USER, FTP_SOC, 0, KEY_ALL_ACCESS,
&theKey);
// Don't update the FTP protocol handler's shell open command when opening
// its registry key fails under HKCU since it most likely doesn't exist.
if (NS_FAILED(rv)) {
return NS_OK;
}
NS_ConvertUTF8toUTF16 oldValueOpen(OLD_VAL_OPEN);
int32_t offset = oldValueOpen.Find("%APPPATH%");
oldValueOpen.Replace(offset, 9, appLongPath);
::ZeroMemory(currValue, sizeof(currValue));
DWORD len = sizeof currValue;
res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr, (LPBYTE)currValue,
&len);
// Don't update the FTP protocol handler's shell open command when the
// current registry value doesn't exist or matches the old format.
if (REG_FAILED(res) ||
_wcsicmp(oldValueOpen.get(), currValue)) {
::RegCloseKey(theKey);
return NS_OK;
}
NS_ConvertUTF8toUTF16 valueData(VAL_OPEN);
valueData.Replace(offset, 9, appLongPath);
const nsString &flatValue = PromiseFlatString(valueData);
res = ::RegSetValueExW(theKey, L"", 0, REG_SZ,
(const BYTE *) flatValue.get(),
(flatValue.Length() + 1) * sizeof(char16_t));
// Close the key that was created.
::RegCloseKey(theKey);
// If updating the FTP protocol handlers shell open command fails try to
// update it using the helper application when setting PaleMoon as the
// default browser.
if (REG_FAILED(res)) {
*aIsDefaultBrowser = false;
}
}
return NS_OK;
}
static nsresult
DynSHOpenWithDialog(HWND hwndParent, const OPENASINFO *poainfo)
{
// shell32.dll is in the knownDLLs list so will always be loaded from the
// system32 directory.
static const wchar_t kSehllLibraryName[] = L"shell32.dll";
HMODULE shellDLL = ::LoadLibraryW(kSehllLibraryName);
if (!shellDLL) {
return NS_ERROR_FAILURE;
}
decltype(SHOpenWithDialog)* SHOpenWithDialogFn =
(decltype(SHOpenWithDialog)*) GetProcAddress(shellDLL, "SHOpenWithDialog");
if (!SHOpenWithDialogFn) {
return NS_ERROR_FAILURE;
}
nsresult rv =
SUCCEEDED(SHOpenWithDialogFn(hwndParent, poainfo)) ? NS_OK :
NS_ERROR_FAILURE;
FreeLibrary(shellDLL);
return rv;
}
nsresult
nsWindowsShellService::LaunchControlPanelDefaultsSelectionUI()
{
IApplicationAssociationRegistrationUI* pAARUI;
HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistrationUI,
NULL,
CLSCTX_INPROC,
IID_IApplicationAssociationRegistrationUI,
(void**)&pAARUI);
if (SUCCEEDED(hr)) {
hr = pAARUI->LaunchAdvancedAssociationUI(APP_REG_NAME);
pAARUI->Release();
}
return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
}
nsresult
nsWindowsShellService::LaunchModernSettingsDialogDefaultApps()
{
IApplicationActivationManager* pActivator;
HRESULT hr = CoCreateInstance(CLSID_ApplicationActivationManager,
nullptr,
CLSCTX_INPROC,
IID_IApplicationActivationManager,
(void**)&pActivator);
if (SUCCEEDED(hr)) {
DWORD pid;
hr = pActivator->ActivateApplication(
L"windows.immersivecontrolpanel_cw5n1h2txyewy"
L"!microsoft.windows.immersivecontrolpanel",
L"page=SettingsPageAppsDefaults", AO_NONE, &pid);
pActivator->Release();
return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult
nsWindowsShellService::InvokeHTTPOpenAsVerb()
{
nsCOMPtr<nsIURLFormatter> formatter(
do_GetService("@mozilla.org/toolkit/URLFormatterService;1"));
if (!formatter) {
return NS_ERROR_UNEXPECTED;
}
nsString urlStr;
nsresult rv = formatter->FormatURLPref(
NS_LITERAL_STRING("app.support.baseURL"), urlStr);
if (NS_FAILED(rv)) {
return rv;
}
if (!StringBeginsWith(urlStr, NS_LITERAL_STRING("https://"))) {
return NS_ERROR_FAILURE;
}
urlStr.AppendLiteral("win10-default-browser");
SHELLEXECUTEINFOW seinfo = { sizeof(SHELLEXECUTEINFOW) };
seinfo.lpVerb = L"openas";
seinfo.lpFile = urlStr.get();
seinfo.nShow = SW_SHOWNORMAL;
if (!ShellExecuteExW(&seinfo)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult
nsWindowsShellService::LaunchHTTPHandlerPane()
{
OPENASINFO info;
info.pcszFile = L"http";
info.pcszClass = nullptr;
info.oaifInFlags = OAIF_FORCE_REGISTRATION |
OAIF_URL_PROTOCOL |
OAIF_REGISTER_EXT;
return DynSHOpenWithDialog(nullptr, &info);
}
NS_IMETHODIMP
nsWindowsShellService::SetDefaultBrowser(bool aClaimAllTypes, bool aForAllUsers)
{
nsAutoString appHelperPath;
if (NS_FAILED(GetHelperPath(appHelperPath)))
return NS_ERROR_FAILURE;
if (aForAllUsers) {
appHelperPath.AppendLiteral(" /SetAsDefaultAppGlobal");
} else {
appHelperPath.AppendLiteral(" /SetAsDefaultAppUser");
}
nsresult rv = LaunchHelper(appHelperPath);
if (NS_SUCCEEDED(rv) && IsWin8OrLater() &&
!RestoreWin8RegistryHashes(aClaimAllTypes)) {
if (aClaimAllTypes) {
if (IsWin10OrLater()) {
rv = LaunchModernSettingsDialogDefaultApps();
} else {
rv = LaunchControlPanelDefaultsSelectionUI();
}
// The above call should never really fail, but just in case
// fall back to showing the HTTP association screen only.
if (NS_FAILED(rv)) {
if (IsWin10OrLater()) {
rv = InvokeHTTPOpenAsVerb();
} else {
rv = LaunchHTTPHandlerPane();
}
}
} else {
// Windows 10 blocks attempts to load the
// HTTP Handler association dialog.
if (IsWin10OrLater()) {
rv = LaunchModernSettingsDialogDefaultApps();
} else {
rv = LaunchHTTPHandlerPane();
}
// The above call should never really fail, but just in case
// fall back to showing control panel for all defaults
if (NS_FAILED(rv)) {
rv = LaunchControlPanelDefaultsSelectionUI();
}
bool isDefault;
SaveWin8RegistryHashes(aClaimAllTypes, &isDefault);
}
}
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
if (prefs) {
(void) prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, true);
// Reset the number of times the dialog should be shown
// before it is silenced.
(void) prefs->SetIntPref(PREF_DEFAULTBROWSERCHECKCOUNT, 0);
}
return rv;
}
static nsresult
WriteBitmap(nsIFile* aFile, imgIContainer* aImage)
{
nsresult rv;
RefPtr<SourceSurface> surface =
aImage->GetFrame(imgIContainer::FRAME_FIRST,
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);
uint32_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;
do {
i -= map.mStride;
stream->Write(((const char*)map.mData) + i, bytesPerRow, &written);
if (written == bytesPerRow) {
rv = NS_OK;
} else {
rv = NS_ERROR_FAILURE;
break;
}
} while (i != 0);
}
}
stream->Close();
}
dataSurface->Unmap();
return rv;
}
NS_IMETHODIMP
nsWindowsShellService::SetDesktopBackground(nsIDOMElement* aElement,
int32_t aPosition)
{
nsresult rv;
nsCOMPtr<imgIContainer> container;
nsCOMPtr<nsIDOMHTMLImageElement> imgElement(do_QueryInterface(aElement));
if (!imgElement) {
// XXX write background loading stuff!
return NS_ERROR_NOT_AVAILABLE;
}
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;
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"
nsString fileLeafName;
rv = shellBundle->GetStringFromName
(MOZ_UTF16("desktopBackgroundLeafNameWin"),
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\PaleMoon\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> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
NS_LITERAL_STRING("Control Panel\\Desktop"),
nsIWindowsRegKey::ACCESS_SET_VALUE);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoString tile;
nsAutoString style;
switch (aPosition) {
case BACKGROUND_TILE:
style.AssignLiteral("0");
tile.AssignLiteral("1");
break;
case BACKGROUND_CENTER:
style.AssignLiteral("0");
tile.AssignLiteral("0");
break;
case BACKGROUND_STRETCH:
style.AssignLiteral("2");
tile.AssignLiteral("0");
break;
case BACKGROUND_FILL:
style.AssignLiteral("10");
tile.AssignLiteral("0");
break;
case BACKGROUND_FIT:
style.AssignLiteral("6");
tile.AssignLiteral("0");
break;
}
rv = regKey->WriteStringValue(NS_LITERAL_STRING("TileWallpaper"), tile);
NS_ENSURE_SUCCESS(rv, rv);
rv = regKey->WriteStringValue(NS_LITERAL_STRING("WallpaperStyle"), style);
NS_ENSURE_SUCCESS(rv, rv);
rv = regKey->Close();
NS_ENSURE_SUCCESS(rv, rv);
::SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, (PVOID)path.get(),
SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
}
return rv;
}
NS_IMETHODIMP
nsWindowsShellService::OpenApplication(int32_t 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;
wchar_t 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);
int32_t end = path.Length();
int32_t 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(nullptr, (LPWSTR)path.get(), nullptr,
nullptr, FALSE, 0, nullptr, nullptr,
&si, &pi);
if (!success)
return NS_ERROR_FAILURE;
return NS_OK;
}
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 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);
nsresult rv;
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
NS_LITERAL_STRING("Control Panel\\Colors"),
nsIWindowsRegKey::ACCESS_SET_VALUE);
NS_ENSURE_SUCCESS(rv, rv);
wchar_t rgb[12];
_snwprintf(rgb, 12, L"%u %u %u", r, g, b);
rv = regKey->WriteStringValue(NS_LITERAL_STRING("Background"),
nsDependentString(rgb));
NS_ENSURE_SUCCESS(rv, rv);
return regKey->Close();
}
nsWindowsShellService::nsWindowsShellService()
{
}
nsWindowsShellService::~nsWindowsShellService()
{
}
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(aURI);
const char* specStr = spec.get();
return process->Run(false, &specStr, 1);
}
NS_IMETHODIMP
nsWindowsShellService::GetDefaultFeedReader(nsIFile** _retval)
{
*_retval = nullptr;
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<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;
}