Bug 1860073 [wpt PR 42637] - Update wpt metadata, a=testonly
[gecko.git] / xpcom / ds / nsWindowsRegKey.cpp
blob5a9bc552b76b884052b5b6523228efd4a36240c2
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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"
15 //-----------------------------------------------------------------------------
17 // According to MSDN, the following limits apply (in characters excluding room
18 // for terminating null character):
19 #define MAX_KEY_NAME_LEN 255
20 #define MAX_VALUE_NAME_LEN 16383
22 class nsWindowsRegKey final : public nsIWindowsRegKey {
23 public:
24 NS_DECL_ISUPPORTS
25 NS_DECL_NSIWINDOWSREGKEY
27 nsWindowsRegKey() : mKey(nullptr) {}
29 private:
30 ~nsWindowsRegKey() { Close(); }
32 HKEY mKey;
35 NS_IMPL_ISUPPORTS(nsWindowsRegKey, nsIWindowsRegKey)
37 NS_IMETHODIMP
38 nsWindowsRegKey::GetKey(HKEY* aKey) {
39 *aKey = mKey;
40 return NS_OK;
43 NS_IMETHODIMP
44 nsWindowsRegKey::SetKey(HKEY aKey) {
45 mKey = aKey;
46 return NS_OK;
49 NS_IMETHODIMP
50 nsWindowsRegKey::Close() {
51 if (mKey) {
52 RegCloseKey(mKey);
53 mKey = nullptr;
55 return NS_OK;
58 NS_IMETHODIMP
59 nsWindowsRegKey::Open(uint32_t aRootKey, const nsAString& aPath,
60 uint32_t aMode) {
61 Close();
63 LONG rv =
64 RegOpenKeyExW((HKEY)(intptr_t)aRootKey, PromiseFlatString(aPath).get(), 0,
65 (REGSAM)aMode, &mKey);
66 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
69 NS_IMETHODIMP
70 nsWindowsRegKey::Create(uint32_t aRootKey, const nsAString& aPath,
71 uint32_t aMode) {
72 Close();
74 DWORD disposition;
75 LONG rv = RegCreateKeyExW(
76 (HKEY)(intptr_t)aRootKey, PromiseFlatString(aPath).get(), 0, nullptr,
77 REG_OPTION_NON_VOLATILE, (REGSAM)aMode, nullptr, &mKey, &disposition);
78 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
81 NS_IMETHODIMP
82 nsWindowsRegKey::OpenChild(const nsAString& aPath, uint32_t aMode,
83 nsIWindowsRegKey** aResult) {
84 if (NS_WARN_IF(!mKey)) {
85 return NS_ERROR_NOT_INITIALIZED;
88 nsCOMPtr<nsIWindowsRegKey> child = new nsWindowsRegKey();
90 nsresult rv = child->Open((uintptr_t)mKey, aPath, aMode);
91 if (NS_FAILED(rv)) {
92 return rv;
95 child.swap(*aResult);
96 return NS_OK;
99 NS_IMETHODIMP
100 nsWindowsRegKey::CreateChild(const nsAString& aPath, uint32_t aMode,
101 nsIWindowsRegKey** aResult) {
102 if (NS_WARN_IF(!mKey)) {
103 return NS_ERROR_NOT_INITIALIZED;
106 nsCOMPtr<nsIWindowsRegKey> child = new nsWindowsRegKey();
108 nsresult rv = child->Create((uintptr_t)mKey, aPath, aMode);
109 if (NS_FAILED(rv)) {
110 return rv;
113 child.swap(*aResult);
114 return NS_OK;
117 NS_IMETHODIMP
118 nsWindowsRegKey::GetChildCount(uint32_t* aResult) {
119 if (NS_WARN_IF(!mKey)) {
120 return NS_ERROR_NOT_INITIALIZED;
123 DWORD numSubKeys;
124 LONG rv =
125 RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, &numSubKeys, nullptr,
126 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
127 if (rv != ERROR_SUCCESS) {
128 return NS_ERROR_FAILURE;
131 *aResult = numSubKeys;
132 return NS_OK;
135 NS_IMETHODIMP
136 nsWindowsRegKey::GetChildName(uint32_t aIndex, nsAString& aResult) {
137 if (NS_WARN_IF(!mKey)) {
138 return NS_ERROR_NOT_INITIALIZED;
141 FILETIME lastWritten;
143 wchar_t nameBuf[MAX_KEY_NAME_LEN + 1];
144 DWORD nameLen = sizeof(nameBuf) / sizeof(nameBuf[0]);
146 LONG rv = RegEnumKeyExW(mKey, aIndex, nameBuf, &nameLen, nullptr, nullptr,
147 nullptr, &lastWritten);
148 if (rv != ERROR_SUCCESS) {
149 return NS_ERROR_NOT_AVAILABLE; // XXX what's the best error code here?
152 aResult.Assign(nameBuf, nameLen);
154 return NS_OK;
157 NS_IMETHODIMP
158 nsWindowsRegKey::HasChild(const nsAString& aName, bool* aResult) {
159 if (NS_WARN_IF(!mKey)) {
160 return NS_ERROR_NOT_INITIALIZED;
163 // Check for the existence of a child key by opening the key with minimal
164 // rights. Perhaps there is a more efficient way to do this?
166 HKEY key;
167 LONG rv = RegOpenKeyExW(mKey, PromiseFlatString(aName).get(), 0,
168 STANDARD_RIGHTS_READ, &key);
170 if ((*aResult = (rv == ERROR_SUCCESS && key))) {
171 RegCloseKey(key);
174 return NS_OK;
177 NS_IMETHODIMP
178 nsWindowsRegKey::GetValueCount(uint32_t* aResult) {
179 if (NS_WARN_IF(!mKey)) {
180 return NS_ERROR_NOT_INITIALIZED;
183 DWORD numValues;
184 LONG rv =
185 RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, nullptr, nullptr,
186 nullptr, &numValues, nullptr, nullptr, nullptr, nullptr);
187 if (rv != ERROR_SUCCESS) {
188 return NS_ERROR_FAILURE;
191 *aResult = numValues;
192 return NS_OK;
195 NS_IMETHODIMP
196 nsWindowsRegKey::GetValueName(uint32_t aIndex, nsAString& aResult) {
197 if (NS_WARN_IF(!mKey)) {
198 return NS_ERROR_NOT_INITIALIZED;
201 wchar_t nameBuf[MAX_VALUE_NAME_LEN];
202 DWORD nameLen = sizeof(nameBuf) / sizeof(nameBuf[0]);
204 LONG rv = RegEnumValueW(mKey, aIndex, nameBuf, &nameLen, nullptr, nullptr,
205 nullptr, nullptr);
206 if (rv != ERROR_SUCCESS) {
207 return NS_ERROR_NOT_AVAILABLE; // XXX what's the best error code here?
210 aResult.Assign(nameBuf, nameLen);
212 return NS_OK;
215 NS_IMETHODIMP
216 nsWindowsRegKey::HasValue(const nsAString& aName, bool* aResult) {
217 if (NS_WARN_IF(!mKey)) {
218 return NS_ERROR_NOT_INITIALIZED;
221 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
222 nullptr, nullptr);
224 *aResult = (rv == ERROR_SUCCESS);
225 return NS_OK;
228 NS_IMETHODIMP
229 nsWindowsRegKey::RemoveChild(const nsAString& aName) {
230 if (NS_WARN_IF(!mKey)) {
231 return NS_ERROR_NOT_INITIALIZED;
234 LONG rv = RegDeleteKeyW(mKey, PromiseFlatString(aName).get());
236 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
239 NS_IMETHODIMP
240 nsWindowsRegKey::RemoveValue(const nsAString& aName) {
241 if (NS_WARN_IF(!mKey)) {
242 return NS_ERROR_NOT_INITIALIZED;
245 LONG rv = RegDeleteValueW(mKey, PromiseFlatString(aName).get());
247 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
250 NS_IMETHODIMP
251 nsWindowsRegKey::GetValueType(const nsAString& aName, uint32_t* aResult) {
252 if (NS_WARN_IF(!mKey)) {
253 return NS_ERROR_NOT_INITIALIZED;
256 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0,
257 (LPDWORD)aResult, nullptr, nullptr);
258 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
261 NS_IMETHODIMP
262 nsWindowsRegKey::ReadStringValue(const nsAString& aName, nsAString& aResult) {
263 if (NS_WARN_IF(!mKey)) {
264 return NS_ERROR_NOT_INITIALIZED;
267 DWORD type, size;
269 const nsString& flatName = PromiseFlatString(aName);
271 LONG rv = RegQueryValueExW(mKey, flatName.get(), 0, &type, nullptr, &size);
272 if (rv != ERROR_SUCCESS) {
273 return NS_ERROR_FAILURE;
276 // This must be a string type in order to fetch the value as a string.
277 // We're being a bit forgiving here by allowing types other than REG_SZ.
278 if (type != REG_SZ && type != REG_EXPAND_SZ && type != REG_MULTI_SZ) {
279 return NS_ERROR_FAILURE;
282 // The buffer size must be a multiple of 2.
283 if (size % 2 != 0) {
284 return NS_ERROR_UNEXPECTED;
287 if (size == 0) {
288 aResult.Truncate();
289 return NS_OK;
292 // |size| may or may not include the terminating null character.
293 DWORD resultLen = size / 2;
295 if (!aResult.SetLength(resultLen, mozilla::fallible)) {
296 return NS_ERROR_OUT_OF_MEMORY;
299 auto* begin = aResult.BeginWriting();
301 rv = RegQueryValueExW(mKey, flatName.get(), 0, &type, (LPBYTE)begin, &size);
303 if (!aResult.CharAt(resultLen - 1)) {
304 // The string passed to us had a null terminator in the final position.
305 aResult.Truncate(resultLen - 1);
308 // Expand the environment variables if needed
309 if (type == REG_EXPAND_SZ) {
310 const nsString& flatSource = PromiseFlatString(aResult);
311 resultLen = ExpandEnvironmentStringsW(flatSource.get(), nullptr, 0);
312 if (resultLen > 1) {
313 nsAutoString expandedResult;
314 // |resultLen| includes the terminating null character
315 --resultLen;
316 if (!expandedResult.SetLength(resultLen, mozilla::fallible)) {
317 return NS_ERROR_OUT_OF_MEMORY;
320 resultLen = ExpandEnvironmentStringsW(
321 flatSource.get(), expandedResult.get(), resultLen + 1);
322 if (resultLen <= 0) {
323 rv = ERROR_UNKNOWN_FEATURE;
324 aResult.Truncate();
325 } else {
326 rv = ERROR_SUCCESS;
327 aResult = expandedResult;
329 } else if (resultLen == 1) {
330 // It apparently expands to nothing (just a null terminator).
331 aResult.Truncate();
335 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
338 NS_IMETHODIMP
339 nsWindowsRegKey::ReadIntValue(const nsAString& aName, uint32_t* aResult) {
340 if (NS_WARN_IF(!mKey)) {
341 return NS_ERROR_NOT_INITIALIZED;
344 DWORD size = sizeof(*aResult);
345 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
346 (LPBYTE)aResult, &size);
347 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
350 NS_IMETHODIMP
351 nsWindowsRegKey::ReadInt64Value(const nsAString& aName, uint64_t* aResult) {
352 if (NS_WARN_IF(!mKey)) {
353 return NS_ERROR_NOT_INITIALIZED;
356 DWORD size = sizeof(*aResult);
357 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
358 (LPBYTE)aResult, &size);
359 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
362 NS_IMETHODIMP
363 nsWindowsRegKey::ReadBinaryValue(const nsAString& aName, nsACString& aResult) {
364 if (NS_WARN_IF(!mKey)) {
365 return NS_ERROR_NOT_INITIALIZED;
368 DWORD size;
369 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
370 nullptr, &size);
372 if (rv != ERROR_SUCCESS) {
373 return NS_ERROR_FAILURE;
376 if (!size) {
377 aResult.Truncate();
378 return NS_OK;
381 if (!aResult.SetLength(size, mozilla::fallible)) {
382 return NS_ERROR_OUT_OF_MEMORY;
385 auto* begin = aResult.BeginWriting();
387 rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr,
388 (LPBYTE)begin, &size);
389 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
392 NS_IMETHODIMP
393 nsWindowsRegKey::WriteStringValue(const nsAString& aName,
394 const nsAString& aValue) {
395 if (NS_WARN_IF(!mKey)) {
396 return NS_ERROR_NOT_INITIALIZED;
399 // Need to indicate complete size of buffer including null terminator.
400 const nsString& flatValue = PromiseFlatString(aValue);
402 LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_SZ,
403 (const BYTE*)flatValue.get(),
404 (flatValue.Length() + 1) * sizeof(char16_t));
405 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
408 NS_IMETHODIMP
409 nsWindowsRegKey::WriteIntValue(const nsAString& aName, uint32_t aValue) {
410 if (NS_WARN_IF(!mKey)) {
411 return NS_ERROR_NOT_INITIALIZED;
414 LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_DWORD,
415 (const BYTE*)&aValue, sizeof(aValue));
416 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
419 NS_IMETHODIMP
420 nsWindowsRegKey::WriteInt64Value(const nsAString& aName, uint64_t aValue) {
421 if (NS_WARN_IF(!mKey)) {
422 return NS_ERROR_NOT_INITIALIZED;
425 LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_QWORD,
426 (const BYTE*)&aValue, sizeof(aValue));
427 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
430 NS_IMETHODIMP
431 nsWindowsRegKey::WriteBinaryValue(const nsAString& aName,
432 const nsACString& aValue) {
433 if (NS_WARN_IF(!mKey)) {
434 return NS_ERROR_NOT_INITIALIZED;
437 const nsCString& flatValue = PromiseFlatCString(aValue);
438 LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_BINARY,
439 (const BYTE*)flatValue.get(), flatValue.Length());
440 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
443 //-----------------------------------------------------------------------------
445 void NS_NewWindowsRegKey(nsIWindowsRegKey** aResult) {
446 RefPtr<nsWindowsRegKey> key = new nsWindowsRegKey();
447 key.forget(aResult);
450 //-----------------------------------------------------------------------------
452 nsresult nsWindowsRegKeyConstructor(const nsIID& aIID, void** aResult) {
453 nsCOMPtr<nsIWindowsRegKey> key;
454 NS_NewWindowsRegKey(getter_AddRefs(key));
455 return key->QueryInterface(aIID, aResult);