Profile sharing - use in prefs. bug=202474, r=jgaunt/sr=alecf/a=asa

This commit is contained in:
ccarlen%netscape.com 2003-05-02 04:00:19 +00:00
parent 47683e635f
commit a7e34e6c37
8 changed files with 815 additions and 54 deletions

View File

@ -45,6 +45,13 @@ REQUIRES = xpcom \
intl \
necko \
$(NULL)
ifdef MOZ_PROFILESHARING
REQUIRES += transmngr \
ipcd \
profilesharingsetup \
$(NULL)
endif
include $(topsrcdir)/config/config.mk
@ -56,6 +63,11 @@ CPPSRCS = nsPref.cpp \
prefapi.cpp \
$(NULL)
ifdef MOZ_PROFILESHARING
CPPSRCS += nsSharedPrefHandler.cpp
SHARED_LIBRARY_LIBS = $(DIST)/lib/$(LIB_PREFIX)ipcdutil_s.$(LIB_SUFFIX)
endif
PREF_JS_EXPORTS = \
$(srcdir)/init/all.js \
$(srcdir)/init/mailnews.js \
@ -87,6 +99,10 @@ ifeq ($(OS_ARCH),OpenVMS)
PREF_JS_EXPORTS += $(srcdir)/unix/openvms.js
endif
ifdef MOZ_PROFILESHARING
PREF_JS_EXPORTS += $(srcdir)/init/non-shared.txt
endif
ifeq ($(OS_ARCH),AIX)
PREF_JS_EXPORTS += $(srcdir)/unix/aix.js
endif

View File

@ -0,0 +1,6 @@
# A list of preferences which are not shared between profile sharing clients.
# An entry may be either a complete preference name, or the name of a branch,
# in which case, none if its children will be shared.
signon.SignonFileName

View File

