Bug 336203 (for tony@ponderer.org) r=darin Add new safe browsing C++ service

This commit is contained in:
brettw%gmail.com 2006-05-05 21:42:47 +00:00
parent 9b87de92dc
commit 4db436d5f2
8 changed files with 854 additions and 1 deletions

View File

@ -85,6 +85,7 @@ REQUIRES = \
unicharutil \
xuldoc \
alerts \
url-classifier \
$(NULL)
EXPORTS = nsToolkitCompsCID.h
@ -96,6 +97,7 @@ LOCAL_INCLUDES = \
-I$(srcdir)/../startup/src \
-I$(srcdir)/../typeaheadfind/src \
-I$(srcdir)/../alerts/src \
-I$(srcdir)/../url-classifier/src \
$(NULL)
SHARED_LIBRARY_LIBS = \
@ -113,6 +115,10 @@ ifdef ALERTS_SERVICE
SHARED_LIBRARY_LIBS += ../alerts/src/$(LIB_PREFIX)alerts_s.$(LIB_SUFFIX)
endif
ifdef MOZ_URL_CLASSIFIER
SHARED_LIBRARY_LIBS += $(DIST)/lib/$(LIB_PREFIX)urlclassifier_s.$(LIB_SUFFIX)
endif
ifndef MOZ_SUITE
# XXX Suite isn't ready to build this just yet
SHARED_LIBRARY_LIBS += ../typeaheadfind/src/$(LIB_PREFIX)fastfind_s.$(LIB_SUFFIX)

View File

@ -74,6 +74,9 @@
#define NS_TYPEAHEADFIND_CONTRACTID \
"@mozilla.org/typeaheadfind;1"
#define NS_URLCLASSIFIERDBSERVICE_CONTRACTID \
"@mozilla.org/url-classifier/dbservice;1"
/////////////////////////////////////////////////////////////////////////////
// {A0CCAAF8-09DA-44D8-B250-9AC3E93C8117}
@ -115,3 +118,5 @@
#define NS_TYPEAHEADFIND_CID \
{ 0xe7f70966, 0x9a37, 0x48d7, { 0x8a, 0xeb, 0x35, 0x99, 0x8f, 0x31, 0x09, 0x0e} }
#define NS_URLCLASSIFIERDBSERVICE_CID \
{ 0x5eb7c3c1, 0xec1f, 0x4007, { 0x87, 0xcc, 0xee, 0xfb, 0x37, 0xd6, 0x8c, 0xe6} }

View File

@ -54,6 +54,10 @@
#include "nsTypeAheadFind.h"
#endif // MOZ_SUITE
#ifdef MOZ_URL_CLASSIFIER
#include "nsUrlClassifierDBService.h"
#endif
/////////////////////////////////////////////////////////////////////////////
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsAppStartup, Init)
@ -73,6 +77,11 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsDownloadProxy)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsTypeAheadFind)
#endif // MOZ_SUITE
#ifdef MOZ_URL_CLASSIFIER
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsUrlClassifierDBService,
nsUrlClassifierDBService::GetInstance)
#endif
/////////////////////////////////////////////////////////////////////////////
static const nsModuleComponentInfo components[] =
@ -110,6 +119,12 @@ static const nsModuleComponentInfo components[] =
nsTypeAheadFindConstructor
},
#endif // MOZ_SUITE
#ifdef MOZ_URL_CLASSIFIER
{ "Url Classifier DB Service",
NS_URLCLASSIFIERDBSERVICE_CID,
NS_URLCLASSIFIERDBSERVICE_CONTRACTID,
nsUrlClassifierDBServiceConstructor },
#endif
};
NS_IMPL_NSGETMODULE(nsToolkitCompsModule, components)

View File

@ -8,7 +8,8 @@ include $(DEPTH)/config/autoconf.mk
MODULE = url-classifier
XPIDL_MODULE = url-classifier
XPIDLSRCS = nsIUrlClassifierTable.idl \
XPIDLSRCS = nsIUrlClassifierDBService.idl \
nsIUrlClassifierTable.idl \
nsIUrlListManager.idl \
$(NULL)

