--- a/toolkit/components/url-classifier/Classifier.cpp
+++ b/toolkit/components/url-classifier/Classifier.cpp
@@ -882,16 +882,28 @@ Classifier::ApplyFullHashes(nsTArray<Tab
aUpdates->ElementAt(i) = nullptr;
}
return NS_OK;
}
void
+Classifier::GetCacheInfo(const nsACString& aTable,
+ nsIUrlClassifierCacheInfo** aCache)
+{
+ LookupCache* lookupCache = GetLookupCache(aTable);
+ if (!lookupCache) {
+ return;
+ }
+
+ lookupCache->GetCacheInfo(aCache);
+}
+
+void
Classifier::DropStores()
{
for (uint32_t i = 0; i < mLookupCaches.Length(); i++) {
delete mLookupCaches[i];
}
mLookupCaches.Clear();
}
--- a/toolkit/components/url-classifier/Classifier.h
+++ b/toolkit/components/url-classifier/Classifier.h
@@ -119,16 +119,19 @@ public:
// Swap in in-memory and on-disk database and remove all
// update intermediaries.
nsresult SwapInNewTablesAndCleanup();
LookupCache *GetLookupCache(const nsACString& aTable,
bool aForUpdate = false);
+ void GetCacheInfo(const nsACString& aTable,
+ nsIUrlClassifierCacheInfo** aCache);
+
private:
void DropStores();
void DeleteTables(nsIFile* aDirectory, const nsTArray<nsCString>& aTables);
nsresult CreateStoreDirectory();
nsresult SetupPathNames();
nsresult RecoverBackups();
nsresult CleanToDelete();
--- a/toolkit/components/url-classifier/LookupCache.cpp
+++ b/toolkit/components/url-classifier/LookupCache.cpp
@@ -1,21 +1,23 @@
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "LookupCache.h"
#include "HashStore.h"
#include "nsISeekableStream.h"
+#include "mozilla/ArrayUtils.h"
#include "mozilla/Telemetry.h"
#include "mozilla/Logging.h"
#include "nsNetUtil.h"
#include "prprf.h"
#include "Classifier.h"
+#include "nsUrlClassifierInfo.h"
// We act as the main entry point for all the real lookups,
// so note that those are not done to the actual HashStore.
// The latter solely exists to store the data needed to handle
// the updates from the protocol.
// This module provides a front for PrefixSet, mUpdateCompletions,
// and mGetHashCache, which together contain everything needed to
@@ -42,16 +44,32 @@ extern mozilla::LazyLogModule gUrlClassi
namespace mozilla {
namespace safebrowsing {
const int CacheResultV2::VER = CacheResult::V2;
const int CacheResultV4::VER = CacheResult::V4;
const int LookupCacheV2::VER = 2;
+static
+void CStringToHexString(const nsACString& aIn, nsACString& aOut)
+{
+ static const char* const lut = "0123456789ABCDEF";
+
+ size_t len = aIn.Length();
+ MOZ_ASSERT(len <= COMPLETE_SIZE);
+
+ aOut.SetCapacity(2 * len);
+ for (size_t i = 0; i < aIn.Length(); ++i) {
+ const char c = static_cast<const char>(aIn[i]);
+ aOut.Append(lut[(c >> 4) & 0x0F]);
+ aOut.Append(lut[c & 15]);
+ }
+}
+
LookupCache::LookupCache(const nsACString& aTableName,
const nsACString& aProvider,
nsIFile* aRootStoreDir)
: mPrimed(false)
, mTableName(aTableName)
, mProvider(aProvider)
, mRootStoreDirectory(aRootStoreDir)
{
@@ -208,16 +226,59 @@ LookupCache::ClearCache()
void
LookupCache::ClearAll()
{
ClearCache();
ClearPrefixes();
mPrimed = false;
}
+void
+LookupCache::GetCacheInfo(nsIUrlClassifierCacheInfo** aCache)
+{
+ MOZ_ASSERT(aCache);
+
+ RefPtr<nsUrlClassifierCacheInfo> info = new nsUrlClassifierCacheInfo;
+ info->table = mTableName;
+
+ for (auto iter = mCache.ConstIter(); !iter.Done(); iter.Next()) {
+ RefPtr<nsUrlClassifierCacheEntry> entry = new nsUrlClassifierCacheEntry;
+
+ // Set prefix of the cache entry.
+ nsAutoCString prefix(reinterpret_cast<const char*>(&iter.Key()), PREFIX_SIZE);
+ CStringToHexString(prefix, entry->prefix);
+
+ // Set expiry of the cache entry.
+ CachedFullHashResponse* response = iter.Data();
+ entry->expirySec = response->negativeCacheExpirySec;
+
+ // Set positive cache.
+ FullHashExpiryCache& fullHashes = response->fullHashes;
+ for (auto iter2 = fullHashes.ConstIter(); !iter2.Done(); iter2.Next()) {
+ RefPtr<nsUrlClassifierPositiveCacheEntry> match =
+ new nsUrlClassifierPositiveCacheEntry;
+
+ // Set fullhash of positive cache entry.
+ CStringToHexString(iter2.Key(), match->fullhash);
+
+ // Set expiry of positive cache entry.
+ match->expirySec = iter2.Data();
+
+ entry->matches.AppendElement(
+ static_cast<nsIUrlClassifierPositiveCacheEntry*>(match));
+ }
+
+ info->entries.AppendElement(static_cast<nsIUrlClassifierCacheEntry*>(entry));
+ }
+
+ NS_ADDREF(*aCache = info);
+
+ return;
+}
+
/* static */ bool
LookupCache::IsCanonicalizedIP(const nsACString& aHost)
{
// The canonicalization process will have left IP addresses in dotted
// decimal with no surprises.
uint32_t i1, i2, i3, i4;
char c;
if (PR_sscanf(PromiseFlatCString(aHost).get(), "%u.%u.%u.%u%c",
@@ -421,31 +482,16 @@ LookupCache::LoadPrefixSet()
}
#endif
return NS_OK;
}
#if defined(DEBUG)
static
-void CStringToHexString(const nsACString& aIn, nsACString& aOut)
-{
- static const char* const lut = "0123456789ABCDEF";
- // 32 bytes is the longest hash
- size_t len = COMPLETE_SIZE;
-
- aOut.SetCapacity(2 * len);
- for (size_t i = 0; i < aIn.Length(); ++i) {
- const char c = static_cast<const char>(aIn[i]);
- aOut.Append(lut[(c >> 4) & 0x0F]);
- aOut.Append(lut[c & 15]);
- }
-}
-
-static
nsCString GetFormattedTimeString(int64_t aCurTimeSec)
{
PRExplodedTime pret;
PR_ExplodeTime(aCurTimeSec * PR_USEC_PER_SEC, PR_GMTParameters, &pret);
return nsPrintfCString(
"%04d-%02d-%02d %02d:%02d:%02d UTC",
pret.tm_year, pret.tm_month + 1, pret.tm_mday,
@@ -456,26 +502,29 @@ void
LookupCache::DumpCache()
{
if (!LOG_ENABLED()) {
return;
}
for (auto iter = mCache.ConstIter(); !iter.Done(); iter.Next()) {
CachedFullHashResponse* response = iter.Data();
- LOG(("Caches prefix: %X, Expire time: %s",
- iter.Key(),
- GetFormattedTimeString(response->negativeCacheExpirySec).get()));
+
+ nsAutoCString prefix;
+ CStringToHexString(
+ nsCString(reinterpret_cast<const char*>(&iter.Key()), PREFIX_SIZE), prefix);
+ LOG(("Cache prefix(%s): %s, Expiry: %s", mTableName.get(), prefix.get(),
+ GetFormattedTimeString(response->negativeCacheExpirySec).get()));
FullHashExpiryCache& fullHashes = response->fullHashes;
for (auto iter2 = fullHashes.ConstIter(); !iter2.Done(); iter2.Next()) {
- nsAutoCString strFullhash;
- CStringToHexString(iter2.Key(), strFullhash);
- LOG((" - %s, Expire time: %s", strFullhash.get(),
- GetFormattedTimeString(iter2.Data()).get()));
+ nsAutoCString fullhash;
+ CStringToHexString(iter2.Key(), fullhash);
+ LOG((" - %s, Expiry: %s", fullhash.get(),
+ GetFormattedTimeString(iter2.Data()).get()));
}
}
}
#endif
nsresult
LookupCacheV2::Init()
{
@@ -596,28 +645,24 @@ LookupCacheV2::AddGethashResultToCache(A
defaultExpirySec = aExpirySec;
}
for (const AddComplete& add : aAddCompletes) {
nsDependentCSubstring fullhash(
reinterpret_cast<const char*>(add.CompleteHash().buf), COMPLETE_SIZE);
CachedFullHashResponse* response = mCache.LookupOrAdd(add.ToUint32());
- // Set negative cache expiry to the same value as positive cache
- // expiry when the gethash request returns a complete match.
- if (response->negativeCacheExpirySec == 0) {
- response->negativeCacheExpirySec = defaultExpirySec;
- }
+ response->negativeCacheExpirySec = defaultExpirySec;
+
FullHashExpiryCache& fullHashes = response->fullHashes;
fullHashes.Put(fullhash, defaultExpirySec);
}
for (const Prefix& prefix : aMissPrefixes) {
CachedFullHashResponse* response = mCache.LookupOrAdd(prefix.ToUint32());
-
response->negativeCacheExpirySec = defaultExpirySec;
}
}
nsresult
LookupCacheV2::ReadCompletions()
{
HashStore store(mTableName, mProvider, mRootStoreDirectory);
--- a/toolkit/components/url-classifier/LookupCache.h
+++ b/toolkit/components/url-classifier/LookupCache.h
@@ -13,16 +13,17 @@
#include "nsIFile.h"
#include "nsIFileStreams.h"
#include "mozilla/RefPtr.h"
#include "nsUrlClassifierPrefixSet.h"
#include "SBTelemetryUtils.h"
#include "VariableLengthPrefixSet.h"
#include "mozilla/Logging.h"
#include "mozilla/TypedEnumBits.h"
+#include "nsIUrlClassifierInfo.h"
namespace mozilla {
namespace safebrowsing {
#define MAX_HOST_COMPONENTS 5
#define MAX_PATH_COMPONENTS 4
class LookupResult {
@@ -204,16 +205,18 @@ public:
// Check if completions can be found in cache.
// Currently this is only used by testcase.
bool IsInCache(uint32_t key) { return mCache.Get(key); };
#if DEBUG
void DumpCache();
#endif
+ void GetCacheInfo(nsIUrlClassifierCacheInfo** aCache);
+
virtual nsresult Open();
virtual nsresult Init() = 0;
virtual nsresult ClearPrefixes() = 0;
virtual nsresult Has(const Completion& aCompletion,
bool* aHas,
uint32_t* aMatchLength,
bool* aConfirmed) = 0;
--- a/toolkit/components/url-classifier/moz.build
+++ b/toolkit/components/url-classifier/moz.build
@@ -7,16 +7,17 @@
with Files('**'):
BUG_COMPONENT = ('Toolkit', 'Safe Browsing')
TEST_DIRS += ['tests']
XPIDL_SOURCES += [
'nsIUrlClassifierDBService.idl',
'nsIUrlClassifierHashCompleter.idl',
+ 'nsIUrlClassifierInfo.idl',
'nsIUrlClassifierPrefixSet.idl',
'nsIUrlClassifierStreamUpdater.idl',
'nsIUrlClassifierUtils.idl',
'nsIUrlListManager.idl',
]
XPIDL_MODULE = 'url-classifier'
@@ -25,16 +26,17 @@ DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = Tru
UNIFIED_SOURCES += [
'ChunkSet.cpp',
'Classifier.cpp',
'LookupCache.cpp',
'LookupCacheV4.cpp',
'nsCheckSummedOutputStream.cpp',
'nsUrlClassifierDBService.cpp',
+ 'nsUrlClassifierInfo.cpp',
'nsUrlClassifierProxies.cpp',
'nsUrlClassifierUtils.cpp',
'protobuf/safebrowsing.pb.cc',
'ProtocolParser.cpp',
'RiceDeltaDecoder.cpp',
'SBTelemetryUtils.cpp',
]
--- a/toolkit/components/url-classifier/nsIUrlClassifierDBService.idl
+++ b/toolkit/components/url-classifier/nsIUrlClassifierDBService.idl
@@ -196,16 +196,21 @@ interface nsIUrlClassifierDBService : ns
void resetDatabase();
/**
* Reload he url-classifier database. This will empty all cache for
* completions from gethash, and reload it from database. Mostly intended
* for use in tests.
*/
void reloadDatabase();
+
+ /**
+ * Empty all the caches.
+ */
+ void clearCache();
};
/**
* This is an internal helper interface for communication between the
* main thread and the dbservice worker thread. It is called for each
* lookup to provide a set of possible results, which the main thread
* may need to expand using an nsIUrlClassifierCompleter.
*/
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/nsIUrlClassifierInfo.idl
@@ -0,0 +1,73 @@
+ /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+#include "nsIArray.idl"
+
+/**
+ * nsIUrlClassifierPositiveCacheEntry Represents a positive cache entry.
+ */
+[scriptable, uuid(b3c27f8c-7db8-4f3f-97a5-5a94d781e565)]
+interface nsIUrlClassifierPositiveCacheEntry : nsISupports {
+ /**
+ * Fullhash for the positive cache entry.
+ */
+ readonly attribute ACString fullhash;
+
+ /**
+ * Positive cache expiry.
+ */
+ readonly attribute long long expiry;
+};
+
+/**
+ * nsIUrlClassifierCacheEntry contains cache information for
+ * a given prefix.
+ */
+[scriptable, uuid(d6297907-8236-4126-adaf-c3aa239a0d40)]
+interface nsIUrlClassifierCacheEntry : nsISupports {
+ /**
+ * Prefix for this cache entry.
+ */
+ readonly attribute ACString prefix;
+
+ /**
+ * Negative cache expiry.
+ */
+ readonly attribute long long expiry;
+
+ /**
+ * An array of nsIUrlClassifierPositiveCacheEntry, each item represents
+ * a positive cache entry with its fullhash and expiry.
+ */
+ readonly attribute nsIArray matches;
+};
+
+/**
+ * Cache information for a given table.
+ */
+[scriptable, function, uuid(69384f24-d9c5-4462-b24e-351c69e3b46a)]
+interface nsIUrlClassifierCacheInfo : nsISupports {
+ /**
+ * Table name.
+ */
+ readonly attribute ACString table;
+
+ /*
+ * An array of nsIUrlClassifierCacheEntry.
+ */
+ readonly attribute nsIArray entries;
+};
+
+/**
+ * Interface to query url-classifier information.
+ */
+[scriptable, function, uuid(411bbff4-1b88-4687-aa36-e2bbdd93f6e8)]
+interface nsIUrlClassifierInfo : nsISupports {
+ /**
+ * A synchronous call to return cache information for the table.
+ */
+ nsIUrlClassifierCacheInfo getCacheInfo(in ACString table);
+};
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -730,16 +730,28 @@ nsUrlClassifierDBServiceWorker::ReloadDa
// Create new mClassifier and load prefixset and completions from disk.
rv = OpenDb();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMETHODIMP
+nsUrlClassifierDBServiceWorker::ClearCache()
+{
+ nsTArray<nsCString> tables;
+ nsresult rv = mClassifier->ActiveTables(tables);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mClassifier->ResetTables(Classifier::Clear_Cache, tables);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
nsUrlClassifierDBServiceWorker::CancelUpdate()
{
LOG(("nsUrlClassifierDBServiceWorker::CancelUpdate"));
if (mUpdateObserver) {
LOG(("UpdateObserver exists, cancelling"));
mUpdateStatus = NS_BINDING_ABORTED;
@@ -939,16 +951,29 @@ nsUrlClassifierDBServiceWorker::ClearLas
{
MOZ_ASSERT(!NS_IsMainThread(), "Must be on the background thread");
if (mLastResults) {
mLastResults->Clear();
}
return NS_OK;
}
+nsresult
+nsUrlClassifierDBServiceWorker::GetCacheInfo(const nsACString& aTable,
+ nsIUrlClassifierCacheInfo** aCache)
+{
+ MOZ_ASSERT(!NS_IsMainThread(), "Must be on the background thread");
+ if (!mClassifier) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ mClassifier->GetCacheInfo(aTable, aCache);
+ return NS_OK;
+}
+
bool
nsUrlClassifierDBServiceWorker::IsSameAsLastResults(CacheResultArray& aResult)
{
if (!mLastResults || mLastResults->Length() != aResult.Length()) {
return false;
}
bool equal = true;
@@ -1502,16 +1527,17 @@ nsUrlClassifierClassifyCallback::HandleR
// Proxy class implementation
NS_IMPL_ADDREF(nsUrlClassifierDBService)
NS_IMPL_RELEASE(nsUrlClassifierDBService)
NS_INTERFACE_MAP_BEGIN(nsUrlClassifierDBService)
// Only nsIURIClassifier is supported in the content process!
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIUrlClassifierDBService, XRE_IsParentProcess())
NS_INTERFACE_MAP_ENTRY(nsIURIClassifier)
+ NS_INTERFACE_MAP_ENTRY(nsIUrlClassifierInfo)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIObserver, XRE_IsParentProcess())
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURIClassifier)
NS_INTERFACE_MAP_END
/* static */ nsUrlClassifierDBService*
nsUrlClassifierDBService::GetInstance(nsresult *result)
{
*result = NS_OK;
@@ -2193,16 +2219,34 @@ nsUrlClassifierDBService::ReloadDatabase
if (mWorker->IsBusyUpdating()) {
LOG(("Failed to ReloadDatabase because of the unfinished update."));
return NS_ERROR_FAILURE;
}
return mWorkerProxy->ReloadDatabase();
}
+NS_IMETHODIMP
+nsUrlClassifierDBService::ClearCache()
+{
+ NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
+
+ return mWorkerProxy->ClearCache();
+}
+
+
+NS_IMETHODIMP
+nsUrlClassifierDBService::GetCacheInfo(const nsACString& aTable,
+ nsIUrlClassifierCacheInfo** aCache)
+{
+ NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
+
+ return mWorkerProxy->GetCacheInfo(aTable, aCache);
+}
+
nsresult
nsUrlClassifierDBService::CacheCompletions(CacheResultArray *results)
{
NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
return mWorkerProxy->CacheCompletions(results);
}
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.h
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.h
@@ -10,16 +10,17 @@
#include "nsID.h"
#include "nsInterfaceHashtable.h"
#include "nsIObserver.h"
#include "nsUrlClassifierPrefixSet.h"
#include "nsIUrlClassifierHashCompleter.h"
#include "nsIUrlListManager.h"
#include "nsIUrlClassifierDBService.h"
+#include "nsIUrlClassifierInfo.h"
#include "nsIURIClassifier.h"
#include "nsToolkitCompsCID.h"
#include "nsICryptoHMAC.h"
#include "mozilla/Attributes.h"
#include "mozilla/Mutex.h"
#include "mozilla/TimeStamp.h"
#include "Entries.h"
@@ -78,31 +79,33 @@ TablesToResponse(const nsACString& table
} // namespace safebrowsing
} // namespace mozilla
// This is a proxy class that just creates a background thread and delagates
// calls to the background thread.
class nsUrlClassifierDBService final : public nsIUrlClassifierDBService,
public nsIURIClassifier,
+ public nsIUrlClassifierInfo,
public nsIObserver
{
public:
// This is thread safe. It throws an exception if the thread is busy.
nsUrlClassifierDBService();
nsresult Init();
static nsUrlClassifierDBService* GetInstance(nsresult *result);
NS_DECLARE_STATIC_IID_ACCESSOR(NS_URLCLASSIFIERDBSERVICE_CID)
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIURLCLASSIFIERDBSERVICE
NS_DECL_NSIURICLASSIFIER
+ NS_DECL_NSIURLCLASSIFIERINFO
NS_DECL_NSIOBSERVER
bool CanComplete(const nsACString &tableName);
bool GetCompleter(const nsACString& tableName,
nsIUrlClassifierHashCompleter** completer);
nsresult CacheCompletions(mozilla::safebrowsing::CacheResultArray *results);
static nsIThread* BackgroundThread();
@@ -224,16 +227,20 @@ public:
bool IsBusyUpdating() const { return !!mUpdateObserver; }
// Delegate Classifier to disable async update. If there is an
// ongoing update on the update thread, we will be blocked until
// the background update is done and callback is fired.
// Should be called on the worker thread.
void FlushAndDisableAsyncUpdate();
+ // A synchronous call to get cache information for the given table.
+ // This is only used by about:url-classifier now.
+ nsresult GetCacheInfo(const nsACString& aTable,
+ nsIUrlClassifierCacheInfo** aCache);
private:
// No subclassing
~nsUrlClassifierDBServiceWorker();
// Disallow copy constructor
nsUrlClassifierDBServiceWorker(nsUrlClassifierDBServiceWorker&);
nsresult NotifyUpdateObserver(nsresult aUpdateStatus);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/nsUrlClassifierInfo.cpp
@@ -0,0 +1,105 @@
+/* This Source Code Form is subject to the terms of the Mozilla
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsUrlClassifierInfo.h"
+
+NS_IMPL_ISUPPORTS(nsUrlClassifierPositiveCacheEntry,
+ nsIUrlClassifierPositiveCacheEntry)
+
+nsUrlClassifierPositiveCacheEntry::nsUrlClassifierPositiveCacheEntry()
+{
+}
+
+NS_IMETHODIMP
+nsUrlClassifierPositiveCacheEntry::GetExpiry(int64_t* aExpiry)
+{
+ if (!aExpiry) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ *aExpiry = expirySec;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierPositiveCacheEntry::GetFullhash(nsACString& aFullHash)
+{
+ aFullHash = fullhash;
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsUrlClassifierCacheEntry,
+ nsIUrlClassifierCacheEntry)
+
+nsUrlClassifierCacheEntry::nsUrlClassifierCacheEntry()
+{
+}
+
+NS_IMETHODIMP
+nsUrlClassifierCacheEntry::GetPrefix(nsACString& aPrefix)
+{
+ aPrefix = prefix;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierCacheEntry::GetExpiry(int64_t* aExpiry)
+{
+ if (!aExpiry) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ *aExpiry = expirySec;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierCacheEntry::GetMatches(nsIArray** aMatches)
+{
+ if (!aMatches) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID));
+
+ for (uint32_t i = 0;i < matches.Length(); i++) {
+ array->AppendElement(matches[i], false);
+ }
+
+ NS_ADDREF(*aMatches = array);
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsUrlClassifierCacheInfo,
+ nsIUrlClassifierCacheInfo)
+
+nsUrlClassifierCacheInfo::nsUrlClassifierCacheInfo()
+{
+}
+
+NS_IMETHODIMP
+nsUrlClassifierCacheInfo::GetTable(nsACString& aTable)
+{
+ aTable = table;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierCacheInfo::GetEntries(nsIArray** aEntries)
+{
+ if (!aEntries) {
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID));
+
+ for (uint32_t i = 0;i < entries.Length(); i++) {
+ array->AppendElement(entries[i], false);
+ }
+
+ NS_ADDREF(*aEntries = array);
+
+ return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/nsUrlClassifierInfo.h
@@ -0,0 +1,64 @@
+/* This Source Code Form is subject to the terms of the Mozilla
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsUrlClassifierInfo_h_
+#define nsUrlClassifierInfo_h_
+
+#include "nsIUrlClassifierInfo.h"
+#include "nsString.h"
+
+class nsUrlClassifierPositiveCacheEntry final : public nsIUrlClassifierPositiveCacheEntry
+{
+public:
+ nsUrlClassifierPositiveCacheEntry();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURLCLASSIFIERPOSITIVECACHEENTRY
+
+private:
+ ~nsUrlClassifierPositiveCacheEntry() {}
+
+public:
+ nsCString fullhash;
+
+ int64_t expirySec;
+};
+
+class nsUrlClassifierCacheEntry final : public nsIUrlClassifierCacheEntry
+{
+public:
+ nsUrlClassifierCacheEntry();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURLCLASSIFIERCACHEENTRY
+
+private:
+ ~nsUrlClassifierCacheEntry() {}
+
+public:
+ nsCString prefix;
+
+ int64_t expirySec;
+
+ nsTArray<nsCOMPtr<nsIUrlClassifierPositiveCacheEntry>> matches;
+};
+
+class nsUrlClassifierCacheInfo final : public nsIUrlClassifierCacheInfo
+{
+public:
+ nsUrlClassifierCacheInfo();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURLCLASSIFIERCACHEINFO
+
+private:
+ ~nsUrlClassifierCacheInfo() {}
+
+public:
+ nsCString table;
+
+ nsTArray<nsCOMPtr<nsIUrlClassifierCacheEntry>> entries;
+};
+
+#endif // nsUrlClassifierInfo_h_
--- a/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp
@@ -174,16 +174,25 @@ NS_IMETHODIMP
UrlClassifierDBServiceWorkerProxy::ReloadDatabase()
{
nsCOMPtr<nsIRunnable> r =
NewRunnableMethod(mTarget,
&nsUrlClassifierDBServiceWorker::ReloadDatabase);
return DispatchToWorkerThread(r);
}
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::ClearCache()
+{
+ nsCOMPtr<nsIRunnable> r =
+ NewRunnableMethod(mTarget,
+ &nsUrlClassifierDBServiceWorker::ClearCache);
+ return DispatchToWorkerThread(r);
+}
+
nsresult
UrlClassifierDBServiceWorkerProxy::OpenDb()
{
nsCOMPtr<nsIRunnable> r =
NewRunnableMethod(mTarget,
&nsUrlClassifierDBServiceWorker::OpenDb);
return DispatchToWorkerThread(r);
}
@@ -219,16 +228,39 @@ UrlClassifierDBServiceWorkerProxy::Clear
}
NS_IMETHODIMP
UrlClassifierDBServiceWorkerProxy::ClearLastResultsRunnable::Run()
{
return mTarget->ClearLastResults();
}
+nsresult
+UrlClassifierDBServiceWorkerProxy::GetCacheInfo(const nsACString& aTable,
+ nsIUrlClassifierCacheInfo** aCache)
+{
+ nsCOMPtr<nsIRunnable> r = new GetCacheInfoRunnable(mTarget, aTable, aCache);
+
+ nsIThread* t = nsUrlClassifierDBService::BackgroundThread();
+ if (!t) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // This blocks main thread but since 'GetCacheInfo' is only used by
+ // about:url-classifier so it should be fine.
+ mozilla::SyncRunnable::DispatchToThread(t, r);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::GetCacheInfoRunnable::Run()
+{
+ return mTarget->GetCacheInfo(mTable, mCache);
+}
+
NS_IMPL_ISUPPORTS(UrlClassifierLookupCallbackProxy,
nsIUrlClassifierLookupCallback)
NS_IMETHODIMP
UrlClassifierLookupCallbackProxy::LookupComplete
(LookupResultArray * aResults)
{
nsCOMPtr<nsIRunnable> r = new LookupCompleteRunnable(mTarget, aResults);
--- a/toolkit/components/url-classifier/nsUrlClassifierProxies.h
+++ b/toolkit/components/url-classifier/nsUrlClassifierProxies.h
@@ -162,26 +162,46 @@ public:
: mTarget(aTarget)
{ }
NS_DECL_NSIRUNNABLE
private:
RefPtr<nsUrlClassifierDBServiceWorker> mTarget;
};
+ class GetCacheInfoRunnable: public mozilla::Runnable
+ {
+ public:
+ explicit GetCacheInfoRunnable(nsUrlClassifierDBServiceWorker* aTarget,
+ const nsACString& aTable,
+ nsIUrlClassifierCacheInfo** aCache)
+ : mTarget(aTarget),
+ mTable(aTable),
+ mCache(aCache)
+ { }
+
+ NS_DECL_NSIRUNNABLE
+ private:
+ RefPtr<nsUrlClassifierDBServiceWorker> mTarget;
+ nsCString mTable;
+ nsIUrlClassifierCacheInfo** mCache;
+ };
+
public:
nsresult DoLocalLookup(const nsACString& spec,
const nsACString& tables,
mozilla::safebrowsing::LookupResultArray* results);
nsresult OpenDb();
nsresult CloseDb();
nsresult CacheCompletions(mozilla::safebrowsing::CacheResultArray * aEntries);
+ nsresult GetCacheInfo(const nsACString& aTable,
+ nsIUrlClassifierCacheInfo** aCache);
private:
~UrlClassifierDBServiceWorkerProxy() {}
RefPtr<nsUrlClassifierDBServiceWorker> mTarget;
};
// The remaining classes here are all proxies to the main thread
--- a/toolkit/components/url-classifier/tests/gtest/TestCaching.cpp
+++ b/toolkit/components/url-classifier/tests/gtest/TestCaching.cpp
@@ -13,46 +13,51 @@ SetupCacheEntry(LookupCacheV2* aLookupCa
bool aNegExpired = false,
bool aPosExpired = false)
{
AddCompleteArray completes;
AddCompleteArray emptyCompletes;
MissPrefixArray misses;
MissPrefixArray emptyMisses;
- nsCOMPtr<nsICryptoHash> cryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID);
+ nsCOMPtr<nsICryptoHash> cryptoHash =
+ do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID);
AddComplete* add = completes.AppendElement(fallible);
add->complete.FromPlaintext(aCompletion, cryptoHash);
Prefix* prefix = misses.AppendElement(fallible);
prefix->FromPlaintext(aCompletion, cryptoHash);
+ // Setup positive cache first otherwise negative cache expiry will be
+ // overwritten.
+ int64_t posExpirySec = aPosExpired ? EXPIRED_TIME_SEC : NOTEXPIRED_TIME_SEC;
+ aLookupCache->AddGethashResultToCache(completes, emptyMisses, posExpirySec);
+
int64_t negExpirySec = aNegExpired ? EXPIRED_TIME_SEC : NOTEXPIRED_TIME_SEC;
aLookupCache->AddGethashResultToCache(emptyCompletes, misses, negExpirySec);
-
- int64_t posExpirySec = aPosExpired ? EXPIRED_TIME_SEC : NOTEXPIRED_TIME_SEC;
- aLookupCache->AddGethashResultToCache(completes, emptyMisses, posExpirySec);
}
static void
SetupCacheEntry(LookupCacheV4* aLookupCache,
const nsCString& aCompletion,
bool aNegExpired = false,
bool aPosExpired = false)
{
FullHashResponseMap map;
Prefix prefix;
- nsCOMPtr<nsICryptoHash> cryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID);
+ nsCOMPtr<nsICryptoHash> cryptoHash =
+ do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID);
prefix.FromPlaintext(aCompletion, cryptoHash);
CachedFullHashResponse* response = map.LookupOrAdd(prefix.ToUint32());
- response->negativeCacheExpirySec = aNegExpired ? EXPIRED_TIME_SEC : NOTEXPIRED_TIME_SEC;
+ response->negativeCacheExpirySec =
+ aNegExpired ? EXPIRED_TIME_SEC : NOTEXPIRED_TIME_SEC;
response->fullHashes.Put(GeneratePrefix(aCompletion, COMPLETE_SIZE),
aPosExpired ? EXPIRED_TIME_SEC : NOTEXPIRED_TIME_SEC);
aLookupCache->AddFullHashResponseToCache(map);
}
template<typename T>
void
--- a/toolkit/content/aboutUrlClassifier.css
+++ b/toolkit/content/aboutUrlClassifier.css
@@ -53,17 +53,21 @@ td {
text-align: start;
border-color: var(--in-content-table-border-dark-color);
}
#provider-table > tbody > tr > td:last-child {
text-align: center;
}
-#debug-table {
+#cache-table > tbody > tr > td:last-child {
+ text-align: center;
+}
+
+#debug-table, #cache-table {
margin-top: 20px;
}
.options > .toggle-container-with-text {
display: inline-flex;
}
button {
--- a/toolkit/content/aboutUrlClassifier.js
+++ b/toolkit/content/aboutUrlClassifier.js
@@ -16,24 +16,26 @@ const UPDATE_FINISH = "safebrowsing-upda
const JSLOG_PREF = "browser.safebrowsing.debug";
const STR_NA = bundle.GetStringFromName("NotAvailable");
function unLoad() {
window.removeEventListener("unload", unLoad);
Provider.uninit();
+ Cache.uninit();
Debug.uninit();
}
function onLoad() {
window.removeEventListener("load", onLoad);
window.addEventListener("unload", unLoad);
Provider.init();
+ Cache.init();
Debug.init();
}
/*
* Provider
*/
var Provider = {
providers: null,
@@ -175,16 +177,155 @@ var Provider = {
let elem = document.getElementById(provider + "-col-lastupdateresult");
elem.childNodes[0].nodeValue = bundle.GetStringFromName("CannotUpdate");
}
},
};
/*
+ * Cache
+ */
+var Cache = {
+ // Tables that show cahe entries.
+ showCacheEnties: null,
+
+ init() {
+ this.showCacheEnties = new Set();
+
+ this.register()
+ this.render();
+ },
+
+ uninit() {
+ Services.obs.removeObserver(this.refresh, UPDATE_FINISH);
+ },
+
+ register() {
+ this.refresh = this.refresh.bind(this);
+ Services.obs.addObserver(this.refresh, UPDATE_FINISH);
+ },
+
+ render() {
+ this.createCacheEntries();
+
+ let refreshBtn = document.getElementById("refresh-cache-btn");
+ refreshBtn.addEventListener("click", () => { this.refresh(); });
+
+ let clearBtn = document.getElementById("clear-cache-btn");
+ clearBtn.addEventListener("click", () => {
+ let dbservice = Cc["@mozilla.org/url-classifier/dbservice;1"]
+ .getService(Ci.nsIUrlClassifierDBService);
+ dbservice.clearCache();
+ // Since clearCache is async call, we just simply assume it will be
+ // updated in 100 milli-seconds.
+ setTimeout(() => { this.refresh(); }, 100);
+ });
+ },
+
+ refresh() {
+ this.clearCacheEntries();
+ this.createCacheEntries();
+ },
+
+ clearCacheEntries() {
+ let ctbody = document.getElementById("cache-table-body");
+ while (ctbody.firstChild) {
+ ctbody.firstChild.remove();
+ }
+
+ let cetbody = document.getElementById("cache-entries-table-body");
+ while (cetbody.firstChild) {
+ cetbody.firstChild.remove();
+ }
+ },
+
+ createCacheEntries() {
+ function createRow(tds, body, cols) {
+ let tr = document.createElement("tr");
+ tds.forEach(function(v, i, a) {
+ let td = document.createElement("td");
+ if (i == 0 && tds.length != cols) {
+ td.setAttribute("colspan", cols - tds.length + 1);
+ }
+ let elem = typeof v === "object" ? v : document.createTextNode(v);
+ td.appendChild(elem);
+ tr.appendChild(td);
+ })
+ body.appendChild(tr);
+ }
+
+ let dbservice = Cc["@mozilla.org/url-classifier/dbservice;1"]
+ .getService(Ci.nsIUrlClassifierInfo);
+
+ for (let provider of Provider.providers) {
+ let pref = "browser.safebrowsing.provider." + provider + ".lists";
+ let tables = Services.prefs.getCharPref(pref, "").split(",");
+
+ for (let table of tables) {
+ let cache = dbservice.getCacheInfo(table);
+ let entries = cache.entries;
+ if (entries.length === 0) {
+ this.showCacheEnties.delete(table);
+ continue;
+ }
+
+ let positiveCacheCount = 0;
+ for (let i = 0; i < entries.length ; i++) {
+ let entry = entries.queryElementAt(i, Ci.nsIUrlClassifierCacheEntry);
+ let matches = entry.matches;
+ positiveCacheCount += matches.length;
+
+ // If we don't have to show cache entries for this table then just
+ // skip the following code.
+ if (!this.showCacheEnties.has(table)) {
+ continue;
+ }
+
+ let tds = [table, entry.prefix, new Date(entry.expiry * 1000).toString()];
+ let j = 0;
+ do {
+ if (matches.length >= 1) {
+ let match =
+ matches.queryElementAt(j, Ci.nsIUrlClassifierPositiveCacheEntry);
+ let list = [match.fullhash, new Date(match.expiry * 1000).toString()];
+ tds = tds.concat(list);
+ } else {
+ tds = tds.concat([STR_NA, STR_NA])
+ }
+ createRow(tds, document.getElementById("cache-entries-table-body"), 5);
+ j++;
+ tds = [""];
+ } while (j < matches.length)
+ }
+
+ // Create cache information entries.
+ let chk = document.createElement("input");
+ chk.type = "checkbox";
+ chk.checked = this.showCacheEnties.has(table);
+ chk.addEventListener("click", () => {
+ if (chk.checked) {
+ this.showCacheEnties.add(table);
+ } else {
+ this.showCacheEnties.delete(table);
+ }
+ this.refresh();
+ });
+
+ let tds = [table, entries.length, positiveCacheCount, chk];
+ createRow(tds, document.getElementById("cache-table-body"), tds.length);
+ }
+ }
+
+ let entries_div = document.getElementById("cache-entries");
+ entries_div.style.display = this.showCacheEnties.size == 0 ? "none" : "block";
+ },
+};
+
+/*
* Debug
*/
var Debug = {
// url-classifier NSPR Log modules.
modules: ["UrlClassifierDbService",
"nsChannelClassifier",
"UrlClassifierProtocolParser",
"UrlClassifierStreamUpdater",
--- a/toolkit/content/aboutUrlClassifier.xhtml
+++ b/toolkit/content/aboutUrlClassifier.xhtml
@@ -32,16 +32,55 @@
<th id="col-update">&aboutUrlClassifier.providerUpdateBtn;</th>
</tr>
</thead>
<tbody id="provider-table-body">
<!-- data is generated in javascript -->
</tbody>
</table>
</div>
+ <div id="cache">
+ <h2 class="major-section">&aboutUrlClassifier.cacheTitle;</h2>
+ <div id="cache-modules" class="options">
+ <button id="refresh-cache-btn">&aboutUrlClassifier.cacheRefreshBtn;</button>
+ <button id="clear-cache-btn">&aboutUrlClassifier.cacheClearBtn;</button>
+ <br></br>
+ </div>
+ <table id="cache-table">
+ <thead>
+ <tr id="cache-head-row">
+ <th id="col-tablename">&aboutUrlClassifier.cacheTableName;</th>
+ <th id="col-negativeentries">&aboutUrlClassifier.cacheNCacheEntries;</th>
+ <th id="col-positiveentries">&aboutUrlClassifier.cachePCacheEntries;</th>
+ <th id="col-showentries">&aboutUrlClassifier.cacheShowEntries;</th>
+ </tr>
+ </thead>
+ <tbody id="cache-table-body">
+ <!-- data is generated in javascript -->
+ </tbody>
+ </table>
+ <br></br>
+ </div>
+ <div id="cache-entries">
+ <h2 class="major-section">&aboutUrlClassifier.cacheEntries;</h2>
+ <table id="cache-entries-table">
+ <thead>
+ <tr id="cache-entries-row">
+ <th id="col-table">&aboutUrlClassifier.cacheTableName;</th>
+ <th id="col-prefix">&aboutUrlClassifier.cachePrefix;</th>
+ <th id="col-n-expire">&aboutUrlClassifier.cacheNCacheExpiry;</th>
+ <th id="col-fullhash">&aboutUrlClassifier.cacheFullhash;</th>
+ <th id="col-p-expire">&aboutUrlClassifier.cachePCacheExpiry;</th>
+ </tr>
+ </thead>
+ <tbody id="cache-entries-table-body">
+ <!-- data is generated in javascript -->
+ </tbody>
+ </table>
+ </div>
<div id="debug">
<h2 class="major-section">&aboutUrlClassifier.debugTitle;</h2>
<div id="debug-modules" class="options">
<input id="log-modules" type="text" value=""/>
<button id="set-log-modules">&aboutUrlClassifier.debugModuleBtn;</button>
<br></br>
<input id="log-file" type="text" value=""/>
<button id="set-log-file">&aboutUrlClassifier.debugFileBtn;</button>
--- a/toolkit/locales/en-US/chrome/global/aboutUrlClassifier.dtd
+++ b/toolkit/locales/en-US/chrome/global/aboutUrlClassifier.dtd
@@ -5,16 +5,28 @@
<!-- LOCALIZATION NOTE the term "url-classifier" should not be translated. -->
<!ENTITY aboutUrlClassifier.pageTitle "Information about the url-classifier">
<!ENTITY aboutUrlClassifier.providerTitle "Provider">
<!ENTITY aboutUrlClassifier.provider "Provider">
<!ENTITY aboutUrlClassifier.providerLastUpdateTime "Last update time">
<!ENTITY aboutUrlClassifier.providerNextUpdateTime "Next update time">
<!ENTITY aboutUrlClassifier.providerLastUpdateStatus "Last update status">
<!ENTITY aboutUrlClassifier.providerUpdateBtn "Update">
+<!ENTITY aboutUrlClassifier.cacheTitle "Cache">
+<!ENTITY aboutUrlClassifier.cacheRefreshBtn "Refresh">
+<!ENTITY aboutUrlClassifier.cacheClearBtn "Clear">
+<!ENTITY aboutUrlClassifier.cacheTableName "Table name">
+<!ENTITY aboutUrlClassifier.cacheNCacheEntries "Number of negative cache entries">
+<!ENTITY aboutUrlClassifier.cachePCacheEntries "Number of positive cache entries">
+<!ENTITY aboutUrlClassifier.cacheShowEntries "Show entries">
+<!ENTITY aboutUrlClassifier.cacheEntries "Cache Entries">
+<!ENTITY aboutUrlClassifier.cachePrefix "Prefix">
+<!ENTITY aboutUrlClassifier.cacheNCacheExpiry "Negative cache expiry">
+<!ENTITY aboutUrlClassifier.cacheFullhash "Full hash">
+<!ENTITY aboutUrlClassifier.cachePCacheExpiry "Positive cache expiry">
<!ENTITY aboutUrlClassifier.lookupTitle "Lookup">
<!ENTITY aboutUrlClassifier.lookupUrl "Url">
<!ENTITY aboutUrlClassifier.lookupMatch "Match">
<!ENTITY aboutUrlClassifier.lookupMatchBtn "Check Match Result">
<!ENTITY aboutUrlClassifier.lookupLookup "Lookup">
<!ENTITY aboutUrlClassifier.lookupBtn "Check Lookup Result">
<!ENTITY aboutUrlClassifier.cacheTitle "Cache">
<!ENTITY aboutUrlClassifier.memoryTitle "Memory">