diff --git a/mozilla/toolkit/components/build/Makefile.in b/mozilla/toolkit/components/build/Makefile.in index 78db03e10b1..a5cae4b19ca 100644 --- a/mozilla/toolkit/components/build/Makefile.in +++ b/mozilla/toolkit/components/build/Makefile.in @@ -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) diff --git a/mozilla/toolkit/components/build/nsToolkitCompsCID.h b/mozilla/toolkit/components/build/nsToolkitCompsCID.h index 3067fd34215..a428ee5e816 100644 --- a/mozilla/toolkit/components/build/nsToolkitCompsCID.h +++ b/mozilla/toolkit/components/build/nsToolkitCompsCID.h @@ -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} } diff --git a/mozilla/toolkit/components/build/nsToolkitCompsModule.cpp b/mozilla/toolkit/components/build/nsToolkitCompsModule.cpp index 559dfe65576..bafdf60fef0 100644 --- a/mozilla/toolkit/components/build/nsToolkitCompsModule.cpp +++ b/mozilla/toolkit/components/build/nsToolkitCompsModule.cpp @@ -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) diff --git a/mozilla/toolkit/components/url-classifier/public/Makefile.in b/mozilla/toolkit/components/url-classifier/public/Makefile.in index fdb7dcb286f..65a2aae5ccb 100644 --- a/mozilla/toolkit/components/url-classifier/public/Makefile.in +++ b/mozilla/toolkit/components/url-classifier/public/Makefile.in @@ -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) diff --git a/mozilla/toolkit/components/url-classifier/public/nsIUrlClassifierDBService.idl b/mozilla/toolkit/components/url-classifier/public/nsIUrlClassifierDBService.idl new file mode 100644 index 00000000000..a1362551b4e --- /dev/null +++ b/mozilla/toolkit/components/url-classifier/public/nsIUrlClassifierDBService.idl @@ -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 (original author) + * Brett Wilson + * + * 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(); +}; diff --git a/mozilla/toolkit/components/url-classifier/src/Makefile.in b/mozilla/toolkit/components/url-classifier/src/Makefile.in index 61c4fb8fa51..95da84a161c 100644 --- a/mozilla/toolkit/components/url-classifier/src/Makefile.in +++ b/mozilla/toolkit/components/url-classifier/src/Makefile.in @@ -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 \ diff --git a/mozilla/toolkit/components/url-classifier/src/nsUrlClassifierDBService.cpp b/mozilla/toolkit/components/url-classifier/src/nsUrlClassifierDBService.cpp new file mode 100644 index 00000000000..08b609d0097 --- /dev/null +++ b/mozilla/toolkit/components/url-classifier/src/nsUrlClassifierDBService.cpp @@ -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 (original author) + * Brett Wilson + * + * 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 selectStatement; + nsCAutoString statement; + statement.AssignLiteral("SELECT value FROM "); + statement.Append(dbTableName); + statement.AppendLiteral(" WHERE key = ?1"); + nsCOMPtr 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 updateStatement; + nsCOMPtr 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 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 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 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 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 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 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 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 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 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 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; +} diff --git a/mozilla/toolkit/components/url-classifier/src/nsUrlClassifierDBService.h b/mozilla/toolkit/components/url-classifier/src/nsUrlClassifierDBService.h new file mode 100644 index 00000000000..4575438b93d --- /dev/null +++ b/mozilla/toolkit/components/url-classifier/src/nsUrlClassifierDBService.h @@ -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 (original author) + * Brett Wilson + * + * 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 + +#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 mWorker; +}; + +#endif // nsUrlClassifierDBService_h_