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 "BinaryPath.h"
9 #include "city.h"
10 #include "imgIContainer.h"
11 #include "imgIRequest.h"
12 #include "mozilla/gfx/2D.h"
13 #include "mozilla/RefPtr.h"
14 #include "nsIContent.h"
15 #include "nsIDOMElement.h"
16 #include "nsIImageLoadingContent.h"
17 #include "nsIOutputStream.h"
18 #include "nsIPrefService.h"
19 #include "nsIPrefLocalizedString.h"
20 #include "nsIServiceManager.h"
21 #include "nsIStringBundle.h"
22 #include "nsNetUtil.h"
23 #include "nsServiceManagerUtils.h"
24 #include "nsShellService.h"
25 #include "nsIProcess.h"
26 #include "nsICategoryManager.h"
27 #include "nsBrowserCompsCID.h"
28 #include "nsDirectoryServiceUtils.h"
29 #include "nsAppDirectoryServiceDefs.h"
30 #include "nsDirectoryServiceDefs.h"
31 #include "nsIWindowsRegKey.h"
32 #include "nsUnicharUtils.h"
33 #include "nsIURLFormatter.h"
34 #include "nsXULAppAPI.h"
35 #include "mozilla/WindowsVersion.h"
36 
37 #include "windows.h"
38 #include "shellapi.h"
39 
40 #ifdef _WIN32_WINNT
41 #undef _WIN32_WINNT
42 #endif
43 #define _WIN32_WINNT 0x0600
44 #define INITGUID
45 #undef NTDDI_VERSION
46 #define NTDDI_VERSION NTDDI_WIN8
47 // Needed for access to IApplicationActivationManager
48 #include <shlobj.h>
49 
50 #include <mbstring.h>
51 #include <shlwapi.h>
52 
53 #include <lm.h>
54 #undef ACCESS_READ
55 
56 #ifndef MAX_BUF
57 #define MAX_BUF 4096
58 #endif
59 
60 #define REG_SUCCEEDED(val) (val == ERROR_SUCCESS)
61 
62 #define REG_FAILED(val) (val != ERROR_SUCCESS)
63 
64 #define APP_REG_NAME_BASE L"Firefox-"
65 
66 using mozilla::IsWin8OrLater;
67 using namespace mozilla;
68 using namespace mozilla::gfx;
69 
NS_IMPL_ISUPPORTS(nsWindowsShellService,nsIShellService)70 NS_IMPL_ISUPPORTS(nsWindowsShellService, nsIShellService)
71 
72 static nsresult OpenKeyForReading(HKEY aKeyRoot, const nsAString& aKeyName,
73                                   HKEY* aKey) {
74   const nsString& flatName = PromiseFlatString(aKeyName);
75 
76   DWORD res = ::RegOpenKeyExW(aKeyRoot, flatName.get(), 0, KEY_READ, aKey);
77   switch (res) {
78     case ERROR_SUCCESS:
79       break;
80     case ERROR_ACCESS_DENIED:
81       return NS_ERROR_FILE_ACCESS_DENIED;
82     case ERROR_FILE_NOT_FOUND:
83       return NS_ERROR_NOT_AVAILABLE;
84   }
85 
86   return NS_OK;
87 }
88 
GetHelperPath(nsAutoString & aPath)89 nsresult GetHelperPath(nsAutoString& aPath) {
90   nsresult rv;
91   nsCOMPtr<nsIProperties> directoryService =
92       do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
93   NS_ENSURE_SUCCESS(rv, rv);
94 
95   nsCOMPtr<nsIFile> appHelper;
96   rv = directoryService->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile),
97                              getter_AddRefs(appHelper));
98   NS_ENSURE_SUCCESS(rv, rv);
99 
100   rv = appHelper->SetNativeLeafName(NS_LITERAL_CSTRING("uninstall"));
101   NS_ENSURE_SUCCESS(rv, rv);
102 
103   rv = appHelper->AppendNative(NS_LITERAL_CSTRING("helper.exe"));
104   NS_ENSURE_SUCCESS(rv, rv);
105 
106   rv = appHelper->GetPath(aPath);
107 
108   aPath.Insert(L'"', 0);
109   aPath.Append(L'"');
110   return rv;
111 }
112 
LaunchHelper(nsAutoString & aPath)113 nsresult LaunchHelper(nsAutoString& aPath) {
114   STARTUPINFOW si = {sizeof(si), 0};
115   PROCESS_INFORMATION pi = {0};
116 
117   if (!CreateProcessW(nullptr, (LPWSTR)aPath.get(), nullptr, nullptr, FALSE, 0,
118                       nullptr, nullptr, &si, &pi)) {
119     return NS_ERROR_FAILURE;
120   }
121 
122   CloseHandle(pi.hProcess);
123   CloseHandle(pi.hThread);
124   return NS_OK;
125 }
126 
IsPathDefaultForClass(const RefPtr<IApplicationAssociationRegistration> & pAAR,wchar_t * exePath,LPCWSTR aClassName)127 static bool IsPathDefaultForClass(
128     const RefPtr<IApplicationAssociationRegistration>& pAAR, wchar_t* exePath,
129     LPCWSTR aClassName) {
130   // Make sure the Prog ID matches what we have
131   LPWSTR registeredApp;
132   bool isProtocol = *aClassName != L'.';
133   ASSOCIATIONTYPE queryType = isProtocol ? AT_URLPROTOCOL : AT_FILEEXTENSION;
134   HRESULT hr = pAAR->QueryCurrentDefault(aClassName, queryType, AL_EFFECTIVE,
135                                          &registeredApp);
136   if (FAILED(hr)) {
137     return false;
138   }
139 
140   LPCWSTR progID = isProtocol ? L"FirefoxURL" : L"FirefoxHTML";
141   bool isDefault = !wcsnicmp(registeredApp, progID, wcslen(progID));
142 
143   nsAutoString regAppName(registeredApp);
144   CoTaskMemFree(registeredApp);
145 
146   if (isDefault) {
147     // Make sure the application path for this progID is this installation.
148     regAppName.AppendLiteral("\\shell\\open\\command");
149     HKEY theKey;
150     nsresult rv = OpenKeyForReading(HKEY_CLASSES_ROOT, regAppName, &theKey);
151     if (NS_FAILED(rv)) {
152       return false;
153     }
154 
155     wchar_t cmdFromReg[MAX_BUF] = L"";
156     DWORD len = sizeof(cmdFromReg);
157     DWORD res = ::RegQueryValueExW(theKey, nullptr, nullptr, nullptr,
158                                    (LPBYTE)cmdFromReg, &len);
159     ::RegCloseKey(theKey);
160     if (REG_FAILED(res)) {
161       return false;
162     }
163 
164     wchar_t fullCmd[MAX_BUF] = L"";
165     _snwprintf(fullCmd, MAX_BUF, L"\"%s\" -osint -url \"%%1\"", exePath);
166 
167     isDefault = _wcsicmp(fullCmd, cmdFromReg) == 0;
168   }
169 
170   return isDefault;
171 }
172 
GetAppRegName(nsAutoString & aAppRegName)173 static nsresult GetAppRegName(nsAutoString& aAppRegName) {
174   nsresult rv;
175   nsCOMPtr<nsIProperties> dirSvc =
176       do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
177   NS_ENSURE_SUCCESS(rv, rv);
178 
179   nsCOMPtr<nsIFile> exeFile;
180   rv = dirSvc->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile),
181                    getter_AddRefs(exeFile));
182   NS_ENSURE_SUCCESS(rv, rv);
183 
184   nsCOMPtr<nsIFile> appDir;
185   rv = exeFile->GetParent(getter_AddRefs(appDir));
186   NS_ENSURE_SUCCESS(rv, rv);
187 
188   nsAutoString appDirStr;
189   rv = appDir->GetPath(appDirStr);
190   NS_ENSURE_SUCCESS(rv, rv);
191 
192   aAppRegName = APP_REG_NAME_BASE;
193   uint64_t hash =
194       CityHash64(static_cast<const char*>(appDirStr.get()),
195                  appDirStr.Length() * sizeof(nsAutoString::char_type));
196   aAppRegName.AppendInt((int)(hash >> 32), 16);
197   aAppRegName.AppendInt((int)hash, 16);
198 
199   return rv;
200 }
201 
202 NS_IMETHODIMP
IsDefaultBrowser(bool aStartupCheck,bool aForAllTypes,bool * aIsDefaultBrowser)203 nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck, bool aForAllTypes,
204                                         bool* aIsDefaultBrowser) {
205   mozilla::Unused << aStartupCheck;
206 
207   *aIsDefaultBrowser = false;
208 
209   RefPtr<IApplicationAssociationRegistration> pAAR;
210   HRESULT hr = CoCreateInstance(
211       CLSID_ApplicationAssociationRegistration, nullptr, CLSCTX_INPROC,
212       IID_IApplicationAssociationRegistration, getter_AddRefs(pAAR));
213   if (FAILED(hr)) {
214     return NS_OK;
215   }
216 
217   wchar_t exePath[MAXPATHLEN] = L"";
218   nsresult rv = BinaryPath::GetLong(exePath);
219 
220   if (NS_FAILED(rv)) {
221     return NS_OK;
222   }
223 
224   *aIsDefaultBrowser = IsPathDefaultForClass(pAAR, exePath, L"http");
225   if (*aIsDefaultBrowser && aForAllTypes) {
226     *aIsDefaultBrowser = IsPathDefaultForClass(pAAR, exePath, L".html");
227   }
228   return NS_OK;
229 }
230 
LaunchControlPanelDefaultsSelectionUI()231 nsresult nsWindowsShellService::LaunchControlPanelDefaultsSelectionUI() {
232   IApplicationAssociationRegistrationUI* pAARUI;
233   HRESULT hr = CoCreateInstance(
234       CLSID_ApplicationAssociationRegistrationUI, NULL, CLSCTX_INPROC,
235       IID_IApplicationAssociationRegistrationUI, (void**)&pAARUI);
236   if (SUCCEEDED(hr)) {
237     nsAutoString appRegName;
238     GetAppRegName(appRegName);
239     hr = pAARUI->LaunchAdvancedAssociationUI(appRegName.get());
240     pAARUI->Release();
241   }
242   return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
243 }
244 
LaunchControlPanelDefaultPrograms()245 nsresult nsWindowsShellService::LaunchControlPanelDefaultPrograms() {
246   // Build the path control.exe path safely
247   WCHAR controlEXEPath[MAX_PATH + 1] = {'\0'};
248   if (!GetSystemDirectoryW(controlEXEPath, MAX_PATH)) {
249     return NS_ERROR_FAILURE;
250   }
251   LPCWSTR controlEXE = L"control.exe";
252   if (wcslen(controlEXEPath) + wcslen(controlEXE) >= MAX_PATH) {
253     return NS_ERROR_FAILURE;
254   }
255   if (!PathAppendW(controlEXEPath, controlEXE)) {
256     return NS_ERROR_FAILURE;
257   }
258 
259   nsAutoString params(NS_LITERAL_STRING(
260       "control.exe /name Microsoft.DefaultPrograms "
261       "/page pageDefaultProgram\\pageAdvancedSettings?pszAppName="));
262   nsAutoString appRegName;
263   GetAppRegName(appRegName);
264   params.Append(appRegName);
265   STARTUPINFOW si = {sizeof(si), 0};
266   si.dwFlags = STARTF_USESHOWWINDOW;
267   si.wShowWindow = SW_SHOWDEFAULT;
268   PROCESS_INFORMATION pi = {0};
269   if (!CreateProcessW(controlEXEPath, static_cast<LPWSTR>(params.get()),
270                       nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi)) {
271     return NS_ERROR_FAILURE;
272   }
273   CloseHandle(pi.hProcess);
274   CloseHandle(pi.hThread);
275 
276   return NS_OK;
277 }
278 
IsWindowsLogonConnected()279 static bool IsWindowsLogonConnected() {
280   WCHAR userName[UNLEN + 1];
281   DWORD size = ArrayLength(userName);
282   if (!GetUserNameW(userName, &size)) {
283     return false;
284   }
285 
286   LPUSER_INFO_24 info;
287   if (NetUserGetInfo(nullptr, userName, 24, (LPBYTE*)&info) != NERR_Success) {
288     return false;
289   }
290   bool connected = info->usri24_internet_identity;
291   NetApiBufferFree(info);
292 
293   return connected;
294 }
295 
SettingsAppBelievesConnected()296 static bool SettingsAppBelievesConnected() {
297   nsresult rv;
298   nsCOMPtr<nsIWindowsRegKey> regKey =
299       do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
300   if (NS_FAILED(rv)) {
301     return false;
302   }
303 
304   rv = regKey->Open(
305       nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
306       NS_LITERAL_STRING("SOFTWARE\\Microsoft\\Windows\\Shell\\Associations"),
307       nsIWindowsRegKey::ACCESS_READ);
308   if (NS_FAILED(rv)) {
309     return false;
310   }
311 
312   uint32_t value;
313   rv = regKey->ReadIntValue(NS_LITERAL_STRING("IsConnectedAtLogon"), &value);
314   if (NS_FAILED(rv)) {
315     return false;
316   }
317 
318   return !!value;
319 }
320 
LaunchModernSettingsDialogDefaultApps()321 nsresult nsWindowsShellService::LaunchModernSettingsDialogDefaultApps() {
322   if (!IsWindowsBuildOrLater(14965) && !IsWindowsLogonConnected() &&
323       SettingsAppBelievesConnected()) {
324     // Use the classic Control Panel to work around a bug of older
325     // builds of Windows 10.
326     return LaunchControlPanelDefaultPrograms();
327   }
328 
329   IApplicationActivationManager* pActivator;
330   HRESULT hr = CoCreateInstance(
331       CLSID_ApplicationActivationManager, nullptr, CLSCTX_INPROC,
332       IID_IApplicationActivationManager, (void**)&pActivator);
333 
334   if (SUCCEEDED(hr)) {
335     DWORD pid;
336     hr = pActivator->ActivateApplication(
337         L"windows.immersivecontrolpanel_cw5n1h2txyewy"
338         L"!microsoft.windows.immersivecontrolpanel",
339         L"page=SettingsPageAppsDefaults", AO_NONE, &pid);
340     if (SUCCEEDED(hr)) {
341       // Do not check error because we could at least open
342       // the "Default apps" setting.
343       pActivator->ActivateApplication(
344           L"windows.immersivecontrolpanel_cw5n1h2txyewy"
345           L"!microsoft.windows.immersivecontrolpanel",
346           L"page=SettingsPageAppsDefaults"
347           L"&target=SystemSettings_DefaultApps_Browser",
348           AO_NONE, &pid);
349     }
350     pActivator->Release();
351     return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
352   }
353   return NS_OK;
354 }
355 
InvokeHTTPOpenAsVerb()356 nsresult nsWindowsShellService::InvokeHTTPOpenAsVerb() {
357   nsCOMPtr<nsIURLFormatter> formatter(
358       do_GetService("@mozilla.org/toolkit/URLFormatterService;1"));
359   if (!formatter) {
360     return NS_ERROR_UNEXPECTED;
361   }
362 
363   nsString urlStr;
364   nsresult rv = formatter->FormatURLPref(
365       NS_LITERAL_STRING("app.support.baseURL"), urlStr);
366   if (NS_FAILED(rv)) {
367     return rv;
368   }
369   if (!StringBeginsWith(urlStr, NS_LITERAL_STRING("https://"))) {
370     return NS_ERROR_FAILURE;
371   }
372   urlStr.AppendLiteral("win10-default-browser");
373 
374   SHELLEXECUTEINFOW seinfo = {sizeof(SHELLEXECUTEINFOW)};
375   seinfo.lpVerb = L"openas";
376   seinfo.lpFile = urlStr.get();
377   seinfo.nShow = SW_SHOWNORMAL;
378   if (!ShellExecuteExW(&seinfo)) {
379     return NS_ERROR_FAILURE;
380   }
381   return NS_OK;
382 }
383 
LaunchHTTPHandlerPane()384 nsresult nsWindowsShellService::LaunchHTTPHandlerPane() {
385   OPENASINFO info;
386   info.pcszFile = L"http";
387   info.pcszClass = nullptr;
388   info.oaifInFlags =
389       OAIF_FORCE_REGISTRATION | OAIF_URL_PROTOCOL | OAIF_REGISTER_EXT;
390 
391   HRESULT hr = SHOpenWithDialog(nullptr, &info);
392   if (SUCCEEDED(hr) || (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))) {
393     return NS_OK;
394   }
395   return NS_ERROR_FAILURE;
396 }
397 
398 NS_IMETHODIMP
SetDefaultBrowser(bool aClaimAllTypes,bool aForAllUsers)399 nsWindowsShellService::SetDefaultBrowser(bool aClaimAllTypes,
400                                          bool aForAllUsers) {
401   nsAutoString appHelperPath;
402   if (NS_FAILED(GetHelperPath(appHelperPath))) return NS_ERROR_FAILURE;
403 
404   if (aForAllUsers) {
405     appHelperPath.AppendLiteral(" /SetAsDefaultAppGlobal");
406   } else {
407     appHelperPath.AppendLiteral(" /SetAsDefaultAppUser");
408   }
409 
410   nsresult rv = LaunchHelper(appHelperPath);
411   if (NS_SUCCEEDED(rv) && IsWin8OrLater()) {
412     if (aClaimAllTypes) {
413       if (IsWin10OrLater()) {
414         rv = LaunchModernSettingsDialogDefaultApps();
415       } else {
416         rv = LaunchControlPanelDefaultsSelectionUI();
417       }
418       // The above call should never really fail, but just in case
419       // fall back to showing the HTTP association screen only.
420       if (NS_FAILED(rv)) {
421         if (IsWin10OrLater()) {
422           rv = InvokeHTTPOpenAsVerb();
423         } else {
424           rv = LaunchHTTPHandlerPane();
425         }
426       }
427     } else {
428       // Windows 10 blocks attempts to load the
429       // HTTP Handler association dialog.
430       if (IsWin10OrLater()) {
431         rv = LaunchModernSettingsDialogDefaultApps();
432       } else {
433         rv = LaunchHTTPHandlerPane();
434       }
435 
436       // The above call should never really fail, but just in case
437       // fall back to showing control panel for all defaults
438       if (NS_FAILED(rv)) {
439         rv = LaunchControlPanelDefaultsSelectionUI();
440       }
441     }
442   }
443 
444   nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
445   if (prefs) {
446     (void)prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, true);
447     // Reset the number of times the dialog should be shown
448     // before it is silenced.
449     (void)prefs->SetIntPref(PREF_DEFAULTBROWSERCHECKCOUNT, 0);
450   }
451 
452   return rv;
453 }
454 
WriteBitmap(nsIFile * aFile,imgIContainer * aImage)455 static nsresult WriteBitmap(nsIFile* aFile, imgIContainer* aImage) {
456   nsresult rv;
457 
458   RefPtr<SourceSurface> surface = aImage->GetFrame(
459       imgIContainer::FRAME_FIRST, imgIContainer::FLAG_SYNC_DECODE);
460   NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
461 
462   // For either of the following formats we want to set the biBitCount member
463   // of the BITMAPINFOHEADER struct to 32, below. For that value the bitmap
464   // format defines that the A8/X8 WORDs in the bitmap byte stream be ignored
465   // for the BI_RGB value we use for the biCompression member.
466   MOZ_ASSERT(surface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
467              surface->GetFormat() == SurfaceFormat::B8G8R8X8);
468 
469   RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
470   NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
471 
472   int32_t width = dataSurface->GetSize().width;
473   int32_t height = dataSurface->GetSize().height;
474   int32_t bytesPerPixel = 4 * sizeof(uint8_t);
475   uint32_t bytesPerRow = bytesPerPixel * width;
476 
477   // initialize these bitmap structs which we will later
478   // serialize directly to the head of the bitmap file
479   BITMAPINFOHEADER bmi;
480   bmi.biSize = sizeof(BITMAPINFOHEADER);
481   bmi.biWidth = width;
482   bmi.biHeight = height;
483   bmi.biPlanes = 1;
484   bmi.biBitCount = (WORD)bytesPerPixel * 8;
485   bmi.biCompression = BI_RGB;
486   bmi.biSizeImage = bytesPerRow * height;
487   bmi.biXPelsPerMeter = 0;
488   bmi.biYPelsPerMeter = 0;
489   bmi.biClrUsed = 0;
490   bmi.biClrImportant = 0;
491 
492   BITMAPFILEHEADER bf;
493   bf.bfType = 0x4D42;  // 'BM'
494   bf.bfReserved1 = 0;
495   bf.bfReserved2 = 0;
496   bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
497   bf.bfSize = bf.bfOffBits + bmi.biSizeImage;
498 
499   // get a file output stream
500   nsCOMPtr<nsIOutputStream> stream;
501   rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile);
502   NS_ENSURE_SUCCESS(rv, rv);
503 
504   DataSourceSurface::MappedSurface map;
505   if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
506     return NS_ERROR_FAILURE;
507   }
508 
509   // write the bitmap headers and rgb pixel data to the file
510   rv = NS_ERROR_FAILURE;
511   if (stream) {
512     uint32_t written;
513     stream->Write((const char*)&bf, sizeof(BITMAPFILEHEADER), &written);
514     if (written == sizeof(BITMAPFILEHEADER)) {
515       stream->Write((const char*)&bmi, sizeof(BITMAPINFOHEADER), &written);
516       if (written == sizeof(BITMAPINFOHEADER)) {
517         // write out the image data backwards because the desktop won't
518         // show bitmaps with negative heights for top-to-bottom
519         uint32_t i = map.mStride * height;
520         do {
521           i -= map.mStride;
522           stream->Write(((const char*)map.mData) + i, bytesPerRow, &written);
523           if (written == bytesPerRow) {
524             rv = NS_OK;
525           } else {
526             rv = NS_ERROR_FAILURE;
527             break;
528           }
529         } while (i != 0);
530       }
531     }
532 
533     stream->Close();
534   }
535 
536   dataSurface->Unmap();
537 
538   return rv;
539 }
540 
541 NS_IMETHODIMP
SetDesktopBackground(nsIDOMElement * aElement,int32_t aPosition,const nsACString & aImageName)542 nsWindowsShellService::SetDesktopBackground(nsIDOMElement* aElement,
543                                             int32_t aPosition,
544                                             const nsACString& aImageName) {
545   nsCOMPtr<nsIContent> content(do_QueryInterface(aElement));
546   if (!content || !content->IsHTMLElement(nsGkAtoms::img)) {
547     // XXX write background loading stuff!
548     return NS_ERROR_NOT_AVAILABLE;
549   }
550 
551   nsresult rv;
552   nsCOMPtr<nsIImageLoadingContent> imageContent =
553       do_QueryInterface(aElement, &rv);
554   if (!imageContent) return rv;
555 
556   // get the image container
557   nsCOMPtr<imgIRequest> request;
558   rv = imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
559                                 getter_AddRefs(request));
560   if (!request) return rv;
561 
562   nsCOMPtr<imgIContainer> container;
563   rv = request->GetImage(getter_AddRefs(container));
564   if (!container) return NS_ERROR_FAILURE;
565 
566   // get the file name from localized strings
567   nsCOMPtr<nsIStringBundleService> bundleService(
568       do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
569   NS_ENSURE_SUCCESS(rv, rv);
570 
571   nsCOMPtr<nsIStringBundle> shellBundle;
572   rv = bundleService->CreateBundle(SHELLSERVICE_PROPERTIES,
573                                    getter_AddRefs(shellBundle));
574   NS_ENSURE_SUCCESS(rv, rv);
575 
576   // e.g. "Desktop Background.bmp"
577   nsAutoString fileLeafName;
578   rv = shellBundle->GetStringFromName("desktopBackgroundLeafNameWin",
579                                       fileLeafName);
580   NS_ENSURE_SUCCESS(rv, rv);
581 
582   // get the profile root directory
583   nsCOMPtr<nsIFile> file;
584   rv = NS_GetSpecialDirectory(NS_APP_APPLICATION_REGISTRY_DIR,
585                               getter_AddRefs(file));
586   NS_ENSURE_SUCCESS(rv, rv);
587 
588   // eventually, the path is "%APPDATA%\Mozilla\Firefox\Desktop Background.bmp"
589   rv = file->Append(fileLeafName);
590   NS_ENSURE_SUCCESS(rv, rv);
591 
592   nsAutoString path;
593   rv = file->GetPath(path);
594   NS_ENSURE_SUCCESS(rv, rv);
595 
596   // write the bitmap to a file in the profile directory
597   rv = WriteBitmap(file, container);
598 
599   // if the file was written successfully, set it as the system wallpaper
600   if (NS_SUCCEEDED(rv)) {
601     nsCOMPtr<nsIWindowsRegKey> regKey =
602         do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
603     NS_ENSURE_SUCCESS(rv, rv);
604 
605     rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
606                         NS_LITERAL_STRING("Control Panel\\Desktop"),
607                         nsIWindowsRegKey::ACCESS_SET_VALUE);
608     NS_ENSURE_SUCCESS(rv, rv);
609 
610     nsAutoString tile;
611     nsAutoString style;
612     switch (aPosition) {
613       case BACKGROUND_TILE:
614         style.Assign('0');
615         tile.Assign('1');
616         break;
617       case BACKGROUND_CENTER:
618         style.Assign('0');
619         tile.Assign('0');
620         break;
621       case BACKGROUND_STRETCH:
622         style.Assign('2');
623         tile.Assign('0');
624         break;
625       case BACKGROUND_FILL:
626         style.AssignLiteral("10");
627         tile.Assign('0');
628         break;
629       case BACKGROUND_FIT:
630         style.Assign('6');
631         tile.Assign('0');
632         break;
633     }
634 
635     rv = regKey->WriteStringValue(NS_LITERAL_STRING("TileWallpaper"), tile);
636     NS_ENSURE_SUCCESS(rv, rv);
637     rv = regKey->WriteStringValue(NS_LITERAL_STRING("WallpaperStyle"), style);
638     NS_ENSURE_SUCCESS(rv, rv);
639     rv = regKey->Close();
640     NS_ENSURE_SUCCESS(rv, rv);
641 
642     ::SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, (PVOID)path.get(),
643                             SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
644   }
645   return rv;
646 }
647 
648 NS_IMETHODIMP
OpenApplication(int32_t aApplication)649 nsWindowsShellService::OpenApplication(int32_t aApplication) {
650   nsAutoString application;
651   switch (aApplication) {
652     case nsIShellService::APPLICATION_MAIL:
653       application.AssignLiteral("Mail");
654       break;
655     case nsIShellService::APPLICATION_NEWS:
656       application.AssignLiteral("News");
657       break;
658   }
659 
660   // The Default Client section of the Windows Registry looks like this:
661   //
662   // Clients\aClient\
663   //  e.g. aClient = "Mail"...
664   //        \Mail\(default) = Client Subkey Name
665   //             \Client Subkey Name
666   //             \Client Subkey Name\shell\open\command\
667   //             \Client Subkey Name\shell\open\command\(default) = path to
668   //             exe
669   //
670 
671   // Find the default application for this class.
672   HKEY theKey;
673   nsresult rv = OpenKeyForReading(HKEY_CLASSES_ROOT, application, &theKey);
674   if (NS_FAILED(rv)) return rv;
675 
676   wchar_t buf[MAX_BUF];
677   DWORD type, len = sizeof buf;
678   DWORD res = ::RegQueryValueExW(theKey, EmptyString().get(), 0, &type,
679                                  (LPBYTE)&buf, &len);
680 
681   if (REG_FAILED(res) || !*buf) return NS_OK;
682 
683   // Close the key we opened.
684   ::RegCloseKey(theKey);
685 
686   // Find the "open" command
687   application.Append('\\');
688   application.Append(buf);
689   application.AppendLiteral("\\shell\\open\\command");
690 
691   rv = OpenKeyForReading(HKEY_CLASSES_ROOT, application, &theKey);
692   if (NS_FAILED(rv)) return rv;
693 
694   ::ZeroMemory(buf, sizeof(buf));
695   len = sizeof buf;
696   res = ::RegQueryValueExW(theKey, EmptyString().get(), 0, &type, (LPBYTE)&buf,
697                            &len);
698   if (REG_FAILED(res) || !*buf) return NS_ERROR_FAILURE;
699 
700   // Close the key we opened.
701   ::RegCloseKey(theKey);
702 
703   // Look for any embedded environment variables and substitute their
704   // values, as |::CreateProcessW| is unable to do this.
705   nsAutoString path(buf);
706   int32_t end = path.Length();
707   int32_t cursor = 0, temp = 0;
708   ::ZeroMemory(buf, sizeof(buf));
709   do {
710     cursor = path.FindChar('%', cursor);
711     if (cursor < 0) break;
712 
713     temp = path.FindChar('%', cursor + 1);
714     ++cursor;
715 
716     ::ZeroMemory(&buf, sizeof(buf));
717 
718     ::GetEnvironmentVariableW(
719         nsAutoString(Substring(path, cursor, temp - cursor)).get(), buf,
720         sizeof(buf));
721 
722     // "+ 2" is to subtract the extra characters used to delimit the environment
723     // variable ('%').
724     path.Replace((cursor - 1), temp - cursor + 2, nsDependentString(buf));
725 
726     ++cursor;
727   } while (cursor < end);
728 
729   STARTUPINFOW si;
730   PROCESS_INFORMATION pi;
731 
732   ::ZeroMemory(&si, sizeof(STARTUPINFOW));
733   ::ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
734 
735   BOOL success = ::CreateProcessW(nullptr, (LPWSTR)path.get(), nullptr, nullptr,
736                                   FALSE, 0, nullptr, nullptr, &si, &pi);
737   if (!success) return NS_ERROR_FAILURE;
738 
739   return NS_OK;
740 }
741 
742 NS_IMETHODIMP
GetDesktopBackgroundColor(uint32_t * aColor)743 nsWindowsShellService::GetDesktopBackgroundColor(uint32_t* aColor) {
744   uint32_t color = ::GetSysColor(COLOR_DESKTOP);
745   *aColor =
746       (GetRValue(color) << 16) | (GetGValue(color) << 8) | GetBValue(color);
747   return NS_OK;
748 }
749 
750 NS_IMETHODIMP
SetDesktopBackgroundColor(uint32_t aColor)751 nsWindowsShellService::SetDesktopBackgroundColor(uint32_t aColor) {
752   int aParameters[2] = {COLOR_BACKGROUND, COLOR_DESKTOP};
753   BYTE r = (aColor >> 16);
754   BYTE g = (aColor << 16) >> 24;
755   BYTE b = (aColor << 24) >> 24;
756   COLORREF colors[2] = {RGB(r, g, b), RGB(r, g, b)};
757 
758   ::SetSysColors(sizeof(aParameters) / sizeof(int), aParameters, colors);
759 
760   nsresult rv;
761   nsCOMPtr<nsIWindowsRegKey> regKey =
762       do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
763   NS_ENSURE_SUCCESS(rv, rv);
764 
765   rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
766                       NS_LITERAL_STRING("Control Panel\\Colors"),
767                       nsIWindowsRegKey::ACCESS_SET_VALUE);
768   NS_ENSURE_SUCCESS(rv, rv);
769 
770   wchar_t rgb[12];
771   _snwprintf(rgb, 12, L"%u %u %u", r, g, b);
772 
773   rv = regKey->WriteStringValue(NS_LITERAL_STRING("Background"),
774                                 nsDependentString(rgb));
775   NS_ENSURE_SUCCESS(rv, rv);
776 
777   return regKey->Close();
778 }
779 
nsWindowsShellService()780 nsWindowsShellService::nsWindowsShellService() {}
781 
~nsWindowsShellService()782 nsWindowsShellService::~nsWindowsShellService() {}
783 
784 NS_IMETHODIMP
OpenApplicationWithURI(nsIFile * aApplication,const nsACString & aURI)785 nsWindowsShellService::OpenApplicationWithURI(nsIFile* aApplication,
786                                               const nsACString& aURI) {
787   nsresult rv;
788   nsCOMPtr<nsIProcess> process =
789       do_CreateInstance("@mozilla.org/process/util;1", &rv);
790   if (NS_FAILED(rv)) return rv;
791 
792   rv = process->Init(aApplication);
793   if (NS_FAILED(rv)) return rv;
794 
795   const nsCString spec(aURI);
796   const char* specStr = spec.get();
797   return process->Run(false, &specStr, 1);
798 }
799 
800 NS_IMETHODIMP
GetDefaultFeedReader(nsIFile ** _retval)801 nsWindowsShellService::GetDefaultFeedReader(nsIFile** _retval) {
802   *_retval = nullptr;
803 
804   nsresult rv;
805   nsCOMPtr<nsIWindowsRegKey> regKey =
806       do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
807   NS_ENSURE_SUCCESS(rv, rv);
808 
809   rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
810                     NS_LITERAL_STRING("feed\\shell\\open\\command"),
811                     nsIWindowsRegKey::ACCESS_READ);
812   NS_ENSURE_SUCCESS(rv, rv);
813 
814   nsAutoString path;
815   rv = regKey->ReadStringValue(EmptyString(), path);
816   NS_ENSURE_SUCCESS(rv, rv);
817   if (path.IsEmpty()) return NS_ERROR_FAILURE;
818 
819   if (path.First() == '"') {
820     // Everything inside the quotes
821     path = Substring(path, 1, path.FindChar('"', 1) - 1);
822   } else {
823     // Everything up to the first space
824     path = Substring(path, 0, path.FindChar(' '));
825   }
826 
827   nsCOMPtr<nsIFile> defaultReader =
828       do_CreateInstance("@mozilla.org/file/local;1", &rv);
829   NS_ENSURE_SUCCESS(rv, rv);
830 
831   rv = defaultReader->InitWithPath(path);
832   NS_ENSURE_SUCCESS(rv, rv);
833 
834   bool exists;
835   rv = defaultReader->Exists(&exists);
836   NS_ENSURE_SUCCESS(rv, rv);
837   if (!exists) return NS_ERROR_FAILURE;
838 
839   NS_ADDREF(*_retval = defaultReader);
840   return NS_OK;
841 }
842