View File

@ -0,0 +1,81 @@
/* ***** 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 Url Classifier code
*
* The Initial Developer of the Original Code is
* Google Inc.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Tony Chang <tony@ponderer.org> (original author)
* Brett Wilson <brettw@gmail.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 "nsISupports.idl"
// Interface for JS function callbacks
[scriptable, function, uuid(4ca27b6b-a674-4b3d-ab30-d21e2da2dffb)]
interface nsIUrlClassifierCallback : nsISupports {
void handleEvent(in ACString value);
};
/**
* This is a proxy class that is instantiated and called from the JS thread.
* It provides async methods for querying and updating the database. As the
* methods complete, they call the callback function.
*/
[scriptable, uuid(88519724-2225-4b4f-8ba7-365b4d138c3a)]
interface nsIUrlClassifierDBService : nsISupports
{
/**
* Looks up a key in the database. After it finds a value, it calls
* callback with the value as the first param. If the key is not in
* the db or the table does not exist, the callback is called with
* an empty string parameter.
*/
void exists(in ACString tableName, in ACString key,
in nsIUrlClassifierCallback c);
/**
* Updates the table in the background. Calls callback after each table
* completes processing with the new table line as the parameter. This
* allows us to keep track of the table version in our main thread.
*/
void updateTables(in ACString updateString, in nsIUrlClassifierCallback c);
};
/**
* Interface for the actual worker thread. Implementations of this need not
* be thread aware and just work on the database.
*/
[scriptable, uuid(5d405325-2ba1-4040-b69b-8bda8353d3d3)]
interface nsIUrlClassifierDBServiceWorker : nsIUrlClassifierDBService
{
// Provide a way to forcibly close the db connection.
void closeDb();
};

View File

@ -5,6 +5,27 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = url-classifier
LIBRARY_NAME = urlclassifier_s
XPIDL_MODULE = url-classifier
MOZILLA_INTERNAL_API = 1
FORCE_STATIC_LIB = 1
REQUIRES = storage \
string \
xpcom \
$(NULL)
CPPSRCS = \
nsUrlClassifierDBService.cpp \
$(NULL)
LOCAL_INCLUDES = \
-I$(srcdir)/../../build
$(NULL)
# EXTRA_COMPONENTS installs components written in JS to dist/bin/components
EXTRA_COMPONENTS = urlClassifierTableUrl.js \
urlClassifierTableDomain.js \

View File

