summaryrefslogtreecommitdiffstats
path: root/xpcom/ds/nsWindowsRegKey.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/ds/nsWindowsRegKey.cpp')
-rw-r--r--xpcom/ds/nsWindowsRegKey.cpp528
1 files changed, 528 insertions, 0 deletions
diff --git a/xpcom/ds/nsWindowsRegKey.cpp b/xpcom/ds/nsWindowsRegKey.cpp
new file mode 100644
index 0000000000..73283de12f
--- /dev/null
+++ b/xpcom/ds/nsWindowsRegKey.cpp
@@ -0,0 +1,528 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <windows.h>
+#include <shlwapi.h>
+#include <stdlib.h>
+#include "nsWindowsRegKey.h"
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "mozilla/Attributes.h"
+
+//-----------------------------------------------------------------------------
+
+// According to MSDN, the following limits apply (in characters excluding room
+// for terminating null character):
+#define MAX_KEY_NAME_LEN 255
+#define MAX_VALUE_NAME_LEN 16383
+
+class nsWindowsRegKey final : public nsIWindowsRegKey {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIWINDOWSREGKEY
+
+ nsWindowsRegKey()
+ : mKey(nullptr), mWatchEvent(nullptr), mWatchRecursive(FALSE) {}
+
+ private:
+ ~nsWindowsRegKey() { Close(); }
+
+ HKEY mKey;
+ HANDLE mWatchEvent;
+ BOOL mWatchRecursive;
+};
+
+NS_IMPL_ISUPPORTS(nsWindowsRegKey, nsIWindowsRegKey)
+
+NS_IMETHODIMP
+nsWindowsRegKey::GetKey(HKEY* aKey) {
+ *aKey = mKey;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::SetKey(HKEY aKey) {
+ // We do not close the older aKey!
+ StopWatching();
+
+ mKey = aKey;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::Close() {
+ StopWatching();
+
+ if (mKey) {
+ RegCloseKey(mKey);
+ mKey = nullptr;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::Open(uint32_t aRootKey, const nsAString& aPath,
+ uint32_t aMode) {
+ Close();
+
+ LONG rv =
+ RegOpenKeyExW((HKEY)(intptr_t)aRootKey, PromiseFlatString(aPath).get(), 0,
+ (REGSAM)aMode, &mKey);
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::Create(uint32_t aRootKey, const nsAString& aPath,
+ uint32_t aMode) {
+ Close();
+
+ DWORD disposition;
+ LONG rv = RegCreateKeyExW(
+ (HKEY)(intptr_t)aRootKey, PromiseFlatString(aPath).get(), 0, nullptr,
+ REG_OPTION_NON_VOLATILE, (REGSAM)aMode, nullptr, &mKey, &disposition);
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::OpenChild(const nsAString& aPath, uint32_t aMode,
+ nsIWindowsRegKey** aResult) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ nsCOMPtr<nsIWindowsRegKey> child = new nsWindowsRegKey();
+
+ nsresult rv = child->Open((uintptr_t)mKey, aPath, aMode);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ child.swap(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::CreateChild(const nsAString& aPath, uint32_t aMode,
+ nsIWindowsRegKey** aResult) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ nsCOMPtr<nsIWindowsRegKey> child = new nsWindowsRegKey();
+
+ nsresult rv = child->Create((uintptr_t)mKey, aPath, aMode);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ child.swap(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::GetChildCount(uint32_t* aResult) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ DWORD numSubKeys;
+ LONG rv =
+ RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, &numSubKeys, nullptr,
+ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
+ if (rv != ERROR_SUCCESS) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aResult = numSubKeys;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::GetChildName(uint32_t aIndex, nsAString& aResult) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ FILETIME lastWritten;
+
+ wchar_t nameBuf[MAX_KEY_NAME_LEN + 1];
+ DWORD nameLen = sizeof(nameBuf) / sizeof(nameBuf[0]);
+
+ LONG rv = RegEnumKeyExW(mKey, aIndex, nameBuf, &nameLen, nullptr, nullptr,
+ nullptr, &lastWritten);
+ if (rv != ERROR_SUCCESS) {
+ return NS_ERROR_NOT_AVAILABLE; // XXX what's the best error code here?
+ }
+
+ aResult.Assign(nameBuf, nameLen);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::HasChild(const nsAString& aName, bool* aResult) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ // Check for the existence of a child key by opening the key with minimal
+ // rights. Perhaps there is a more efficient way to do this?
+
+ HKEY key;
+ LONG rv = RegOpenKeyExW(mKey, PromiseFlatString(aName).get(), 0,
+ STANDARD_RIGHTS_READ, &key);
+
+ if ((*aResult = (rv == ERROR_SUCCESS && key))) {
+ RegCloseKey(key);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::GetValueCount(uint32_t* aResult) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ DWORD numValues;
+ LONG rv =
+ RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, nullptr, nullptr,
+ nullptr, &numValues, nullptr, nullptr, nullptr, nullptr);
+ if (rv != ERROR_SUCCESS) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aResult = numValues;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::GetValueName(uint32_t aIndex, nsAString& aResult) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ wchar_t nameBuf[MAX_VALUE_NAME_LEN];
+ DWORD nameLen = sizeof(nameBuf) / sizeof(nameBuf[0]);
+
+ LONG rv = RegEnumValueW(mKey, aIndex, nameBuf, &nameLen, nullptr, nullptr,
+ nullptr, nullptr);
+ if (rv != ERROR_SUCCESS) {
+ return NS_ERROR_NOT_AVAILABLE; // XXX what's the best error code here?
+ }
+
+ aResult.Assign(nameBuf, nameLen);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::HasValue(const nsAString& aName, bool* aResult) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
+ nullptr, nullptr);
+
+ *aResult = (rv == ERROR_SUCCESS);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::RemoveChild(const nsAString& aName) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ LONG rv = RegDeleteKeyW(mKey, PromiseFlatString(aName).get());
+
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::RemoveValue(const nsAString& aName) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ LONG rv = RegDeleteValueW(mKey, PromiseFlatString(aName).get());
+
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::GetValueType(const nsAString& aName, uint32_t* aResult) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0,
+ (LPDWORD)aResult, nullptr, nullptr);
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::ReadStringValue(const nsAString& aName, nsAString& aResult) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ DWORD type, size;
+
+ const nsString& flatName = PromiseFlatString(aName);
+
+ LONG rv = RegQueryValueExW(mKey, flatName.get(), 0, &type, nullptr, &size);
+ if (rv != ERROR_SUCCESS) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // This must be a string type in order to fetch the value as a string.
+ // We're being a bit forgiving here by allowing types other than REG_SZ.
+ if (type != REG_SZ && type != REG_EXPAND_SZ && type != REG_MULTI_SZ) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // The buffer size must be a multiple of 2.
+ if (size % 2 != 0) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (size == 0) {
+ aResult.Truncate();
+ return NS_OK;
+ }
+
+ // |size| may or may not include the terminating null character.
+ DWORD resultLen = size / 2;
+
+ if (!aResult.SetLength(resultLen, mozilla::fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ auto begin = aResult.BeginWriting();
+
+ rv = RegQueryValueExW(mKey, flatName.get(), 0, &type, (LPBYTE)begin, &size);
+
+ if (!aResult.CharAt(resultLen - 1)) {
+ // The string passed to us had a null terminator in the final position.
+ aResult.Truncate(resultLen - 1);
+ }
+
+ // Expand the environment variables if needed
+ if (type == REG_EXPAND_SZ) {
+ const nsString& flatSource = PromiseFlatString(aResult);
+ resultLen = ExpandEnvironmentStringsW(flatSource.get(), nullptr, 0);
+ if (resultLen > 1) {
+ nsAutoString expandedResult;
+ // |resultLen| includes the terminating null character
+ --resultLen;
+ if (!expandedResult.SetLength(resultLen, mozilla::fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ resultLen = ExpandEnvironmentStringsW(
+ flatSource.get(), expandedResult.get(), resultLen + 1);
+ if (resultLen <= 0) {
+ rv = ERROR_UNKNOWN_FEATURE;
+ aResult.Truncate();
+ } else {
+ rv = ERROR_SUCCESS;
+ aResult = expandedResult;
+ }
+ } else if (resultLen == 1) {
+ // It apparently expands to nothing (just a null terminator).
+ aResult.Truncate();
+ }
+ }
+
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::ReadIntValue(const nsAString& aName, uint32_t* aResult) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ DWORD size = sizeof(*aResult);
+ LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
+ (LPBYTE)aResult, &size);
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::ReadInt64Value(const nsAString& aName, uint64_t* aResult) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ DWORD size = sizeof(*aResult);
+ LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
+ (LPBYTE)aResult, &size);
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::ReadBinaryValue(const nsAString& aName, nsACString& aResult) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ DWORD size;
+ LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
+ nullptr, &size);
+
+ if (rv != ERROR_SUCCESS) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!size) {
+ aResult.Truncate();
+ return NS_OK;
+ }
+
+ if (!aResult.SetLength(size, mozilla::fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ auto begin = aResult.BeginWriting();
+
+ rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
+ (LPBYTE)begin, &size);
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::WriteStringValue(const nsAString& aName,
+ const nsAString& aValue) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ // Need to indicate complete size of buffer including null terminator.
+ const nsString& flatValue = PromiseFlatString(aValue);
+
+ LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_SZ,
+ (const BYTE*)flatValue.get(),
+ (flatValue.Length() + 1) * sizeof(char16_t));
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::WriteIntValue(const nsAString& aName, uint32_t aValue) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_DWORD,
+ (const BYTE*)&aValue, sizeof(aValue));
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::WriteInt64Value(const nsAString& aName, uint64_t aValue) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_QWORD,
+ (const BYTE*)&aValue, sizeof(aValue));
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::WriteBinaryValue(const nsAString& aName,
+ const nsACString& aValue) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ const nsCString& flatValue = PromiseFlatCString(aValue);
+ LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_BINARY,
+ (const BYTE*)flatValue.get(), flatValue.Length());
+ return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::StartWatching(bool aRecurse) {
+ if (NS_WARN_IF(!mKey)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ if (mWatchEvent) {
+ return NS_OK;
+ }
+
+ mWatchEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
+ if (!mWatchEvent) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ DWORD filter = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES |
+ REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY;
+
+ LONG rv = RegNotifyChangeKeyValue(mKey, aRecurse, filter, mWatchEvent, TRUE);
+ if (rv != ERROR_SUCCESS) {
+ StopWatching();
+ // On older versions of Windows, this call is not implemented, so simply
+ // return NS_OK in those cases and pretend that the watching is happening.
+ return (rv == ERROR_CALL_NOT_IMPLEMENTED) ? NS_OK : NS_ERROR_FAILURE;
+ }
+
+ mWatchRecursive = aRecurse;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::StopWatching() {
+ if (mWatchEvent) {
+ CloseHandle(mWatchEvent);
+ mWatchEvent = nullptr;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::HasChanged(bool* aResult) {
+ if (mWatchEvent && WaitForSingleObject(mWatchEvent, 0) == WAIT_OBJECT_0) {
+ // An event only gets signaled once, then it's done, so we have to set up
+ // another event to watch.
+ StopWatching();
+ StartWatching(mWatchRecursive);
+ *aResult = true;
+ } else {
+ *aResult = false;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsRegKey::IsWatching(bool* aResult) {
+ *aResult = (mWatchEvent != nullptr);
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+void NS_NewWindowsRegKey(nsIWindowsRegKey** aResult) {
+ RefPtr<nsWindowsRegKey> key = new nsWindowsRegKey();
+ key.forget(aResult);
+}
+
+//-----------------------------------------------------------------------------
+
+nsresult nsWindowsRegKeyConstructor(nsISupports* aDelegate, const nsIID& aIID,
+ void** aResult) {
+ if (aDelegate) {
+ return NS_ERROR_NO_AGGREGATION;
+ }
+
+ nsCOMPtr<nsIWindowsRegKey> key;
+ NS_NewWindowsRegKey(getter_AddRefs(key));
+ return key->QueryInterface(aIID, aResult);
+}