1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
|
/* -*- 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 "Common.h"
#include "Classifier.h"
#include "HashStore.h"
#include "mozilla/Components.h"
#include "mozilla/SpinEventLoopUntil.h"
#include "mozilla/Unused.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsIThread.h"
#include "nsTArray.h"
#include "nsThreadUtils.h"
#include "nsUrlClassifierUtils.h"
using namespace mozilla;
using namespace mozilla::safebrowsing;
nsresult SyncApplyUpdates(TableUpdateArray& aUpdates) {
// We need to spin a new thread specifically because the callback
// will be on the caller thread. If we call Classifier::AsyncApplyUpdates
// and wait on the same thread, this function will never return.
nsresult ret = NS_ERROR_FAILURE;
bool done = false;
auto onUpdateComplete = [&done, &ret](nsresult rv) {
// We are on the "ApplyUpdate" thread. Post an event to main thread
// so that we can avoid busy waiting on the main thread.
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableFunction("SyncApplyUpdates", [&done, &ret, rv] {
ret = rv;
done = true;
});
NS_DispatchToMainThread(r);
};
nsCOMPtr<nsIFile> file;
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction("SyncApplyUpdates", [&]() {
RefPtr<Classifier> classifier = new Classifier();
classifier->Open(*file);
nsresult rv = classifier->AsyncApplyUpdates(aUpdates, onUpdateComplete);
if (NS_FAILED(rv)) {
onUpdateComplete(rv);
}
});
nsCOMPtr<nsIThread> testingThread;
NS_NewNamedThread("ApplyUpdates", getter_AddRefs(testingThread));
if (!testingThread) {
return NS_ERROR_FAILURE;
}
testingThread->Dispatch(r, NS_DISPATCH_NORMAL);
// NS_NewCheckSummedOutputStream in HashStore::WriteFile
// will synchronously init NS_CRYPTO_HASH_CONTRACTID on
// the main thread. As a result we have to keep processing
// pending event until |done| becomes true. If there's no
// more pending event, what we only can do is wait.
MOZ_ALWAYS_TRUE(SpinEventLoopUntil("url-classifier:SyncApplyUpdates"_ns,
[&]() { return done; }));
return ret;
}
already_AddRefed<nsIFile> GetFile(const nsTArray<nsString>& path) {
nsCOMPtr<nsIFile> file;
nsresult rv =
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
for (uint32_t i = 0; i < path.Length(); i++) {
file->Append(path[i]);
}
return file.forget();
}
void ApplyUpdate(TableUpdateArray& updates) {
// Force nsUrlClassifierUtils loading on main thread
// because nsIUrlClassifierDBService will not run in advance
// in gtest.
nsUrlClassifierUtils::GetInstance();
SyncApplyUpdates(updates);
}
void ApplyUpdate(TableUpdate* update) {
TableUpdateArray updates = {update};
ApplyUpdate(updates);
}
nsresult PrefixArrayToPrefixStringMap(const _PrefixArray& aPrefixArray,
PrefixStringMap& aOut) {
aOut.Clear();
// Buckets are keyed by prefix length and contain an array of
// all prefixes of that length.
nsClassHashtable<nsUint32HashKey, _PrefixArray> table;
for (const auto& prefix : aPrefixArray) {
_PrefixArray* array = table.GetOrInsertNew(prefix.Length());
array->AppendElement(prefix);
}
// The resulting map entries will be a concatenation of all
// prefix data for the prefixes of a given size.
for (const auto& entry : table) {
uint32_t size = entry.GetKey();
uint32_t count = entry.GetData()->Length();
auto str = MakeUnique<_Prefix>();
str->SetLength(size * count);
char* dst = str->BeginWriting();
entry.GetData()->Sort();
for (uint32_t i = 0; i < count; i++) {
memcpy(dst, entry.GetData()->ElementAt(i).get(), size);
dst += size;
}
aOut.InsertOrUpdate(size, std::move(str));
}
return NS_OK;
}
nsresult PrefixArrayToAddPrefixArray(const _PrefixArray& aPrefixArray,
AddPrefixArray& aOut) {
aOut.Clear();
for (const auto& prefix : aPrefixArray) {
// Create prefix hash from string
AddPrefix* add = aOut.AppendElement(fallible);
if (!add) {
return NS_ERROR_OUT_OF_MEMORY;
}
add->addChunk = 1;
add->prefix.Assign(prefix);
}
EntrySort(aOut);
return NS_OK;
}
_Prefix CreatePrefixFromURL(const char* aURL, uint8_t aPrefixSize) {
return CreatePrefixFromURL(nsCString(aURL), aPrefixSize);
}
_Prefix CreatePrefixFromURL(const nsCString& aURL, uint8_t aPrefixSize) {
Completion complete;
complete.FromPlaintext(aURL);
_Prefix prefix;
prefix.Assign((const char*)complete.buf, aPrefixSize);
return prefix;
}
void CheckContent(LookupCacheV4* aCache, const _PrefixArray& aPrefixArray) {
PrefixStringMap vlPSetMap;
aCache->GetPrefixes(vlPSetMap);
PrefixStringMap expected;
PrefixArrayToPrefixStringMap(aPrefixArray, expected);
for (const auto& entry : vlPSetMap) {
nsCString* expectedPrefix = expected.Get(entry.GetKey());
nsCString* resultPrefix = entry.GetWeak();
ASSERT_TRUE(resultPrefix->Equals(*expectedPrefix));
}
}
nsresult BuildLookupCache(const RefPtr<Classifier>& classifier,
const nsACString& aTable,
_PrefixArray& aPrefixArray) {
RefPtr<LookupCache> cache = classifier->GetLookupCache(aTable, false);
if (!cache) {
return NS_ERROR_FAILURE;
}
if (LookupCache::Cast<LookupCacheV4>(cache)) {
// V4
RefPtr<LookupCacheV4> cacheV4 = LookupCache::Cast<LookupCacheV4>(cache);
PrefixStringMap map;
PrefixArrayToPrefixStringMap(aPrefixArray, map);
return cacheV4->Build(map);
} else {
// V2
RefPtr<LookupCacheV2> cacheV2 = LookupCache::Cast<LookupCacheV2>(cache);
AddPrefixArray addPrefixes;
AddCompleteArray addComples;
PrefixArrayToAddPrefixArray(aPrefixArray, addPrefixes);
return cacheV2->Build(addPrefixes, addComples);
}
}
RefPtr<Classifier> GetClassifier() {
nsCOMPtr<nsIFile> file;
NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
RefPtr<Classifier> classifier = new Classifier();
nsresult rv = classifier->Open(*file);
EXPECT_TRUE(rv == NS_OK);
return classifier;
}
|