@ -63,11 +63,20 @@
#include "nsITimelineService.h"
#ifdef MOZ_PROFILESHARING
#include "nsIProfileSharingSetup.h"
#include "nsSharedPrefHandler.h"
#endif
// Definitions
#define INITIAL_MAX_DEFAULT_PREF_FILES 10
// Prototypes
#ifdef MOZ_PROFILESHARING
static PRBool isSharingEnabled();
#endif
static nsresult openPrefFile(nsIFile* aFile, PRBool aIsErrorFatal,
PRBool aIsGlobalContext, PRBool aSkipFirstLine);
@ -75,19 +84,25 @@ static nsresult openPrefFile(nsIFile* aFile, PRBool aIsErrorFatal,
// needed so we can still get the JS Runtime Service during XPCOM shutdown
static nsIJSRuntimeService* gJSRuntimeService = nsnull; // owning reference
//-----------------------------------------------------------------------------
/*
* Constructor/Destructor
*/
nsPrefService::nsPrefService()
: mCurrentFile(nsnull)
: mCurrentFile(nsnull),
mErrorOpeningUserPrefs(PR_FALSE)
{
nsPrefBranch *rootBranch;
rootBranch = new nsPrefBranch("", PR_FALSE);
mRootBranch = (nsIPrefBranch *)rootBranch;
#if MOZ_PROFILESHARING
mCurrentSharedFile = nsnull;
mErrorOpeningSharedUserPrefs = PR_FALSE;
#endif
}
nsPrefService::~nsPrefService()
@ -95,6 +110,11 @@ nsPrefService::~nsPrefService()
PREF_Cleanup();
NS_IF_RELEASE(mCurrentFile);
NS_IF_RELEASE(gJSRuntimeService);
#ifdef MOZ_PROFILESHARING
NS_IF_RELEASE(mCurrentSharedFile);
NS_IF_RELEASE(gSharedPrefHandler);
#endif
}
@ -149,6 +169,11 @@ nsresult nsPrefService::Init()
rv = observerService->AddObserver(this, "profile-do-change", PR_TRUE);
}
}
#ifdef MOZ_PROFILESHARING
rv = NS_CreateSharedPrefHandler(this);
#endif
return(rv);
}
@ -164,10 +189,22 @@ NS_IMETHODIMP nsPrefService::Observe(nsISupports *aSubject, const char *aTopic,
}
} else {
rv = SavePrefFile(nsnull);
#ifdef MOZ_PROFILESHARING
if (isSharingEnabled())
rv = gSharedPrefHandler->OnSessionEnd();
#endif
}
} else if (!nsCRT::strcmp(aTopic, "profile-do-change")) {
ResetUserPrefs();
rv = ReadUserPrefs(nsnull);
#ifdef MOZ_PROFILESHARING
if (isSharingEnabled())
rv = gSharedPrefHandler->OnSessionBegin();
else
#endif
{
ResetUserPrefs();
rv = ReadUserPrefs(nsnull);
}
}
return rv;
}
@ -182,19 +219,10 @@ NS_IMETHODIMP nsPrefService::ReadUserPrefs(nsIFile *aFile)
UseUserPrefFile();
NotifyServiceObservers(NS_PREFSERVICE_READ_TOPIC_ID);
JS_MaybeGC(gMochaContext);
} else {
if (mCurrentFile == aFile)
return NS_OK;
NS_IF_RELEASE(mCurrentFile);
mCurrentFile = aFile;
NS_ADDREF(mCurrentFile);
gErrorOpeningUserPrefs = PR_FALSE;
rv = openPrefFile(mCurrentFile, PR_TRUE, PR_FALSE, PR_TRUE);
rv = ReadAndOwnUserPrefFile(aFile);
}
return rv;
}
@ -218,19 +246,12 @@ NS_IMETHODIMP nsPrefService::ResetUserPrefs()
NS_IMETHODIMP nsPrefService::SavePrefFile(nsIFile *aFile)
{
if (nsnull == aFile) {
// the gDirty flag tells us if we should write to mCurrentFile
// we only check this flag when the caller wants to write to the default
if (!gDirty) {
NS_WARNING("not writing prefs because they haven't changed");
return NS_OK;
}
// It's possible that we never got a prefs file.
return mCurrentFile ? WritePrefFile(mCurrentFile) : NS_OK;
} else {
return WritePrefFile(aFile);
}
#ifdef MOZ_PROFILESHARING
// sharing only applies to the default prefs file
if (aFile == nsnull && isSharingEnabled())
return gSharedPrefHandler->OnSavePrefs();
#endif
return SavePrefFileInternal(aFile);
}
NS_IMETHODIMP nsPrefService::GetBranch(const char *aPrefRoot, nsIPrefBranch **_retval)
@ -305,29 +326,39 @@ nsresult nsPrefService::NotifyServiceObservers(const char *aTopic)
nsresult nsPrefService::UseDefaultPrefFile()
{
nsresult rv;
nsresult rv, rv2;
nsCOMPtr<nsIFile> aFile;
// Anything which calls NS_InitXPCOM will have this
rv = NS_GetSpecialDirectory(NS_APP_PREFS_50_FILE, getter_AddRefs(aFile));
if (!aFile) {
// We know we have XPCOM directory services, but we might not have a provider which
// knows about NS_APP_PREFS_50_FILE. Put the file in NS_XPCOM_CURRENT_PROCESS_DIR.
rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR, getter_AddRefs(aFile));
if (NS_FAILED(rv)) return rv;
rv = aFile->AppendNative(NS_LITERAL_CSTRING("default_prefs.js"));
if (NS_FAILED(rv)) return rv;
}
rv = ReadUserPrefs(aFile);
if (NS_SUCCEEDED(rv)) {
return rv;
#ifdef MOZ_PROFILESHARING
// First, read the shared file.
if (isSharingEnabled()) {
rv = NS_GetSpecialDirectory(NS_SHARED NS_APP_PREFS_50_FILE, getter_AddRefs(aFile));
if (NS_SUCCEEDED(rv)) {
rv = ReadAndOwnSharedUserPrefFile(aFile);
// Most likely cause of failure here is that the file didn't
// exist, so save a new one. mSharedUserPrefReadFailed will be
// used to catch an error in actually reading the file.
if (NS_FAILED(rv)) {
rv2 = SavePrefFileInternal(aFile);
NS_ASSERTION(NS_SUCCEEDED(rv2), "Failed to save new shared pref file");
}
}
}
// Continue on to read the nonshared file.
#endif
// need to save the prefs now
SavePrefFile(aFile);
rv = NS_GetSpecialDirectory(NS_APP_PREFS_50_FILE, getter_AddRefs(aFile));
if (NS_SUCCEEDED(rv)) {
rv = ReadAndOwnUserPrefFile(aFile);
// Most likely cause of failure here is that the file didn't
// exist, so save a new one. mUserPrefReadFailed will be
// used to catch an error in actually reading the file.
if (NS_FAILED(rv)) {
rv2 = SavePrefFileInternal(aFile);
NS_ASSERTION(NS_SUCCEEDED(rv2), "Failed to save new shared pref file");
}
}
return rv;
}
@ -336,7 +367,15 @@ nsresult nsPrefService::UseUserPrefFile()
nsresult rv = NS_OK;
nsCOMPtr<nsIFile> aFile;
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(aFile));
#ifdef MOZ_PROFILESHARING
nsCAutoString prefsDirProp(NS_APP_PREFS_50_DIR);
if (isSharingEnabled())
prefsDirProp.Insert(NS_SHARED, 0); // Prepend modifier so we get shared file
#else
nsDependentCString prefsDirProp(NS_APP_PREFS_50_DIR);
#endif
rv = NS_GetSpecialDirectory(prefsDirProp.get(), getter_AddRefs(aFile));
if (NS_SUCCEEDED(rv) && aFile) {
rv = aFile->AppendNative(NS_LITERAL_CSTRING("user.js"));
if (NS_SUCCEEDED(rv)) {
@ -346,6 +385,93 @@ nsresult nsPrefService::UseUserPrefFile()
return rv;
}
nsresult nsPrefService::ReadAndOwnUserPrefFile(nsIFile *aFile)
{
NS_ENSURE_ARG(aFile);
if (mCurrentFile == aFile)
return NS_OK;
NS_IF_RELEASE(mCurrentFile);
mCurrentFile = aFile;
NS_ADDREF(mCurrentFile);
#ifdef MOZ_PROFILESHARING
// We don't want prefs set here to cause transactions
gSharedPrefHandler->ReadingUserPrefs(PR_TRUE);
#endif
// We need to track errors in reading the shared and the
// non-shared files independently. Clear gErrorOpeningUserPrefs
// and set the appropriate member variable from it after reading.
gErrorOpeningUserPrefs = PR_FALSE;
nsresult rv = openPrefFile(mCurrentFile, PR_TRUE, PR_FALSE, PR_TRUE);
mErrorOpeningUserPrefs = gErrorOpeningUserPrefs;
#ifdef MOZ_PROFILESHARING
gSharedPrefHandler->ReadingUserPrefs(PR_FALSE);
#endif
return rv;
}
#ifdef MOZ_PROFILESHARING
nsresult nsPrefService::ReadAndOwnSharedUserPrefFile(nsIFile *aFile)
{
NS_ENSURE_ARG(aFile);
if (mCurrentSharedFile == aFile)
return NS_OK;
NS_IF_RELEASE(mCurrentSharedFile);
mCurrentSharedFile = aFile;
NS_ADDREF(mCurrentSharedFile);
#ifdef MOZ_PROFILESHARING
// We don't want prefs set here to cause transactions
gSharedPrefHandler->ReadingUserPrefs(PR_TRUE);
#endif
// We need to track errors in reading the shared and the
// non-shared files independently. Clear gErrorOpeningUserPrefs
// and set the appropriate member variable from it after reading.
gErrorOpeningUserPrefs = PR_FALSE;
nsresult rv = openPrefFile(mCurrentSharedFile, PR_TRUE, PR_FALSE, PR_TRUE);
mErrorOpeningSharedUserPrefs = gErrorOpeningUserPrefs;
#ifdef MOZ_PROFILESHARING
gSharedPrefHandler->ReadingUserPrefs(PR_FALSE);
#endif
return rv;
}
#endif
nsresult nsPrefService::SavePrefFileInternal(nsIFile *aFile)
{
if (nsnull == aFile) {
// the gDirty flag tells us if we should write to mCurrentFile
// we only check this flag when the caller wants to write to the default
if (!gDirty)
return NS_OK;
// It's possible that we never got a prefs file.
nsresult rv = NS_OK;
if (mCurrentFile)
rv = WritePrefFile(mCurrentFile);
#ifdef MOZ_PROFILESHARING
if (mCurrentSharedFile) {
nsresult rv2 = WritePrefFile(mCurrentSharedFile);
if (NS_SUCCEEDED(rv))
rv = rv2;
}
#endif
return rv;
} else {
return WritePrefFile(aFile);
}
}
nsresult nsPrefService::WritePrefFile(nsIFile* aFile)
{
const char outHeader[] =
@ -378,8 +504,12 @@ nsresult nsPrefService::WritePrefFile(nsIFile* aFile)
return NS_ERROR_NOT_INITIALIZED;
/* ?! Don't save (blank) user prefs if there was an error reading them */
if (gErrorOpeningUserPrefs)
if (aFile == mCurrentFile && mErrorOpeningUserPrefs)
return NS_OK;
#if MOZ_PROFILESHARING
if (aFile == mCurrentSharedFile && mErrorOpeningSharedUserPrefs)
return NS_OK;
#endif
// execute a "safe" save by saving through a tempfile
PRInt32 numCopies = 1;
@ -397,12 +527,25 @@ nsresult nsPrefService::WritePrefFile(nsIFile* aFile)
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outStream), aFile);
if (NS_FAILED(rv))
return rv;
// write out the file header
rv = outStream->Write(outHeader, sizeof(outHeader) - 1, &writeAmount);
pref_saveArgs saveArgs;
saveArgs.prefArray = valueArray;
saveArgs.saveTypes = SAVE_ALL;
#if MOZ_PROFILESHARING
if (isSharingEnabled()) {
if (aFile == mCurrentSharedFile)
saveArgs.saveTypes = SAVE_SHARED;
else if (aFile == mCurrentFile)
saveArgs.saveTypes = SAVE_NONSHARED;
}
#endif
// get the lines that we're supposed to be writing to the file
PL_DHashTableEnumerate(&gHashTable, pref_savePref, valueArray);
PL_DHashTableEnumerate(&gHashTable, pref_savePref, &saveArgs);
/* Sort the preferences to make a readable file on disk */
NS_QuickSort(valueArray, gHashTable.entryCount, sizeof(char *), pref_CompareStrings, NULL);
@ -436,6 +579,22 @@ nsresult nsPrefService::WritePrefFile(nsIFile* aFile)
return rv;
}
#ifdef MOZ_PROFILESHARING
static PRBool isSharingEnabled()
{
static PRBool gSharingEnabled = PR_FALSE;
// If FALSE, query again. It may not have been set yet.
if (!gSharingEnabled) {
nsCOMPtr<nsIProfileSharingSetup> sharingSetup =
do_GetService("@mozilla.org/embedcomp/profile-sharing-setup;1");
if (sharingSetup)
sharingSetup->GetIsSharingEnabled(&gSharingEnabled);
}
return gSharingEnabled;
}
#endif
static nsresult openPrefFile(nsIFile* aFile, PRBool aIsErrorFatal,
PRBool aIsGlobalContext, PRBool aSkipFirstLine)
{

View File

@ -51,6 +51,8 @@ class nsPrefService : public nsIPrefService,
public nsIPrefBranchInternal,
public nsSupportsWeakReference
{
friend class nsSharedPrefHandler;
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPREFSERVICE
@ -67,11 +69,17 @@ protected:
nsresult NotifyServiceObservers(const char *aSubject);
nsresult UseDefaultPrefFile();
nsresult UseUserPrefFile();
nsresult ReadAndOwnUserPrefFile(nsIFile *aFile);
nsresult ReadAndOwnSharedUserPrefFile(nsIFile *aFile);
nsresult SavePrefFileInternal(nsIFile* aFile);
nsresult WritePrefFile(nsIFile* aFile);
private:
nsCOMPtr<nsIPrefBranch> mRootBranch;
nsIFile* mCurrentFile;
PRPackedBool mErrorOpeningUserPrefs;
PRPackedBool mErrorOpeningSharedUserPrefs;
nsIFile* mCurrentSharedFile;
};

View File

@ -0,0 +1,432 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2003
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Conrad Carlen <ccarlen@netscape.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsSharedPrefHandler.h"
#include "nsPrefService.h"
#include "nsIServiceManagerUtils.h"
#include "nsILocalFile.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsReadableUtils.h"
#include "ipcMessageReader.h"
#include "ipcMessageWriter.h"
#if defined(PR_LOGGING)
// set NSPR_LOG_MODULES=nsSharedPrefHandler:5
PRLogModuleInfo *gPrefsTransactionObserverLog = nsnull;
#define LOG(args) PR_LOG(PrefsTransactionObserver, PR_LOG_DEBUG, args)
#else
#define LOG(args)
#endif
nsSharedPrefHandler *gSharedPrefHandler = nsnull;
// Constants
#define kPrefsTSQueueName NS_LITERAL_CSTRING("prefs")
#define kExceptionListFileName NS_LITERAL_CSTRING("nonshared.txt")
const char kExceptionListCommentChar = '#';
const PRUint32 kCurrentPrefsTransactionDataVersion = 1;
// Static function prototypes
static PRBool PR_CALLBACK enumFind(void* aElement, void *aData);
static PRBool PR_CALLBACK enumFree(void* aElement, void *aData);
static PRInt32 ReadLine(FILE* inStm, nsACString& destString);
//****************************************************************************
// Transaction Format for kCurrentPrefsTransactionDataVersion
//****************************************************************************
/*
NOTE: All string data is null terminated.
The data length includes the null character.
PRUint32 dataVersion
PRUint32 action SET or CLEAR
PRUint32 prefNameLen
char prefName[prefNameLen]
PRUint32 prefValueKind
PRUint32 prefValueLen
char prefValue[prefValueLen]
*/
//*****************************************************************************
// nsSharedPrefHandler
//*****************************************************************************
nsSharedPrefHandler::nsSharedPrefHandler() :
mPrefService(nsnull), mPrefsTSQueueName("prefs"),
mSessionActive(PR_FALSE),
mReadingUserPrefs(PR_FALSE),
mProcessingTransaction(PR_FALSE)
{
#if defined(PR_LOGGING)
if (!gPrefsTransactionObserverLog)
gPrefsTransactionObserverLog = PR_NewLogModule("nsSharedPrefHandler");
#endif
}
nsSharedPrefHandler::~nsSharedPrefHandler()
{
mExceptionList.EnumerateForwards(enumFree, nsnull);
}
nsresult nsSharedPrefHandler::OnSessionBegin()
{
nsresult rv = EnsureTransactionService();
NS_ENSURE_SUCCESS(rv, rv);
// When the reply to this comes (synchronously), we'll actually
// read in our data. The transaction service holds a lock on
// our data file during our reply.
rv = mTransService->Attach(kPrefsTSQueueName, this, PR_TRUE);
NS_ASSERTION(NS_SUCCEEDED(rv), "tmITransactionService::Attach() failed");
if (NS_SUCCEEDED(rv))
mSessionActive = PR_TRUE;
return rv;
}
nsresult nsSharedPrefHandler::OnSessionEnd()
{
nsresult rv = EnsureTransactionService();
NS_ENSURE_SUCCESS(rv, rv);
rv = mTransService->Detach(kPrefsTSQueueName);
NS_ASSERTION(NS_SUCCEEDED(rv), "tmITransactionService::Detach() failed");
mSessionActive = PR_FALSE;
return rv;
}
nsresult nsSharedPrefHandler::OnSavePrefs()
{
nsresult rv = EnsureTransactionService();
NS_ENSURE_SUCCESS(rv, rv);
// When the reply to this comes (synchronously), we'll actually
// write out our data. The transaction service holds a lock on
// our data file during our reply.
rv = mTransService->Flush(kPrefsTSQueueName, PR_TRUE);
NS_ASSERTION(NS_SUCCEEDED(rv), "tmITransactionService::Flush() failed");
return NS_OK;
}
nsresult nsSharedPrefHandler::OnPrefChanged(PrefAction action,
PrefHashEntry* pref,
PrefValue newValue)
{
if (!mSessionActive)
return NS_OK;
if (action != PREF_SETUSER)
return NS_OK;
if (!IsPrefShared(pref->key))
return NS_OK;
if (mReadingUserPrefs || mProcessingTransaction)
return NS_OK;
nsresult rv = EnsureTransactionService();
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 valueLen, prefNameLen = strlen(pref->key);
ipcMessageWriter outMsg(256);
outMsg.PutInt32(kCurrentPrefsTransactionDataVersion);
outMsg.PutInt32(action);
outMsg.PutInt32(prefNameLen + 1);
outMsg.PutBytes(pref->key, prefNameLen + 1);
switch (pref->flags & PREF_VALUETYPE_MASK) {
case PREF_STRING:
outMsg.PutInt32(PREF_STRING);
valueLen = strlen(newValue.stringVal) + 1;
outMsg.PutInt32(valueLen);
outMsg.PutBytes(newValue.stringVal, valueLen);
break;
case PREF_INT:
outMsg.PutInt32(PREF_INT);
outMsg.PutInt32(sizeof(PRInt32));
outMsg.PutInt32(newValue.intVal);
break;
case PREF_BOOL:
outMsg.PutInt32(PREF_BOOL);
outMsg.PutInt32(sizeof(PRInt32));
outMsg.PutInt32(newValue.boolVal);
break;
default:
return NS_ERROR_UNEXPECTED;
}
rv = outMsg.HasError() ? NS_ERROR_FAILURE : NS_OK;
NS_ASSERTION(NS_SUCCEEDED(rv), "OnPrefChanged: outMsg failed");
if (NS_SUCCEEDED(rv)) {
rv = mTransService->PostTransaction(kPrefsTSQueueName, outMsg.GetBuffer(), outMsg.GetSize());
NS_ASSERTION(NS_SUCCEEDED(rv), "tmITransactionService::PostTransaction() failed");
}
return rv;
}
PRBool nsSharedPrefHandler::IsPrefShared(const char* prefName)
{
if (!mExceptionList.Count()) // quick check for empty list
return PR_TRUE;
// returns PR_TRUE if we reached the end without finding it.
return mExceptionList.EnumerateForwards(enumFind, NS_CONST_CAST(char*, prefName));
}
//*****************************************************************************
// nsSharedPrefHandler Protected Methods
//*****************************************************************************
nsresult nsSharedPrefHandler::Init(nsPrefService* aOwner)
{
NS_ENSURE_ARG(aOwner);
mPrefService = aOwner;
(void)ReadExceptionFile();
return NS_OK;
}
nsresult nsSharedPrefHandler::ReadExceptionFile()
{
nsresult rv;
nsCOMPtr<nsIProperties> directoryService =
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsILocalFile> exceptionFile;
rv = directoryService->Get(NS_APP_PREF_DEFAULTS_50_DIR, NS_GET_IID(nsILocalFile),
getter_AddRefs(exceptionFile));
if (NS_SUCCEEDED(rv)) {
rv = exceptionFile->AppendNative(kExceptionListFileName);
if (NS_SUCCEEDED(rv)) {
FILE *inStm;
rv = exceptionFile->OpenANSIFileDesc("r", &inStm);
if (NS_SUCCEEDED(rv)) {
nsCAutoString lineStr;
while (ReadLine(inStm, lineStr) != EOF) {
lineStr.CompressWhitespace();
if (lineStr.IsEmpty() || lineStr.CharAt(0) == kExceptionListCommentChar)
continue;
char *rawStr = ToNewCString(lineStr);
if (!rawStr) {
rv = NS_ERROR_OUT_OF_MEMORY;
break;
}
mExceptionList.AppendElement(rawStr);
}
fclose(inStm);
}
}
}
}
return rv;
}
nsresult nsSharedPrefHandler::EnsureTransactionService()
{
if (mTransService)
return NS_OK;
nsresult rv;
mTransService = do_GetService("@mozilla.org/transaction/service;1", &rv);
return rv;
}
//*****************************************************************************
// nsSharedPrefHandler::nsISupports
//*****************************************************************************
NS_IMPL_ISUPPORTS1(nsSharedPrefHandler, tmITransactionObserver)
//*****************************************************************************
// nsSharedPrefHandler::tmITransactionObserver
//*****************************************************************************
NS_IMETHODIMP nsSharedPrefHandler::OnTransactionAvailable(PRUint32 aQueueID, const PRUint8 *aData, PRUint32 aDataLen)
{
LOG(("nsSharedPrefHandler::OnTransactionAvailable [%s]\n", aData));
ipcMessageReader inMsg(aData, aDataLen);
PRUint32 dataVersion, prefAction, dataLen, prefKind, tempInt32;
const char *stringStart;
dataVersion = inMsg.GetInt32();
NS_ENSURE_TRUE(dataVersion == kCurrentPrefsTransactionDataVersion, NS_ERROR_INVALID_ARG);
prefAction = inMsg.GetInt32();
dataLen = inMsg.GetInt32(); // includes terminating null
stringStart = (const char *)inMsg.GetPtr();
nsDependentCString prefNameStr(stringStart);
inMsg.AdvancePtr(dataLen);
prefKind = inMsg.GetInt32();
dataLen = inMsg.GetInt32();
mProcessingTransaction = PR_TRUE; // Don't generate transactions for these
switch (prefKind) {
case PREF_STRING:
{
stringStart = (const char *)inMsg.GetPtr();
nsDependentCString prefStrValueStr(stringStart);
inMsg.AdvancePtr(dataLen);
NS_ASSERTION(!inMsg.HasError(), "error in reading transaction");
if (!inMsg.HasError())
PREF_SetCharPref(prefNameStr.get(), prefStrValueStr.get());
}
break;
case PREF_INT:
tempInt32 = inMsg.GetInt32();
NS_ASSERTION(!inMsg.HasError(), "error in reading transaction");
if (!inMsg.HasError())
PREF_SetIntPref(prefNameStr.get(), tempInt32);
break;
case PREF_BOOL:
tempInt32 = inMsg.GetInt32();
NS_ASSERTION(!inMsg.HasError(), "error in reading transaction");
if (!inMsg.HasError())
PREF_SetBoolPref(prefNameStr.get(), tempInt32);
break;
}
mProcessingTransaction = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP nsSharedPrefHandler::OnAttachReply(PRUint32 aQueueID, PRUint32 aStatus)
{
LOG(("nsSharedPrefHandler::OnAttachReply [%d]\n", aStatus));
// The transaction service holds a lock on the file during this call.
mPrefService->ResetUserPrefs();
mPrefService->ReadUserPrefs(nsnull);
return NS_OK;
}
NS_IMETHODIMP nsSharedPrefHandler::OnDetachReply(PRUint32 aQueueID, PRUint32 aStatus)
{
LOG(("tmModuleTest: nsSharedPrefHandler::OnDetachReply [%d]\n", aStatus));
return NS_OK;
}
NS_IMETHODIMP nsSharedPrefHandler::OnFlushReply(PRUint32 aQueueID, PRUint32 aStatus)
{
LOG(("tmModuleTest: nsSharedPrefHandler::OnFlushReply [%d]\n", aStatus));
// Call the internal method to write immediately
// The transaction service holds a lock on the file during this call.
mPrefService->SavePrefFileInternal(nsnull);
return NS_OK;
}
//*****************************************************************************
// Static functions
//*****************************************************************************
static PRBool PR_CALLBACK enumFind(void* aElement, void *aData)
{
char *elemStr = NS_STATIC_CAST(char*, aElement);
char *searchStr = NS_STATIC_CAST(char*, aData);
// return PR_FALSE for a match and to stop search
return (strncmp(elemStr, searchStr, strlen(elemStr)) != 0);
}
static PRBool PR_CALLBACK enumFree(void* aElement, void *aData)
{
if (aElement)
nsMemory::Free(aElement);
return PR_TRUE;
}
static PRInt32 ReadLine(FILE* inStm, nsACString& destString)
{
char stackBuf[512];
PRUint32 charsInBuf = 0;
destString.Truncate();
int c;
while (1) {
c = getc(inStm);
if (c == EOF)
break;
else if (c == '\r') {
c = getc(inStm);
if (c != '\n')
ungetc(c, inStm);
break;
}
else if (c == '\n')
break;
else {
if (charsInBuf >= sizeof(stackBuf)) {
destString.Append(stackBuf, charsInBuf);
charsInBuf = 0;
}
stackBuf[charsInBuf++] = c;
}
}
if (charsInBuf)
destString.Append(stackBuf, charsInBuf);
return (c == EOF && destString.IsEmpty()) ? EOF : 1;
}
//*****************************************************************************
// NS_CreateSharedPrefHandler
//*****************************************************************************
nsresult NS_CreateSharedPrefHandler(nsPrefService *aOwner)
{
nsSharedPrefHandler *local = new nsSharedPrefHandler;
if (!local)
return NS_ERROR_OUT_OF_MEMORY;
nsresult rv = local->Init(aOwner);
if (NS_FAILED(rv)) {
delete local;
return rv;
}
NS_ADDREF(gSharedPrefHandler = local);
return NS_OK;
}

View File

@ -0,0 +1,115 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2003
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Conrad Carlen <ccarlen@netscape.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsSharedPrefHandler_h__
#define nsSharedPrefHandler_h__
// Interfaces needed
#include "tmITransactionService.h"
// Includes
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsVoidArray.h"
// Local includes
#include "prefapi.h"
// Forward declarations
class nsPrefService;
// --------------------------------------------------------------------------------------
// nsSharedPrefHandler
// --------------------------------------------------------------------------------------
class nsSharedPrefHandler : public tmITransactionObserver
{
friend nsresult NS_CreateSharedPrefHandler(nsPrefService*);
NS_DECL_ISUPPORTS
NS_DECL_TMITRANSACTIONOBSERVER
public:
nsresult OnSessionBegin();
nsresult OnSessionEnd();
nsresult OnSavePrefs();
nsresult OnPrefChanged(PrefAction action,
PrefHashEntry* pref,
PrefValue newValue);
void ReadingUserPrefs(PRBool isReading)
{ mReadingUserPrefs = isReading; }
PRBool IsPrefShared(const char* prefName);
protected:
nsSharedPrefHandler();
virtual ~nsSharedPrefHandler();
nsresult Init(nsPrefService *aOwner);
nsresult ReadExceptionFile();
nsresult EnsureTransactionService();
protected:
nsPrefService *mPrefService; // weak ref
nsCOMPtr<tmITransactionService> mTransService;
const nsCString mPrefsTSQueueName;
PRPackedBool mSessionActive;
PRPackedBool mReadingUserPrefs;
PRPackedBool mProcessingTransaction;
nsAutoVoidArray mExceptionList;
};
// --------------------------------------------------------------------------------------
extern nsSharedPrefHandler *gSharedPrefHandler; // greasy, but used by procedural code.
/**
* Global method to create gSharedPrefHandler
*
* @param aOwner
*/
extern nsresult NS_CreateSharedPrefHandler(nsPrefService *aOwner);
#endif // nsSharedPrefHandler_h__

View File

@ -69,6 +69,10 @@
#include "nsPrintfCString.h"
#include "prlink.h"
#ifdef MOZ_PROFILESHARING
#include "nsSharedPrefHandler.h"
#endif
#ifdef XP_OS2
#define INCL_DOS
#include <os2.h>
@ -542,7 +546,7 @@ PREF_SetDefaultBoolPref(const char *pref_name,PRBool value)
PLDHashOperator
pref_savePref(PLDHashTable *table, PLDHashEntryHdr *heh, PRUint32 i, void *arg)
{
char **prefArray = (char**) arg;
pref_saveArgs *argData = NS_STATIC_CAST(pref_saveArgs *, arg);
PrefHashEntry *pref = NS_STATIC_CAST(PrefHashEntry *, heh);
PR_ASSERT(pref);
@ -565,6 +569,14 @@ pref_savePref(PLDHashTable *table, PLDHashEntryHdr *heh, PRUint32 i, void *arg)
// do not save default prefs that haven't changed
return PL_DHASH_NEXT;
#if MOZ_PROFILESHARING
if ((argData->saveTypes == SAVE_SHARED &&
!gSharedPrefHandler->IsPrefShared(pref->key)) ||
(argData->saveTypes == SAVE_NONSHARED &&
gSharedPrefHandler->IsPrefShared(pref->key)))
return PL_DHASH_NEXT;
#endif
// strings are in quotes!
if (pref->flags & PREF_STRING) {
prefValue = '\"';
@ -581,7 +593,7 @@ pref_savePref(PLDHashTable *table, PLDHashEntryHdr *heh, PRUint32 i, void *arg)
nsCAutoString prefName;
str_escape(pref->key, prefName);
prefArray[i] = ToNewCString(NS_LITERAL_CSTRING("user_pref(\"") +
argData->prefArray[i] = ToNewCString(NS_LITERAL_CSTRING("user_pref(\"") +
prefName +
NS_LITERAL_CSTRING("\", ") +
prefValue +
@ -1017,6 +1029,10 @@ PrefResult pref_HashPref(const char *key, PrefValue value, PrefType type, PrefAc
if (result2 < 0)
result = result2;
}
#ifdef MOZ_PROFILESHARING
if (gSharedPrefHandler)
gSharedPrefHandler->OnPrefChanged(action, pref, value);
#endif
}
return result;
}

View File

@ -41,6 +41,15 @@ extern JSContext * gMochaContext;
extern PRBool gErrorOpeningUserPrefs;
extern PLDHashTable gHashTable;
extern PRBool gDirty;
enum pref_SaveTypes { SAVE_NONSHARED, SAVE_SHARED, SAVE_ALL };
// Passed as the arg to pref_savePref
struct pref_saveArgs {
char **prefArray;
pref_SaveTypes saveTypes;
};
PLDHashOperator PR_CALLBACK pref_savePref(PLDHashTable*, PLDHashEntryHdr *, PRUint32, void *arg);
int PR_CALLBACK pref_CompareStrings(const void *v1, const void *v2, void* unused);