Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
6 #include "imgIContainer.h"
7 #include "imgIRequest.h"
8 #include "mozilla/gfx/2D.h"
9 #include "mozilla/RefPtr.h"
10 #include "nsIDOMElement.h"
11 #include "nsIDOMHTMLImageElement.h"
12 #include "nsIImageLoadingContent.h"
13 #include "nsIPrefService.h"
14 #include "nsIPrefLocalizedString.h"
15 #include "nsIServiceManager.h"
16 #include "nsIStringBundle.h"
17 #include "nsNetUtil.h"
18 #include "nsShellService.h"
19 #include "nsWindowsShellService.h"
20 #include "nsIProcess.h"
21 #include "nsICategoryManager.h"
22 #include "nsBrowserCompsCID.h"
23 #include "nsDirectoryServiceUtils.h"
24 #include "nsAppDirectoryServiceDefs.h"
25 #include "nsDirectoryServiceDefs.h"
26 #include "nsIWindowsRegKey.h"
27 #include "nsUnicharUtils.h"
28 #include "nsIWinTaskbar.h"
29 #include "nsISupportsPrimitives.h"
30 #include "nsThreadUtils.h"
31 #include "nsXULAppAPI.h"
32 #include "mozilla/WindowsVersion.h"
34 #include "windows.h"
35 #include "shellapi.h"
37 #ifdef _WIN32_WINNT
38 #undef _WIN32_WINNT
39 #endif
40 #define _WIN32_WINNT 0x0600
41 #define INITGUID
42 #include <shlobj.h>
44 #include <mbstring.h>
45 #include <shlwapi.h>
47 #ifndef MAX_BUF
48 #define MAX_BUF 4096
49 #endif
51 #define REG_SUCCEEDED(val) \
52 (val == ERROR_SUCCESS)
54 #define REG_FAILED(val) \
55 (val != ERROR_SUCCESS)
57 #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
59 using mozilla::IsWin8OrLater;
60 using namespace mozilla;
61 using namespace mozilla::gfx;
63 NS_IMPL_ISUPPORTS(nsWindowsShellService, nsIWindowsShellService, nsIShellService)
65 static nsresult
66 OpenKeyForReading(HKEY aKeyRoot, const nsAString& aKeyName, HKEY* aKey)
67 {
68 const nsString &flatName = PromiseFlatString(aKeyName);
70 DWORD res = ::RegOpenKeyExW(aKeyRoot, flatName.get(), 0, KEY_READ, aKey);
71 switch (res) {
72 case ERROR_SUCCESS:
73 break;
74 case ERROR_ACCESS_DENIED:
75 return NS_ERROR_FILE_ACCESS_DENIED;
76 case ERROR_FILE_NOT_FOUND:
77 return NS_ERROR_NOT_AVAILABLE;
78 }
80 return NS_OK;
81 }
83 ///////////////////////////////////////////////////////////////////////////////
84 // Default Browser Registry Settings
85 //
86 // The setting of these values are made by an external binary since writing
87 // these values may require elevation.
88 //
89 // - File Extension Mappings
90 // -----------------------
91 // The following file extensions:
92 // .htm .html .shtml .xht .xhtml
93 // are mapped like so:
94 //
95 // HKCU\SOFTWARE\Classes\.<ext>\ (default) REG_SZ FirefoxHTML
96 //
97 // as aliases to the class:
98 //
99 // HKCU\SOFTWARE\Classes\FirefoxHTML\
100 // DefaultIcon (default) REG_SZ <apppath>,1
101 // shell\open\command (default) REG_SZ <apppath> -osint -url "%1"
102 // shell\open\ddeexec (default) REG_SZ <empty string>
103 //
104 // - Windows Vista and above Protocol Handler
105 //
106 // HKCU\SOFTWARE\Classes\FirefoxURL\ (default) REG_SZ <appname> URL
107 // EditFlags REG_DWORD 2
108 // FriendlyTypeName REG_SZ <appname> URL
109 // DefaultIcon (default) REG_SZ <apppath>,1
110 // shell\open\command (default) REG_SZ <apppath> -osint -url "%1"
111 // shell\open\ddeexec (default) REG_SZ <empty string>
112 //
113 // - Protocol Mappings
114 // -----------------
115 // The following protocols:
116 // HTTP, HTTPS, FTP
117 // are mapped like so:
118 //
119 // HKCU\SOFTWARE\Classes\<protocol>\
120 // DefaultIcon (default) REG_SZ <apppath>,1
121 // shell\open\command (default) REG_SZ <apppath> -osint -url "%1"
122 // shell\open\ddeexec (default) REG_SZ <empty string>
123 //
124 // - Windows Start Menu (XP SP1 and newer)
125 // -------------------------------------------------
126 // The following keys are set to make Firefox appear in the Start Menu as the
127 // browser:
128 //
129 // HKCU\SOFTWARE\Clients\StartMenuInternet\FIREFOX.EXE\
130 // (default) REG_SZ <appname>
131 // DefaultIcon (default) REG_SZ <apppath>,0
132 // InstallInfo HideIconsCommand REG_SZ <uninstpath> /HideShortcuts
133 // InstallInfo IconsVisible REG_DWORD 1
134 // InstallInfo ReinstallCommand REG_SZ <uninstpath> /SetAsDefaultAppGlobal
135 // InstallInfo ShowIconsCommand REG_SZ <uninstpath> /ShowShortcuts
136 // shell\open\command (default) REG_SZ <apppath>
137 // shell\properties (default) REG_SZ <appname> &Options
138 // shell\properties\command (default) REG_SZ <apppath> -preferences
139 // shell\safemode (default) REG_SZ <appname> &Safe Mode
140 // shell\safemode\command (default) REG_SZ <apppath> -safe-mode
141 //
143 // The values checked are all default values so the value name is not needed.
144 typedef struct {
145 const char* keyName;
146 const char* valueData;
147 const char* oldValueData;
148 } SETTING;
150 #define APP_REG_NAME L"Firefox"
151 #define VAL_FILE_ICON "%APPPATH%,1"
152 #define VAL_OPEN "\"%APPPATH%\" -osint -url \"%1\""
153 #define OLD_VAL_OPEN "\"%APPPATH%\" -requestPending -osint -url \"%1\""
154 #define DI "\\DefaultIcon"
155 #define SOC "\\shell\\open\\command"
156 #define SOD "\\shell\\open\\ddeexec"
157 // Used for updating the FTP protocol handler's shell open command under HKCU.
158 #define FTP_SOC L"Software\\Classes\\ftp\\shell\\open\\command"
160 #define MAKE_KEY_NAME1(PREFIX, MID) \
161 PREFIX MID
163 // The DefaultIcon registry key value should never be used when checking if
164 // Firefox is the default browser for file handlers since other applications
165 // (e.g. MS Office) may modify the DefaultIcon registry key value to add Icon
166 // Handlers. see http://msdn2.microsoft.com/en-us/library/aa969357.aspx for
167 // more info. The FTP protocol is not checked so advanced users can set the FTP
168 // handler to another application and still have Firefox check if it is the
169 // default HTTP and HTTPS handler.
170 // *** Do not add additional checks here unless you skip them when aForAllTypes
171 // is false below***.
172 static SETTING gSettings[] = {
173 // File Handler Class
174 // ***keep this as the first entry because when aForAllTypes is not set below
175 // it will skip over this check.***
176 { MAKE_KEY_NAME1("FirefoxHTML", SOC), VAL_OPEN, OLD_VAL_OPEN },
178 // Protocol Handler Class - for Vista and above
179 { MAKE_KEY_NAME1("FirefoxURL", SOC), VAL_OPEN, OLD_VAL_OPEN },
181 // Protocol Handlers
182 { MAKE_KEY_NAME1("HTTP", DI), VAL_FILE_ICON },
183 { MAKE_KEY_NAME1("HTTP", SOC), VAL_OPEN, OLD_VAL_OPEN },
184 { MAKE_KEY_NAME1("HTTPS", DI), VAL_FILE_ICON },
185 { MAKE_KEY_NAME1("HTTPS", SOC), VAL_OPEN, OLD_VAL_OPEN }
186 };
188 // The settings to disable DDE are separate from the default browser settings
189 // since they are only checked when Firefox is the default browser and if they
190 // are incorrect they are fixed without notifying the user.
191 static SETTING gDDESettings[] = {
192 // File Handler Class
193 { MAKE_KEY_NAME1("Software\\Classes\\FirefoxHTML", SOD) },
195 // Protocol Handler Class - for Vista and above
196 { MAKE_KEY_NAME1("Software\\Classes\\FirefoxURL", SOD) },
198 // Protocol Handlers
199 { MAKE_KEY_NAME1("Software\\Classes\\FTP", SOD) },
200 { MAKE_KEY_NAME1("Software\\Classes\\HTTP", SOD) },
201 { MAKE_KEY_NAME1("Software\\Classes\\HTTPS", SOD) }
202 };
204 nsresult
205 GetHelperPath(nsAutoString& aPath)
206 {
207 nsresult rv;
208 nsCOMPtr<nsIProperties> directoryService =
209 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
210 NS_ENSURE_SUCCESS(rv, rv);
212 nsCOMPtr<nsIFile> appHelper;
213 rv = directoryService->Get(XRE_EXECUTABLE_FILE,
214 NS_GET_IID(nsIFile),
215 getter_AddRefs(appHelper));
216 NS_ENSURE_SUCCESS(rv, rv);
218 rv = appHelper->SetNativeLeafName(NS_LITERAL_CSTRING("uninstall"));
219 NS_ENSURE_SUCCESS(rv, rv);
221 rv = appHelper->AppendNative(NS_LITERAL_CSTRING("helper.exe"));
222 NS_ENSURE_SUCCESS(rv, rv);
224 rv = appHelper->GetPath(aPath);
226 aPath.Insert(L'"', 0);
227 aPath.Append(L'"');
228 return rv;
229 }
231 nsresult
232 LaunchHelper(nsAutoString& aPath)
233 {
234 STARTUPINFOW si = {sizeof(si), 0};
235 PROCESS_INFORMATION pi = {0};
237 if (!CreateProcessW(nullptr, (LPWSTR)aPath.get(), nullptr, nullptr, FALSE,
238 0, nullptr, nullptr, &si, &pi)) {
239 return NS_ERROR_FAILURE;
240 }
242 CloseHandle(pi.hProcess);
243 CloseHandle(pi.hThread);
244 return NS_OK;
245 }
247 NS_IMETHODIMP
248 nsWindowsShellService::ShortcutMaintenance()
249 {
250 nsresult rv;
252 // XXX App ids were updated to a constant install path hash,
253 // XXX this code can be removed after a few upgrade cycles.
255 // Launch helper.exe so it can update the application user model ids on
256 // shortcuts in the user's taskbar and start menu. This keeps older pinned
257 // shortcuts grouped correctly after major updates. Note, we also do this
258 // through the upgrade installer script, however, this is the only place we
259 // have a chance to trap links created by users who do control the install/
260 // update process of the browser.
262 nsCOMPtr<nsIWinTaskbar> taskbarInfo =
263 do_GetService(NS_TASKBAR_CONTRACTID);
264 if (!taskbarInfo) // If we haven't built with win7 sdk features, this fails.
265 return NS_OK;
267 // Avoid if this isn't Win7+
268 bool isSupported = false;
269 taskbarInfo->GetAvailable(&isSupported);
270 if (!isSupported)
271 return NS_OK;
273 nsAutoString appId;
274 if (NS_FAILED(taskbarInfo->GetDefaultGroupId(appId)))
275 return NS_ERROR_UNEXPECTED;
277 NS_NAMED_LITERAL_CSTRING(prefName, "browser.taskbar.lastgroupid");
278 nsCOMPtr<nsIPrefService> prefs =
279 do_GetService(NS_PREFSERVICE_CONTRACTID);
280 if (!prefs)
281 return NS_ERROR_UNEXPECTED;
283 nsCOMPtr<nsIPrefBranch> prefBranch;
284 prefs->GetBranch(nullptr, getter_AddRefs(prefBranch));
285 if (!prefBranch)
286 return NS_ERROR_UNEXPECTED;
288 nsCOMPtr<nsISupportsString> prefString;
289 rv = prefBranch->GetComplexValue(prefName.get(),
290 NS_GET_IID(nsISupportsString),
291 getter_AddRefs(prefString));
292 if (NS_SUCCEEDED(rv)) {
293 nsAutoString version;
294 prefString->GetData(version);
295 if (!version.IsEmpty() && version.Equals(appId)) {
296 // We're all good, get out of here.
297 return NS_OK;
298 }
299 }
300 // Update the version in prefs
301 prefString =
302 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
303 if (NS_FAILED(rv))
304 return rv;
306 prefString->SetData(appId);
307 rv = prefBranch->SetComplexValue(prefName.get(),
308 NS_GET_IID(nsISupportsString),
309 prefString);
310 if (NS_FAILED(rv)) {
311 NS_WARNING("Couldn't set last user model id!");
312 return NS_ERROR_UNEXPECTED;
313 }
315 nsAutoString appHelperPath;
316 if (NS_FAILED(GetHelperPath(appHelperPath)))
317 return NS_ERROR_UNEXPECTED;
319 appHelperPath.AppendLiteral(" /UpdateShortcutAppUserModelIds");
321 return LaunchHelper(appHelperPath);
322 }
324 static bool
325 IsAARDefaultHTTP(IApplicationAssociationRegistration* pAAR,
326 bool* aIsDefaultBrowser)
327 {
328 // Make sure the Prog ID matches what we have
329 LPWSTR registeredApp;
330 HRESULT hr = pAAR->QueryCurrentDefault(L"http", AT_URLPROTOCOL, AL_EFFECTIVE,
331 ®isteredApp);
332 if (SUCCEEDED(hr)) {
333 LPCWSTR firefoxHTTPProgID = L"FirefoxURL";
334 *aIsDefaultBrowser = !wcsicmp(registeredApp, firefoxHTTPProgID);
335 CoTaskMemFree(registeredApp);
336 } else {
337 *aIsDefaultBrowser = false;
338 }
339 return SUCCEEDED(hr);
340 }
342 static bool
343 IsAARDefaultHTML(IApplicationAssociationRegistration* pAAR,
344 bool* aIsDefaultBrowser)
345 {
346 LPWSTR registeredApp;
347 HRESULT hr = pAAR->QueryCurrentDefault(L".html", AT_FILEEXTENSION, AL_EFFECTIVE,
348 ®isteredApp);
349 if (SUCCEEDED(hr)) {
350 LPCWSTR firefoxHTMLProgID = L"FirefoxHTML";
351 *aIsDefaultBrowser = !wcsicmp(registeredApp, firefoxHTMLProgID);
352 CoTaskMemFree(registeredApp);
353 } else {
354 *aIsDefaultBrowser = false;
355 }
356 return SUCCEEDED(hr);
357 }
359 /*
360 * Query's the AAR for the default status.
361 * This only checks for FirefoxURL and if aCheckAllTypes is set, then
362 * it also checks for FirefoxHTML. Note that those ProgIDs are shared
363 * by all Firefox browsers.
364 */
365 bool
366 nsWindowsShellService::IsDefaultBrowserVista(bool aCheckAllTypes,
367 bool* aIsDefaultBrowser)
368 {
369 IApplicationAssociationRegistration* pAAR;
370 HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
371 nullptr,
372 CLSCTX_INPROC,
373 IID_IApplicationAssociationRegistration,
374 (void**)&pAAR);
376 if (SUCCEEDED(hr)) {
377 if (aCheckAllTypes) {
378 BOOL res;
379 hr = pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE,
380 APP_REG_NAME,
381 &res);
382 *aIsDefaultBrowser = res;
384 // If we have all defaults, let's make sure that our ProgID
385 // is explicitly returned as well. Needed only for Windows 8.
386 if (*aIsDefaultBrowser && IsWin8OrLater()) {
387 IsAARDefaultHTTP(pAAR, aIsDefaultBrowser);
388 if (*aIsDefaultBrowser) {
389 IsAARDefaultHTML(pAAR, aIsDefaultBrowser);
390 }
391 }
392 } else {
393 IsAARDefaultHTTP(pAAR, aIsDefaultBrowser);
394 }
396 pAAR->Release();
397 return true;
398 }
399 return false;
400 }
402 NS_IMETHODIMP
403 nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
404 bool aForAllTypes,
405 bool* aIsDefaultBrowser)
406 {
407 // If this is the first browser window, maintain internal state that we've
408 // checked this session (so that subsequent window opens don't show the
409 // default browser dialog).
410 if (aStartupCheck)
411 mCheckedThisSession = true;
413 // Assume we're the default unless one of the several checks below tell us
414 // otherwise.
415 *aIsDefaultBrowser = true;
417 wchar_t exePath[MAX_BUF];
418 if (!::GetModuleFileNameW(0, exePath, MAX_BUF))
419 return NS_ERROR_FAILURE;
421 // Convert the path to a long path since GetModuleFileNameW returns the path
422 // that was used to launch Firefox which is not necessarily a long path.
423 if (!::GetLongPathNameW(exePath, exePath, MAX_BUF))
424 return NS_ERROR_FAILURE;
426 nsAutoString appLongPath(exePath);
428 HKEY theKey;
429 DWORD res;
430 nsresult rv;
431 wchar_t currValue[MAX_BUF];
433 SETTING* settings = gSettings;
434 if (!aForAllTypes && IsWin8OrLater()) {
435 // Skip over the file handler check
436 settings++;
437 }
439 SETTING* end = gSettings + sizeof(gSettings) / sizeof(SETTING);
441 for (; settings < end; ++settings) {
442 NS_ConvertUTF8toUTF16 keyName(settings->keyName);
443 NS_ConvertUTF8toUTF16 valueData(settings->valueData);
444 int32_t offset = valueData.Find("%APPPATH%");
445 valueData.Replace(offset, 9, appLongPath);
447 rv = OpenKeyForReading(HKEY_CLASSES_ROOT, keyName, &theKey);
448 if (NS_FAILED(rv)) {
449 *aIsDefaultBrowser = false;
450 return NS_OK;
451 }
453 ::ZeroMemory(currValue, sizeof(currValue));
454 DWORD len = sizeof currValue;
455 res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr,
456 (LPBYTE)currValue, &len);
457 // Close the key that was opened.
458 ::RegCloseKey(theKey);
459 if (REG_FAILED(res) ||
460 _wcsicmp(valueData.get(), currValue)) {
461 // Key wasn't set or was set to something other than our registry entry.
462 NS_ConvertUTF8toUTF16 oldValueData(settings->oldValueData);
463 offset = oldValueData.Find("%APPPATH%");
464 oldValueData.Replace(offset, 9, appLongPath);
465 // The current registry value doesn't match the current or the old format.
466 if (_wcsicmp(oldValueData.get(), currValue)) {
467 *aIsDefaultBrowser = false;
468 return NS_OK;
469 }
471 res = ::RegOpenKeyExW(HKEY_CLASSES_ROOT, PromiseFlatString(keyName).get(),
472 0, KEY_SET_VALUE, &theKey);
473 if (REG_FAILED(res)) {
474 // If updating the open command fails try to update it using the helper
475 // application when setting Firefox as the default browser.
476 *aIsDefaultBrowser = false;
477 return NS_OK;
478 }
480 const nsString &flatValue = PromiseFlatString(valueData);
481 res = ::RegSetValueExW(theKey, L"", 0, REG_SZ,
482 (const BYTE *) flatValue.get(),
483 (flatValue.Length() + 1) * sizeof(char16_t));
484 // Close the key that was created.
485 ::RegCloseKey(theKey);
486 if (REG_FAILED(res)) {
487 // If updating the open command fails try to update it using the helper
488 // application when setting Firefox as the default browser.
489 *aIsDefaultBrowser = false;
490 return NS_OK;
491 }
492 }
493 }
495 // Only check if Firefox is the default browser on Vista and above if the
496 // previous checks show that Firefox is the default browser.
497 if (*aIsDefaultBrowser) {
498 IsDefaultBrowserVista(aForAllTypes, aIsDefaultBrowser);
499 }
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
509 end = gDDESettings + sizeof(gDDESettings) / sizeof(SETTING);
511 for (settings = gDDESettings; settings < end; ++settings) {
512 NS_ConvertUTF8toUTF16 keyName(settings->keyName);
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 }
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 const nsString &flatName = PromiseFlatString(keyName);
533 ::SHDeleteKeyW(HKEY_CURRENT_USER, flatName.get());
534 res = ::RegCreateKeyExW(HKEY_CURRENT_USER, flatName.get(), 0, nullptr,
535 REG_OPTION_NON_VOLATILE, KEY_SET_VALUE,
536 nullptr, &theKey, nullptr);
537 if (REG_FAILED(res)) {
538 // If disabling DDE fails try to disable it using the helper
539 // application when setting Firefox as the default browser.
540 *aIsDefaultBrowser = false;
541 return NS_OK;
542 }
544 res = ::RegSetValueExW(theKey, L"", 0, REG_SZ, (const BYTE *) L"",
545 sizeof(char16_t));
546 // Close the key that was created.
547 ::RegCloseKey(theKey);
548 if (REG_FAILED(res)) {
549 // If disabling DDE fails try to disable it using the helper
550 // application when setting Firefox as the default browser.
551 *aIsDefaultBrowser = false;
552 return NS_OK;
553 }
554 }
555 }
557 // Update the FTP protocol handler's shell open command if it is the old
558 // format.
559 res = ::RegOpenKeyExW(HKEY_CURRENT_USER, FTP_SOC, 0, KEY_ALL_ACCESS,
560 &theKey);
561 // Don't update the FTP protocol handler's shell open command when opening
562 // its registry key fails under HKCU since it most likely doesn't exist.
563 if (NS_FAILED(rv)) {
564 return NS_OK;
565 }
567 NS_ConvertUTF8toUTF16 oldValueOpen(OLD_VAL_OPEN);
568 int32_t offset = oldValueOpen.Find("%APPPATH%");
569 oldValueOpen.Replace(offset, 9, appLongPath);
571 ::ZeroMemory(currValue, sizeof(currValue));
572 DWORD len = sizeof currValue;
573 res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr, (LPBYTE)currValue,
574 &len);
576 // Don't update the FTP protocol handler's shell open command when the
577 // current registry value doesn't exist or matches the old format.
578 if (REG_FAILED(res) ||
579 _wcsicmp(oldValueOpen.get(), currValue)) {
580 ::RegCloseKey(theKey);
581 return NS_OK;
582 }
584 NS_ConvertUTF8toUTF16 valueData(VAL_OPEN);
585 valueData.Replace(offset, 9, appLongPath);
586 const nsString &flatValue = PromiseFlatString(valueData);
587 res = ::RegSetValueExW(theKey, L"", 0, REG_SZ,
588 (const BYTE *) flatValue.get(),
589 (flatValue.Length() + 1) * sizeof(char16_t));
590 // Close the key that was created.
591 ::RegCloseKey(theKey);
592 // If updating the FTP protocol handlers shell open command fails try to
593 // update it using the helper application when setting Firefox as the
594 // default browser.
595 if (REG_FAILED(res)) {
596 *aIsDefaultBrowser = false;
597 }
598 }
600 return NS_OK;
601 }
603 NS_IMETHODIMP
604 nsWindowsShellService::GetCanSetDesktopBackground(bool* aResult)
605 {
606 *aResult = true;
607 return NS_OK;
608 }
610 static nsresult
611 DynSHOpenWithDialog(HWND hwndParent, const OPENASINFO *poainfo)
612 {
613 // shell32.dll is in the knownDLLs list so will always be loaded from the
614 // system32 directory.
615 static const wchar_t kSehllLibraryName[] = L"shell32.dll";
616 HMODULE shellDLL = ::LoadLibraryW(kSehllLibraryName);
617 if (!shellDLL) {
618 return NS_ERROR_FAILURE;
619 }
621 decltype(SHOpenWithDialog)* SHOpenWithDialogFn =
622 (decltype(SHOpenWithDialog)*) GetProcAddress(shellDLL, "SHOpenWithDialog");
624 if (!SHOpenWithDialogFn) {
625 return NS_ERROR_FAILURE;
626 }
628 nsresult rv =
629 SUCCEEDED(SHOpenWithDialogFn(hwndParent, poainfo)) ? NS_OK :
630 NS_ERROR_FAILURE;
631 FreeLibrary(shellDLL);
632 return rv;
633 }
635 nsresult
636 nsWindowsShellService::LaunchControlPanelDefaultPrograms()
637 {
638 // Build the path control.exe path safely
639 WCHAR controlEXEPath[MAX_PATH + 1] = { '\0' };
640 if (!GetSystemDirectoryW(controlEXEPath, MAX_PATH)) {
641 return NS_ERROR_FAILURE;
642 }
643 LPCWSTR controlEXE = L"control.exe";
644 if (wcslen(controlEXEPath) + wcslen(controlEXE) >= MAX_PATH) {
645 return NS_ERROR_FAILURE;
646 }
647 if (!PathAppendW(controlEXEPath, controlEXE)) {
648 return NS_ERROR_FAILURE;
649 }
651 WCHAR params[] = L"control.exe /name Microsoft.DefaultPrograms /page pageDefaultProgram";
652 STARTUPINFOW si = {sizeof(si), 0};
653 si.dwFlags = STARTF_USESHOWWINDOW;
654 si.wShowWindow = SW_SHOWDEFAULT;
655 PROCESS_INFORMATION pi = {0};
656 if (!CreateProcessW(controlEXEPath, params, nullptr, nullptr, FALSE,
657 0, nullptr, nullptr, &si, &pi)) {
658 return NS_ERROR_FAILURE;
659 }
660 CloseHandle(pi.hProcess);
661 CloseHandle(pi.hThread);
663 return NS_OK;
664 }
666 nsresult
667 nsWindowsShellService::LaunchHTTPHandlerPane()
668 {
669 OPENASINFO info;
670 info.pcszFile = L"http";
671 info.pcszClass = nullptr;
672 info.oaifInFlags = OAIF_FORCE_REGISTRATION |
673 OAIF_URL_PROTOCOL |
674 OAIF_REGISTER_EXT;
675 return DynSHOpenWithDialog(nullptr, &info);
676 }
678 NS_IMETHODIMP
679 nsWindowsShellService::SetDefaultBrowser(bool aClaimAllTypes, bool aForAllUsers)
680 {
681 nsAutoString appHelperPath;
682 if (NS_FAILED(GetHelperPath(appHelperPath)))
683 return NS_ERROR_FAILURE;
685 if (aForAllUsers) {
686 appHelperPath.AppendLiteral(" /SetAsDefaultAppGlobal");
687 } else {
688 appHelperPath.AppendLiteral(" /SetAsDefaultAppUser");
689 }
691 nsresult rv = LaunchHelper(appHelperPath);
692 if (NS_SUCCEEDED(rv) && IsWin8OrLater()) {
693 if (aClaimAllTypes) {
694 rv = LaunchControlPanelDefaultPrograms();
695 // The above call should never really fail, but just in case
696 // fall back to showing the HTTP association screen only.
697 if (NS_FAILED(rv)) {
698 rv = LaunchHTTPHandlerPane();
699 }
700 } else {
701 rv = LaunchHTTPHandlerPane();
702 // The above calls hould never really fail, but just in case
703 // fallb ack to showing control panel for all defaults
704 if (NS_FAILED(rv)) {
705 rv = LaunchControlPanelDefaultPrograms();
706 }
707 }
708 }
710 return rv;
711 }
713 NS_IMETHODIMP
714 nsWindowsShellService::GetShouldCheckDefaultBrowser(bool* aResult)
715 {
716 NS_ENSURE_ARG_POINTER(aResult);
718 // If we've already checked, the browser has been started and this is a
719 // new window open, and we don't want to check again.
720 if (mCheckedThisSession) {
721 *aResult = false;
722 return NS_OK;
723 }
725 nsCOMPtr<nsIPrefBranch> prefs;
726 nsresult rv;
727 nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
728 NS_ENSURE_SUCCESS(rv, rv);
730 rv = pserve->GetBranch("", getter_AddRefs(prefs));
731 NS_ENSURE_SUCCESS(rv, rv);
733 return prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult);
734 }
736 NS_IMETHODIMP
737 nsWindowsShellService::SetShouldCheckDefaultBrowser(bool aShouldCheck)
738 {
739 nsCOMPtr<nsIPrefBranch> prefs;
740 nsresult rv;
742 nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
743 NS_ENSURE_SUCCESS(rv, rv);
745 rv = pserve->GetBranch("", getter_AddRefs(prefs));
746 NS_ENSURE_SUCCESS(rv, rv);
748 return prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck);
749 }
751 static nsresult
752 WriteBitmap(nsIFile* aFile, imgIContainer* aImage)
753 {
754 nsresult rv;
756 RefPtr<SourceSurface> surface =
757 aImage->GetFrame(imgIContainer::FRAME_FIRST,
758 imgIContainer::FLAG_SYNC_DECODE);
759 NS_ENSURE_TRUE(surface, NS_ERROR_FAILURE);
761 // For either of the following formats we want to set the biBitCount member
762 // of the BITMAPINFOHEADER struct to 32, below. For that value the bitmap
763 // format defines that the A8/X8 WORDs in the bitmap byte stream be ignored
764 // for the BI_RGB value we use for the biCompression member.
765 MOZ_ASSERT(surface->GetFormat() == SurfaceFormat::B8G8R8A8 ||
766 surface->GetFormat() == SurfaceFormat::B8G8R8X8);
768 RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
769 NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
771 int32_t width = dataSurface->GetSize().width;
772 int32_t height = dataSurface->GetSize().height;
773 int32_t bytesPerPixel = 4 * sizeof(uint8_t);
774 uint32_t bytesPerRow = bytesPerPixel * width;
776 // initialize these bitmap structs which we will later
777 // serialize directly to the head of the bitmap file
778 BITMAPINFOHEADER bmi;
779 bmi.biSize = sizeof(BITMAPINFOHEADER);
780 bmi.biWidth = width;
781 bmi.biHeight = height;
782 bmi.biPlanes = 1;
783 bmi.biBitCount = (WORD)bytesPerPixel*8;
784 bmi.biCompression = BI_RGB;
785 bmi.biSizeImage = bytesPerRow * height;
786 bmi.biXPelsPerMeter = 0;
787 bmi.biYPelsPerMeter = 0;
788 bmi.biClrUsed = 0;
789 bmi.biClrImportant = 0;
791 BITMAPFILEHEADER bf;
792 bf.bfType = 0x4D42; // 'BM'
793 bf.bfReserved1 = 0;
794 bf.bfReserved2 = 0;
795 bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
796 bf.bfSize = bf.bfOffBits + bmi.biSizeImage;
798 // get a file output stream
799 nsCOMPtr<nsIOutputStream> stream;
800 rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile);
801 NS_ENSURE_SUCCESS(rv, rv);
803 DataSourceSurface::MappedSurface map;
804 if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
805 return NS_ERROR_FAILURE;
806 }
808 // write the bitmap headers and rgb pixel data to the file
809 rv = NS_ERROR_FAILURE;
810 if (stream) {
811 uint32_t written;
812 stream->Write((const char*)&bf, sizeof(BITMAPFILEHEADER), &written);
813 if (written == sizeof(BITMAPFILEHEADER)) {
814 stream->Write((const char*)&bmi, sizeof(BITMAPINFOHEADER), &written);
815 if (written == sizeof(BITMAPINFOHEADER)) {
816 // write out the image data backwards because the desktop won't
817 // show bitmaps with negative heights for top-to-bottom
818 uint32_t i = map.mStride * height;
819 do {
820 i -= map.mStride;
821 stream->Write(((const char*)map.mData) + i, bytesPerRow, &written);
822 if (written == bytesPerRow) {
823 rv = NS_OK;
824 } else {
825 rv = NS_ERROR_FAILURE;
826 break;
827 }
828 } while (i != 0);
829 }
830 }
832 stream->Close();
833 }
835 dataSurface->Unmap();
837 return rv;
838 }
840 NS_IMETHODIMP
841 nsWindowsShellService::SetDesktopBackground(nsIDOMElement* aElement,
842 int32_t aPosition)
843 {
844 nsresult rv;
846 nsCOMPtr<imgIContainer> container;
847 nsCOMPtr<nsIDOMHTMLImageElement> imgElement(do_QueryInterface(aElement));
848 if (!imgElement) {
849 // XXX write background loading stuff!
850 return NS_ERROR_NOT_AVAILABLE;
851 }
852 else {
853 nsCOMPtr<nsIImageLoadingContent> imageContent =
854 do_QueryInterface(aElement, &rv);
855 if (!imageContent)
856 return rv;
858 // get the image container
859 nsCOMPtr<imgIRequest> request;
860 rv = imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
861 getter_AddRefs(request));
862 if (!request)
863 return rv;
864 rv = request->GetImage(getter_AddRefs(container));
865 if (!container)
866 return NS_ERROR_FAILURE;
867 }
869 // get the file name from localized strings
870 nsCOMPtr<nsIStringBundleService>
871 bundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
872 NS_ENSURE_SUCCESS(rv, rv);
874 nsCOMPtr<nsIStringBundle> shellBundle;
875 rv = bundleService->CreateBundle(SHELLSERVICE_PROPERTIES,
876 getter_AddRefs(shellBundle));
877 NS_ENSURE_SUCCESS(rv, rv);
879 // e.g. "Desktop Background.bmp"
880 nsString fileLeafName;
881 rv = shellBundle->GetStringFromName
882 (MOZ_UTF16("desktopBackgroundLeafNameWin"),
883 getter_Copies(fileLeafName));
884 NS_ENSURE_SUCCESS(rv, rv);
886 // get the profile root directory
887 nsCOMPtr<nsIFile> file;
888 rv = NS_GetSpecialDirectory(NS_APP_APPLICATION_REGISTRY_DIR,
889 getter_AddRefs(file));
890 NS_ENSURE_SUCCESS(rv, rv);
892 // eventually, the path is "%APPDATA%\Mozilla\Firefox\Desktop Background.bmp"
893 rv = file->Append(fileLeafName);
894 NS_ENSURE_SUCCESS(rv, rv);
896 nsAutoString path;
897 rv = file->GetPath(path);
898 NS_ENSURE_SUCCESS(rv, rv);
900 // write the bitmap to a file in the profile directory
901 rv = WriteBitmap(file, container);
903 // if the file was written successfully, set it as the system wallpaper
904 if (NS_SUCCEEDED(rv)) {
905 nsCOMPtr<nsIWindowsRegKey> regKey =
906 do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
907 NS_ENSURE_SUCCESS(rv, rv);
909 rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
910 NS_LITERAL_STRING("Control Panel\\Desktop"),
911 nsIWindowsRegKey::ACCESS_SET_VALUE);
912 NS_ENSURE_SUCCESS(rv, rv);
914 nsAutoString tile;
915 nsAutoString style;
916 switch (aPosition) {
917 case BACKGROUND_TILE:
918 style.AssignLiteral("0");
919 tile.AssignLiteral("1");
920 break;
921 case BACKGROUND_CENTER:
922 style.AssignLiteral("0");
923 tile.AssignLiteral("0");
924 break;
925 case BACKGROUND_STRETCH:
926 style.AssignLiteral("2");
927 tile.AssignLiteral("0");
928 break;
929 case BACKGROUND_FILL:
930 style.AssignLiteral("10");
931 tile.AssignLiteral("0");
932 break;
933 case BACKGROUND_FIT:
934 style.AssignLiteral("6");
935 tile.AssignLiteral("0");
936 break;
937 }
939 rv = regKey->WriteStringValue(NS_LITERAL_STRING("TileWallpaper"), tile);
940 NS_ENSURE_SUCCESS(rv, rv);
941 rv = regKey->WriteStringValue(NS_LITERAL_STRING("WallpaperStyle"), style);
942 NS_ENSURE_SUCCESS(rv, rv);
943 rv = regKey->Close();
944 NS_ENSURE_SUCCESS(rv, rv);
946 ::SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, (PVOID)path.get(),
947 SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
948 }
949 return rv;
950 }
952 NS_IMETHODIMP
953 nsWindowsShellService::OpenApplication(int32_t aApplication)
954 {
955 nsAutoString application;
956 switch (aApplication) {
957 case nsIShellService::APPLICATION_MAIL:
958 application.AssignLiteral("Mail");
959 break;
960 case nsIShellService::APPLICATION_NEWS:
961 application.AssignLiteral("News");
962 break;
963 }
965 // The Default Client section of the Windows Registry looks like this:
966 //
967 // Clients\aClient\
968 // e.g. aClient = "Mail"...
969 // \Mail\(default) = Client Subkey Name
970 // \Client Subkey Name
971 // \Client Subkey Name\shell\open\command\
972 // \Client Subkey Name\shell\open\command\(default) = path to exe
973 //
975 // Find the default application for this class.
976 HKEY theKey;
977 nsresult rv = OpenKeyForReading(HKEY_CLASSES_ROOT, application, &theKey);
978 if (NS_FAILED(rv))
979 return rv;
981 wchar_t buf[MAX_BUF];
982 DWORD type, len = sizeof buf;
983 DWORD res = ::RegQueryValueExW(theKey, EmptyString().get(), 0,
984 &type, (LPBYTE)&buf, &len);
986 if (REG_FAILED(res) || !*buf)
987 return NS_OK;
989 // Close the key we opened.
990 ::RegCloseKey(theKey);
992 // Find the "open" command
993 application.AppendLiteral("\\");
994 application.Append(buf);
995 application.AppendLiteral("\\shell\\open\\command");
997 rv = OpenKeyForReading(HKEY_CLASSES_ROOT, application, &theKey);
998 if (NS_FAILED(rv))
999 return rv;
1001 ::ZeroMemory(buf, sizeof(buf));
1002 len = sizeof buf;
1003 res = ::RegQueryValueExW(theKey, EmptyString().get(), 0,
1004 &type, (LPBYTE)&buf, &len);
1005 if (REG_FAILED(res) || !*buf)
1006 return NS_ERROR_FAILURE;
1008 // Close the key we opened.
1009 ::RegCloseKey(theKey);
1011 // Look for any embedded environment variables and substitute their
1012 // values, as |::CreateProcessW| is unable to do this.
1013 nsAutoString path(buf);
1014 int32_t end = path.Length();
1015 int32_t cursor = 0, temp = 0;
1016 ::ZeroMemory(buf, sizeof(buf));
1017 do {
1018 cursor = path.FindChar('%', cursor);
1019 if (cursor < 0)
1020 break;
1022 temp = path.FindChar('%', cursor + 1);
1023 ++cursor;
1025 ::ZeroMemory(&buf, sizeof(buf));
1027 ::GetEnvironmentVariableW(nsAutoString(Substring(path, cursor, temp - cursor)).get(),
1028 buf, sizeof(buf));
1030 // "+ 2" is to subtract the extra characters used to delimit the environment
1031 // variable ('%').
1032 path.Replace((cursor - 1), temp - cursor + 2, nsDependentString(buf));
1034 ++cursor;
1035 }
1036 while (cursor < end);
1038 STARTUPINFOW si;
1039 PROCESS_INFORMATION pi;
1041 ::ZeroMemory(&si, sizeof(STARTUPINFOW));
1042 ::ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
1044 BOOL success = ::CreateProcessW(nullptr, (LPWSTR)path.get(), nullptr,
1045 nullptr, FALSE, 0, nullptr, nullptr,
1046 &si, &pi);
1047 if (!success)
1048 return NS_ERROR_FAILURE;
1050 return NS_OK;
1051 }
1053 NS_IMETHODIMP
1054 nsWindowsShellService::GetDesktopBackgroundColor(uint32_t* aColor)
1055 {
1056 uint32_t color = ::GetSysColor(COLOR_DESKTOP);
1057 *aColor = (GetRValue(color) << 16) | (GetGValue(color) << 8) | GetBValue(color);
1058 return NS_OK;
1059 }
1061 NS_IMETHODIMP
1062 nsWindowsShellService::SetDesktopBackgroundColor(uint32_t aColor)
1063 {
1064 int aParameters[2] = { COLOR_BACKGROUND, COLOR_DESKTOP };
1065 BYTE r = (aColor >> 16);
1066 BYTE g = (aColor << 16) >> 24;
1067 BYTE b = (aColor << 24) >> 24;
1068 COLORREF colors[2] = { RGB(r,g,b), RGB(r,g,b) };
1070 ::SetSysColors(sizeof(aParameters) / sizeof(int), aParameters, colors);
1072 nsresult rv;
1073 nsCOMPtr<nsIWindowsRegKey> regKey =
1074 do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
1075 NS_ENSURE_SUCCESS(rv, rv);
1077 rv = regKey->Create(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
1078 NS_LITERAL_STRING("Control Panel\\Colors"),
1079 nsIWindowsRegKey::ACCESS_SET_VALUE);
1080 NS_ENSURE_SUCCESS(rv, rv);
1082 wchar_t rgb[12];
1083 _snwprintf(rgb, 12, L"%u %u %u", r, g, b);
1085 rv = regKey->WriteStringValue(NS_LITERAL_STRING("Background"),
1086 nsDependentString(rgb));
1087 NS_ENSURE_SUCCESS(rv, rv);
1089 return regKey->Close();
1090 }
1092 nsWindowsShellService::nsWindowsShellService() :
1093 mCheckedThisSession(false)
1094 {
1095 }
1097 nsWindowsShellService::~nsWindowsShellService()
1098 {
1099 }
1101 NS_IMETHODIMP
1102 nsWindowsShellService::OpenApplicationWithURI(nsIFile* aApplication,
1103 const nsACString& aURI)
1104 {
1105 nsresult rv;
1106 nsCOMPtr<nsIProcess> process =
1107 do_CreateInstance("@mozilla.org/process/util;1", &rv);
1108 if (NS_FAILED(rv))
1109 return rv;
1111 rv = process->Init(aApplication);
1112 if (NS_FAILED(rv))
1113 return rv;
1115 const nsCString spec(aURI);
1116 const char* specStr = spec.get();
1117 return process->Run(false, &specStr, 1);
1118 }
1120 NS_IMETHODIMP
1121 nsWindowsShellService::GetDefaultFeedReader(nsIFile** _retval)
1122 {
1123 *_retval = nullptr;
1125 nsresult rv;
1126 nsCOMPtr<nsIWindowsRegKey> regKey =
1127 do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
1128 NS_ENSURE_SUCCESS(rv, rv);
1130 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
1131 NS_LITERAL_STRING("feed\\shell\\open\\command"),
1132 nsIWindowsRegKey::ACCESS_READ);
1133 NS_ENSURE_SUCCESS(rv, rv);
1135 nsAutoString path;
1136 rv = regKey->ReadStringValue(EmptyString(), path);
1137 NS_ENSURE_SUCCESS(rv, rv);
1138 if (path.IsEmpty())
1139 return NS_ERROR_FAILURE;
1141 if (path.First() == '"') {
1142 // Everything inside the quotes
1143 path = Substring(path, 1, path.FindChar('"', 1) - 1);
1144 }
1145 else {
1146 // Everything up to the first space
1147 path = Substring(path, 0, path.FindChar(' '));
1148 }
1150 nsCOMPtr<nsIFile> defaultReader =
1151 do_CreateInstance("@mozilla.org/file/local;1", &rv);
1152 NS_ENSURE_SUCCESS(rv, rv);
1154 rv = defaultReader->InitWithPath(path);
1155 NS_ENSURE_SUCCESS(rv, rv);
1157 bool exists;
1158 rv = defaultReader->Exists(&exists);
1159 NS_ENSURE_SUCCESS(rv, rv);
1160 if (!exists)
1161 return NS_ERROR_FAILURE;
1163 NS_ADDREF(*_retval = defaultReader);
1164 return NS_OK;
1165 }