michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsPrefBranch_h michael@0: #define nsPrefBranch_h michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIObserver.h" michael@0: #include "nsIPrefBranch.h" michael@0: #include "nsIPrefBranchInternal.h" michael@0: #include "nsIPrefLocalizedString.h" michael@0: #include "nsXPCOM.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsIRelativeFilePref.h" michael@0: #include "nsIFile.h" michael@0: #include "nsString.h" michael@0: #include "nsTArray.h" michael@0: #include "nsWeakReference.h" michael@0: #include "nsClassHashtable.h" michael@0: #include "nsCRT.h" michael@0: #include "nsISupportsImpl.h" michael@0: #include "mozilla/HashFunctions.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: michael@0: namespace mozilla { michael@0: class PreferenceServiceReporter; michael@0: } // namespace mozilla; michael@0: michael@0: class nsPrefBranch; michael@0: michael@0: class PrefCallback : public PLDHashEntryHdr { michael@0: friend class mozilla::PreferenceServiceReporter; michael@0: michael@0: public: michael@0: typedef PrefCallback* KeyType; michael@0: typedef const PrefCallback* KeyTypePointer; michael@0: michael@0: static const PrefCallback* KeyToPointer(PrefCallback *aKey) michael@0: { michael@0: return aKey; michael@0: } michael@0: michael@0: static PLDHashNumber HashKey(const PrefCallback *aKey) michael@0: { michael@0: uint32_t hash = mozilla::HashString(aKey->mDomain); michael@0: return mozilla::AddToHash(hash, aKey->mCanonical); michael@0: } michael@0: michael@0: michael@0: public: michael@0: // Create a PrefCallback with a strong reference to its observer. michael@0: PrefCallback(const char *aDomain, nsIObserver *aObserver, michael@0: nsPrefBranch *aBranch) michael@0: : mDomain(aDomain), michael@0: mBranch(aBranch), michael@0: mWeakRef(nullptr), michael@0: mStrongRef(aObserver) michael@0: { michael@0: MOZ_COUNT_CTOR(PrefCallback); michael@0: nsCOMPtr canonical = do_QueryInterface(aObserver); michael@0: mCanonical = canonical; michael@0: } michael@0: michael@0: // Create a PrefCallback with a weak reference to its observer. michael@0: PrefCallback(const char *aDomain, michael@0: nsISupportsWeakReference *aObserver, michael@0: nsPrefBranch *aBranch) michael@0: : mDomain(aDomain), michael@0: mBranch(aBranch), michael@0: mWeakRef(do_GetWeakReference(aObserver)), michael@0: mStrongRef(nullptr) michael@0: { michael@0: MOZ_COUNT_CTOR(PrefCallback); michael@0: nsCOMPtr canonical = do_QueryInterface(aObserver); michael@0: mCanonical = canonical; michael@0: } michael@0: michael@0: // Copy constructor needs to be explicit or the linker complains. michael@0: PrefCallback(const PrefCallback *&aCopy) michael@0: : mDomain(aCopy->mDomain), michael@0: mBranch(aCopy->mBranch), michael@0: mWeakRef(aCopy->mWeakRef), michael@0: mStrongRef(aCopy->mStrongRef), michael@0: mCanonical(aCopy->mCanonical) michael@0: { michael@0: MOZ_COUNT_CTOR(PrefCallback); michael@0: } michael@0: michael@0: ~PrefCallback() michael@0: { michael@0: MOZ_COUNT_DTOR(PrefCallback); michael@0: } michael@0: michael@0: bool KeyEquals(const PrefCallback *aKey) const michael@0: { michael@0: // We want to be able to look up a weakly-referencing PrefCallback after michael@0: // its observer has died so we can remove it from the table. Once the michael@0: // callback's observer dies, its canonical pointer is stale -- in michael@0: // particular, we may have allocated a new observer in the same spot in michael@0: // memory! So we can't just compare canonical pointers to determine michael@0: // whether aKey refers to the same observer as this. michael@0: // michael@0: // Our workaround is based on the way we use this hashtable: When we ask michael@0: // the hashtable to remove a PrefCallback whose weak reference has michael@0: // expired, we use as the key for removal the same object as was inserted michael@0: // into the hashtable. Thus we can say that if one of the keys' weak michael@0: // references has expired, the two keys are equal iff they're the same michael@0: // object. michael@0: michael@0: if (IsExpired() || aKey->IsExpired()) michael@0: return this == aKey; michael@0: michael@0: if (mCanonical != aKey->mCanonical) michael@0: return false; michael@0: michael@0: return mDomain.Equals(aKey->mDomain); michael@0: } michael@0: michael@0: PrefCallback *GetKey() const michael@0: { michael@0: return const_cast(this); michael@0: } michael@0: michael@0: // Get a reference to the callback's observer, or null if the observer was michael@0: // weakly referenced and has been destroyed. michael@0: already_AddRefed GetObserver() const michael@0: { michael@0: if (!IsWeak()) { michael@0: nsCOMPtr copy = mStrongRef; michael@0: return copy.forget(); michael@0: } michael@0: michael@0: nsCOMPtr observer = do_QueryReferent(mWeakRef); michael@0: return observer.forget(); michael@0: } michael@0: michael@0: const nsCString& GetDomain() const michael@0: { michael@0: return mDomain; michael@0: } michael@0: michael@0: nsPrefBranch* GetPrefBranch() const michael@0: { michael@0: return mBranch; michael@0: } michael@0: michael@0: // Has this callback's weak reference died? michael@0: bool IsExpired() const michael@0: { michael@0: if (!IsWeak()) michael@0: return false; michael@0: michael@0: nsCOMPtr observer(do_QueryReferent(mWeakRef)); michael@0: return !observer; michael@0: } michael@0: michael@0: enum { ALLOW_MEMMOVE = true }; michael@0: michael@0: private: michael@0: nsCString mDomain; michael@0: nsPrefBranch *mBranch; michael@0: michael@0: // Exactly one of mWeakRef and mStrongRef should be non-null. michael@0: nsWeakPtr mWeakRef; michael@0: nsCOMPtr mStrongRef; michael@0: michael@0: // We need a canonical nsISupports pointer, per bug 578392. michael@0: nsISupports *mCanonical; michael@0: michael@0: bool IsWeak() const michael@0: { michael@0: return !!mWeakRef; michael@0: } michael@0: }; michael@0: michael@0: class nsPrefBranch : public nsIPrefBranchInternal, michael@0: public nsIObserver, michael@0: public nsSupportsWeakReference michael@0: { michael@0: friend class mozilla::PreferenceServiceReporter; michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIPREFBRANCH michael@0: NS_DECL_NSIPREFBRANCH2 michael@0: NS_DECL_NSIOBSERVER michael@0: michael@0: nsPrefBranch(const char *aPrefRoot, bool aDefaultBranch); michael@0: virtual ~nsPrefBranch(); michael@0: michael@0: int32_t GetRootLength() { return mPrefRootLength; } michael@0: michael@0: nsresult RemoveObserverFromMap(const char *aDomain, nsISupports *aObserver); michael@0: michael@0: static void NotifyObserver(const char *newpref, void *data); michael@0: michael@0: size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf); michael@0: michael@0: protected: michael@0: nsPrefBranch() /* disallow use of this constructer */ michael@0: { } michael@0: michael@0: nsresult GetDefaultFromPropertiesFile(const char *aPrefName, char16_t **return_buf); michael@0: // As SetCharPref, but without any check on the length of |aValue| michael@0: nsresult SetCharPrefInternal(const char *aPrefName, const char *aValue); michael@0: // Reject strings that are more than 1Mb, warn if strings are more than 16kb michael@0: nsresult CheckSanityOfStringLength(const char* aPrefName, const nsAString& aValue); michael@0: nsresult CheckSanityOfStringLength(const char* aPrefName, const char* aValue); michael@0: nsresult CheckSanityOfStringLength(const char* aPrefName, const uint32_t aLength); michael@0: void RemoveExpiredCallback(PrefCallback *aCallback); michael@0: const char *getPrefName(const char *aPrefName); michael@0: void freeObserverList(void); michael@0: michael@0: friend PLDHashOperator michael@0: FreeObserverFunc(PrefCallback *aKey, michael@0: nsAutoPtr &aCallback, michael@0: void *aArgs); michael@0: michael@0: private: michael@0: int32_t mPrefRootLength; michael@0: nsCString mPrefRoot; michael@0: bool mIsDefault; michael@0: michael@0: bool mFreeingObserverList; michael@0: nsClassHashtable mObservers; michael@0: }; michael@0: michael@0: michael@0: class nsPrefLocalizedString : public nsIPrefLocalizedString, michael@0: public nsISupportsString michael@0: { michael@0: public: michael@0: nsPrefLocalizedString(); michael@0: virtual ~nsPrefLocalizedString(); michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_FORWARD_NSISUPPORTSSTRING(mUnicodeString->) michael@0: NS_FORWARD_NSISUPPORTSPRIMITIVE(mUnicodeString->) michael@0: michael@0: nsresult Init(); michael@0: michael@0: private: michael@0: NS_IMETHOD GetData(char16_t**); michael@0: NS_IMETHOD SetData(const char16_t* aData); michael@0: NS_IMETHOD SetDataWithLength(uint32_t aLength, const char16_t *aData); michael@0: michael@0: nsCOMPtr mUnicodeString; michael@0: }; michael@0: michael@0: michael@0: class nsRelativeFilePref : public nsIRelativeFilePref michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIRELATIVEFILEPREF michael@0: michael@0: nsRelativeFilePref(); michael@0: virtual ~nsRelativeFilePref(); michael@0: michael@0: private: michael@0: nsCOMPtr mFile; michael@0: nsCString mRelativeToKey; michael@0: }; michael@0: michael@0: #endif