@ -0,0 +1,634 @@
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** 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 Url Classifier code
*
* The Initial Developer of the Original Code is
* Google Inc.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Tony Chang <tony@ponderer.org> (original author)
* Brett Wilson <brettw@gmail.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 "mozIStorageService.h"
#include "mozIStorageConnection.h"
#include "mozIStorageStatement.h"
#include "mozStorageCID.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsAutoLock.h"
#include "nsCOMPtr.h"
#include "nsCRT.h"
#include "nsIDirectoryService.h"
#include "nsIEventQueueService.h"
#include "nsIObserverService.h"
#include "nsIProperties.h"
#include "nsIProxyObjectManager.h"
#include "nsToolkitCompsCID.h"
#include "nsUrlClassifierDBService.h"
#include "nsString.h"
#include "plevent.h"
#include "prlog.h"
#include "prmon.h"
#include "prthread.h"
#if defined(PR_LOGGING)
static const PRLogModuleInfo *gUrlClassifierDbServiceLog = nsnull;
#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args)
#else
#define LOG(args)
#endif
#define DATABASE_FILENAME "urlclassifier.sqlite"
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
static NS_DEFINE_CID(kProxyObjectManagerCID, NS_PROXYEVENT_MANAGER_CID);
// Singleton instance.
static nsUrlClassifierDBService* sUrlClassifierDBService;
// The event queue used to pass around messages between threads.
static nsIEventQueue* gEventQ = nsnull;
// Used to ensure that the event queue is initialized before
static PRMonitor *gMonitor = nsnull;
// Thread that we do the updates on.
static PRThread* gDbBackgroundThread = nsnull;
// The flag that tells us it's time to stop the background thread.
static PRBool gKeepRunning = PR_TRUE;
// The background thread event loop. Creates an nsIEventQueue and processes
// jobs as they come in.
PR_STATIC_CALLBACK(void) EventLoop(void *arg);
// -------------------------------------------------------------------------
// Actual worker implemenatation
class nsUrlClassifierDBServiceWorker : public nsIUrlClassifierDBServiceWorker
{
public:
nsUrlClassifierDBServiceWorker();
NS_DECL_ISUPPORTS
NS_DECL_NSIURLCLASSIFIERDBSERVICE
NS_DECL_NSIURLCLASSIFIERDBSERVICEWORKER
private:
// No subclassing
~nsUrlClassifierDBServiceWorker();
// Disallow copy constructor
nsUrlClassifierDBServiceWorker(nsUrlClassifierDBServiceWorker&);
// Table names have hyphens in them, which SQL doesn't allow,
// so we convert them to underscores.
void GetDbTableName(const nsACString& aTableName, nsCString* aDbTableName);
// Try to open the db, DATABASE_FILENAME.
nsresult OpenDb();
// Create a table in the db if it doesn't exist.
nsresult MaybeCreateTable(const nsCString& aTableName);
// Handle a new table line of the form [table-name #.####]. We create the
// table if it doesn't exist and set the aTableName, aUpdateStatement,
// and aDeleteStatement.
nsresult ProcessNewTable(const nsDependentCSubstring& aLine,
nsCString* aTableName,
mozIStorageStatement** aUpdateStatement,
mozIStorageStatement** aDeleteStatement);
// Handle an add or remove line. We execute additional update or delete
// statements.
nsresult ProcessUpdateTable(const nsDependentCSubstring& aLine,
const nsCString& aTableName,
mozIStorageStatement* aUpdateStatement,
mozIStorageStatement* aDeleteStatement);
// Holds a connection to the Db. We lazily initialize this because it has
// to be created in the background thread (currently mozStorageConnection
// isn't thread safe).
mozIStorageConnection* mConnection;
};
NS_IMPL_THREADSAFE_ISUPPORTS1(nsUrlClassifierDBServiceWorker,
nsIUrlClassifierDBServiceWorker)
nsUrlClassifierDBServiceWorker::nsUrlClassifierDBServiceWorker()
: mConnection(nsnull)
{
}
nsUrlClassifierDBServiceWorker::~nsUrlClassifierDBServiceWorker()
{
NS_ASSERTION(mConnection != nsnull,
"Db connection not closed, leaking memory! Call CloseDb "
"to close the connection.");
}
// Lookup a key in the db.
NS_IMETHODIMP
nsUrlClassifierDBServiceWorker::Exists(const nsACString& tableName,
const nsACString& key,
nsIUrlClassifierCallback *c) {
nsresult rv = OpenDb();
if (NS_FAILED(rv)) {
NS_ERROR("Unable to open database");
return NS_ERROR_FAILURE;
}
nsCAutoString dbTableName;
GetDbTableName(tableName, &dbTableName);
nsCOMPtr<mozIStorageStatement> selectStatement;
nsCAutoString statement;
statement.AssignLiteral("SELECT value FROM ");
statement.Append(dbTableName);
statement.AppendLiteral(" WHERE key = ?1");
nsCOMPtr<mozIStorageStatement> foostatement;
nsString value;
rv = mConnection->CreateStatement(statement,
getter_AddRefs(selectStatement));
// If CreateStatment failed, this probably means the table doesn't exist.
// That's ok, we just return an emptry string.
if (NS_SUCCEEDED(rv)) {
rv = selectStatement->BindUTF8StringParameter(0, key);
NS_ENSURE_SUCCESS(rv, rv);
PRBool hasMore = PR_FALSE;
rv = selectStatement->ExecuteStep(&hasMore);
// If the table has any columns, take the first value.
if (NS_SUCCEEDED(rv) && hasMore) {
selectStatement->GetString(0, value);
}
}
c->HandleEvent(NS_ConvertUTF16toUTF8(value));
return NS_OK;
}
// Do a batch update of the database. After we complete processing a table,
// we call the callback with the table line.
NS_IMETHODIMP
nsUrlClassifierDBServiceWorker::UpdateTables(const nsACString& updateString,
nsIUrlClassifierCallback *c) {
LOG(("Updating tables\n"));
nsresult rv = OpenDb();
if (NS_FAILED(rv)) {
NS_ERROR("Unable to open database");
return NS_ERROR_FAILURE;
}
rv = mConnection->BeginTransaction();
NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to begin transaction");
// Split the update string into lines
PRUint32 cur = 0;
PRInt32 next;
PRInt32 count = 0;
nsCAutoString dbTableName;
nsCAutoString lastTableLine;
nsCOMPtr<mozIStorageStatement> updateStatement;
nsCOMPtr<mozIStorageStatement> deleteStatement;
while(cur < updateString.Length() &&
(next = updateString.FindChar('\n', cur)) != kNotFound) {
count ++;
const nsDependentCSubstring &line = Substring(updateString,
cur, next - cur);
cur = next + 1; // prepare for next run
// Skip blank lines
if (line.Length() == 0)
continue;
if ('[' == line[0]) {
rv = ProcessNewTable(line, &dbTableName,
getter_AddRefs(updateStatement),
getter_AddRefs(deleteStatement));
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "malformed table line");
if (NS_SUCCEEDED(rv)) {
// If it's a new table, we must have completed the last table.
// Go ahead and post the completion to the UI thread.
if (lastTableLine.Length() > 0)
c->HandleEvent(lastTableLine);
lastTableLine.Assign(line);
}
} else {
rv = ProcessUpdateTable(line, dbTableName, updateStatement,
deleteStatement);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "malformed update line");
}
}
LOG(("Num update lines: %d\n", count));
// Commit the transaction
rv = mConnection->CommitTransaction();
NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to commit");
if (lastTableLine.Length() > 0)
c->HandleEvent(lastTableLine);
LOG(("Finishing table update\n"));
return NS_OK;
}
// Allows the main thread to delete the connection which may be in
// a background thread.
// XXX This could be turned into a single shutdown event so the logic
// is simpler in nsUrlClassifierDBService::Shutdown.
NS_IMETHODIMP
nsUrlClassifierDBServiceWorker::CloseDb()
{
if (mConnection != nsnull) {
delete mConnection;
mConnection = nsnull;
}
return NS_OK;
}
nsresult
nsUrlClassifierDBServiceWorker::ProcessNewTable(
const nsDependentCSubstring& aLine,
nsCString* aDbTableName,
mozIStorageStatement** aUpdateStatement,
mozIStorageStatement** aDeleteStatement)
{
// The line format is "[table-name #.####]" or "[table-name #.#### update]"
PRInt32 spacePos = aLine.FindChar(' ');
if (spacePos == kNotFound) {
// bad table header
return NS_ERROR_FAILURE;
}
const nsDependentCSubstring &tableName = Substring(aLine, 1, spacePos - 1);
GetDbTableName(tableName, aDbTableName);
// Create the table
nsresult rv = MaybeCreateTable(*aDbTableName);
if (NS_FAILED(rv))
return rv;
LOG(("Create table ok.\n"));
// insert statement
nsCAutoString statement;
statement.AssignLiteral("INSERT OR REPLACE INTO ");
statement.Append(*aDbTableName);
statement.AppendLiteral(" VALUES (?1, ?2)");
rv = mConnection->CreateStatement(statement, aUpdateStatement);
NS_ENSURE_SUCCESS(rv, rv);
// delete statement
statement.AssignLiteral("DELETE FROM ");
statement.Append(*aDbTableName);
statement.AppendLiteral(" WHERE key = ?1");
rv = mConnection->CreateStatement(statement, aDeleteStatement);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
nsUrlClassifierDBServiceWorker::ProcessUpdateTable(
const nsDependentCSubstring& aLine,
const nsCString& aTableName,
mozIStorageStatement* aUpdateStatement,
mozIStorageStatement* aDeleteStatement)
{
// We should have seen a table name line by now.
if (aTableName.Length() == 0)
return NS_ERROR_FAILURE;
if (!aUpdateStatement || !aDeleteStatement) {
NS_NOTREACHED("Statements NULL but table is not");
return NS_ERROR_FAILURE;
}
// There should at least be an op char and a key
if (aLine.Length() < 2)
return NS_ERROR_FAILURE;
char op = aLine[0];
PRInt32 spacePos = aLine.FindChar('\t');
nsresult rv = NS_ERROR_FAILURE;
if ('+' == op && spacePos != kNotFound) {
// Insert operation of the form "+KEY VALUE"
const nsDependentCSubstring &key = Substring(aLine, 1, spacePos - 1);
const nsDependentCSubstring &value = Substring(aLine, spacePos + 1);
aUpdateStatement->BindUTF8StringParameter(0, key);
aUpdateStatement->BindUTF8StringParameter(1, value);
rv = aUpdateStatement->Execute();
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to update");
} else if ('-' == op && spacePos == kNotFound) {
// Remove operation of the form "-KEY"
const nsDependentCSubstring &key = Substring(aLine, 1);
aDeleteStatement->BindUTF8StringParameter(0, key);
rv = aDeleteStatement->Execute();
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to delete");
}
return rv;
}
nsresult
nsUrlClassifierDBServiceWorker::OpenDb()
{
// Connection already open, don't do anything.
if (mConnection != nsnull)
return NS_OK;
// Compute database filename
nsCOMPtr<nsIFile> dbFile;
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
getter_AddRefs(dbFile));
NS_ENSURE_SUCCESS(rv, rv);
rv = dbFile->Append(NS_LITERAL_STRING(DATABASE_FILENAME));
NS_ENSURE_SUCCESS(rv, rv);
// open the connection
nsCOMPtr<mozIStorageService> storageService =
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
return storageService->OpenDatabase(dbFile, &mConnection);
}
nsresult
nsUrlClassifierDBServiceWorker::MaybeCreateTable(const nsCString& aTableName) {
LOG(("MaybeCreateTable %s\n", aTableName.get()));
nsCOMPtr<mozIStorageStatement> createStatement;
nsCString statement;
statement.Assign("CREATE TABLE IF NOT EXISTS ");
statement.Append(aTableName);
statement.Append(" (key TEXT PRIMARY KEY, value TEXT)");
nsresult rv = mConnection->CreateStatement(statement,
getter_AddRefs(createStatement));
NS_ENSURE_SUCCESS(rv, rv);
return createStatement->Execute();
}
void
nsUrlClassifierDBServiceWorker::GetDbTableName(const nsACString& aTableName,
nsCString* aDbTableName) {
aDbTableName->Assign(aTableName);
aDbTableName->ReplaceChar('-', '_');
}
// -------------------------------------------------------------------------
// Proxy class implementation
NS_IMPL_THREADSAFE_ISUPPORTS2(nsUrlClassifierDBService,
nsIUrlClassifierDBService,
nsIObserver)
/* static */ nsUrlClassifierDBService*
nsUrlClassifierDBService::GetInstance()
{
if (!sUrlClassifierDBService) {
sUrlClassifierDBService = new nsUrlClassifierDBService();
if (!sUrlClassifierDBService)
return nsnull;
NS_ADDREF(sUrlClassifierDBService); // addref the global
if (NS_FAILED(sUrlClassifierDBService->Init())) {
NS_RELEASE(sUrlClassifierDBService);
return nsnull;
}
} else {
// Already exists, just add a ref
NS_ADDREF(sUrlClassifierDBService); // addref the return result
}
return sUrlClassifierDBService;
}
nsUrlClassifierDBService::nsUrlClassifierDBService()
{
}
// Callback functions for event used in destructor.
PR_STATIC_CALLBACK(void *) EventHandler(PLEvent *ev);
PR_STATIC_CALLBACK(void) DestroyHandler(PLEvent *ev);
nsUrlClassifierDBService::~nsUrlClassifierDBService()
{
sUrlClassifierDBService = nsnull;
PR_DestroyMonitor(gMonitor);
}
nsresult
nsUrlClassifierDBService::Init()
{
#if defined(PR_LOGGING)
if (!gUrlClassifierDbServiceLog)
gUrlClassifierDbServiceLog = PR_NewLogModule("UrlClassifierDbService");
#endif
gMonitor = PR_NewMonitor();
// Start the background thread.
gDbBackgroundThread = PR_CreateThread(PR_USER_THREAD,
EventLoop,
nsnull,
PR_PRIORITY_NORMAL,
PR_GLOBAL_THREAD,
PR_JOINABLE_THREAD,
0);
if (!gDbBackgroundThread)
return NS_ERROR_OUT_OF_MEMORY;
mWorker = new nsUrlClassifierDBServiceWorker();
if (!mWorker)
return NS_ERROR_OUT_OF_MEMORY;
// Add an observer for shutdown
nsCOMPtr<nsIObserverService> observerService =
do_GetService("@mozilla.org/observer-service;1");
if (!observerService)
return NS_ERROR_FAILURE;
observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
return NS_OK;
}
NS_IMETHODIMP
nsUrlClassifierDBService::Exists(const nsACString& tableName,
const nsACString& key,
nsIUrlClassifierCallback *c)
{
EnsureThreadStarted();
nsresult rv;
// The proxy callback uses the current thread.
nsCOMPtr<nsIUrlClassifierCallback> proxyCallback;
rv = NS_GetProxyForObject(NS_CURRENT_EVENTQ,
NS_GET_IID(nsIUrlClassifierCallback),
c,
PROXY_ASYNC,
getter_AddRefs(proxyCallback));
NS_ENSURE_SUCCESS(rv, rv);
// The actual worker uses the background thread.
nsCOMPtr<nsIUrlClassifierDBServiceWorker> proxy;
rv = NS_GetProxyForObject(gEventQ,
NS_GET_IID(nsIUrlClassifierDBServiceWorker),
mWorker,
PROXY_ASYNC,
getter_AddRefs(proxy));
NS_ENSURE_SUCCESS(rv, rv);
return proxy->Exists(tableName, key, proxyCallback);
}
NS_IMETHODIMP
nsUrlClassifierDBService::UpdateTables(const nsACString& updateString,
nsIUrlClassifierCallback *c)
{
EnsureThreadStarted();
nsresult rv;
// The proxy callback uses the current thread.
nsCOMPtr<nsIUrlClassifierCallback> proxyCallback;
rv = NS_GetProxyForObject(NS_CURRENT_EVENTQ,
NS_GET_IID(nsIUrlClassifierCallback),
c,
PROXY_ASYNC,
getter_AddRefs(proxyCallback));
NS_ENSURE_SUCCESS(rv, rv);
// The actual worker uses the background thread.
nsCOMPtr<nsIUrlClassifierDBServiceWorker> proxy;
rv = NS_GetProxyForObject(gEventQ,
NS_GET_IID(nsIUrlClassifierDBServiceWorker),
mWorker,
PROXY_ASYNC,
getter_AddRefs(proxy));
NS_ENSURE_SUCCESS(rv, rv);
return proxy->UpdateTables(updateString, proxyCallback);
}
NS_IMETHODIMP
nsUrlClassifierDBService::Observe(nsISupports *aSubject, const char *aTopic,
const PRUnichar *aData)
{
if (nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
Shutdown();
}
return NS_OK;
}
// Make sure the event queue is intialized before we use it.
void
nsUrlClassifierDBService::EnsureThreadStarted()
{
nsAutoMonitor mon(gMonitor);
while (!gEventQ)
mon.Wait();
}
// Join the background thread if it exists.
nsresult
nsUrlClassifierDBService::Shutdown()
{
// First close the db connection.
nsCOMPtr<nsIUrlClassifierDBServiceWorker> proxy;
nsresult rv = NS_GetProxyForObject(gEventQ,
NS_GET_IID(nsIUrlClassifierDBServiceWorker),
mWorker,
PROXY_ASYNC,
getter_AddRefs(proxy));
proxy->CloseDb();
NS_ENSURE_SUCCESS(rv, rv);
PLEvent* ev = new PLEvent;
PL_InitEvent(ev, nsnull, EventHandler, DestroyHandler);
if (NS_FAILED(gEventQ->PostEvent(ev))) {
PL_DestroyEvent(ev);
}
LOG(("joining background thread"));
PR_JoinThread(gDbBackgroundThread);
gDbBackgroundThread = nsnull;
return NS_OK;
}
PR_STATIC_CALLBACK(void)
EventLoop(void *arg)
{
nsresult rv;
//LOG(("Starting background thread.\n"));
nsCOMPtr<nsIEventQueueService> eventQService =
do_GetService(kEventQueueServiceCID, &rv);
NS_ASSERTION(NS_SUCCEEDED(rv), "do_GetService(EventQueueService)");
rv = eventQService->CreateMonitoredThreadEventQueue();
NS_ASSERTION(NS_SUCCEEDED(rv), "CreateMonitoredThreadEventQueue");
{
nsAutoMonitor mon(gMonitor);
rv = eventQService->GetSpecialEventQueue(
nsIEventQueueService::CURRENT_THREAD_EVENT_QUEUE,
&gEventQ);
NS_ASSERTION(NS_SUCCEEDED(rv), "GetSpecialEventQueue");
PR_Notify(gMonitor);
}
while (gKeepRunning) {
PLEvent* ev;
if (NS_SUCCEEDED(gEventQ->WaitForEvent(&ev))) {
gEventQ->HandleEvent(ev);
}
}
// It's ok to miss pending events, we'll get them during the next update.
LOG(("Exiting background thread.\n"));
}
// Shutdown event
PR_STATIC_CALLBACK(void *)
EventHandler(PLEvent *ev)
{
gKeepRunning = PR_FALSE;
return nsnull;
}
// Clean up shutdown event
PR_STATIC_CALLBACK(void)
DestroyHandler(PLEvent *ev)
{
delete ev;
}

