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 /* 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
24 {
25 public:
26 NS_DECL_ISUPPORTS
27 NS_DECL_NSIWINDOWSREGKEY
29 nsWindowsRegKey()
30 : mKey(nullptr)
31 , mWatchEvent(nullptr)
32 , mWatchRecursive(FALSE)
33 {
34 }
36 private:
37 ~nsWindowsRegKey()
38 {
39 Close();
40 }
42 HKEY mKey;
43 HANDLE mWatchEvent;
44 BOOL mWatchRecursive;
45 };
47 NS_IMPL_ISUPPORTS(nsWindowsRegKey, nsIWindowsRegKey)
49 NS_IMETHODIMP
50 nsWindowsRegKey::GetKey(HKEY *key)
51 {
52 *key = mKey;
53 return NS_OK;
54 }
56 NS_IMETHODIMP
57 nsWindowsRegKey::SetKey(HKEY key)
58 {
59 // We do not close the older key!
60 StopWatching();
62 mKey = key;
63 return NS_OK;
64 }
66 NS_IMETHODIMP
67 nsWindowsRegKey::Close()
68 {
69 StopWatching();
71 if (mKey) {
72 RegCloseKey(mKey);
73 mKey = nullptr;
74 }
75 return NS_OK;
76 }
78 NS_IMETHODIMP
79 nsWindowsRegKey::Open(uint32_t rootKey, const nsAString &path, uint32_t mode)
80 {
81 Close();
83 LONG rv = RegOpenKeyExW((HKEY)(intptr_t) rootKey, PromiseFlatString(path).get(), 0,
84 (REGSAM) mode, &mKey);
86 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
87 }
89 NS_IMETHODIMP
90 nsWindowsRegKey::Create(uint32_t rootKey, const nsAString &path, uint32_t mode)
91 {
92 Close();
94 DWORD disposition;
95 LONG rv = RegCreateKeyExW((HKEY)(intptr_t) rootKey, PromiseFlatString(path).get(), 0,
96 nullptr, REG_OPTION_NON_VOLATILE, (REGSAM) mode, nullptr,
97 &mKey, &disposition);
99 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
100 }
102 NS_IMETHODIMP
103 nsWindowsRegKey::OpenChild(const nsAString &path, uint32_t mode,
104 nsIWindowsRegKey **result)
105 {
106 if (NS_WARN_IF(!mKey))
107 return NS_ERROR_NOT_INITIALIZED;
109 nsCOMPtr<nsIWindowsRegKey> child = new nsWindowsRegKey();
111 nsresult rv = child->Open((uintptr_t) mKey, path, mode);
112 if (NS_FAILED(rv))
113 return rv;
115 child.swap(*result);
116 return NS_OK;
117 }
119 NS_IMETHODIMP
120 nsWindowsRegKey::CreateChild(const nsAString &path, uint32_t mode,
121 nsIWindowsRegKey **result)
122 {
123 if (NS_WARN_IF(!mKey))
124 return NS_ERROR_NOT_INITIALIZED;
126 nsCOMPtr<nsIWindowsRegKey> child = new nsWindowsRegKey();
128 nsresult rv = child->Create((uintptr_t) mKey, path, mode);
129 if (NS_FAILED(rv))
130 return rv;
132 child.swap(*result);
133 return NS_OK;
134 }
136 NS_IMETHODIMP
137 nsWindowsRegKey::GetChildCount(uint32_t *result)
138 {
139 if (NS_WARN_IF(!mKey))
140 return NS_ERROR_NOT_INITIALIZED;
142 DWORD numSubKeys;
143 LONG rv = RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, &numSubKeys,
144 nullptr, nullptr, nullptr, nullptr, nullptr,
145 nullptr, nullptr);
146 if (rv != ERROR_SUCCESS)
147 return NS_ERROR_FAILURE;
149 *result = numSubKeys;
150 return NS_OK;
151 }
153 NS_IMETHODIMP
154 nsWindowsRegKey::GetChildName(uint32_t index, nsAString &result)
155 {
156 if (NS_WARN_IF(!mKey))
157 return NS_ERROR_NOT_INITIALIZED;
159 FILETIME lastWritten;
161 wchar_t nameBuf[MAX_KEY_NAME_LEN + 1];
162 DWORD nameLen = sizeof(nameBuf) / sizeof(nameBuf[0]);
164 LONG rv = RegEnumKeyExW(mKey, index, nameBuf, &nameLen, nullptr, nullptr,
165 nullptr, &lastWritten);
166 if (rv != ERROR_SUCCESS)
167 return NS_ERROR_NOT_AVAILABLE; // XXX what's the best error code here?
169 result.Assign(nameBuf, nameLen);
171 return NS_OK;
172 }
174 NS_IMETHODIMP
175 nsWindowsRegKey::HasChild(const nsAString &name, bool *result)
176 {
177 if (NS_WARN_IF(!mKey))
178 return NS_ERROR_NOT_INITIALIZED;
180 // Check for the existence of a child key by opening the key with minimal
181 // rights. Perhaps there is a more efficient way to do this?
183 HKEY key;
184 LONG rv = RegOpenKeyExW(mKey, PromiseFlatString(name).get(), 0,
185 STANDARD_RIGHTS_READ, &key);
187 if ((*result = (rv == ERROR_SUCCESS && key)))
188 RegCloseKey(key);
190 return NS_OK;
191 }
193 NS_IMETHODIMP
194 nsWindowsRegKey::GetValueCount(uint32_t *result)
195 {
196 if (NS_WARN_IF(!mKey))
197 return NS_ERROR_NOT_INITIALIZED;
199 DWORD numValues;
200 LONG rv = RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, nullptr,
201 nullptr, nullptr, &numValues, nullptr, nullptr,
202 nullptr, nullptr);
203 if (rv != ERROR_SUCCESS)
204 return NS_ERROR_FAILURE;
206 *result = numValues;
207 return NS_OK;
208 }
210 NS_IMETHODIMP
211 nsWindowsRegKey::GetValueName(uint32_t index, nsAString &result)
212 {
213 if (NS_WARN_IF(!mKey))
214 return NS_ERROR_NOT_INITIALIZED;
216 wchar_t nameBuf[MAX_VALUE_NAME_LEN];
217 DWORD nameLen = sizeof(nameBuf) / sizeof(nameBuf[0]);
219 LONG rv = RegEnumValueW(mKey, index, nameBuf, &nameLen, nullptr, nullptr,
220 nullptr, nullptr);
221 if (rv != ERROR_SUCCESS)
222 return NS_ERROR_NOT_AVAILABLE; // XXX what's the best error code here?
224 result.Assign(nameBuf, nameLen);
226 return NS_OK;
227 }
229 NS_IMETHODIMP
230 nsWindowsRegKey::HasValue(const nsAString &name, bool *result)
231 {
232 if (NS_WARN_IF(!mKey))
233 return NS_ERROR_NOT_INITIALIZED;
235 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(name).get(), 0, nullptr,
236 nullptr, nullptr);
238 *result = (rv == ERROR_SUCCESS);
239 return NS_OK;
240 }
242 NS_IMETHODIMP
243 nsWindowsRegKey::RemoveChild(const nsAString &name)
244 {
245 if (NS_WARN_IF(!mKey))
246 return NS_ERROR_NOT_INITIALIZED;
248 LONG rv = RegDeleteKeyW(mKey, PromiseFlatString(name).get());
250 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
251 }
253 NS_IMETHODIMP
254 nsWindowsRegKey::RemoveValue(const nsAString &name)
255 {
256 if (NS_WARN_IF(!mKey))
257 return NS_ERROR_NOT_INITIALIZED;
259 LONG rv = RegDeleteValueW(mKey, PromiseFlatString(name).get());
261 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
262 }
264 NS_IMETHODIMP
265 nsWindowsRegKey::GetValueType(const nsAString &name, uint32_t *result)
266 {
267 if (NS_WARN_IF(!mKey))
268 return NS_ERROR_NOT_INITIALIZED;
270 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(name).get(), 0,
271 (LPDWORD) result, nullptr, nullptr);
273 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
274 }
276 NS_IMETHODIMP
277 nsWindowsRegKey::ReadStringValue(const nsAString &name, nsAString &result)
278 {
279 if (NS_WARN_IF(!mKey))
280 return NS_ERROR_NOT_INITIALIZED;
282 DWORD type, size;
284 const nsString &flatName = PromiseFlatString(name);
286 LONG rv = RegQueryValueExW(mKey, flatName.get(), 0, &type, nullptr, &size);
287 if (rv != ERROR_SUCCESS)
288 return NS_ERROR_FAILURE;
290 // This must be a string type in order to fetch the value as a string.
291 // We're being a bit forgiving here by allowing types other than REG_SZ.
292 if (type != REG_SZ && type == REG_EXPAND_SZ && type == REG_MULTI_SZ)
293 return NS_ERROR_FAILURE;
295 // The buffer size must be a multiple of 2.
296 if (size % 2 != 0)
297 return NS_ERROR_UNEXPECTED;
299 if (size == 0) {
300 result.Truncate();
301 return NS_OK;
302 }
304 // |size| may or may not include the terminating null character.
305 DWORD resultLen = size / 2;
307 result.SetLength(resultLen);
308 nsAString::iterator begin;
309 result.BeginWriting(begin);
310 if (begin.size_forward() != resultLen)
311 return NS_ERROR_OUT_OF_MEMORY;
313 rv = RegQueryValueExW(mKey, flatName.get(), 0, &type, (LPBYTE) begin.get(),
314 &size);
316 if (!result.CharAt(resultLen-1)) {
317 // The string passed to us had a null terminator in the final position.
318 result.Truncate(resultLen-1);
319 }
321 // Expand the environment variables if needed
322 if (type == REG_EXPAND_SZ) {
323 const nsString &flatSource = PromiseFlatString(result);
324 resultLen = ExpandEnvironmentStringsW(flatSource.get(), nullptr, 0);
325 if (resultLen > 1) {
326 nsAutoString expandedResult;
327 // |resultLen| includes the terminating null character
328 --resultLen;
329 expandedResult.SetLength(resultLen);
330 nsAString::iterator begin;
331 expandedResult.BeginWriting(begin);
332 if (begin.size_forward() != resultLen)
333 return NS_ERROR_OUT_OF_MEMORY;
335 resultLen = ExpandEnvironmentStringsW(flatSource.get(),
336 wwc(begin.get()),
337 resultLen + 1);
338 if (resultLen <= 0) {
339 rv = ERROR_UNKNOWN_FEATURE;
340 result.Truncate();
341 } else {
342 rv = ERROR_SUCCESS;
343 result = expandedResult;
344 }
345 } else if (resultLen == 1) {
346 // It apparently expands to nothing (just a null terminator).
347 result.Truncate();
348 }
349 }
351 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
352 }
354 NS_IMETHODIMP
355 nsWindowsRegKey::ReadIntValue(const nsAString &name, uint32_t *result)
356 {
357 if (NS_WARN_IF(!mKey))
358 return NS_ERROR_NOT_INITIALIZED;
360 DWORD size = sizeof(*result);
361 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(name).get(), 0, nullptr,
362 (LPBYTE) result, &size);
364 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
365 }
367 NS_IMETHODIMP
368 nsWindowsRegKey::ReadInt64Value(const nsAString &name, uint64_t *result)
369 {
370 if (NS_WARN_IF(!mKey))
371 return NS_ERROR_NOT_INITIALIZED;
373 DWORD size = sizeof(*result);
374 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(name).get(), 0, nullptr,
375 (LPBYTE) result, &size);
377 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
378 }
380 NS_IMETHODIMP
381 nsWindowsRegKey::ReadBinaryValue(const nsAString &name, nsACString &result)
382 {
383 if (NS_WARN_IF(!mKey))
384 return NS_ERROR_NOT_INITIALIZED;
386 DWORD size;
387 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(name).get(), 0,
388 nullptr, nullptr, &size);
390 if (rv != ERROR_SUCCESS)
391 return NS_ERROR_FAILURE;
393 if (!size) {
394 result.Truncate();
395 return NS_OK;
396 }
398 result.SetLength(size);
399 nsACString::iterator begin;
400 result.BeginWriting(begin);
401 if (begin.size_forward() != size)
402 return NS_ERROR_OUT_OF_MEMORY;
404 rv = RegQueryValueExW(mKey, PromiseFlatString(name).get(), 0, nullptr,
405 (LPBYTE) begin.get(), &size);
407 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
408 }
410 NS_IMETHODIMP
411 nsWindowsRegKey::WriteStringValue(const nsAString &name, const nsAString &value)
412 {
413 if (NS_WARN_IF(!mKey))
414 return NS_ERROR_NOT_INITIALIZED;
416 // Need to indicate complete size of buffer including null terminator.
417 const nsString &flatValue = PromiseFlatString(value);
419 LONG rv = RegSetValueExW(mKey, PromiseFlatString(name).get(), 0, REG_SZ,
420 (const BYTE *) flatValue.get(),
421 (flatValue.Length() + 1) * sizeof(char16_t));
423 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
424 }
426 NS_IMETHODIMP
427 nsWindowsRegKey::WriteIntValue(const nsAString &name, uint32_t value)
428 {
429 if (NS_WARN_IF(!mKey))
430 return NS_ERROR_NOT_INITIALIZED;
432 LONG rv = RegSetValueExW(mKey, PromiseFlatString(name).get(), 0, REG_DWORD,
433 (const BYTE *) &value, sizeof(value));
435 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
436 }
438 NS_IMETHODIMP
439 nsWindowsRegKey::WriteInt64Value(const nsAString &name, uint64_t value)
440 {
441 if (NS_WARN_IF(!mKey))
442 return NS_ERROR_NOT_INITIALIZED;
444 LONG rv = RegSetValueExW(mKey, PromiseFlatString(name).get(), 0, REG_QWORD,
445 (const BYTE *) &value, sizeof(value));
447 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
448 }
450 NS_IMETHODIMP
451 nsWindowsRegKey::WriteBinaryValue(const nsAString &name, const nsACString &value)
452 {
453 if (NS_WARN_IF(!mKey))
454 return NS_ERROR_NOT_INITIALIZED;
456 const nsCString &flatValue = PromiseFlatCString(value);
457 LONG rv = RegSetValueExW(mKey, PromiseFlatString(name).get(), 0, REG_BINARY,
458 (const BYTE *) flatValue.get(), flatValue.Length());
460 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
461 }
463 NS_IMETHODIMP
464 nsWindowsRegKey::StartWatching(bool recurse)
465 {
466 if (NS_WARN_IF(!mKey))
467 return NS_ERROR_NOT_INITIALIZED;
469 if (mWatchEvent)
470 return NS_OK;
472 mWatchEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
473 if (!mWatchEvent)
474 return NS_ERROR_OUT_OF_MEMORY;
476 DWORD filter = REG_NOTIFY_CHANGE_NAME |
477 REG_NOTIFY_CHANGE_ATTRIBUTES |
478 REG_NOTIFY_CHANGE_LAST_SET |
479 REG_NOTIFY_CHANGE_SECURITY;
481 LONG rv = RegNotifyChangeKeyValue(mKey, recurse, filter, mWatchEvent, TRUE);
482 if (rv != ERROR_SUCCESS) {
483 StopWatching();
484 // On older versions of Windows, this call is not implemented, so simply
485 // return NS_OK in those cases and pretend that the watching is happening.
486 return (rv == ERROR_CALL_NOT_IMPLEMENTED) ? NS_OK : NS_ERROR_FAILURE;
487 }
489 mWatchRecursive = recurse;
490 return NS_OK;
491 }
493 NS_IMETHODIMP
494 nsWindowsRegKey::StopWatching()
495 {
496 if (mWatchEvent) {
497 CloseHandle(mWatchEvent);
498 mWatchEvent = nullptr;
499 }
500 return NS_OK;
501 }
503 NS_IMETHODIMP
504 nsWindowsRegKey::HasChanged(bool *result)
505 {
506 if (mWatchEvent && WaitForSingleObject(mWatchEvent, 0) == WAIT_OBJECT_0) {
507 // An event only gets signaled once, then it's done, so we have to set up
508 // another event to watch.
509 StopWatching();
510 StartWatching(mWatchRecursive);
511 *result = true;
512 } else {
513 *result = false;
514 }
515 return NS_OK;
516 }
518 NS_IMETHODIMP
519 nsWindowsRegKey::IsWatching(bool *result)
520 {
521 *result = (mWatchEvent != nullptr);
522 return NS_OK;
523 }
525 //-----------------------------------------------------------------------------
527 nsresult
528 NS_NewWindowsRegKey(nsIWindowsRegKey **result)
529 {
530 nsRefPtr<nsWindowsRegKey> key = new nsWindowsRegKey();
531 key.forget(result);
532 return NS_OK;
533 }
535 //-----------------------------------------------------------------------------
537 nsresult
538 nsWindowsRegKeyConstructor(nsISupports *delegate, const nsIID &iid,
539 void **result)
540 {
541 if (delegate)
542 return NS_ERROR_NO_AGGREGATION;
544 nsCOMPtr<nsIWindowsRegKey> key;
545 nsresult rv = NS_NewWindowsRegKey(getter_AddRefs(key));
546 if (NS_SUCCEEDED(rv))
547 rv = key->QueryInterface(iid, result);
548 return rv;
549 }