mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
431 lines
14 KiB
C++
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;
|
|
}
|
|
|