gecko/modules/libpref/src/nsSharedPrefHandler.cpp

431 lines
14 KiB
C++

/* -*- 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 "nsServiceManagerUtils.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 enumFind(void* aElement, void *aData);
static PRBool 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), "ipcITransactionService::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), "ipcITransactionService::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), "ipcITransactionService::Flush() failed");
return NS_OK;
}
nsresult nsSharedPrefHandler::OnPrefChanged(PRBool defaultPref,
PrefHashEntry* pref,
PrefValue newValue)
{
if (!mSessionActive
|| defaultPref
|| !IsPrefShared(pref->key)
|| 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(defaultPref); // XXX: Is not used?
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), "ipcITransactionService::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, 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(IPC_TRANSACTIONSERVICE_CONTRACTID, &rv);
return rv;
}
//*****************************************************************************
// nsSharedPrefHandler::nsISupports
//*****************************************************************************
NS_IMPL_ISUPPORTS1(nsSharedPrefHandler, ipcITransactionObserver)
//*****************************************************************************
// nsSharedPrefHandler::ipcITransactionObserver
//*****************************************************************************
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(); //XXX: Is not used?
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 enumFind(void* aElement, void *aData)
{
char *elemStr = static_cast<char*>(aElement);
char *searchStr = static_cast<char*>(aData);
// return PR_FALSE for a match and to stop search
return (strncmp(elemStr, searchStr, strlen(elemStr)) != 0);
}
static PRBool 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;
}