1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "nsWindowsShellService.h"
7 
8 #include "imgIContainer.h"
9 #include "imgIRequest.h"
10 #include "mozilla/gfx/2D.h"
11 #include "mozilla/RefPtr.h"
12 #include "nsIDOMElement.h"
13 #include "nsIDOMHTMLImageElement.h"
14 #include "nsIImageLoadingContent.h"
15 #include "nsIOutputStream.h"
16 #include "nsIPrefService.h"
17 #include "nsIPrefLocalizedString.h"
18 #include "nsIServiceManager.h"
19 #include "nsIStringBundle.h"
20 #include "nsNetUtil.h"
21 #include "nsServiceManagerUtils.h"
22 #include "nsShellService.h"
23 #include "nsIProcess.h"
24 #include "nsICategoryManager.h"
25 #include "nsBrowserCompsCID.h"
26 #include "nsDirectoryServiceUtils.h"
27 #include "nsAppDirectoryServiceDefs.h"
28 #include "nsDirectoryServiceDefs.h"
29 #include "nsIWindowsRegKey.h"
30 #include "nsUnicharUtils.h"
31 #include "nsIWinTaskbar.h"
32 #include "nsISupportsPrimitives.h"
33 #include "nsIURLFormatter.h"
34 #include "nsThreadUtils.h"
35 #include "nsXULAppAPI.h"
36 #include "mozilla/WindowsVersion.h"
37 
38 #include "windows.h"
39 #include "shellapi.h"
40 
41 #ifdef _WIN32_WINNT
42 #undef _WIN32_WINNT
43 #endif
44 #define _WIN32_WINNT 0x0600
45 #define INITGUID
46 #undef NTDDI_VERSION
47 #define NTDDI_VERSION NTDDI_WIN8
48 // Needed for access to IApplicationActivationManager
49 #include <shlobj.h>
50 
51 #include <mbstring.h>
52 #include <shlwapi.h>
53 
54 #include <lm.h>
55 #undef ACCESS_READ
56 
57 #ifndef MAX_BUF
58 #define MAX_BUF 4096
59 #endif
60 
61 #define REG_SUCCEEDED(val) \
62   (val == ERROR_SUCCESS)
63 
64 #define REG_FAILED(val) \
65   (val != ERROR_SUCCESS)
66 
67 #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
68 
69 using mozilla::IsWin8OrLater;
70 using namespace mozilla;
71 using namespace mozilla::gfx;
72 
NS_IMPL_ISUPPORTS(nsWindowsShellService,nsIWindowsShellService,nsIShellService)73 NS_IMPL_ISUPPORTS(nsWindowsShellService, nsIWindowsShellService, nsIShellService)
74 
75 static nsresult
76 OpenKeyForReading(HKEY aKeyRoot, const nsAString& aKeyName, HKEY* aKey)
77 {
78   const nsString &flatName = PromiseFlatString(aKeyName);
79 
80   DWORD res = ::RegOpenKeyExW(aKeyRoot, flatName.get(), 0, KEY_READ, aKey);
81   switch (res) {
82   case ERROR_SUCCESS:
83     break;
84   case ERROR_ACCESS_DENIED:
85     return NS_ERROR_FILE_ACCESS_DENIED;
86   case ERROR_FILE_NOT_FOUND:
87     return NS_ERROR_NOT_AVAILABLE;
88   }
89 
90   return NS_OK;
91 }
92 
93 ///////////////////////////////////////////////////////////////////////////////
94 // Default Browser Registry Settings
95 //
96 // The setting of these values are made by an external binary since writing
97 // these values may require elevation.
98 //
99 // - File Extension Mappings
100 //   -----------------------
101 //   The following file extensions:
102 //    .htm .html .shtml .xht .xhtml
103 //   are mapped like so:
104 //
105 //   HKCU\SOFTWARE\Classes\.<ext>\      (default)         REG_SZ     FirefoxHTML
106 //
107 //   as aliases to the class:
108 //
109 //   HKCU\SOFTWARE\Classes\FirefoxHTML\
110 //     DefaultIcon                      (default)         REG_SZ     <apppath>,1
111 //     shell\open\command               (default)         REG_SZ     <apppath> -osint -url "%1"
112 //     shell\open\ddeexec               (default)         REG_SZ     <empty string>
113 //
114 // - Windows Vista and above Protocol Handler
115 //
116 //   HKCU\SOFTWARE\Classes\FirefoxURL\  (default)         REG_SZ     <appname> URL
117 //                                      EditFlags         REG_DWORD  2
118 //                                      FriendlyTypeName  REG_SZ     <appname> URL
119 //     DefaultIcon                      (default)         REG_SZ     <apppath>,1
120 //     shell\open\command               (default)         REG_SZ     <apppath> -osint -url "%1"
121 //     shell\open\ddeexec               (default)         REG_SZ     <empty string>
122 //
123 // - Protocol Mappings
124 //   -----------------
125 //   The following protocols:
126 //    HTTP, HTTPS, FTP
127 //   are mapped like so:
128 //
129 //   HKCU\SOFTWARE\Classes\<protocol>\
130 //     DefaultIcon                      (default)         REG_SZ     <apppath>,1
131 //     shell\open\command               (default)         REG_SZ     <apppath> -osint -url "%1"
132 //     shell\open\ddeexec               (default)         REG_SZ     <empty string>
133 //
134 // - Windows Start Menu (XP SP1 and newer)
135 //   -------------------------------------------------
136 //   The following keys are set to make Firefox appear in the Start Menu as the
137 //   browser:
138 //
139 //   HKCU\SOFTWARE\Clients\StartMenuInternet\FIREFOX.EXE\
140 //                                      (default)         REG_SZ     <appname>
141 //     DefaultIcon                      (default)         REG_SZ     <apppath>,0
142 //     InstallInfo                      HideIconsCommand  REG_SZ     <uninstpath> /HideShortcuts
143 //     InstallInfo                      IconsVisible      REG_DWORD  1
144 //     InstallInfo                      ReinstallCommand  REG_SZ     <uninstpath> /SetAsDefaultAppGlobal
145 //     InstallInfo                      ShowIconsCommand  REG_SZ     <uninstpath> /ShowShortcuts
146 //     shell\open\command               (default)         REG_SZ     <apppath>
147 //     shell\properties                 (default)         REG_SZ     <appname> &Options
148 //     shell\properties\command         (default)         REG_SZ     <apppath> -preferences
149 //     shell\safemode                   (default)         REG_SZ     <appname> &Safe Mode
150 //     shell\safemode\command           (default)         REG_SZ     <apppath> -safe-mode
151 //
152 
153 // The values checked are all default values so the value name is not needed.
154 typedef struct {
155   const char* keyName;
156   const char* valueData;
157   const char* oldValueData;
158 } SETTING;
159 
160 #define APP_REG_NAME L"Firefox"
161 #define VAL_FILE_ICON "%APPPATH%,1"
162 #define VAL_OPEN "\"%APPPATH%\" -osint -url \"%1\""
163 #define OLD_VAL_OPEN "\"%APPPATH%\" -requestPending -osint -url \"%1\""
164 #define DI "\\DefaultIcon"
165 #define SOC "\\shell\\open\\command"
166 #define SOD "\\shell\\open\\ddeexec"
167 // Used for updating the FTP protocol handler's shell open command under HKCU.
168 #define FTP_SOC L"Software\\Classes\\ftp\\shell\\open\\command"
169 
170 #define MAKE_KEY_NAME1(PREFIX, MID) \
171   PREFIX MID
172 
173 // The DefaultIcon registry key value should never be used when checking if
174 // Firefox is the default browser for file handlers since other applications
175 // (e.g. MS Office) may modify the DefaultIcon registry key value to add Icon
176 // Handlers. see http://msdn2.microsoft.com/en-us/library/aa969357.aspx for
177 // more info. The FTP protocol is not checked so advanced users can set the FTP
178 // handler to another application and still have Firefox check if it is the
179 // default HTTP and HTTPS handler.
180 // *** Do not add additional checks here unless you skip them when aForAllTypes
181 // is false below***.
182 static SETTING gSettings[] = {
183   // File Handler Class
184   // ***keep this as the first entry because when aForAllTypes is not set below
185   // it will skip over this check.***
186   { MAKE_KEY_NAME1("FirefoxHTML", SOC), VAL_OPEN, OLD_VAL_OPEN },
187 
188   // Protocol Handler Class - for Vista and above
189   { MAKE_KEY_NAME1("FirefoxURL", SOC), VAL_OPEN, OLD_VAL_OPEN },
190 
191   // Protocol Handlers
192   { MAKE_KEY_NAME1("HTTP", DI), VAL_FILE_ICON },
193   { MAKE_KEY_NAME1("HTTP", SOC), VAL_OPEN, OLD_VAL_OPEN },
194   { MAKE_KEY_NAME1("HTTPS", DI), VAL_FILE_ICON },
195   { MAKE_KEY_NAME1("HTTPS", SOC), VAL_OPEN, OLD_VAL_OPEN }
196 };
197 
198 // The settings to disable DDE are separate from the default browser settings
199 // since they are only checked when Firefox is the default browser and if they
200 // are incorrect they are fixed without notifying the user.
201 static SETTING gDDESettings[] = {
202   // File Handler Class
203   { MAKE_KEY_NAME1("Software\\Classes\\FirefoxHTML", SOD) },
204 
205   // Protocol Handler Class - for Vista and above
206   { MAKE_KEY_NAME1("Software\\Classes\\FirefoxURL", SOD) },
207 
208   // Protocol Handlers
209   { MAKE_KEY_NAME1("Software\\Classes\\FTP", SOD) },
210   { MAKE_KEY_NAME1("Software\\Classes\\HTTP", SOD) },
211   { MAKE_KEY_NAME1("Software\\Classes\\HTTPS", SOD) }
212 };
213 
214 nsresult
GetHelperPath(nsAutoString & aPath)215 GetHelperPath(nsAutoString& aPath)
216 {
217   nsresult rv;
218   nsCOMPtr<nsIProperties> directoryService =
219     do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
220   NS_ENSURE_SUCCESS(rv, rv);
221 
222   nsCOMPtr<nsIFile> appHelper;
223   rv = directoryService->Get(XRE_EXECUTABLE_FILE,
224                              NS_GET_IID(nsIFile),
225                              getter_AddRefs(appHelper));
226   NS_ENSURE_SUCCESS(rv, rv);
227 
228   rv = appHelper->SetNativeLeafName(NS_LITERAL_CSTRING("uninstall"));
229   NS_ENSURE_SUCCESS(rv, rv);
230 
231   rv = appHelper->AppendNative(NS_LITERAL_CSTRING("helper.exe"));
232   NS_ENSURE_SUCCESS(rv, rv);
233 
234   rv = appHelper->GetPath(aPath);
235 
236   aPath.Insert(L'"', 0);
237   aPath.Append(L'"');
238   return rv;
239 }
240 
241 nsresult
LaunchHelper(nsAutoString & aPath)242 LaunchHelper(nsAutoString& aPath)
243 {
244   STARTUPINFOW si = {sizeof(si), 0};
245   PROCESS_INFORMATION pi = {0};
246 
247   if (!CreateProcessW(nullptr, (LPWSTR)aPath.get(), nullptr, nullptr, FALSE,
248                       0, nullptr, nullptr, &si, &pi)) {
249     return NS_ERROR_FAILURE;
250   }
251 
252   CloseHandle(pi.hProcess);
253   CloseHandle(pi.hThread);
254   return NS_OK;
255 }
256 
257 NS_IMETHODIMP
ShortcutMaintenance()258 nsWindowsShellService::ShortcutMaintenance()
259 {
260   nsresult rv;
261 
262   // XXX App ids were updated to a constant install path hash,
263   // XXX this code can be removed after a few upgrade cycles.
264 
265   // Launch helper.exe so it can update the application user model ids on
266   // shortcuts in the user's taskbar and start menu. This keeps older pinned
267   // shortcuts grouped correctly after major updates. Note, we also do this
268   // through the upgrade installer script, however, this is the only place we
269   // have a chance to trap links created by users who do control the install/
270   // update process of the browser.
271 
272   nsCOMPtr<nsIWinTaskbar> taskbarInfo =
273     do_GetService(NS_TASKBAR_CONTRACTID);
274   if (!taskbarInfo) // If we haven't built with win7 sdk features, this fails.
275     return NS_OK;
276 
277   // Avoid if this isn't Win7+
278   bool isSupported = false;
279   taskbarInfo->GetAvailable(&isSupported);
280   if (!isSupported)
281     return NS_OK;
282 
283   nsAutoString appId;
284   if (NS_FAILED(taskbarInfo->GetDefaultGroupId(appId)))
285     return NS_ERROR_UNEXPECTED;
286 
287   NS_NAMED_LITERAL_CSTRING(prefName, "browser.taskbar.lastgroupid");
288   nsCOMPtr<nsIPrefBranch> prefs =
289     do_GetService(NS_PREFSERVICE_CONTRACTID);
290   if (!prefs)
291     return NS_ERROR_UNEXPECTED;
292 
293   nsCOMPtr<nsISupportsString> prefString;
294   rv = prefs->GetComplexValue(prefName.get(),
295                               NS_GET_IID(nsISupportsString),
296                               getter_AddRefs(prefString));
297   if (NS_SUCCEEDED(rv)) {
298     nsAutoString version;
299     prefString->GetData(version);
300     if (!version.IsEmpty() && version.Equals(appId)) {
301       // We're all good, get out of here.
302       return NS_OK;
303     }
304   }
305   // Update the version in prefs
306   prefString =
307     do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
308   if (NS_FAILED(rv))
309     return rv;
310 
311   prefString->SetData(appId);
312   rv = prefs->SetComplexValue(prefName.get(),
313                               NS_GET_IID(nsISupportsString),
314                               prefString);
315   if (NS_FAILED(rv)) {
316     NS_WARNING("Couldn't set last user model id!");
317     return NS_ERROR_UNEXPECTED;
318   }
319 
320   nsAutoString appHelperPath;
321   if (NS_FAILED(GetHelperPath(appHelperPath)))
322     return NS_ERROR_UNEXPECTED;
323 
324   appHelperPath.AppendLiteral(" /UpdateShortcutAppUserModelIds");
325 
326   return LaunchHelper(appHelperPath);
327 }
328 
329 static bool
IsAARDefault(const RefPtr<IApplicationAssociationRegistration> & pAAR,LPCWSTR aClassName)330 IsAARDefault(const RefPtr<IApplicationAssociationRegistration>& pAAR,
331              LPCWSTR aClassName)
332 {
333   // Make sure the Prog ID matches what we have
334   LPWSTR registeredApp;
335   bool isProtocol = *aClassName != L'.';
336   ASSOCIATIONTYPE queryType = isProtocol ? AT_URLPROTOCOL : AT_FILEEXTENSION;
337   HRESULT hr = pAAR->QueryCurrentDefault(aClassName, queryType, AL_EFFECTIVE,
338                                          &registeredApp);
339   if (FAILED(hr)) {
340     return false;
341   }
342 
343   LPCWSTR progID = isProtocol ? L"FirefoxURL" : L"FirefoxHTML";
344   bool isDefault = !wcsicmp(registeredApp, progID);
345   CoTaskMemFree(registeredApp);
346 
347   return isDefault;
348 }
349 
350 static void
IsDefaultBrowserWin8(bool aCheckAllTypes,bool * aIsDefaultBrowser)351 IsDefaultBrowserWin8(bool aCheckAllTypes, bool* aIsDefaultBrowser)
352 {
353   RefPtr<IApplicationAssociationRegistration> pAAR;
354   HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
355                                 nullptr,
356                                 CLSCTX_INPROC,
357                                 IID_IApplicationAssociationRegistration,
358                                 getter_AddRefs(pAAR));
359   if (FAILED(hr)) {
360     return;
361   }
362 
363   bool res = IsAARDefault(pAAR, L"http");
364   if (*aIsDefaultBrowser) {
365     *aIsDefaultBrowser = res;
366   }
367   res = IsAARDefault(pAAR, L".html");
368   if (*aIsDefaultBrowser && aCheckAllTypes) {
369     *aIsDefaultBrowser = res;
370   }
371 }
372 
373 /*
374  * Query's the AAR for the default status.
375  * This only checks for FirefoxURL and if aCheckAllTypes is set, then
376  * it also checks for FirefoxHTML.  Note that those ProgIDs are shared
377  * by all Firefox browsers.
378 */
379 bool
IsDefaultBrowserVista(bool aCheckAllTypes,bool * aIsDefaultBrowser)380 nsWindowsShellService::IsDefaultBrowserVista(bool aCheckAllTypes,
381                                              bool* aIsDefaultBrowser)
382 {
383   RefPtr<IApplicationAssociationRegistration> pAAR;
384   HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
385                                 nullptr,
386                                 CLSCTX_INPROC,
387                                 IID_IApplicationAssociationRegistration,
388                                 getter_AddRefs(pAAR));
389   if (FAILED(hr)) {
390     return false;
391   }
392 
393   if (aCheckAllTypes) {
394     BOOL res;
395     hr = pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE,
396                                     APP_REG_NAME,
397                                     &res);
398     *aIsDefaultBrowser = res;
399   } else if (!IsWin8OrLater()) {
400     *aIsDefaultBrowser = IsAARDefault(pAAR, L"http");
401   }
402 
403   return true;
404 }
405 
406 NS_IMETHODIMP
IsDefaultBrowser(bool aStartupCheck,bool aForAllTypes,bool * aIsDefaultBrowser)407 nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
408                                         bool aForAllTypes,
409                                         bool* aIsDefaultBrowser)
410 {
411   // Assume we're the default unless one of the several checks below tell us
412   // otherwise.
413   *aIsDefaultBrowser = true;
414 
415   wchar_t exePath[MAX_BUF];
416   if (!::GetModuleFileNameW(0, exePath, MAX_BUF))
417     return NS_ERROR_FAILURE;
418 
419   // Convert the path to a long path since GetModuleFileNameW returns the path
420   // that was used to launch Firefox which is not necessarily a long path.
421   if (!::GetLongPathNameW(exePath, exePath, MAX_BUF))
422     return NS_ERROR_FAILURE;
423 
424   nsAutoString appLongPath(exePath);
425 
426   HKEY theKey;
427   DWORD res;
428   nsresult rv;
429   wchar_t currValue[MAX_BUF];
430 
431   SETTING* settings = gSettings;
432   if (!aForAllTypes && IsWin8OrLater()) {
433     // Skip over the file handler check
434     settings++;
435   }
436 
437   SETTING* end = gSettings + sizeof(gSettings) / sizeof(SETTING);
438 
439   for (; settings < end; ++settings) {
440     NS_ConvertUTF8toUTF16 keyName(settings->keyName);
441     NS_ConvertUTF8toUTF16 valueData(settings->valueData);
442     int32_t offset = valueData.Find("%APPPATH%");
443     valueData.Replace(offset, 9, appLongPath);
444 
445     rv = OpenKeyForReading(HKEY_CLASSES_ROOT, keyName, &theKey);
446     if (NS_FAILED(rv)) {
447       *aIsDefaultBrowser = false;
448       return NS_OK;
449     }
450 
451     ::ZeroMemory(currValue, sizeof(currValue));
452     DWORD len = sizeof currValue;
453     res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr,
454                              (LPBYTE)currValue, &len);
455     // Close the key that was opened.
456     ::RegCloseKey(theKey);
457     if (REG_FAILED(res) ||
458         _wcsicmp(valueData.get(), currValue)) {
459       // Key wasn't set or was set to something other than our registry entry.
460       NS_ConvertUTF8toUTF16 oldValueData(settings->oldValueData);
461       offset = oldValueData.Find("%APPPATH%");
462       oldValueData.Replace(offset, 9, appLongPath);
463       // The current registry value doesn't match the current or the old format.
464       if (_wcsicmp(oldValueData.get(), currValue)) {
465         *aIsDefaultBrowser = false;
466         return NS_OK;
467       }
468 
469       res = ::RegOpenKeyExW(HKEY_CLASSES_ROOT, keyName.get(),
470                             0, KEY_SET_VALUE, &theKey);
471       if (REG_FAILED(res)) {
472         // If updating the open command fails try to update it using the helper
473         // application when setting Firefox as the default browser.
474         *aIsDefaultBrowser = false;
475         return NS_OK;
476       }
477 
478       res = ::RegSetValueExW(theKey, L"", 0, REG_SZ,
479                              (const BYTE *) valueData.get(),
480                              (valueData.Length() + 1) * sizeof(char16_t));
481       // Close the key that was created.
482       ::RegCloseKey(theKey);
483       if (REG_FAILED(res)) {
484         // If updating the open command fails try to update it using the helper
485         // application when setting Firefox as the default browser.
486         *aIsDefaultBrowser = false;
487         return NS_OK;
488       }
489     }
490   }
491 
492   // Only check if Firefox is the default browser on Vista and above if the
493   // previous checks show that Firefox is the default browser.
494   if (*aIsDefaultBrowser) {
495     IsDefaultBrowserVista(aForAllTypes, aIsDefaultBrowser);
496     if (IsWin8OrLater()) {
497       IsDefaultBrowserWin8(aForAllTypes, aIsDefaultBrowser);
498     }
499   }
500 
501   // To handle the case where DDE isn't disabled due for a user because there
502   // account didn't perform a Firefox update this will check if Firefox is the
503   // default browser and if dde is disabled for each handler
504   // and if it isn't disable it. When Firefox is not the default browser the
505   // helper application will disable dde for each handler.
506   if (*aIsDefaultBrowser && aForAllTypes) {
507     // Check ftp settings
508 
509     end = gDDESettings + sizeof(gDDESettings) / sizeof(SETTING);
510 
511     for (settings = gDDESettings; settings < end; ++settings) {
512       NS_ConvertUTF8toUTF16 keyName(settings->keyName);
513 
514       rv = OpenKeyForReading(HKEY_CURRENT_USER, keyName, &theKey);
515       if (NS_FAILED(rv)) {
516         ::RegCloseKey(theKey);
517         // If disabling DDE fails try to disable it using the helper
518         // application when setting Firefox as the default browser.
519         *aIsDefaultBrowser = false;
520         return NS_OK;
521       }
522 
523       ::ZeroMemory(currValue, sizeof(currValue));
524       DWORD len = sizeof currValue;
525       res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr,
526                                (LPBYTE)currValue, &len);
527       // Close the key that was opened.
528       ::RegCloseKey(theKey);
529       if (REG_FAILED(res) || char16_t('\0') != *currValue) {
530         // Key wasn't set or was set to something other than our registry entry.
531         // Delete the key along with all of its childrean and then recreate it.
532         ::SHDeleteKeyW(HKEY_CURRENT_USER, keyName.get());
533         res = ::RegCreateKeyExW(HKEY_CURRENT_USER, keyName.get(), 0, nullptr,
534                                 REG_OPTION_NON_VOLATILE, KEY_SET_VALUE,
535                                 nullptr, &theKey, nullptr);
536         if (REG_FAILED(res)) {
537           // If disabling DDE fails try to disable it using the helper
538           // application when setting Firefox as the default browser.
539           *aIsDefaultBrowser = false;
540           return NS_OK;
541         }
542 
543         res = ::RegSetValueExW(theKey, L"", 0, REG_SZ, (const BYTE *) L"",
544                                sizeof(char16_t));
545         // Close the key that was created.
546         ::RegCloseKey(theKey);
547         if (REG_FAILED(res)) {
548           // If disabling DDE fails try to disable it using the helper
549           // application when setting Firefox as the default browser.
550           *aIsDefaultBrowser = false;
551           return NS_OK;
552         }
553       }
554     }
555 
556     // Update the FTP protocol handler's shell open command if it is the old
557     // format.
558     res = ::RegOpenKeyExW(HKEY_CURRENT_USER, FTP_SOC, 0, KEY_ALL_ACCESS,
559                           &theKey);
560     // Don't update the FTP protocol handler's shell open command when opening
561     // its registry key fails under HKCU since it most likely doesn't exist.
562     if (NS_FAILED(rv)) {
563       return NS_OK;
564     }
565 
566     NS_ConvertUTF8toUTF16 oldValueOpen(OLD_VAL_OPEN);
567     int32_t offset = oldValueOpen.Find("%APPPATH%");
568     oldValueOpen.Replace(offset, 9, appLongPath);
569 
570     ::ZeroMemory(currValue, sizeof(currValue));
571     DWORD len = sizeof currValue;
572     res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr, (LPBYTE)currValue,
573                              &len);
574 
575     // Don't update the FTP protocol handler's shell open command when the
576     // current registry value doesn't exist or matches the old format.
577     if (REG_FAILED(res) ||
578         _wcsicmp(oldValueOpen.get(), currValue)) {
579       ::RegCloseKey(theKey);
580       return NS_OK;
581     }
582 
583     NS_ConvertUTF8toUTF16 valueData(VAL_OPEN);
584     valueData.Replace(offset, 9, appLongPath);
585     res = ::RegSetValueExW(theKey, L"", 0, REG_SZ,
586                            (const BYTE *) valueData.get(),
587                            (valueData.Length() + 1) * sizeof(char16_t));
588     // Close the key that was created.
589     ::RegCloseKey(theKey);
590     // If updating the FTP protocol handlers shell open command fails try to
591     // update it using the helper application when setting Firefox as the
592     // default browser.
593     if (REG_FAILED(res)) {
594       *aIsDefaultBrowser = false;
595     }
596   }
597 
598   return NS_OK;
599 }
600 
601 static nsresult
DynSHOpenWithDialog(HWND hwndParent,const OPENASINFO * poainfo)602 DynSHOpenWithDialog(HWND hwndParent, const OPENASINFO *poainfo)
603 {
604   // shell32.dll is in the knownDLLs list so will always be loaded from the
605   // system32 directory.
606   static const wchar_t kSehllLibraryName[] =  L"shell32.dll";
607   HMODULE shellDLL = ::LoadLibraryW(kSehllLibraryName);
608   if (!shellDLL) {
609     return NS_ERROR_FAILURE;
610   }
611 
612   decltype(SHOpenWithDialog)* SHOpenWithDialogFn =
613     (decltype(SHOpenWithDialog)*) GetProcAddress(shellDLL, "SHOpenWithDialog");
614 
615   if (!SHOpenWithDialogFn) {
616     return NS_ERROR_FAILURE;
617   }
618 
619   nsresult rv;
620   HRESULT hr = SHOpenWithDialogFn(hwndParent, poainfo);
621   if (SUCCEEDED(hr) || (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))) {
622     rv = NS_OK;
623   } else {
624     rv = NS_ERROR_FAILURE;
625   }
626   FreeLibrary(shellDLL);
627   return rv;
628 }
629 
630 nsresult
LaunchControlPanelDefaultsSelectionUI()631 nsWindowsShellService::LaunchControlPanelDefaultsSelectionUI()
632 {
633   IApplicationAssociationRegistrationUI* pAARUI;
634   HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistrationUI,
635                                 NULL,
636                                 CLSCTX_INPROC,
637                                 IID_IApplicationAssociationRegistrationUI,
638                                 (void**)&pAARUI);
639   if (SUCCEEDED(hr)) {
640     hr = pAARUI->LaunchAdvancedAssociationUI(APP_REG_NAME);
641     pAARUI->Release();
642   }
643   return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
644 }
645 
646 nsresult
LaunchControlPanelDefaultPrograms()647 nsWindowsShellService::LaunchControlPanelDefaultPrograms()
648 {
649   // This Default Programs feature is Win7+ only.
650   if (!IsWin7OrLater()) {
651     return NS_ERROR_FAILURE;
652   }
653 
654   // Build the path control.exe path safely
655   WCHAR controlEXEPath[MAX_PATH + 1] = { '\0' };
656   if (!GetSystemDirectoryW(controlEXEPath, MAX_PATH)) {
657     return NS_ERROR_FAILURE;
658   }
659   LPCWSTR controlEXE = L"control.exe";
660   if (wcslen(controlEXEPath) + wcslen(controlEXE) >= MAX_PATH) {
661     return NS_ERROR_FAILURE;
662   }
663   if (!PathAppendW(controlEXEPath, controlEXE)) {
664     return NS_ERROR_FAILURE;
665   }
666 
667   WCHAR params[] = L"control.exe /name Microsoft.DefaultPrograms /page "
668     "pageDefaultProgram\\pageAdvancedSettings?pszAppName=" APP_REG_NAME;
669   STARTUPINFOW si = {sizeof(si), 0};
670   si.dwFlags = STARTF_USESHOWWINDOW;
671   si.wShowWindow = SW_SHOWDEFAULT;
672   PROCESS_INFORMATION pi = {0};
673   if (!CreateProcessW(controlEXEPath, params, nullptr, nullptr, FALSE,
674                       0, nullptr, nullptr, &si, &pi)) {
675     return NS_ERROR_FAILURE;
676   }
677   CloseHandle(pi.hProcess);
678   CloseHandle(pi.hThread);
679 
680   return NS_OK;
681 }
682 
683 static bool
IsWindowsLogonConnected()684 IsWindowsLogonConnected()
685 {
686   WCHAR userName[UNLEN + 1];
687   DWORD size = ArrayLength(userName);
688   if (!GetUserNameW(userName, &size)) {
689     return false;
690   }
691 
692   LPUSER_INFO_24 info;
693   if (NetUserGetInfo(nullptr, userName, 24, (LPBYTE *)&info)
694       != NERR_Success) {
695     return false;
696   }
697   bool connected = info->usri24_internet_identity;
698   NetApiBufferFree(info);
699 
700   return connected;
701 }
702 
703 static bool
SettingsAppBelievesConnected()704 SettingsAppBelievesConnected()
705 {
706   nsresult rv;
707   nsCOMPtr<nsIWindowsRegKey> regKey =
708     do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
709   if (NS_FAILED(rv)) {
710     return false;
711   }
712 
713   rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
714                     NS_LITERAL_STRING("SOFTWARE\\Microsoft\\Windows\\Shell\\Associations"),
715                     nsIWindowsRegKey::ACCESS_READ);
716   if (NS_FAILED(rv)) {
717     return false;
718   }
719 
720   uint32_t value;
721   rv = regKey->ReadIntValue(NS_LITERAL_STRING("IsConnectedAtLogon"), &value);
722   if (NS_FAILED(rv)) {
723     return false;
724   }
725 
726   return !!value;
727 }
728 
729 nsresult
LaunchModernSettingsDialogDefaultApps()730 nsWindowsShellService::LaunchModernSettingsDialogDefaultApps()
731 {
732   if (!IsWindowsBuildOrLater(14965) &&
733       !IsWindowsLogonConnected() && SettingsAppBelievesConnected()) {
734     // Use the classic Control Panel to work around a bug of older
735     // builds of Windows 10.
736     return LaunchControlPanelDefaultPrograms();
737   }
738 
739   IApplicationActivationManager* pActivator;
740   HRESULT hr = CoCreateInstance(CLSID_ApplicationActivationManager,
741                                 nullptr,
742                                 CLSCTX_INPROC,
743                                 IID_IApplicationActivationManager,
744                                 (void**)&pActivator);
745 
746   if (SUCCEEDED(hr)) {
747     DWORD pid;
748     hr = pActivator->ActivateApplication(
749            L"windows.immersivecontrolpanel_cw5n1h2txyewy"
750            L"!microsoft.windows.immersivecontrolpanel",
751            L"page=SettingsPageAppsDefaults", AO_NONE, &pid);
752     if (SUCCEEDED(hr)) {
753       // Do not check error because we could at least open
754       // the "Default apps" setting.
755       pActivator->ActivateApplication(
756              L"windows.immersivecontrolpanel_cw5n1h2txyewy"
757              L"!microsoft.windows.immersivecontrolpanel",
758              L"page=SettingsPageAppsDefaults"
759              L"&target=SystemSettings_DefaultApps_Browser", AO_NONE, &pid);
760     }
761     pActivator->Release();
762     return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
763   }
764   return NS_OK;
765 }
766 
767 nsresult
InvokeHTTPOpenAsVerb()768 nsWindowsShellService::InvokeHTTPOpenAsVerb()
769 {
770   nsCOMPtr<nsIURLFormatter> formatter(
771     do_GetService("@mozilla.org/toolkit/URLFormatterService;1"));
772   if (!formatter) {
773     return NS_ERROR_UNEXPECTED;
774   }
775 
776   nsString urlStr;
777   nsresult rv = formatter->FormatURLPref(
778     NS_LITERAL_STRING("app.support.baseURL"), urlStr);
779   if (NS_FAILED(rv)) {
780     return rv;
781   }
782   if (!StringBeginsWith(urlStr, NS_LITERAL_STRING("https://"))) {
783     return NS_ERROR_FAILURE;
784   }
785   urlStr.AppendLiteral("win10-default-browser");
786 
787   SHELLEXECUTEINFOW seinfo = { sizeof(SHELLEXECUTEINFOW) };
788   seinfo.lpVerb = L"openas";
789   seinfo.lpFile = urlStr.get();
790   seinfo.nShow = SW_SHOWNORMAL;
791   if (!ShellExecuteExW(&seinfo)) {
792     return NS_ERROR_FAILURE;
793   }
794   return NS_OK;
795 }
796 
797 nsresult
LaunchHTTPHandlerPane()798 nsWindowsShellService::LaunchHTTPHandlerPane()
799 {
800   OPENASINFO info;
801   info.pcszFile = L"http";
802   info.pcszClass = nullptr;
803   info.oaifInFlags = OAIF_FORCE_REGISTRATION |
804                      OAIF_URL_PROTOCOL |
805                      OAIF_REGISTER_EXT;
806   return DynSHOpenWithDialog(nullptr, &info);
807 }
808 
809 NS_IMETHODIMP
SetDefaultBrowser(bool aClaimAllTypes,bool aForAllUsers)810 nsWindowsShellService::SetDefaultBrowser(bool aClaimAllTypes, bool aForAllUsers)
811 {
812   nsAutoString appHelperPath;
813   if (NS_FAILED(GetHelperPath(appHelperPath)))
814     return NS_ERROR_FAILURE;
815 
816   if (aForAllUsers) {
817     appHelperPath.AppendLiteral(" /SetAsDefaultAppGlobal");
818   } else {
819     appHelperPath.AppendLiteral(" /SetAsDefaultAppUser");
820   }
821 
822   nsresult rv = LaunchHelper(appHelperPath);
823   if (NS_SUCCEEDED(rv) && IsWin8OrLater()) {
824     if (aClaimAllTypes) {
825       if (IsWin10OrLater()) {
826         rv = LaunchModernSettingsDialogDefaultApps();
827       } else {
828         rv = LaunchControlPanelDefaultsSelectionUI();
829       }
830       // The above call should never really fail, but just in case
831       // fall back to showing the HTTP association screen only.
832       if (NS_FAILED(rv)) {
833         if (IsWin10OrLater()) {
834           rv = InvokeHTTPOpenAsVerb();
835         } else {
836           rv = LaunchHTTPHandlerPane();
837         }
838       }
839     } else {
840       // Windows 10 blocks attempts to load the
841       // HTTP Handler association dialog.
842       if (IsWin10OrLater()) {
843         rv = LaunchModernSettingsDialogDefaultApps();
844       } else {
845         rv = LaunchHTTPHandlerPane();
846       }
847 
848       // The above call should never really fail, but just in case
849       // fall back to showing control panel for all defaults
850       if (NS_FAILED(rv)) {
851         rv = LaunchControlPanelDefaultsSelectionUI();
852       }
853     }
854   }
855 
856   nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
857   if (prefs) {
858     (void) prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, true);
859     // Reset the number of times the dialog should be shown
860     // before it is silenced.
861     (void) prefs->SetIntPref(PREF_DEFAULTBROWSERCHECKCOUNT, 0);
862   }
863 
864   return rv;
865 }
866 
867 static nsresult
WriteBitmap(nsIFile * aFile,imgIContainer * aImage)868 WriteBitmap(nsIFile* aFile, imgIContainer* aImage)
869 {
870   nsresult rv;
871 
872   RefPtr<SourceSurface> surface =
873     aImage->GetFrame(imgIContainer::FRAME_FIRST,
874                      imgIContainer::FLAG_SYNC_DECODE);
875   NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
876 
877   // For either of the following formats we want to set the biBitCount member
878   // of the BITMAPINFOHEADER struct to 32, below. For that value the bitmap
879   // format defines that the A8/X8 WORDs in the bitmap byte stream be ignored
880   // for the BI_RGB value we use for the biCompression member.
881   MOZ_ASSERT(surface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
882              surface->GetFormat() == SurfaceFormat::B8G8R8X8);
883 
884   RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
885   NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
886 
887   int32_t width = dataSurface->GetSize().width;
888   int32_t height = dataSurface->GetSize().height;
889   int32_t bytesPerPixel = 4 * sizeof(uint8_t);
890   uint32_t bytesPerRow = bytesPerPixel * width;
891 
892   // initialize these bitmap structs which we will later
893   // serialize directly to the head of the bitmap file
894   BITMAPINFOHEADER bmi;
895   bmi.biSize = sizeof(BITMAPINFOHEADER);
896   bmi.biWidth = width;
897   bmi.biHeight = height;
898   bmi.biPlanes = 1;
899   bmi.biBitCount = (WORD)bytesPerPixel*8;
900   bmi.biCompression = BI_RGB;
901   bmi.biSizeImage = bytesPerRow * height;
902   bmi.biXPelsPerMeter = 0;
903   bmi.biYPelsPerMeter = 0;
904   bmi.biClrUsed = 0;
905   bmi.biClrImportant = 0;
906 
907   BITMAPFILEHEADER bf;
908   bf.bfType = 0x4D42; // 'BM'
909   bf.bfReserved1 = 0;
910   bf.bfReserved2 = 0;
911   bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
912   bf.bfSize = bf.bfOffBits + bmi.biSizeImage;
913 
914   // get a file output stream
915   nsCOMPtr<nsIOutputStream> stream;
916   rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile);
917   NS_ENSURE_SUCCESS(rv, rv);
918 
919   DataSourceSurface::MappedSurface map;
920   if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
921     return NS_ERROR_FAILURE;
922   }
923 
924   // write the bitmap headers and rgb pixel data to the file
925   rv = NS_ERROR_FAILURE;
926   if (stream) {
927     uint32_t written;
928     stream->Write((const char*)&bf, sizeof(BITMAPFILEHEADER), &written);
929     if (written == sizeof(BITMAPFILEHEADER)) {
930       stream->Write((const char*)&bmi, sizeof(BITMAPINFOHEADER), &written);
931       if (written == sizeof(BITMAPINFOHEADER)) {
932         // write out the image data backwards because the desktop won't
933         // show bitmaps with negative heights for top-to-bottom
934         uint32_t i = map.mStride * height;
935         do {
936           i -= map.mStride;
937           stream->Write(((const char*)map.mData) + i, bytesPerRow, &written);
938           if (written == bytesPerRow) {
939             rv = NS_OK;
940           } else {
941             rv = NS_ERROR_FAILURE;
942             break;
943           }
944         } while (i != 0);
945       }
946     }
947 
948     stream->Close();
949   }
950 
951   dataSurface->Unmap();
952 
953   return rv;
954 }
955 
956 NS_IMETHODIMP
SetDesktopBackground(nsIDOMElement * aElement,int32_t aPosition)957 nsWindowsShellService::SetDesktopBackground(nsIDOMElement* aElement,
958                                             int32_t aPosition)
959 {
960   nsresult rv;
961 
962   nsCOMPtr<imgIContainer> container;
963   nsCOMPtr<nsIDOMHTMLImageElement> imgElement(do_QueryInterface(aElement));
964   if (!imgElement) {
965     // XXX write background loading stuff!
966     return NS_ERROR_NOT_AVAILABLE;
967   }
968   else {
969     nsCOMPtr<nsIImageLoadingContent> imageContent =
970       do_QueryInterface(aElement, &rv);
971     if (!imageContent)
972       return rv;
973 
974     // get the image container
975     nsCOMPtr<imgIRequest> request;
976     rv = imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
977                                   getter_AddRefs(request));
978     if (!request)
979       return rv;
980     rv = request->GetImage(getter_AddRefs(container));
981     if (!container)
982       return NS_ERROR_FAILURE;
983   }
984 
985   // get the file name from localized strings
986   nsCOMPtr<nsIStringBundleService>
987     bundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
988   NS_ENSURE_SUCCESS(rv, rv);
989 
990   nsCOMPtr<nsIStringBundle> shellBundle;
991   rv = bundleService->CreateBundle(SHELLSERVICE_PROPERTIES,
992                                    getter_AddRefs(shellBundle));
993   NS_ENSURE_SUCCESS(rv, rv);
994 
995   // e.g. "Desktop Background.bmp"
996   nsString fileLeafName;
997   rv = shellBundle->GetStringFromName
998                       (u"desktopBackgroundLeafNameWin",
999                        getter_Copies(fileLeafName));
1000   NS_ENSURE_SUCCESS(rv, rv);
1001 
1002   // get the profile root directory
1003   nsCOMPtr<nsIFile> file;
1004   rv = NS_GetSpecialDirectory(NS_APP_APPLICATION_REGISTRY_DIR,
1005                               getter_AddRefs(file));
1006   NS_ENSURE_SUCCESS(rv, rv);
1007 
1008   // eventually, the path is "%APPDATA%\Mozilla\Firefox\Desktop Background.bmp"
1009   rv = file->Append(fileLeafName);
1010   NS_ENSURE_SUCCESS(rv, rv);
1011 
1012   nsAutoString path;
1013   rv = file->GetPath(path);
1014   NS_ENSURE_SUCCESS(rv, rv);
1015 
1016   // write the bitmap to a file in the profile directory
1017   rv = WriteBitmap(file, container);
1018 
1019   // if the file was written successfully, set it as the system wallpaper
1020   if (NS_SUCCEEDED(rv)) {
1021     nsCOMPtr<nsIWindowsRegKey> regKey =
1022       do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
1023     NS_ENSURE_SUCCESS(rv, rv);
1024 
1025     rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
1026                         NS_LITERAL_STRING("Control Panel\\Desktop"),
1027                         nsIWindowsRegKey::ACCESS_SET_VALUE);
1028     NS_ENSURE_SUCCESS(rv, rv);
1029 
1030     nsAutoString tile;
1031     nsAutoString style;
1032     switch (aPosition) {
1033       case BACKGROUND_TILE:
1034         style.Assign('0');
1035         tile.Assign('1');
1036         break;
1037       case BACKGROUND_CENTER:
1038         style.Assign('0');
1039         tile.Assign('0');
1040         break;
1041       case BACKGROUND_STRETCH:
1042         style.Assign('2');
1043         tile.Assign('0');
1044         break;
1045       case BACKGROUND_FILL:
1046         style.AssignLiteral("10");
1047         tile.Assign('0');
1048         break;
1049       case BACKGROUND_FIT:
1050         style.Assign('6');
1051         tile.Assign('0');
1052         break;
1053     }
1054 
1055     rv = regKey->WriteStringValue(NS_LITERAL_STRING("TileWallpaper"), tile);
1056     NS_ENSURE_SUCCESS(rv, rv);
1057     rv = regKey->WriteStringValue(NS_LITERAL_STRING("WallpaperStyle"), style);
1058     NS_ENSURE_SUCCESS(rv, rv);
1059     rv = regKey->Close();
1060     NS_ENSURE_SUCCESS(rv, rv);
1061 
1062     ::SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, (PVOID)path.get(),
1063                             SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
1064   }
1065   return rv;
1066 }
1067 
1068 NS_IMETHODIMP
OpenApplication(int32_t aApplication)1069 nsWindowsShellService::OpenApplication(int32_t aApplication)
1070 {
1071   nsAutoString application;
1072   switch (aApplication) {
1073   case nsIShellService::APPLICATION_MAIL:
1074     application.AssignLiteral("Mail");
1075     break;
1076   case nsIShellService::APPLICATION_NEWS:
1077     application.AssignLiteral("News");
1078     break;
1079   }
1080 
1081   // The Default Client section of the Windows Registry looks like this:
1082   //
1083   // Clients\aClient\
1084   //  e.g. aClient = "Mail"...
1085   //        \Mail\(default) = Client Subkey Name
1086   //             \Client Subkey Name
1087   //             \Client Subkey Name\shell\open\command\
1088   //             \Client Subkey Name\shell\open\command\(default) = path to exe
1089   //
1090 
1091   // Find the default application for this class.
1092   HKEY theKey;
1093   nsresult rv = OpenKeyForReading(HKEY_CLASSES_ROOT, application, &theKey);
1094   if (NS_FAILED(rv))
1095     return rv;
1096 
1097   wchar_t buf[MAX_BUF];
1098   DWORD type, len = sizeof buf;
1099   DWORD res = ::RegQueryValueExW(theKey, EmptyString().get(), 0,
1100                                  &type, (LPBYTE)&buf, &len);
1101 
1102   if (REG_FAILED(res) || !*buf)
1103     return NS_OK;
1104 
1105   // Close the key we opened.
1106   ::RegCloseKey(theKey);
1107 
1108   // Find the "open" command
1109   application.Append('\\');
1110   application.Append(buf);
1111   application.AppendLiteral("\\shell\\open\\command");
1112 
1113   rv = OpenKeyForReading(HKEY_CLASSES_ROOT, application, &theKey);
1114   if (NS_FAILED(rv))
1115     return rv;
1116 
1117   ::ZeroMemory(buf, sizeof(buf));
1118   len = sizeof buf;
1119   res = ::RegQueryValueExW(theKey, EmptyString().get(), 0,
1120                            &type, (LPBYTE)&buf, &len);
1121   if (REG_FAILED(res) || !*buf)
1122     return NS_ERROR_FAILURE;
1123 
1124   // Close the key we opened.
1125   ::RegCloseKey(theKey);
1126 
1127   // Look for any embedded environment variables and substitute their
1128   // values, as |::CreateProcessW| is unable to do this.
1129   nsAutoString path(buf);
1130   int32_t end = path.Length();
1131   int32_t cursor = 0, temp = 0;
1132   ::ZeroMemory(buf, sizeof(buf));
1133   do {
1134     cursor = path.FindChar('%', cursor);
1135     if (cursor < 0)
1136       break;
1137 
1138     temp = path.FindChar('%', cursor + 1);
1139     ++cursor;
1140 
1141     ::ZeroMemory(&buf, sizeof(buf));
1142 
1143     ::GetEnvironmentVariableW(nsAutoString(Substring(path, cursor, temp - cursor)).get(),
1144                               buf, sizeof(buf));
1145 
1146     // "+ 2" is to subtract the extra characters used to delimit the environment
1147     // variable ('%').
1148     path.Replace((cursor - 1), temp - cursor + 2, nsDependentString(buf));
1149 
1150     ++cursor;
1151   }
1152   while (cursor < end);
1153 
1154   STARTUPINFOW si;
1155   PROCESS_INFORMATION pi;
1156 
1157   ::ZeroMemory(&si, sizeof(STARTUPINFOW));
1158   ::ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
1159 
1160   BOOL success = ::CreateProcessW(nullptr, (LPWSTR)path.get(), nullptr,
1161                                   nullptr, FALSE, 0, nullptr,  nullptr,
1162                                   &si, &pi);
1163   if (!success)
1164     return NS_ERROR_FAILURE;
1165 
1166   return NS_OK;
1167 }
1168 
1169 NS_IMETHODIMP
GetDesktopBackgroundColor(uint32_t * aColor)1170 nsWindowsShellService::GetDesktopBackgroundColor(uint32_t* aColor)
1171 {
1172   uint32_t color = ::GetSysColor(COLOR_DESKTOP);
1173   *aColor = (GetRValue(color) << 16) | (GetGValue(color) << 8) | GetBValue(color);
1174   return NS_OK;
1175 }
1176 
1177 NS_IMETHODIMP
SetDesktopBackgroundColor(uint32_t aColor)1178 nsWindowsShellService::SetDesktopBackgroundColor(uint32_t aColor)
1179 {
1180   int aParameters[2] = { COLOR_BACKGROUND, COLOR_DESKTOP };
1181   BYTE r = (aColor >> 16);
1182   BYTE g = (aColor << 16) >> 24;
1183   BYTE b = (aColor << 24) >> 24;
1184   COLORREF colors[2] = { RGB(r,g,b), RGB(r,g,b) };
1185 
1186   ::SetSysColors(sizeof(aParameters) / sizeof(int), aParameters, colors);
1187 
1188   nsresult rv;
1189   nsCOMPtr<nsIWindowsRegKey> regKey =
1190     do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
1191   NS_ENSURE_SUCCESS(rv, rv);
1192 
1193   rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
1194                       NS_LITERAL_STRING("Control Panel\\Colors"),
1195                       nsIWindowsRegKey::ACCESS_SET_VALUE);
1196   NS_ENSURE_SUCCESS(rv, rv);
1197 
1198   wchar_t rgb[12];
1199   _snwprintf(rgb, 12, L"%u %u %u", r, g, b);
1200 
1201   rv = regKey->WriteStringValue(NS_LITERAL_STRING("Background"),
1202                                 nsDependentString(rgb));
1203   NS_ENSURE_SUCCESS(rv, rv);
1204 
1205   return regKey->Close();
1206 }
1207 
nsWindowsShellService()1208 nsWindowsShellService::nsWindowsShellService()
1209 {
1210 }
1211 
~nsWindowsShellService()1212 nsWindowsShellService::~nsWindowsShellService()
1213 {
1214 }
1215 
1216 NS_IMETHODIMP
OpenApplicationWithURI(nsIFile * aApplication,const nsACString & aURI)1217 nsWindowsShellService::OpenApplicationWithURI(nsIFile* aApplication,
1218                                               const nsACString& aURI)
1219 {
1220   nsresult rv;
1221   nsCOMPtr<nsIProcess> process =
1222     do_CreateInstance("@mozilla.org/process/util;1", &rv);
1223   if (NS_FAILED(rv))
1224     return rv;
1225 
1226   rv = process->Init(aApplication);
1227   if (NS_FAILED(rv))
1228     return rv;
1229 
1230   const nsCString spec(aURI);
1231   const char* specStr = spec.get();
1232   return process->Run(false, &specStr, 1);
1233 }
1234 
1235 NS_IMETHODIMP
GetDefaultFeedReader(nsIFile ** _retval)1236 nsWindowsShellService::GetDefaultFeedReader(nsIFile** _retval)
1237 {
1238   *_retval = nullptr;
1239 
1240   nsresult rv;
1241   nsCOMPtr<nsIWindowsRegKey> regKey =
1242     do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
1243   NS_ENSURE_SUCCESS(rv, rv);
1244 
1245   rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
1246                     NS_LITERAL_STRING("feed\\shell\\open\\command"),
1247                     nsIWindowsRegKey::ACCESS_READ);
1248   NS_ENSURE_SUCCESS(rv, rv);
1249 
1250   nsAutoString path;
1251   rv = regKey->ReadStringValue(EmptyString(), path);
1252   NS_ENSURE_SUCCESS(rv, rv);
1253   if (path.IsEmpty())
1254     return NS_ERROR_FAILURE;
1255 
1256   if (path.First() == '"') {
1257     // Everything inside the quotes
1258     path = Substring(path, 1, path.FindChar('"', 1) - 1);
1259   }
1260   else {
1261     // Everything up to the first space
1262     path = Substring(path, 0, path.FindChar(' '));
1263   }
1264 
1265   nsCOMPtr<nsIFile> defaultReader =
1266     do_CreateInstance("@mozilla.org/file/local;1", &rv);
1267   NS_ENSURE_SUCCESS(rv, rv);
1268 
1269   rv = defaultReader->InitWithPath(path);
1270   NS_ENSURE_SUCCESS(rv, rv);
1271 
1272   bool exists;
1273   rv = defaultReader->Exists(&exists);
1274   NS_ENSURE_SUCCESS(rv, rv);
1275   if (!exists)
1276     return NS_ERROR_FAILURE;
1277 
1278   NS_ADDREF(*_retval = defaultReader);
1279   return NS_OK;
1280 }
1281