Bumping manifests a=b2g-bump
[gecko.git] / xpcom / ds / nsWindowsRegKey.cpp
blob9699e34e73aa35e9b788f33b7a93ef7d8c2bbff8
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include <windows.h>
8 #include <shlwapi.h>
9 #include <stdlib.h>
10 #include "nsWindowsRegKey.h"
11 #include "nsString.h"
12 #include "nsCOMPtr.h"
13 #include "mozilla/Attributes.h"
14 #include "nsAutoPtr.h"
16 //-----------------------------------------------------------------------------
18 // According to MSDN, the following limits apply (in characters excluding room
19 // for terminating null character):
20 #define MAX_KEY_NAME_LEN 255
21 #define MAX_VALUE_NAME_LEN 16383
23 class nsWindowsRegKey MOZ_FINAL : public nsIWindowsRegKey
25 public:
26 NS_DECL_ISUPPORTS
27 NS_DECL_NSIWINDOWSREGKEY
29 nsWindowsRegKey()
30 : mKey(nullptr)
31 , mWatchEvent(nullptr)
32 , mWatchRecursive(FALSE)
36 private:
37 ~nsWindowsRegKey()
39 Close();
42 HKEY mKey;
43 HANDLE mWatchEvent;
44 BOOL mWatchRecursive;
47 NS_IMPL_ISUPPORTS(nsWindowsRegKey, nsIWindowsRegKey)
49 NS_IMETHODIMP
50 nsWindowsRegKey::GetKey(HKEY* aKey)
52 *aKey = mKey;
53 return NS_OK;
56 NS_IMETHODIMP
57 nsWindowsRegKey::SetKey(HKEY aKey)
59 // We do not close the older aKey!
60 StopWatching();
62 mKey = aKey;
63 return NS_OK;
66 NS_IMETHODIMP
67 nsWindowsRegKey::Close()
69 StopWatching();
71 if (mKey) {
72 RegCloseKey(mKey);
73 mKey = nullptr;
75 return NS_OK;
78 NS_IMETHODIMP
79 nsWindowsRegKey::Open(uint32_t aRootKey, const nsAString& aPath,
80 uint32_t aMode)
82 Close();
84 LONG rv = RegOpenKeyExW((HKEY)(intptr_t)aRootKey,
85 PromiseFlatString(aPath).get(), 0, (REGSAM)aMode,
86 &mKey);
87 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
90 NS_IMETHODIMP
91 nsWindowsRegKey::Create(uint32_t aRootKey, const nsAString& aPath,
92 uint32_t aMode)
94 Close();
96 DWORD disposition;
97 LONG rv = RegCreateKeyExW((HKEY)(intptr_t)aRootKey,
98 PromiseFlatString(aPath).get(), 0, nullptr,
99 REG_OPTION_NON_VOLATILE, (REGSAM)aMode, nullptr,
100 &mKey, &disposition);
101 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
104 NS_IMETHODIMP
105 nsWindowsRegKey::OpenChild(const nsAString& aPath, uint32_t aMode,
106 nsIWindowsRegKey** aResult)
108 if (NS_WARN_IF(!mKey)) {
109 return NS_ERROR_NOT_INITIALIZED;
112 nsCOMPtr<nsIWindowsRegKey> child = new nsWindowsRegKey();
114 nsresult rv = child->Open((uintptr_t)mKey, aPath, aMode);
115 if (NS_FAILED(rv)) {
116 return rv;
119 child.swap(*aResult);
120 return NS_OK;
123 NS_IMETHODIMP
124 nsWindowsRegKey::CreateChild(const nsAString& aPath, uint32_t aMode,
125 nsIWindowsRegKey** aResult)
127 if (NS_WARN_IF(!mKey)) {
128 return NS_ERROR_NOT_INITIALIZED;
131 nsCOMPtr<nsIWindowsRegKey> child = new nsWindowsRegKey();
133 nsresult rv = child->Create((uintptr_t)mKey, aPath, aMode);
134 if (NS_FAILED(rv)) {
135 return rv;
138 child.swap(*aResult);
139 return NS_OK;
142 NS_IMETHODIMP
143 nsWindowsRegKey::GetChildCount(uint32_t* aResult)
145 if (NS_WARN_IF(!mKey)) {
146 return NS_ERROR_NOT_INITIALIZED;
149 DWORD numSubKeys;
150 LONG rv = RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, &numSubKeys,
151 nullptr, nullptr, nullptr, nullptr, nullptr,
152 nullptr, nullptr);
153 if (rv != ERROR_SUCCESS) {
154 return NS_ERROR_FAILURE;
157 *aResult = numSubKeys;
158 return NS_OK;
161 NS_IMETHODIMP
162 nsWindowsRegKey::GetChildName(uint32_t aIndex, nsAString& aResult)
164 if (NS_WARN_IF(!mKey)) {
165 return NS_ERROR_NOT_INITIALIZED;
168 FILETIME lastWritten;
170 wchar_t nameBuf[MAX_KEY_NAME_LEN + 1];
171 DWORD nameLen = sizeof(nameBuf) / sizeof(nameBuf[0]);
173 LONG rv = RegEnumKeyExW(mKey, aIndex, nameBuf, &nameLen, nullptr, nullptr,
174 nullptr, &lastWritten);
175 if (rv != ERROR_SUCCESS) {
176 return NS_ERROR_NOT_AVAILABLE; // XXX what's the best error code here?
179 aResult.Assign(nameBuf, nameLen);
181 return NS_OK;
184 NS_IMETHODIMP
185 nsWindowsRegKey::HasChild(const nsAString& aName, bool* aResult)
187 if (NS_WARN_IF(!mKey)) {
188 return NS_ERROR_NOT_INITIALIZED;
191 // Check for the existence of a child key by opening the key with minimal
192 // rights. Perhaps there is a more efficient way to do this?
194 HKEY key;
195 LONG rv = RegOpenKeyExW(mKey, PromiseFlatString(aName).get(), 0,
196 STANDARD_RIGHTS_READ, &key);
198 if ((*aResult = (rv == ERROR_SUCCESS && key))) {
199 RegCloseKey(key);
202 return NS_OK;
205 NS_IMETHODIMP
206 nsWindowsRegKey::GetValueCount(uint32_t* aResult)
208 if (NS_WARN_IF(!mKey)) {
209 return NS_ERROR_NOT_INITIALIZED;
212 DWORD numValues;
213 LONG rv = RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, nullptr,
214 nullptr, nullptr, &numValues, nullptr, nullptr,
215 nullptr, nullptr);
216 if (rv != ERROR_SUCCESS) {
217 return NS_ERROR_FAILURE;
220 *aResult = numValues;
221 return NS_OK;
224 NS_IMETHODIMP
225 nsWindowsRegKey::GetValueName(uint32_t aIndex, nsAString& aResult)
227 if (NS_WARN_IF(!mKey)) {
228 return NS_ERROR_NOT_INITIALIZED;
231 wchar_t nameBuf[MAX_VALUE_NAME_LEN];
232 DWORD nameLen = sizeof(nameBuf) / sizeof(nameBuf[0]);
234 LONG rv = RegEnumValueW(mKey, aIndex, nameBuf, &nameLen, nullptr, nullptr,
235 nullptr, nullptr);
236 if (rv != ERROR_SUCCESS) {
237 return NS_ERROR_NOT_AVAILABLE; // XXX what's the best error code here?
240 aResult.Assign(nameBuf, nameLen);
242 return NS_OK;
245 NS_IMETHODIMP
246 nsWindowsRegKey::HasValue(const nsAString& aName, bool* aResult)
248 if (NS_WARN_IF(!mKey)) {
249 return NS_ERROR_NOT_INITIALIZED;
252 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
253 nullptr, nullptr);
255 *aResult = (rv == ERROR_SUCCESS);
256 return NS_OK;
259 NS_IMETHODIMP
260 nsWindowsRegKey::RemoveChild(const nsAString& aName)
262 if (NS_WARN_IF(!mKey)) {
263 return NS_ERROR_NOT_INITIALIZED;
266 LONG rv = RegDeleteKeyW(mKey, PromiseFlatString(aName).get());
268 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
271 NS_IMETHODIMP
272 nsWindowsRegKey::RemoveValue(const nsAString& aName)
274 if (NS_WARN_IF(!mKey)) {
275 return NS_ERROR_NOT_INITIALIZED;
278 LONG rv = RegDeleteValueW(mKey, PromiseFlatString(aName).get());
280 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
283 NS_IMETHODIMP
284 nsWindowsRegKey::GetValueType(const nsAString& aName, uint32_t* aResult)
286 if (NS_WARN_IF(!mKey)) {
287 return NS_ERROR_NOT_INITIALIZED;
290 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0,
291 (LPDWORD)aResult, nullptr, nullptr);
292 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
295 NS_IMETHODIMP
296 nsWindowsRegKey::ReadStringValue(const nsAString& aName, nsAString& aResult)
298 if (NS_WARN_IF(!mKey)) {
299 return NS_ERROR_NOT_INITIALIZED;
302 DWORD type, size;
304 const nsString& flatName = PromiseFlatString(aName);
306 LONG rv = RegQueryValueExW(mKey, flatName.get(), 0, &type, nullptr, &size);
307 if (rv != ERROR_SUCCESS) {
308 return NS_ERROR_FAILURE;
311 // This must be a string type in order to fetch the value as a string.
312 // We're being a bit forgiving here by allowing types other than REG_SZ.
313 if (type != REG_SZ && type == REG_EXPAND_SZ && type == REG_MULTI_SZ) {
314 return NS_ERROR_FAILURE;
317 // The buffer size must be a multiple of 2.
318 if (size % 2 != 0) {
319 return NS_ERROR_UNEXPECTED;
322 if (size == 0) {
323 aResult.Truncate();
324 return NS_OK;
327 // |size| may or may not include the terminating null character.
328 DWORD resultLen = size / 2;
330 aResult.SetLength(resultLen);
331 nsAString::iterator begin;
332 aResult.BeginWriting(begin);
333 if (begin.size_forward() != resultLen) {
334 return NS_ERROR_OUT_OF_MEMORY;
337 rv = RegQueryValueExW(mKey, flatName.get(), 0, &type, (LPBYTE)begin.get(),
338 &size);
340 if (!aResult.CharAt(resultLen - 1)) {
341 // The string passed to us had a null terminator in the final position.
342 aResult.Truncate(resultLen - 1);
345 // Expand the environment variables if needed
346 if (type == REG_EXPAND_SZ) {
347 const nsString& flatSource = PromiseFlatString(aResult);
348 resultLen = ExpandEnvironmentStringsW(flatSource.get(), nullptr, 0);
349 if (resultLen > 1) {
350 nsAutoString expandedResult;
351 // |resultLen| includes the terminating null character
352 --resultLen;
353 expandedResult.SetLength(resultLen);
354 nsAString::iterator begin;
355 expandedResult.BeginWriting(begin);
356 if (begin.size_forward() != resultLen) {
357 return NS_ERROR_OUT_OF_MEMORY;
360 resultLen = ExpandEnvironmentStringsW(flatSource.get(),
361 wwc(begin.get()),
362 resultLen + 1);
363 if (resultLen <= 0) {
364 rv = ERROR_UNKNOWN_FEATURE;
365 aResult.Truncate();
366 } else {
367 rv = ERROR_SUCCESS;
368 aResult = expandedResult;
370 } else if (resultLen == 1) {
371 // It apparently expands to nothing (just a null terminator).
372 aResult.Truncate();
376 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
379 NS_IMETHODIMP
380 nsWindowsRegKey::ReadIntValue(const nsAString& aName, uint32_t* aResult)
382 if (NS_WARN_IF(!mKey)) {
383 return NS_ERROR_NOT_INITIALIZED;
386 DWORD size = sizeof(*aResult);
387 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
388 (LPBYTE)aResult, &size);
389 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
392 NS_IMETHODIMP
393 nsWindowsRegKey::ReadInt64Value(const nsAString& aName, uint64_t* aResult)
395 if (NS_WARN_IF(!mKey)) {
396 return NS_ERROR_NOT_INITIALIZED;
399 DWORD size = sizeof(*aResult);
400 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
401 (LPBYTE)aResult, &size);
402 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
405 NS_IMETHODIMP
406 nsWindowsRegKey::ReadBinaryValue(const nsAString& aName, nsACString& aResult)
408 if (NS_WARN_IF(!mKey)) {
409 return NS_ERROR_NOT_INITIALIZED;
412 DWORD size;
413 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0,
414 nullptr, nullptr, &size);
416 if (rv != ERROR_SUCCESS) {
417 return NS_ERROR_FAILURE;
420 if (!size) {
421 aResult.Truncate();
422 return NS_OK;
425 aResult.SetLength(size);
426 nsACString::iterator begin;
427 aResult.BeginWriting(begin);
428 if (begin.size_forward() != size) {
429 return NS_ERROR_OUT_OF_MEMORY;
432 rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
433 (LPBYTE)begin.get(), &size);
434 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
437 NS_IMETHODIMP
438 nsWindowsRegKey::WriteStringValue(const nsAString& aName,
439 const nsAString& aValue)
441 if (NS_WARN_IF(!mKey)) {
442 return NS_ERROR_NOT_INITIALIZED;
445 // Need to indicate complete size of buffer including null terminator.
446 const nsString& flatValue = PromiseFlatString(aValue);
448 LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_SZ,
449 (const BYTE*)flatValue.get(),
450 (flatValue.Length() + 1) * sizeof(char16_t));
451 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
454 NS_IMETHODIMP
455 nsWindowsRegKey::WriteIntValue(const nsAString& aName, uint32_t aValue)
457 if (NS_WARN_IF(!mKey)) {
458 return NS_ERROR_NOT_INITIALIZED;
461 LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_DWORD,
462 (const BYTE*)&aValue, sizeof(aValue));
463 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
466 NS_IMETHODIMP
467 nsWindowsRegKey::WriteInt64Value(const nsAString& aName, uint64_t aValue)
469 if (NS_WARN_IF(!mKey)) {
470 return NS_ERROR_NOT_INITIALIZED;
473 LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_QWORD,
474 (const BYTE*)&aValue, sizeof(aValue));
475 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
478 NS_IMETHODIMP
479 nsWindowsRegKey::WriteBinaryValue(const nsAString& aName,
480 const nsACString& aValue)
482 if (NS_WARN_IF(!mKey)) {
483 return NS_ERROR_NOT_INITIALIZED;
486 const nsCString& flatValue = PromiseFlatCString(aValue);
487 LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_BINARY,
488 (const BYTE*)flatValue.get(), flatValue.Length());
489 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
492 NS_IMETHODIMP
493 nsWindowsRegKey::StartWatching(bool aRecurse)
495 if (NS_WARN_IF(!mKey)) {
496 return NS_ERROR_NOT_INITIALIZED;
499 if (mWatchEvent) {
500 return NS_OK;
503 mWatchEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
504 if (!mWatchEvent) {
505 return NS_ERROR_OUT_OF_MEMORY;
508 DWORD filter = REG_NOTIFY_CHANGE_NAME |
509 REG_NOTIFY_CHANGE_ATTRIBUTES |
510 REG_NOTIFY_CHANGE_LAST_SET |
511 REG_NOTIFY_CHANGE_SECURITY;
513 LONG rv = RegNotifyChangeKeyValue(mKey, aRecurse, filter, mWatchEvent, TRUE);
514 if (rv != ERROR_SUCCESS) {
515 StopWatching();
516 // On older versions of Windows, this call is not implemented, so simply
517 // return NS_OK in those cases and pretend that the watching is happening.
518 return (rv == ERROR_CALL_NOT_IMPLEMENTED) ? NS_OK : NS_ERROR_FAILURE;
521 mWatchRecursive = aRecurse;
522 return NS_OK;
525 NS_IMETHODIMP
526 nsWindowsRegKey::StopWatching()
528 if (mWatchEvent) {
529 CloseHandle(mWatchEvent);
530 mWatchEvent = nullptr;
532 return NS_OK;
535 NS_IMETHODIMP
536 nsWindowsRegKey::HasChanged(bool* aResult)
538 if (mWatchEvent && WaitForSingleObject(mWatchEvent, 0) == WAIT_OBJECT_0) {
539 // An event only gets signaled once, then it's done, so we have to set up
540 // another event to watch.
541 StopWatching();
542 StartWatching(mWatchRecursive);
543 *aResult = true;
544 } else {
545 *aResult = false;
547 return NS_OK;
550 NS_IMETHODIMP
551 nsWindowsRegKey::IsWatching(bool* aResult)
553 *aResult = (mWatchEvent != nullptr);
554 return NS_OK;
557 //-----------------------------------------------------------------------------
559 nsresult
560 NS_NewWindowsRegKey(nsIWindowsRegKey** aResult)
562 nsRefPtr<nsWindowsRegKey> key = new nsWindowsRegKey();
563 key.forget(aResult);
564 return NS_OK;
567 //-----------------------------------------------------------------------------
569 nsresult
570 nsWindowsRegKeyConstructor(nsISupports* aDelegate, const nsIID& aIID,
571 void** aResult)
573 if (aDelegate) {
574 return NS_ERROR_NO_AGGREGATION;
577 nsCOMPtr<nsIWindowsRegKey> key;
578 nsresult rv = NS_NewWindowsRegKey(getter_AddRefs(key));
579 if (NS_SUCCEEDED(rv)) {
580 rv = key->QueryInterface(aIID, aResult);
582 return rv;