View File

@ -0,0 +1,90 @@
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-/
/* ***** 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 Url Classifier code
*
* The Initial Developer of the Original Code is
* Google Inc.
* Portions created by the Initial Developer are Copyright (C) 2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Tony Chang <tony@ponderer.org> (original author)
* Brett Wilson <brettw@gmail.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 nsUrlClassifierDBService_h_
#define nsUrlClassifierDBService_h_
#include <nsISupportsUtils.h>
#include "nsID.h"
#include "nsIObserver.h"
#include "nsIUrlClassifierDBService.h"
class nsUrlClassifierDBServiceWorker;
// This is a proxy class that just creates a background thread and delagates
// calls to the background thread.
class nsUrlClassifierDBService : public nsIUrlClassifierDBService,
public nsIObserver
{
public:
// This is thread safe. It throws an exception if the thread is busy.
nsUrlClassifierDBService();
nsresult Init();
static nsUrlClassifierDBService* GetInstance();
#ifdef MOZILLA_1_8_BRANCH
NS_DEFINE_STATIC_IID_ACCESSOR(NS_URLCLASSIFIERDBSERVICE_CID)
#else
NS_DECLARE_STATIC_IID_ACCESSOR(NS_URLCLASSIFIERDBSERVICE_CID)
#endif
NS_DECL_ISUPPORTS
NS_DECL_NSIURLCLASSIFIERDBSERVICE
NS_DECL_NSIOBSERVER
private:
// No subclassing
~nsUrlClassifierDBService();
// Disallow copy constructor
nsUrlClassifierDBService(nsUrlClassifierDBService&);
// Make sure the event queue is intialized before we use it.
void EnsureThreadStarted();
// Close db connection and join the background thread if it exists.
nsresult Shutdown();
nsCOMPtr<nsUrlClassifierDBServiceWorker> mWorker;
};
#endif // nsUrlClassifierDBService_h_