Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / browser / components / shell / src / nsWindowsShellService.cpp
blob46247fe5d34ea85ab87dc715cbea5a8be52f685e
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Shell Service.
17 * The Initial Developer of the Original Code is mozilla.org.
18 * Portions created by the Initial Developer are Copyright (C) 2004
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
22 * Ben Goodger <ben@mozilla.org> (Clients, Mail, New Default Browser)
23 * Joe Hewitt <hewitt@netscape.com> (Set Background)
24 * Blake Ross <blake@cs.stanford.edu> (Desktop Color, DDE support)
25 * Jungshik Shin <jshin@mailaps.org> (I18N)
26 * Robert Strong <robert.bugzilla@gmail.com>
27 * Asaf Romano <mano@mozilla.com>
28 * Ryan Jones <sciguyryan@gmail.com>
30 * Alternatively, the contents of this file may be used under the terms of
31 * either the GNU General Public License Version 2 or later (the "GPL"), or
32 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 * in which case the provisions of the GPL or the LGPL are applicable instead
34 * of those above. If you wish to allow use of your version of this file only
35 * under the terms of either the GPL or the LGPL, and not to allow others to
36 * use your version of this file under the terms of the MPL, indicate your
37 * decision by deleting the provisions above and replace them with the notice
38 * and other provisions required by the GPL or the LGPL. If you do not delete
39 * the provisions above, a recipient may use your version of this file under
40 * the terms of any one of the MPL, the GPL or the LGPL.
42 * ***** END LICENSE BLOCK ***** */
44 #include "gfxIImageFrame.h"
45 #include "imgIContainer.h"
46 #include "imgIRequest.h"
47 #include "nsIDOMDocument.h"
48 #include "nsIDOMElement.h"
49 #include "nsIDOMHTMLImageElement.h"
50 #include "nsIImageLoadingContent.h"
51 #include "nsIPrefService.h"
52 #include "nsIPrefLocalizedString.h"
53 #include "nsIServiceManager.h"
54 #include "nsIStringBundle.h"
55 #include "nsNetUtil.h"
56 #include "nsShellService.h"
57 #include "nsWindowsShellService.h"
58 #include "nsIProcess.h"
59 #include "nsICategoryManager.h"
60 #include "nsBrowserCompsCID.h"
61 #include "nsDirectoryServiceUtils.h"
62 #include "nsAppDirectoryServiceDefs.h"
63 #include "nsDirectoryServiceDefs.h"
64 #include "nsIWindowsRegKey.h"
65 #include "nsUnicharUtils.h"
67 #include "windows.h"
68 #include "shellapi.h"
70 #ifdef _WIN32_WINNT
71 #undef _WIN32_WINNT
72 #endif
73 #define _WIN32_WINNT 0x0600
74 #define INITGUID
75 #include <shlobj.h>
77 #include <mbstring.h>
79 #ifndef MAX_BUF
80 #define MAX_BUF 4096
81 #endif
83 #define REG_SUCCEEDED(val) \
84 (val == ERROR_SUCCESS)
86 #define REG_FAILED(val) \
87 (val != ERROR_SUCCESS)
89 NS_IMPL_ISUPPORTS2(nsWindowsShellService, nsIWindowsShellService, nsIShellService)
91 static nsresult
92 OpenKeyForReading(HKEY aKeyRoot, const nsAString& aKeyName, HKEY* aKey)
94 const nsString &flatName = PromiseFlatString(aKeyName);
96 DWORD res = ::RegOpenKeyExW(aKeyRoot, flatName.get(), 0, KEY_READ, aKey);
97 switch (res) {
98 case ERROR_SUCCESS:
99 break;
100 case ERROR_ACCESS_DENIED:
101 return NS_ERROR_FILE_ACCESS_DENIED;
102 case ERROR_FILE_NOT_FOUND:
103 return NS_ERROR_NOT_AVAILABLE;
106 return NS_OK;
109 ///////////////////////////////////////////////////////////////////////////////
110 // Default Browser Registry Settings
112 // The setting of these values are made by an external binary since writing
113 // these values may require elevation.
115 // - File Extension Mappings
116 // -----------------------
117 // The following file extensions:
118 // .htm .html .shtml .xht .xhtml
119 // are mapped like so:
121 // HKCU\SOFTWARE\Classes\.<ext>\ (default) REG_SZ FirefoxHTML
123 // as aliases to the class:
125 // HKCU\SOFTWARE\Classes\FirefoxHTML\
126 // DefaultIcon (default) REG_SZ <apppath>,1
127 // shell\open\command (default) REG_SZ <apppath> -requestPending -osint -url "%1"
128 // shell\open\ddeexec (default) REG_SZ "%1",,0,0,,,,
129 // shell\open\ddeexec NoActivateHandler REG_SZ
130 // \Application (default) REG_SZ Firefox
131 // \Topic (default) REG_SZ WWW_OpenURL
133 // - Windows Vista Protocol Handler
135 // HKCU\SOFTWARE\Classes\FirefoxURL\ (default) REG_SZ <appname> URL
136 // EditFlags REG_DWORD 2
137 // FriendlyTypeName REG_SZ <appname> URL
138 // DefaultIcon (default) REG_SZ <apppath>,1
139 // shell\open\command (default) REG_SZ <apppath> -requestPending -osint -url "%1"
140 // shell\open\ddeexec (default) REG_SZ "%1",,0,0,,,,
141 // shell\open\ddeexec NoActivateHandler REG_SZ
142 // \Application (default) REG_SZ Firefox
143 // \Topic (default) REG_SZ WWW_OpenURL
145 // - Protocol Mappings
146 // -----------------
147 // The following protocols:
148 // HTTP, HTTPS, FTP
149 // are mapped like so:
151 // HKCU\SOFTWARE\Classes\<protocol>\
152 // DefaultIcon (default) REG_SZ <apppath>,1
153 // shell\open\command (default) REG_SZ <apppath> -requestPending -osint -url "%1"
154 // shell\open\ddeexec (default) REG_SZ "%1",,0,0,,,,
155 // shell\open\ddeexec NoActivateHandler REG_SZ
156 // \Application (default) REG_SZ Firefox
157 // \Topic (default) REG_SZ WWW_OpenURL
159 // - Windows Start Menu (Win2K SP2, XP SP1, and newer)
160 // -------------------------------------------------
161 // The following keys are set to make Firefox appear in the Start Menu as the
162 // browser:
164 // HKCU\SOFTWARE\Clients\StartMenuInternet\FIREFOX.EXE\
165 // (default) REG_SZ <appname>
166 // DefaultIcon (default) REG_SZ <apppath>,0
167 // InstallInfo HideIconsCommand REG_SZ <uninstpath> /HideShortcuts
168 // InstallInfo IconsVisible REG_DWORD 1
169 // InstallInfo ReinstallCommand REG_SZ <uninstpath> /SetAsDefaultAppGlobal
170 // InstallInfo ShowIconsCommand REG_SZ <uninstpath> /ShowShortcuts
171 // shell\open\command (default) REG_SZ <apppath>
172 // shell\properties (default) REG_SZ <appname> &Options
173 // shell\properties\command (default) REG_SZ <apppath> -preferences
174 // shell\safemode (default) REG_SZ <appname> &Safe Mode
175 // shell\safemode\command (default) REG_SZ <apppath> -safe-mode
178 typedef enum {
179 NO_SUBSTITUTION = 0x00,
180 APP_PATH_SUBSTITUTION = 0x01,
181 EXE_NAME_SUBSTITUTION = 0x02
182 } SettingFlags;
184 typedef struct {
185 char* keyName;
186 char* valueName;
187 char* valueData;
189 PRInt32 flags;
190 } SETTING;
192 #define APP_REG_NAME L"Firefox"
193 #define DI "\\DefaultIcon"
194 #define SOP "\\shell\\open\\command"
196 #define CLS_HTML "FirefoxHTML"
197 #define CLS_URL "FirefoxURL"
198 #define VAL_FILE_ICON "%APPPATH%,1"
199 #define VAL_OPEN "\"%APPPATH%\" -requestPending -osint -url \"%1\""
201 #define MAKE_KEY_NAME1(PREFIX, MID) \
202 PREFIX MID
204 // The DefaultIcon registry key value should never be used when checking if
205 // Firefox is the default browser since other applications (e.g. MS Office) may
206 // modify the DefaultIcon registry key value to add Icon Handlers.
207 // see http://msdn2.microsoft.com/en-us/library/aa969357.aspx for more info.
208 static SETTING gSettings[] = {
209 // File Extension Class - as of 1.8.1.2 the value for VAL_OPEN is also checked
210 // for CLS_HTML since Firefox should also own opeing local files when set as
211 // the default browser.
212 { MAKE_KEY_NAME1(CLS_HTML, SOP), "", VAL_OPEN, APP_PATH_SUBSTITUTION },
214 // Protocol Handler Class - for Vista and above
215 { MAKE_KEY_NAME1(CLS_URL, SOP), "", VAL_OPEN, APP_PATH_SUBSTITUTION },
217 // Protocol Handlers
218 { MAKE_KEY_NAME1("HTTP", DI), "", VAL_FILE_ICON, APP_PATH_SUBSTITUTION },
219 { MAKE_KEY_NAME1("HTTP", SOP), "", VAL_OPEN, APP_PATH_SUBSTITUTION },
220 { MAKE_KEY_NAME1("HTTPS", DI), "", VAL_FILE_ICON, APP_PATH_SUBSTITUTION },
221 { MAKE_KEY_NAME1("HTTPS", SOP), "", VAL_OPEN, APP_PATH_SUBSTITUTION }
224 PRBool
225 nsWindowsShellService::IsDefaultBrowserVista(PRBool* aIsDefaultBrowser)
227 #if !defined(MOZ_DISABLE_VISTA_SDK_REQUIREMENTS)
228 IApplicationAssociationRegistration* pAAR;
230 HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
231 NULL,
232 CLSCTX_INPROC,
233 IID_IApplicationAssociationRegistration,
234 (void**)&pAAR);
236 if (SUCCEEDED(hr)) {
237 hr = pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE,
238 APP_REG_NAME,
239 aIsDefaultBrowser);
241 pAAR->Release();
242 return PR_TRUE;
244 #endif
245 return PR_FALSE;
248 NS_IMETHODIMP
249 nsWindowsShellService::IsDefaultBrowser(PRBool aStartupCheck,
250 PRBool* aIsDefaultBrowser)
252 // If this is the first browser window, maintain internal state that we've
253 // checked this session (so that subsequent window opens don't show the
254 // default browser dialog).
255 if (aStartupCheck)
256 mCheckedThisSession = PR_TRUE;
258 SETTING* settings;
259 SETTING* end = gSettings + sizeof(gSettings)/sizeof(SETTING);
261 *aIsDefaultBrowser = PR_TRUE;
263 PRUnichar exePath[MAX_BUF];
264 if (!::GetModuleFileNameW(0, exePath, MAX_BUF))
265 return NS_ERROR_FAILURE;
267 nsAutoString appLongPath(exePath);
269 // Support short path to the exe so if it is already set the user is not
270 // prompted to set the default browser again.
271 if (!::GetShortPathNameW(exePath, exePath, sizeof(exePath)))
272 return NS_ERROR_FAILURE;
274 nsAutoString appShortPath(exePath);
275 ToUpperCase(appShortPath);
277 nsCOMPtr<nsILocalFile> lf;
278 nsresult rv = NS_NewLocalFile(appShortPath, PR_TRUE,
279 getter_AddRefs(lf));
280 if (NS_FAILED(rv))
281 return rv;
283 nsAutoString exeName;
284 rv = lf->GetLeafName(exeName);
285 if (NS_FAILED(rv))
286 return rv;
287 ToUpperCase(exeName);
289 PRUnichar currValue[MAX_BUF];
290 for (settings = gSettings; settings < end; ++settings) {
291 NS_ConvertUTF8toUTF16 dataLongPath(settings->valueData);
292 NS_ConvertUTF8toUTF16 dataShortPath(settings->valueData);
293 NS_ConvertUTF8toUTF16 key(settings->keyName);
294 NS_ConvertUTF8toUTF16 value(settings->valueName);
295 if (settings->flags & APP_PATH_SUBSTITUTION) {
296 PRInt32 offset = dataLongPath.Find("%APPPATH%");
297 dataLongPath.Replace(offset, 9, appLongPath);
298 // Remove the quotes around %APPPATH% in VAL_OPEN for short paths
299 PRInt32 offsetQuoted = dataShortPath.Find("\"%APPPATH%\"");
300 if (offsetQuoted != -1)
301 dataShortPath.Replace(offsetQuoted, 11, appShortPath);
302 else
303 dataShortPath.Replace(offset, 9, appShortPath);
305 if (settings->flags & EXE_NAME_SUBSTITUTION) {
306 PRInt32 offset = key.Find("%APPEXE%");
307 key.Replace(offset, 8, exeName);
310 ::ZeroMemory(currValue, sizeof(currValue));
311 HKEY theKey;
312 rv = OpenKeyForReading(HKEY_CLASSES_ROOT, key, &theKey);
313 if (NS_FAILED(rv)) {
314 *aIsDefaultBrowser = PR_FALSE;
315 return NS_OK;
318 DWORD len = sizeof currValue;
319 DWORD res = ::RegQueryValueExW(theKey, PromiseFlatString(value).get(),
320 NULL, NULL, (LPBYTE)currValue, &len);
321 // Close the key we opened.
322 ::RegCloseKey(theKey);
323 if (REG_FAILED(res) ||
324 !dataLongPath.Equals(currValue, CaseInsensitiveCompare) &&
325 !dataShortPath.Equals(currValue, CaseInsensitiveCompare)) {
326 // Key wasn't set, or was set to something other than our registry entry
327 *aIsDefaultBrowser = PR_FALSE;
328 return NS_OK;
332 // Only check if Firefox is the default browser on Vista if the previous
333 // checks show that Firefox is the default browser.
334 if (*aIsDefaultBrowser)
335 IsDefaultBrowserVista(aIsDefaultBrowser);
337 return NS_OK;
340 NS_IMETHODIMP
341 nsWindowsShellService::SetDefaultBrowser(PRBool aClaimAllTypes, PRBool aForAllUsers)
343 nsresult rv;
344 nsCOMPtr<nsIProperties> directoryService =
345 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
346 NS_ENSURE_SUCCESS(rv, rv);
348 nsCOMPtr<nsILocalFile> appHelper;
349 rv = directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(appHelper));
350 NS_ENSURE_SUCCESS(rv, rv);
352 rv = appHelper->AppendNative(NS_LITERAL_CSTRING("uninstall"));
353 NS_ENSURE_SUCCESS(rv, rv);
355 rv = appHelper->AppendNative(NS_LITERAL_CSTRING("helper.exe"));
356 NS_ENSURE_SUCCESS(rv, rv);
358 nsAutoString appHelperPath;
359 rv = appHelper->GetPath(appHelperPath);
360 NS_ENSURE_SUCCESS(rv, rv);
362 if (aForAllUsers) {
363 appHelperPath.AppendLiteral(" /SetAsDefaultAppGlobal");
364 } else {
365 appHelperPath.AppendLiteral(" /SetAsDefaultAppUser");
368 STARTUPINFOW si = {sizeof(si), 0};
369 PROCESS_INFORMATION pi = {0};
371 BOOL ok = CreateProcessW(NULL, (LPWSTR)appHelperPath.get(), NULL, NULL,
372 FALSE, 0, NULL, NULL, &si, &pi);
374 if (!ok)
375 return NS_ERROR_FAILURE;
377 CloseHandle(pi.hProcess);
378 CloseHandle(pi.hThread);
380 return NS_OK;
383 NS_IMETHODIMP
384 nsWindowsShellService::GetShouldCheckDefaultBrowser(PRBool* aResult)
386 // If we've already checked, the browser has been started and this is a
387 // new window open, and we don't want to check again.
388 if (mCheckedThisSession) {
389 *aResult = PR_FALSE;
390 return NS_OK;
393 nsCOMPtr<nsIPrefBranch> prefs;
394 nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID));
395 if (pserve)
396 pserve->GetBranch("", getter_AddRefs(prefs));
398 prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult);
400 return NS_OK;
403 NS_IMETHODIMP
404 nsWindowsShellService::SetShouldCheckDefaultBrowser(PRBool aShouldCheck)
406 nsCOMPtr<nsIPrefBranch> prefs;
407 nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID));
408 if (pserve)
409 pserve->GetBranch("", getter_AddRefs(prefs));
411 prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck);
413 return NS_OK;
416 static nsresult
417 WriteBitmap(nsIFile* aFile, gfxIImageFrame* aImage)
419 PRInt32 width, height;
420 aImage->GetWidth(&width);
421 aImage->GetHeight(&height);
423 PRUint8* bits;
424 PRUint32 length;
425 aImage->LockImageData();
426 aImage->GetImageData(&bits, &length);
427 if (!bits) {
428 aImage->UnlockImageData();
429 return NS_ERROR_FAILURE;
432 PRUint32 bpr;
433 aImage->GetImageBytesPerRow(&bpr);
434 PRInt32 bitCount = bpr/width;
436 // initialize these bitmap structs which we will later
437 // serialize directly to the head of the bitmap file
438 BITMAPINFOHEADER bmi;
439 bmi.biSize = sizeof(BITMAPINFOHEADER);
440 bmi.biWidth = width;
441 bmi.biHeight = height;
442 bmi.biPlanes = 1;
443 bmi.biBitCount = (WORD)bitCount*8;
444 bmi.biCompression = BI_RGB;
445 bmi.biSizeImage = length;
446 bmi.biXPelsPerMeter = 0;
447 bmi.biYPelsPerMeter = 0;
448 bmi.biClrUsed = 0;
449 bmi.biClrImportant = 0;
451 BITMAPFILEHEADER bf;
452 bf.bfType = 0x4D42; // 'BM'
453 bf.bfReserved1 = 0;
454 bf.bfReserved2 = 0;
455 bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
456 bf.bfSize = bf.bfOffBits + bmi.biSizeImage;
458 // get a file output stream
459 nsCOMPtr<nsIOutputStream> stream;
460 nsresult rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile);
461 NS_ENSURE_SUCCESS(rv, rv);
463 // write the bitmap headers and rgb pixel data to the file
464 rv = NS_ERROR_FAILURE;
465 if (stream) {
466 PRUint32 written;
467 stream->Write((const char*)&bf, sizeof(BITMAPFILEHEADER), &written);
468 if (written == sizeof(BITMAPFILEHEADER)) {
469 stream->Write((const char*)&bmi, sizeof(BITMAPINFOHEADER), &written);
470 if (written == sizeof(BITMAPINFOHEADER)) {
471 // write out the image data backwards because the desktop won't
472 // show bitmaps with negative heights for top-to-bottom
473 PRUint32 i = length;
474 do {
475 i -= bpr;
476 stream->Write(((const char*)bits) + i, bpr, &written);
477 if (written == bpr) {
478 rv = NS_OK;
479 } else {
480 rv = NS_ERROR_FAILURE;
481 break;
483 } while (i != 0);
487 stream->Close();
490 aImage->UnlockImageData();
491 return rv;
494 NS_IMETHODIMP
495 nsWindowsShellService::SetDesktopBackground(nsIDOMElement* aElement,
496 PRInt32 aPosition)
498 nsresult rv;
500 nsCOMPtr<gfxIImageFrame> gfxFrame;
502 nsCOMPtr<nsIDOMHTMLImageElement> imgElement(do_QueryInterface(aElement));
503 if (!imgElement) {
504 // XXX write background loading stuff!
506 else {
507 nsCOMPtr<nsIImageLoadingContent> imageContent =
508 do_QueryInterface(aElement, &rv);
509 if (!imageContent)
510 return rv;
512 // get the image container
513 nsCOMPtr<imgIRequest> request;
514 rv = imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
515 getter_AddRefs(request));
516 if (!request)
517 return rv;
518 nsCOMPtr<imgIContainer> container;
519 rv = request->GetImage(getter_AddRefs(container));
520 if (!container)
521 return NS_ERROR_FAILURE;
523 // get the current frame, which holds the image data
524 container->GetCurrentFrame(getter_AddRefs(gfxFrame));
527 if (!gfxFrame)
528 return NS_ERROR_FAILURE;
530 // get the file name from localized strings
531 nsCOMPtr<nsIStringBundleService>
532 bundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
533 NS_ENSURE_SUCCESS(rv, rv);
535 nsCOMPtr<nsIStringBundle> shellBundle;
536 rv = bundleService->CreateBundle(SHELLSERVICE_PROPERTIES,
537 getter_AddRefs(shellBundle));
538 NS_ENSURE_SUCCESS(rv, rv);
540 // e.g. "Desktop Background.bmp"
541 nsString fileLeafName;
542 rv = shellBundle->GetStringFromName
543 (NS_LITERAL_STRING("desktopBackgroundLeafNameWin").get(),
544 getter_Copies(fileLeafName));
545 NS_ENSURE_SUCCESS(rv, rv);
547 // get the profile root directory
548 nsCOMPtr<nsIFile> file;
549 rv = NS_GetSpecialDirectory(NS_APP_APPLICATION_REGISTRY_DIR,
550 getter_AddRefs(file));
551 NS_ENSURE_SUCCESS(rv, rv);
553 // eventually, the path is "%APPDATA%\Mozilla\Firefox\Desktop Background.bmp"
554 rv = file->Append(fileLeafName);
555 NS_ENSURE_SUCCESS(rv, rv);
557 nsAutoString path;
558 rv = file->GetPath(path);
559 NS_ENSURE_SUCCESS(rv, rv);
561 // write the bitmap to a file in the profile directory
562 rv = WriteBitmap(file, gfxFrame);
564 // if the file was written successfully, set it as the system wallpaper
565 if (NS_SUCCEEDED(rv)) {
566 PRBool result = PR_FALSE;
567 DWORD dwDisp = 0;
568 HKEY key;
569 // Try to create/open a subkey under HKLM.
570 DWORD res = ::RegCreateKeyExW(HKEY_CURRENT_USER,
571 L"Control Panel\\Desktop",
572 0, NULL, REG_OPTION_NON_VOLATILE,
573 KEY_WRITE, NULL, &key, &dwDisp);
574 if (REG_SUCCEEDED(res)) {
575 PRUnichar tile[2], style[2];
576 switch (aPosition) {
577 case BACKGROUND_TILE:
578 tile[0] = '1';
579 style[0] = '1';
580 break;
581 case BACKGROUND_CENTER:
582 tile[0] = '0';
583 style[0] = '0';
584 break;
585 case BACKGROUND_STRETCH:
586 tile[0] = '0';
587 style[0] = '2';
588 break;
590 tile[1] = '\0';
591 style[1] = '\0';
593 // The size is always 3 unicode characters.
594 PRInt32 size = 3 * sizeof(PRUnichar);
595 ::RegSetValueExW(key, L"TileWallpaper",
596 0, REG_SZ, (const BYTE *)tile, size);
597 ::RegSetValueExW(key, L"WallpaperStyle",
598 0, REG_SZ, (const BYTE *)style, size);
599 ::SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, (PVOID)path.get(),
600 SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);
601 // Close the key we opened.
602 ::RegCloseKey(key);
605 return rv;
608 NS_IMETHODIMP
609 nsWindowsShellService::OpenApplication(PRInt32 aApplication)
611 nsAutoString application;
612 switch (aApplication) {
613 case nsIShellService::APPLICATION_MAIL:
614 application.AssignLiteral("Mail");
615 break;
616 case nsIShellService::APPLICATION_NEWS:
617 application.AssignLiteral("News");
618 break;
621 // The Default Client section of the Windows Registry looks like this:
623 // Clients\aClient\
624 // e.g. aClient = "Mail"...
625 // \Mail\(default) = Client Subkey Name
626 // \Client Subkey Name
627 // \Client Subkey Name\shell\open\command\
628 // \Client Subkey Name\shell\open\command\(default) = path to exe
631 // Find the default application for this class.
632 HKEY theKey;
633 nsresult rv = OpenKeyForReading(HKEY_CLASSES_ROOT, application, &theKey);
634 if (NS_FAILED(rv))
635 return rv;
637 PRUnichar buf[MAX_BUF];
638 DWORD type, len = sizeof buf;
639 DWORD res = ::RegQueryValueExW(theKey, EmptyString().get(), 0,
640 &type, (LPBYTE)&buf, &len);
642 if (REG_FAILED(res) || !*buf)
643 return NS_OK;
645 // Close the key we opened.
646 ::RegCloseKey(theKey);
648 // Find the "open" command
649 application.AppendLiteral("\\");
650 application.Append(buf);
651 application.AppendLiteral("\\shell\\open\\command");
653 rv = OpenKeyForReading(HKEY_CLASSES_ROOT, application, &theKey);
654 if (NS_FAILED(rv))
655 return rv;
657 ::ZeroMemory(buf, sizeof(buf));
658 len = sizeof buf;
659 res = ::RegQueryValueExW(theKey, EmptyString().get(), 0,
660 &type, (LPBYTE)&buf, &len);
661 if (REG_FAILED(res) || !*buf)
662 return NS_ERROR_FAILURE;
664 // Close the key we opened.
665 ::RegCloseKey(theKey);
667 // Look for any embedded environment variables and substitute their
668 // values, as |::CreateProcessW| is unable to do this.
669 nsAutoString path(buf);
670 PRInt32 end = path.Length();
671 PRInt32 cursor = 0, temp = 0;
672 ::ZeroMemory(buf, sizeof(buf));
673 do {
674 cursor = path.FindChar('%', cursor);
675 if (cursor < 0)
676 break;
678 temp = path.FindChar('%', cursor + 1);
679 ++cursor;
681 ::ZeroMemory(&buf, sizeof(buf));
683 ::GetEnvironmentVariableW(nsAutoString(Substring(path, cursor, temp - cursor)).get(),
684 buf, sizeof(buf));
686 // "+ 2" is to subtract the extra characters used to delimit the environment
687 // variable ('%').
688 path.Replace((cursor - 1), temp - cursor + 2, nsDependentString(buf));
690 ++cursor;
692 while (cursor < end);
694 STARTUPINFOW si;
695 PROCESS_INFORMATION pi;
697 ::ZeroMemory(&si, sizeof(STARTUPINFOW));
698 ::ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
700 BOOL success = ::CreateProcessW(NULL, (LPWSTR)path.get(), NULL,
701 NULL, FALSE, 0, NULL, NULL,
702 &si, &pi);
703 if (!success)
704 return NS_ERROR_FAILURE;
706 return NS_OK;
709 NS_IMETHODIMP
710 nsWindowsShellService::GetDesktopBackgroundColor(PRUint32* aColor)
712 PRUint32 color = ::GetSysColor(COLOR_DESKTOP);
713 *aColor = (GetRValue(color) << 16) | (GetGValue(color) << 8) | GetBValue(color);
714 return NS_OK;
717 NS_IMETHODIMP
718 nsWindowsShellService::SetDesktopBackgroundColor(PRUint32 aColor)
720 int aParameters[2] = { COLOR_BACKGROUND, COLOR_DESKTOP };
721 BYTE r = (aColor >> 16);
722 BYTE g = (aColor << 16) >> 24;
723 BYTE b = (aColor << 24) >> 24;
724 COLORREF colors[2] = { RGB(r,g,b), RGB(r,g,b) };
726 ::SetSysColors(sizeof(aParameters) / sizeof(int), aParameters, colors);
728 PRBool result = PR_FALSE;
729 DWORD dwDisp = 0;
730 HKEY key;
731 // Try to create/open a subkey under HKLM.
732 DWORD rv = ::RegCreateKeyExW(HKEY_CURRENT_USER,
733 L"Control Panel\\Colors", 0, NULL,
734 REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
735 &key, &dwDisp);
737 if (REG_SUCCEEDED(rv)) {
738 char rgb[12];
739 sprintf((char*)rgb, "%u %u %u\0", r, g, b);
740 NS_ConvertUTF8toUTF16 backColor(rgb);
742 ::RegSetValueExW(key, L"Background",
743 0, REG_SZ, (const BYTE *)backColor.get(),
744 (backColor.Length() + 1) * sizeof(PRUnichar));
747 // Close the key we opened.
748 ::RegCloseKey(key);
749 return NS_OK;
752 NS_IMETHODIMP
753 nsWindowsShellService::GetUnreadMailCount(PRUint32* aCount)
755 *aCount = 0;
757 HKEY accountKey;
758 if (GetMailAccountKey(&accountKey)) {
759 DWORD type, unreadCount;
760 DWORD len = sizeof unreadCount;
761 DWORD res = ::RegQueryValueExW(accountKey, L"MessageCount", 0,
762 &type, (LPBYTE)&unreadCount, &len);
763 if (REG_SUCCEEDED(res))
764 *aCount = unreadCount;
766 // Close the key we opened.
767 ::RegCloseKey(accountKey);
770 return NS_OK;
773 PRBool
774 nsWindowsShellService::GetMailAccountKey(HKEY* aResult)
776 NS_NAMED_LITERAL_STRING(unread,
777 "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\UnreadMail\\");
779 HKEY mailKey;
780 DWORD res = ::RegOpenKeyExW(HKEY_CURRENT_USER, unread.get(), 0,
781 KEY_ENUMERATE_SUB_KEYS, &mailKey);
783 PRInt32 i = 0;
784 do {
785 PRUnichar subkeyName[MAX_BUF];
786 DWORD len = sizeof subkeyName;
787 res = ::RegEnumKeyExW(mailKey, i++, subkeyName, &len, NULL, NULL,
788 NULL, NULL);
789 if (REG_SUCCEEDED(res)) {
790 HKEY accountKey;
791 res = ::RegOpenKeyExW(mailKey, PromiseFlatString(subkeyName).get(),
792 0, KEY_READ, &accountKey);
793 if (REG_SUCCEEDED(res)) {
794 *aResult = accountKey;
796 // Close the key we opened.
797 ::RegCloseKey(mailKey);
799 return PR_TRUE;
802 else
803 break;
805 while (1);
807 // Close the key we opened.
808 ::RegCloseKey(mailKey);
809 return PR_FALSE;
812 NS_IMETHODIMP
813 nsWindowsShellService::OpenApplicationWithURI(nsILocalFile* aApplication,
814 const nsACString& aURI)
816 nsresult rv;
817 nsCOMPtr<nsIProcess> process =
818 do_CreateInstance("@mozilla.org/process/util;1", &rv);
819 if (NS_FAILED(rv))
820 return rv;
822 rv = process->Init(aApplication);
823 if (NS_FAILED(rv))
824 return rv;
826 const nsCString spec(aURI);
827 const char* specStr = spec.get();
828 PRUint32 pid;
829 return process->Run(PR_FALSE, &specStr, 1, &pid);
832 NS_IMETHODIMP
833 nsWindowsShellService::GetDefaultFeedReader(nsILocalFile** _retval)
835 *_retval = nsnull;
837 nsresult rv;
838 nsCOMPtr<nsIWindowsRegKey> regKey =
839 do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
840 NS_ENSURE_SUCCESS(rv, rv);
842 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
843 NS_LITERAL_STRING("feed\\shell\\open\\command"),
844 nsIWindowsRegKey::ACCESS_READ);
845 NS_ENSURE_SUCCESS(rv, rv);
847 nsAutoString path;
848 rv = regKey->ReadStringValue(EmptyString(), path);
849 NS_ENSURE_SUCCESS(rv, rv);
850 if (path.IsEmpty())
851 return NS_ERROR_FAILURE;
853 if (path.First() == '"') {
854 // Everything inside the quotes
855 path = Substring(path, 1, path.FindChar('"', 1) - 1);
857 else {
858 // Everything up to the first space
859 path = Substring(path, 0, path.FindChar(' '));
862 nsCOMPtr<nsILocalFile> defaultReader =
863 do_CreateInstance("@mozilla.org/file/local;1", &rv);
864 NS_ENSURE_SUCCESS(rv, rv);
866 rv = defaultReader->InitWithPath(path);
867 NS_ENSURE_SUCCESS(rv, rv);
869 PRBool exists;
870 rv = defaultReader->Exists(&exists);
871 NS_ENSURE_SUCCESS(rv, rv);
872 if (!exists)
873 return NS_ERROR_FAILURE;
875 NS_ADDREF(*_retval = defaultReader);
876 return NS_OK;