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 ®isteredApp);
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