CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / modules / libpref / src / nsPrefBranch.cpp
blob423caff55ddc1d79a2ea7d9f5eef6bbddad9012b
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 Mozilla Communicator client code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Alec Flett <alecf@netscape.com>
24 * Brian Nesse <bnesse@netscape.com>
25 * Frederic Plourde <frederic.plourde@collabora.co.uk>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #ifdef MOZ_IPC
42 #include "mozilla/dom/ContentChild.h"
43 #include "nsXULAppAPI.h"
44 #endif
46 #include "nsPrefBranch.h"
47 #include "nsILocalFile.h"
48 #include "nsIObserverService.h"
49 #include "nsXPCOM.h"
50 #include "nsISupportsPrimitives.h"
51 #include "nsIDirectoryService.h"
52 #include "nsString.h"
53 #include "nsReadableUtils.h"
54 #include "nsXPIDLString.h"
55 #include "nsIStringBundle.h"
56 #include "prefapi.h"
57 #include "prmem.h"
58 #include "pldhash.h"
60 #include "plstr.h"
61 #include "nsCRT.h"
62 #include "mozilla/Services.h"
64 #include "prefapi_private_data.h"
66 // Definitions
67 struct EnumerateData {
68 const char *parent;
69 nsTArray<nsCString> *pref_list;
72 // Prototypes
73 static PLDHashOperator
74 pref_enumChild(PLDHashTable *table, PLDHashEntryHdr *heh,
75 PRUint32 i, void *arg);
77 #ifdef MOZ_IPC
78 using mozilla::dom::ContentChild;
80 static ContentChild*
81 GetContentChild()
83 if (XRE_GetProcessType() == GeckoProcessType_Content) {
84 ContentChild* cpc = ContentChild::GetSingleton();
85 if (!cpc) {
86 NS_RUNTIMEABORT("Content Protocol is NULL! We're going to crash!");
88 return cpc;
90 return nsnull;
92 #endif // MOZ_IPC
95 * Constructor/Destructor
98 nsPrefBranch::nsPrefBranch(const char *aPrefRoot, PRBool aDefaultBranch)
100 mPrefRoot = aPrefRoot;
101 mPrefRootLength = mPrefRoot.Length();
102 mIsDefault = aDefaultBranch;
103 mFreeingObserverList = PR_FALSE;
104 mObservers.Init();
106 nsCOMPtr<nsIObserverService> observerService =
107 mozilla::services::GetObserverService();
108 if (observerService) {
109 ++mRefCnt; // Our refcnt must be > 0 when we call this, or we'll get deleted!
110 // add weak so we don't have to clean up at shutdown
111 observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_TRUE);
112 --mRefCnt;
116 nsPrefBranch::~nsPrefBranch()
118 freeObserverList();
120 nsCOMPtr<nsIObserverService> observerService =
121 mozilla::services::GetObserverService();
122 if (observerService)
123 observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
128 * nsISupports Implementation
131 NS_IMPL_THREADSAFE_ADDREF(nsPrefBranch)
132 NS_IMPL_THREADSAFE_RELEASE(nsPrefBranch)
134 NS_INTERFACE_MAP_BEGIN(nsPrefBranch)
135 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPrefBranch)
136 NS_INTERFACE_MAP_ENTRY(nsIPrefBranch)
137 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIPrefBranch2, !mIsDefault)
138 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIPrefBranchInternal, !mIsDefault)
139 NS_INTERFACE_MAP_ENTRY(nsIObserver)
140 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
141 NS_INTERFACE_MAP_END
145 * nsIPrefBranch Implementation
148 NS_IMETHODIMP nsPrefBranch::GetRoot(char **aRoot)
150 NS_ENSURE_ARG_POINTER(aRoot);
151 mPrefRoot.Truncate(mPrefRootLength);
152 *aRoot = ToNewCString(mPrefRoot);
153 return NS_OK;
156 NS_IMETHODIMP nsPrefBranch::GetPrefType(const char *aPrefName, PRInt32 *_retval)
158 NS_ENSURE_ARG(aPrefName);
159 const char *pref = getPrefName(aPrefName);
160 *_retval = PREF_GetPrefType(pref);
161 return NS_OK;
164 NS_IMETHODIMP nsPrefBranch::GetBoolPref(const char *aPrefName, PRBool *_retval)
166 NS_ENSURE_ARG(aPrefName);
167 const char *pref = getPrefName(aPrefName);
168 return PREF_GetBoolPref(pref, _retval, mIsDefault);
171 NS_IMETHODIMP nsPrefBranch::SetBoolPref(const char *aPrefName, PRInt32 aValue)
173 #ifdef MOZ_IPC
174 if (GetContentChild()) {
175 NS_ERROR("cannot set pref from content process");
176 return NS_ERROR_NOT_AVAILABLE;
178 #endif
180 NS_ENSURE_ARG(aPrefName);
181 const char *pref = getPrefName(aPrefName);
182 return PREF_SetBoolPref(pref, aValue, mIsDefault);
185 NS_IMETHODIMP nsPrefBranch::GetCharPref(const char *aPrefName, char **_retval)
187 NS_ENSURE_ARG(aPrefName);
188 const char *pref = getPrefName(aPrefName);
189 return PREF_CopyCharPref(pref, _retval, mIsDefault);
192 NS_IMETHODIMP nsPrefBranch::SetCharPref(const char *aPrefName, const char *aValue)
194 #ifdef MOZ_IPC
195 if (GetContentChild()) {
196 NS_ERROR("cannot set pref from content process");
197 return NS_ERROR_NOT_AVAILABLE;
199 #endif
201 NS_ENSURE_ARG(aPrefName);
202 NS_ENSURE_ARG(aValue);
203 const char *pref = getPrefName(aPrefName);
204 return PREF_SetCharPref(pref, aValue, mIsDefault);
207 NS_IMETHODIMP nsPrefBranch::GetIntPref(const char *aPrefName, PRInt32 *_retval)
209 NS_ENSURE_ARG(aPrefName);
210 const char *pref = getPrefName(aPrefName);
211 return PREF_GetIntPref(pref, _retval, mIsDefault);
214 NS_IMETHODIMP nsPrefBranch::SetIntPref(const char *aPrefName, PRInt32 aValue)
216 #ifdef MOZ_IPC
217 if (GetContentChild()) {
218 NS_ERROR("cannot set pref from content process");
219 return NS_ERROR_NOT_AVAILABLE;
221 #endif
223 NS_ENSURE_ARG(aPrefName);
224 const char *pref = getPrefName(aPrefName);
225 return PREF_SetIntPref(pref, aValue, mIsDefault);
228 NS_IMETHODIMP nsPrefBranch::GetComplexValue(const char *aPrefName, const nsIID & aType, void **_retval)
230 NS_ENSURE_ARG(aPrefName);
232 nsresult rv;
233 nsXPIDLCString utf8String;
235 // we have to do this one first because it's different than all the rest
236 if (aType.Equals(NS_GET_IID(nsIPrefLocalizedString))) {
237 nsCOMPtr<nsIPrefLocalizedString> theString(do_CreateInstance(NS_PREFLOCALIZEDSTRING_CONTRACTID, &rv));
238 if (NS_FAILED(rv)) return rv;
240 const char *pref = getPrefName(aPrefName);
241 PRBool bNeedDefault = PR_FALSE;
243 if (mIsDefault) {
244 bNeedDefault = PR_TRUE;
245 } else {
246 // if there is no user (or locked) value
247 if (!PREF_HasUserPref(pref) && !PREF_PrefIsLocked(pref)) {
248 bNeedDefault = PR_TRUE;
252 // if we need to fetch the default value, do that instead, otherwise use the
253 // value we pulled in at the top of this function
254 if (bNeedDefault) {
255 nsXPIDLString utf16String;
256 rv = GetDefaultFromPropertiesFile(pref, getter_Copies(utf16String));
257 if (NS_SUCCEEDED(rv)) {
258 theString->SetData(utf16String.get());
260 } else {
261 rv = GetCharPref(aPrefName, getter_Copies(utf8String));
262 if (NS_SUCCEEDED(rv)) {
263 theString->SetData(NS_ConvertUTF8toUTF16(utf8String).get());
267 if (NS_SUCCEEDED(rv)) {
268 const char *pref = getPrefName(aPrefName);
269 PRBool bNeedDefault = PR_FALSE;
271 if (mIsDefault) {
272 bNeedDefault = PR_TRUE;
273 } else {
274 // if there is no user (or locked) value
275 if (!PREF_HasUserPref(pref) && !PREF_PrefIsLocked(pref)) {
276 bNeedDefault = PR_TRUE;
280 // if we need to fetch the default value, do that instead, otherwise use the
281 // value we pulled in at the top of this function
282 if (bNeedDefault) {
283 nsXPIDLString utf16String;
284 rv = GetDefaultFromPropertiesFile(pref, getter_Copies(utf16String));
285 if (NS_SUCCEEDED(rv)) {
286 rv = theString->SetData(utf16String.get());
288 } else {
289 rv = GetCharPref(aPrefName, getter_Copies(utf8String));
290 if (NS_SUCCEEDED(rv)) {
291 rv = theString->SetData(NS_ConvertUTF8toUTF16(utf8String).get());
294 if (NS_SUCCEEDED(rv)) {
295 nsIPrefLocalizedString *temp = theString;
297 NS_ADDREF(temp);
298 *_retval = (void *)temp;
302 return rv;
305 // if we can't get the pref, there's no point in being here
306 rv = GetCharPref(aPrefName, getter_Copies(utf8String));
307 if (NS_FAILED(rv)) {
308 return rv;
311 if (aType.Equals(NS_GET_IID(nsILocalFile))) {
312 #ifdef MOZ_IPC
313 if (GetContentChild()) {
314 NS_ERROR("cannot get nsILocalFile pref from content process");
315 return NS_ERROR_NOT_AVAILABLE;
317 #endif
319 nsCOMPtr<nsILocalFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
321 if (NS_SUCCEEDED(rv)) {
322 rv = file->SetPersistentDescriptor(utf8String);
323 if (NS_SUCCEEDED(rv)) {
324 file.forget(reinterpret_cast<nsILocalFile**>(_retval));
325 return NS_OK;
328 return rv;
331 if (aType.Equals(NS_GET_IID(nsIRelativeFilePref))) {
332 #ifdef MOZ_IPC
333 if (GetContentChild()) {
334 NS_ERROR("cannot get nsIRelativeFilePref from content process");
335 return NS_ERROR_NOT_AVAILABLE;
337 #endif
339 nsACString::const_iterator keyBegin, strEnd;
340 utf8String.BeginReading(keyBegin);
341 utf8String.EndReading(strEnd);
343 // The pref has the format: [fromKey]a/b/c
344 if (*keyBegin++ != '[')
345 return NS_ERROR_FAILURE;
346 nsACString::const_iterator keyEnd(keyBegin);
347 if (!FindCharInReadable(']', keyEnd, strEnd))
348 return NS_ERROR_FAILURE;
349 nsCAutoString key(Substring(keyBegin, keyEnd));
351 nsCOMPtr<nsILocalFile> fromFile;
352 nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
353 if (NS_FAILED(rv))
354 return rv;
355 rv = directoryService->Get(key.get(), NS_GET_IID(nsILocalFile), getter_AddRefs(fromFile));
356 if (NS_FAILED(rv))
357 return rv;
359 nsCOMPtr<nsILocalFile> theFile;
360 rv = NS_NewNativeLocalFile(EmptyCString(), PR_TRUE, getter_AddRefs(theFile));
361 if (NS_FAILED(rv))
362 return rv;
363 rv = theFile->SetRelativeDescriptor(fromFile, Substring(++keyEnd, strEnd));
364 if (NS_FAILED(rv))
365 return rv;
366 nsCOMPtr<nsIRelativeFilePref> relativePref;
367 rv = NS_NewRelativeFilePref(theFile, key, getter_AddRefs(relativePref));
368 if (NS_FAILED(rv))
369 return rv;
371 relativePref.forget(reinterpret_cast<nsIRelativeFilePref**>(_retval));
372 return NS_OK;
375 if (aType.Equals(NS_GET_IID(nsISupportsString))) {
376 nsCOMPtr<nsISupportsString> theString(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv));
378 if (NS_SUCCEEDED(rv)) {
379 theString->SetData(NS_ConvertUTF8toUTF16(utf8String));
380 theString.forget(reinterpret_cast<nsISupportsString**>(_retval));
382 return rv;
385 NS_WARNING("nsPrefBranch::GetComplexValue - Unsupported interface type");
386 return NS_NOINTERFACE;
389 NS_IMETHODIMP nsPrefBranch::SetComplexValue(const char *aPrefName, const nsIID & aType, nsISupports *aValue)
391 #ifdef MOZ_IPC
392 if (GetContentChild()) {
393 NS_ERROR("cannot set pref from content process");
394 return NS_ERROR_NOT_AVAILABLE;
396 #endif
398 NS_ENSURE_ARG(aPrefName);
400 nsresult rv = NS_NOINTERFACE;
402 if (aType.Equals(NS_GET_IID(nsILocalFile))) {
403 nsCOMPtr<nsILocalFile> file = do_QueryInterface(aValue);
404 if (!file)
405 return NS_NOINTERFACE;
406 nsCAutoString descriptorString;
408 rv = file->GetPersistentDescriptor(descriptorString);
409 if (NS_SUCCEEDED(rv)) {
410 rv = SetCharPref(aPrefName, descriptorString.get());
412 return rv;
415 if (aType.Equals(NS_GET_IID(nsIRelativeFilePref))) {
416 nsCOMPtr<nsIRelativeFilePref> relFilePref = do_QueryInterface(aValue);
417 if (!relFilePref)
418 return NS_NOINTERFACE;
420 nsCOMPtr<nsILocalFile> file;
421 relFilePref->GetFile(getter_AddRefs(file));
422 if (!file)
423 return NS_NOINTERFACE;
424 nsCAutoString relativeToKey;
425 (void) relFilePref->GetRelativeToKey(relativeToKey);
427 nsCOMPtr<nsILocalFile> relativeToFile;
428 nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv));
429 if (NS_FAILED(rv))
430 return rv;
431 rv = directoryService->Get(relativeToKey.get(), NS_GET_IID(nsILocalFile), getter_AddRefs(relativeToFile));
432 if (NS_FAILED(rv))
433 return rv;
435 nsCAutoString relDescriptor;
436 rv = file->GetRelativeDescriptor(relativeToFile, relDescriptor);
437 if (NS_FAILED(rv))
438 return rv;
440 nsCAutoString descriptorString;
441 descriptorString.Append('[');
442 descriptorString.Append(relativeToKey);
443 descriptorString.Append(']');
444 descriptorString.Append(relDescriptor);
445 return SetCharPref(aPrefName, descriptorString.get());
448 if (aType.Equals(NS_GET_IID(nsISupportsString))) {
449 nsCOMPtr<nsISupportsString> theString = do_QueryInterface(aValue);
451 if (theString) {
452 nsAutoString wideString;
454 rv = theString->GetData(wideString);
455 if (NS_SUCCEEDED(rv)) {
456 rv = SetCharPref(aPrefName, NS_ConvertUTF16toUTF8(wideString).get());
459 return rv;
462 if (aType.Equals(NS_GET_IID(nsIPrefLocalizedString))) {
463 nsCOMPtr<nsIPrefLocalizedString> theString = do_QueryInterface(aValue);
465 if (theString) {
466 nsXPIDLString wideString;
468 rv = theString->GetData(getter_Copies(wideString));
469 if (NS_SUCCEEDED(rv)) {
470 rv = SetCharPref(aPrefName, NS_ConvertUTF16toUTF8(wideString).get());
473 return rv;
476 NS_WARNING("nsPrefBranch::SetComplexValue - Unsupported interface type");
477 return NS_NOINTERFACE;
480 NS_IMETHODIMP nsPrefBranch::ClearUserPref(const char *aPrefName)
482 #ifdef MOZ_IPC
483 if (GetContentChild()) {
484 NS_ERROR("cannot set pref from content process");
485 return NS_ERROR_NOT_AVAILABLE;
487 #endif
489 NS_ENSURE_ARG(aPrefName);
490 const char *pref = getPrefName(aPrefName);
491 return PREF_ClearUserPref(pref);
494 NS_IMETHODIMP nsPrefBranch::PrefHasUserValue(const char *aPrefName, PRBool *_retval)
496 NS_ENSURE_ARG_POINTER(_retval);
497 NS_ENSURE_ARG(aPrefName);
498 const char *pref = getPrefName(aPrefName);
499 *_retval = PREF_HasUserPref(pref);
500 return NS_OK;
503 NS_IMETHODIMP nsPrefBranch::LockPref(const char *aPrefName)
505 #ifdef MOZ_IPC
506 if (GetContentChild()) {
507 NS_ERROR("cannot lock pref from content process");
508 return NS_ERROR_NOT_AVAILABLE;
510 #endif
512 NS_ENSURE_ARG(aPrefName);
513 const char *pref = getPrefName(aPrefName);
514 return PREF_LockPref(pref, PR_TRUE);
517 NS_IMETHODIMP nsPrefBranch::PrefIsLocked(const char *aPrefName, PRBool *_retval)
519 #ifdef MOZ_IPC
520 if (GetContentChild()) {
521 NS_ERROR("cannot check lock pref from content process");
522 return NS_ERROR_NOT_AVAILABLE;
524 #endif
526 NS_ENSURE_ARG_POINTER(_retval);
527 NS_ENSURE_ARG(aPrefName);
528 const char *pref = getPrefName(aPrefName);
529 *_retval = PREF_PrefIsLocked(pref);
530 return NS_OK;
533 NS_IMETHODIMP nsPrefBranch::UnlockPref(const char *aPrefName)
535 #ifdef MOZ_IPC
536 if (GetContentChild()) {
537 NS_ERROR("cannot unlock pref from content process");
538 return NS_ERROR_NOT_AVAILABLE;
540 #endif
542 NS_ENSURE_ARG(aPrefName);
543 const char *pref = getPrefName(aPrefName);
544 return PREF_LockPref(pref, PR_FALSE);
547 /* void resetBranch (in string startingAt); */
548 NS_IMETHODIMP nsPrefBranch::ResetBranch(const char *aStartingAt)
550 return NS_ERROR_NOT_IMPLEMENTED;
553 NS_IMETHODIMP nsPrefBranch::DeleteBranch(const char *aStartingAt)
555 #ifdef MOZ_IPC
556 if (GetContentChild()) {
557 NS_ERROR("cannot set pref from content process");
558 return NS_ERROR_NOT_AVAILABLE;
560 #endif
562 NS_ENSURE_ARG(aStartingAt);
563 const char *pref = getPrefName(aStartingAt);
564 return PREF_DeleteBranch(pref);
567 NS_IMETHODIMP nsPrefBranch::GetChildList(const char *aStartingAt, PRUint32 *aCount, char ***aChildArray)
569 char **outArray;
570 PRInt32 numPrefs;
571 PRInt32 dwIndex;
572 EnumerateData ed;
573 nsAutoTArray<nsCString, 32> prefArray;
575 NS_ENSURE_ARG(aStartingAt);
576 NS_ENSURE_ARG_POINTER(aCount);
577 NS_ENSURE_ARG_POINTER(aChildArray);
579 *aChildArray = nsnull;
580 *aCount = 0;
582 if (!gHashTable.ops)
583 return NS_ERROR_NOT_INITIALIZED;
585 // this will contain a list of all the pref name strings
586 // allocate on the stack for speed
588 ed.parent = getPrefName(aStartingAt);
589 ed.pref_list = &prefArray;
590 PL_DHashTableEnumerate(&gHashTable, pref_enumChild, &ed);
592 // now that we've built up the list, run the callback on
593 // all the matching elements
594 numPrefs = prefArray.Length();
596 if (numPrefs) {
597 outArray = (char **)nsMemory::Alloc(numPrefs * sizeof(char *));
598 if (!outArray)
599 return NS_ERROR_OUT_OF_MEMORY;
601 for (dwIndex = 0; dwIndex < numPrefs; ++dwIndex) {
602 // we need to lop off mPrefRoot in case the user is planning to pass this
603 // back to us because if they do we are going to add mPrefRoot again.
604 const nsCString& element = prefArray[dwIndex];
605 outArray[dwIndex] = (char *)nsMemory::Clone(
606 element.get() + mPrefRootLength, element.Length() - mPrefRootLength + 1);
608 if (!outArray[dwIndex]) {
609 // we ran out of memory... this is annoying
610 NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(dwIndex, outArray);
611 return NS_ERROR_OUT_OF_MEMORY;
614 *aChildArray = outArray;
616 *aCount = numPrefs;
618 return NS_OK;
623 * nsIPrefBranch2 methods
626 NS_IMETHODIMP nsPrefBranch::AddObserver(const char *aDomain, nsIObserver *aObserver, PRBool aHoldWeak)
628 PrefCallback *pCallback;
629 const char *pref;
631 NS_ENSURE_ARG(aDomain);
632 NS_ENSURE_ARG(aObserver);
634 // hold a weak reference to the observer if so requested
635 if (aHoldWeak) {
636 nsCOMPtr<nsISupportsWeakReference> weakRefFactory = do_QueryInterface(aObserver);
637 if (!weakRefFactory) {
638 // the caller didn't give us a object that supports weak reference... tell them
639 return NS_ERROR_INVALID_ARG;
642 // Construct a PrefCallback with a weak reference to the observer.
643 pCallback = new PrefCallback(aDomain, weakRefFactory, this);
645 } else {
646 // Construct a PrefCallback with a strong reference to the observer.
647 pCallback = new PrefCallback(aDomain, aObserver, this);
650 if (mObservers.Get(pCallback)) {
651 NS_WARNING("Ignoring duplicate observer.");
652 delete pCallback;
653 return NS_OK;
656 PRBool putSucceeded = mObservers.Put(pCallback, pCallback);
658 if (!putSucceeded) {
659 delete pCallback;
660 return NS_ERROR_FAILURE;
663 // We must pass a fully qualified preference name to the callback
664 // aDomain == nsnull is the only possible failure, and we trapped it with
665 // NS_ENSURE_ARG above.
666 pref = getPrefName(aDomain);
667 PREF_RegisterCallback(pref, NotifyObserver, pCallback);
668 return NS_OK;
671 NS_IMETHODIMP nsPrefBranch::RemoveObserver(const char *aDomain, nsIObserver *aObserver)
673 NS_ENSURE_ARG(aDomain);
674 NS_ENSURE_ARG(aObserver);
676 nsresult rv = NS_OK;
678 // If we're in the middle of a call to freeObserverList, don't process this
679 // RemoveObserver call -- the observer in question will be removed soon, if
680 // it hasn't been already.
682 // It's important that we don't touch mObservers in any way -- even a Get()
683 // which retuns null might cause the hashtable to resize itself, which will
684 // break the Enumerator in freeObserverList.
685 if (mFreeingObserverList)
686 return NS_OK;
688 // Remove the relevant PrefCallback from mObservers and get an owning
689 // pointer to it. Unregister the callback first, and then let the owning
690 // pointer go out of scope and destroy the callback.
691 PrefCallback key(aDomain, aObserver, this);
692 nsAutoPtr<PrefCallback> pCallback;
693 mObservers.RemoveAndForget(&key, pCallback);
694 if (pCallback) {
695 // aDomain == nsnull is the only possible failure, trapped above
696 const char *pref = getPrefName(aDomain);
697 rv = PREF_UnregisterCallback(pref, NotifyObserver, pCallback);
700 return rv;
703 NS_IMETHODIMP nsPrefBranch::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
705 // watch for xpcom shutdown and free our observers to eliminate any cyclic references
706 if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
707 freeObserverList();
709 return NS_OK;
712 /* static */
713 nsresult nsPrefBranch::NotifyObserver(const char *newpref, void *data)
715 PrefCallback *pCallback = (PrefCallback *)data;
717 nsCOMPtr<nsIObserver> observer = pCallback->GetObserver();
718 if (!observer) {
719 // The observer has expired. Let's remove this callback.
720 pCallback->GetPrefBranch()->RemoveExpiredCallback(pCallback);
721 return NS_OK;
724 // remove any root this string may contain so as to not confuse the observer
725 // by passing them something other than what they passed us as a topic
726 PRUint32 len = pCallback->GetPrefBranch()->GetRootLength();
727 nsCAutoString suffix(newpref + len);
729 observer->Observe(static_cast<nsIPrefBranch *>(pCallback->GetPrefBranch()),
730 NS_PREFBRANCH_PREFCHANGE_TOPIC_ID,
731 NS_ConvertASCIItoUTF16(suffix).get());
732 return NS_OK;
735 PLDHashOperator
736 FreeObserverFunc(PrefCallback *aKey,
737 nsAutoPtr<PrefCallback> &aCallback,
738 void *aArgs)
740 // Calling NS_RELEASE below might trigger a call to
741 // nsPrefBranch::RemoveObserver, since some classes remove themselves from
742 // the pref branch on destruction. We don't need to worry about this causing
743 // double-frees, however, because freeObserverList sets mFreeingObserverList
744 // to true, which prevents RemoveObserver calls from doing anything.
746 nsPrefBranch *prefBranch = aCallback->GetPrefBranch();
747 const char *pref = prefBranch->getPrefName(aCallback->GetDomain().get());
748 PREF_UnregisterCallback(pref, nsPrefBranch::NotifyObserver, aCallback);
750 return PL_DHASH_REMOVE;
753 void nsPrefBranch::freeObserverList(void)
755 // We need to prevent anyone from modifying mObservers while we're
756 // enumerating over it. In particular, some clients will call
757 // RemoveObserver() when they're destructed; we need to keep those calls from
758 // touching mObservers.
759 mFreeingObserverList = PR_TRUE;
760 mObservers.Enumerate(&FreeObserverFunc, nsnull);
761 mFreeingObserverList = PR_FALSE;
764 void
765 nsPrefBranch::RemoveExpiredCallback(PrefCallback *aCallback)
767 NS_PRECONDITION(aCallback->IsExpired(), "Callback should be expired.");
768 mObservers.Remove(aCallback);
771 nsresult nsPrefBranch::GetDefaultFromPropertiesFile(const char *aPrefName, PRUnichar **return_buf)
773 nsresult rv;
775 // the default value contains a URL to a .properties file
777 nsXPIDLCString propertyFileURL;
778 rv = PREF_CopyCharPref(aPrefName, getter_Copies(propertyFileURL), PR_TRUE);
779 if (NS_FAILED(rv))
780 return rv;
782 nsCOMPtr<nsIStringBundleService> bundleService =
783 mozilla::services::GetStringBundleService();
784 if (!bundleService)
785 return NS_ERROR_FAILURE;
787 nsCOMPtr<nsIStringBundle> bundle;
788 rv = bundleService->CreateBundle(propertyFileURL,
789 getter_AddRefs(bundle));
790 if (NS_FAILED(rv))
791 return rv;
793 // string names are in unicode
794 nsAutoString stringId;
795 stringId.AssignASCII(aPrefName);
797 return bundle->GetStringFromName(stringId.get(), return_buf);
800 const char *nsPrefBranch::getPrefName(const char *aPrefName)
802 NS_ASSERTION(aPrefName, "null pref name!");
804 // for speed, avoid strcpy if we can:
805 if (mPrefRoot.IsEmpty())
806 return aPrefName;
808 // isn't there a better way to do this? this is really kind of gross.
809 mPrefRoot.Truncate(mPrefRootLength);
810 mPrefRoot.Append(aPrefName);
811 return mPrefRoot.get();
814 static PLDHashOperator
815 pref_enumChild(PLDHashTable *table, PLDHashEntryHdr *heh,
816 PRUint32 i, void *arg)
818 PrefHashEntry *he = static_cast<PrefHashEntry*>(heh);
819 EnumerateData *d = reinterpret_cast<EnumerateData *>(arg);
820 if (strncmp(he->key, d->parent, strlen(d->parent)) == 0) {
821 d->pref_list->AppendElement(he->key);
823 return PL_DHASH_NEXT;
826 //----------------------------------------------------------------------------
827 // nsPrefLocalizedString
828 //----------------------------------------------------------------------------
830 nsPrefLocalizedString::nsPrefLocalizedString()
834 nsPrefLocalizedString::~nsPrefLocalizedString()
840 * nsISupports Implementation
843 NS_IMPL_THREADSAFE_ADDREF(nsPrefLocalizedString)
844 NS_IMPL_THREADSAFE_RELEASE(nsPrefLocalizedString)
846 NS_INTERFACE_MAP_BEGIN(nsPrefLocalizedString)
847 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPrefLocalizedString)
848 NS_INTERFACE_MAP_ENTRY(nsIPrefLocalizedString)
849 NS_INTERFACE_MAP_ENTRY(nsISupportsString)
850 NS_INTERFACE_MAP_END
852 nsresult nsPrefLocalizedString::Init()
854 nsresult rv;
855 mUnicodeString = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
857 return rv;
860 NS_IMETHODIMP
861 nsPrefLocalizedString::GetData(PRUnichar **_retval)
863 nsAutoString data;
865 nsresult rv = GetData(data);
866 if (NS_FAILED(rv))
867 return rv;
869 *_retval = ToNewUnicode(data);
870 if (!*_retval)
871 return NS_ERROR_OUT_OF_MEMORY;
873 return NS_OK;
876 NS_IMETHODIMP
877 nsPrefLocalizedString::SetData(const PRUnichar *aData)
879 if (!aData)
880 return SetData(EmptyString());
881 return SetData(nsDependentString(aData));
884 NS_IMETHODIMP
885 nsPrefLocalizedString::SetDataWithLength(PRUint32 aLength,
886 const PRUnichar *aData)
888 if (!aData)
889 return SetData(EmptyString());
890 return SetData(Substring(aData, aData + aLength));
893 //----------------------------------------------------------------------------
894 // nsRelativeFilePref
895 //----------------------------------------------------------------------------
897 NS_IMPL_THREADSAFE_ISUPPORTS1(nsRelativeFilePref, nsIRelativeFilePref)
899 nsRelativeFilePref::nsRelativeFilePref()
903 nsRelativeFilePref::~nsRelativeFilePref()
907 NS_IMETHODIMP nsRelativeFilePref::GetFile(nsILocalFile **aFile)
909 NS_ENSURE_ARG_POINTER(aFile);
910 *aFile = mFile;
911 NS_IF_ADDREF(*aFile);
912 return NS_OK;
915 NS_IMETHODIMP nsRelativeFilePref::SetFile(nsILocalFile *aFile)
917 mFile = aFile;
918 return NS_OK;
921 NS_IMETHODIMP nsRelativeFilePref::GetRelativeToKey(nsACString& aRelativeToKey)
923 aRelativeToKey.Assign(mRelativeToKey);
924 return NS_OK;
927 NS_IMETHODIMP nsRelativeFilePref::SetRelativeToKey(const nsACString& aRelativeToKey)
929 mRelativeToKey.Assign(aRelativeToKey);
930 return NS_OK;