Line data Source code
1 : //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "LookupCacheV4.h"
7 : #include "HashStore.h"
8 : #include "mozilla/Unused.h"
9 : #include <string>
10 :
11 : // MOZ_LOG=UrlClassifierDbService:5
12 : extern mozilla::LazyLogModule gUrlClassifierDbServiceLog;
13 : #define LOG(args) MOZ_LOG(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug, args)
14 : #define LOG_ENABLED() MOZ_LOG_TEST(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug)
15 :
16 : #define METADATA_SUFFIX NS_LITERAL_CSTRING(".metadata")
17 :
18 : namespace mozilla {
19 : namespace safebrowsing {
20 :
21 : const int LookupCacheV4::VER = 4;
22 :
23 : // Prefixes coming from updates and VLPrefixSet are both stored in the HashTable
24 : // where the (key, value) pair is a prefix size and a lexicographic-sorted string.
25 : // The difference is prefixes from updates use std:string(to avoid additional copies)
26 : // and prefixes from VLPrefixSet use nsCString.
27 : // This class provides a common interface for the partial update algorithm to make it
28 : // easier to operate on two different kind prefix string map..
29 0 : class VLPrefixSet
30 : {
31 : public:
32 : explicit VLPrefixSet(const PrefixStringMap& aMap);
33 : explicit VLPrefixSet(const TableUpdateV4::PrefixStdStringMap& aMap);
34 :
35 : // This function will merge the prefix map in VLPrefixSet to aPrefixMap.
36 : void Merge(PrefixStringMap& aPrefixMap);
37 :
38 : // Find the smallest string from the map in VLPrefixSet.
39 : bool GetSmallestPrefix(nsDependentCSubstring& aOutString);
40 :
41 : // Return the number of prefixes in the map
42 0 : uint32_t Count() const { return mCount; }
43 :
44 : private:
45 : // PrefixString structure contains a lexicographic-sorted string with
46 : // a |pos| variable to indicate which substring we are pointing to right now.
47 : // |pos| increases each time GetSmallestPrefix finds the smallest string.
48 0 : struct PrefixString {
49 0 : PrefixString(const nsACString& aStr, uint32_t aSize)
50 0 : : pos(0)
51 0 : , size(aSize)
52 : {
53 0 : data.Rebind(aStr.BeginReading(), aStr.Length());
54 0 : }
55 :
56 0 : const char* get() {
57 0 : return pos < data.Length() ? data.BeginReading() + pos : nullptr;
58 : }
59 0 : void next() { pos += size; }
60 0 : uint32_t remaining() { return data.Length() - pos; }
61 :
62 : nsDependentCSubstring data;
63 : uint32_t pos;
64 : uint32_t size;
65 : };
66 :
67 : nsClassHashtable<nsUint32HashKey, PrefixString> mMap;
68 : uint32_t mCount;
69 : };
70 :
71 : nsresult
72 0 : LookupCacheV4::Init()
73 : {
74 0 : mVLPrefixSet = new VariableLengthPrefixSet();
75 0 : nsresult rv = mVLPrefixSet->Init(mTableName);
76 0 : NS_ENSURE_SUCCESS(rv, rv);
77 :
78 0 : return NS_OK;
79 : }
80 :
81 : nsresult
82 0 : LookupCacheV4::Has(const Completion& aCompletion,
83 : bool* aHas,
84 : uint32_t* aMatchLength,
85 : bool* aConfirmed)
86 : {
87 0 : *aHas = *aConfirmed = false;
88 0 : *aMatchLength = 0;
89 :
90 0 : uint32_t length = 0;
91 0 : nsDependentCSubstring fullhash;
92 0 : fullhash.Rebind((const char *)aCompletion.buf, COMPLETE_SIZE);
93 :
94 0 : nsresult rv = mVLPrefixSet->Matches(fullhash, &length);
95 0 : NS_ENSURE_SUCCESS(rv, rv);
96 :
97 0 : MOZ_ASSERT(length == 0 || (length >= PREFIX_SIZE && length <= COMPLETE_SIZE));
98 :
99 0 : *aHas = length >= PREFIX_SIZE;
100 0 : *aMatchLength = length;
101 :
102 0 : if (LOG_ENABLED()) {
103 0 : uint32_t prefix = aCompletion.ToUint32();
104 0 : LOG(("Probe in V4 %s: %X, found %d, complete %d", mTableName.get(),
105 : prefix, *aHas, length == COMPLETE_SIZE));
106 : }
107 :
108 : // Check if fullhash match any prefix in the local database
109 0 : if (!(*aHas)) {
110 0 : return NS_OK;
111 : }
112 :
113 : // Even though V4 supports variable-length prefix, we always send 4-bytes for
114 : // completion (Bug 1323953). This means cached prefix length is also 4-bytes.
115 0 : return CheckCache(aCompletion, aHas, aConfirmed);
116 : }
117 :
118 : bool
119 0 : LookupCacheV4::IsEmpty()
120 : {
121 : bool isEmpty;
122 0 : mVLPrefixSet->IsEmpty(&isEmpty);
123 0 : return isEmpty;
124 : }
125 :
126 : nsresult
127 0 : LookupCacheV4::Build(PrefixStringMap& aPrefixMap)
128 : {
129 0 : Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_VLPS_CONSTRUCT_TIME> timer;
130 :
131 0 : return mVLPrefixSet->SetPrefixes(aPrefixMap);
132 : }
133 :
134 : nsresult
135 0 : LookupCacheV4::GetPrefixes(PrefixStringMap& aPrefixMap)
136 : {
137 0 : return mVLPrefixSet->GetPrefixes(aPrefixMap);
138 : }
139 :
140 : nsresult
141 0 : LookupCacheV4::GetFixedLengthPrefixes(FallibleTArray<uint32_t>& aPrefixes)
142 : {
143 0 : return mVLPrefixSet->GetFixedLengthPrefixes(aPrefixes);
144 : }
145 :
146 : nsresult
147 0 : LookupCacheV4::ClearPrefixes()
148 : {
149 : // Clear by seting a empty map
150 0 : PrefixStringMap map;
151 0 : return mVLPrefixSet->SetPrefixes(map);
152 : }
153 :
154 : nsresult
155 0 : LookupCacheV4::StoreToFile(nsIFile* aFile)
156 : {
157 0 : return mVLPrefixSet->StoreToFile(aFile);
158 : }
159 :
160 : nsresult
161 0 : LookupCacheV4::LoadFromFile(nsIFile* aFile)
162 : {
163 0 : nsresult rv = mVLPrefixSet->LoadFromFile(aFile);
164 0 : if (NS_FAILED(rv)) {
165 0 : return rv;
166 : }
167 :
168 0 : nsCString state, checksum;
169 0 : rv = LoadMetadata(state, checksum);
170 0 : if (NS_FAILED(rv)) {
171 0 : return rv;
172 : }
173 :
174 0 : rv = VerifyChecksum(checksum);
175 0 : Telemetry::Accumulate(Telemetry::URLCLASSIFIER_VLPS_LOAD_CORRUPT,
176 0 : rv == NS_ERROR_FILE_CORRUPTED);
177 :
178 0 : return rv;
179 : }
180 :
181 : size_t
182 0 : LookupCacheV4::SizeOfPrefixSet()
183 : {
184 0 : return mVLPrefixSet->SizeOfIncludingThis(moz_malloc_size_of);
185 : }
186 :
187 : static void
188 0 : AppendPrefixToMap(PrefixStringMap& prefixes, nsDependentCSubstring& prefix)
189 : {
190 0 : if (!prefix.Length()) {
191 0 : return;
192 : }
193 :
194 0 : nsCString* prefixString = prefixes.LookupOrAdd(prefix.Length());
195 0 : prefixString->Append(prefix.BeginReading(), prefix.Length());
196 : }
197 :
198 : // Read prefix into a buffer and also update the hash which
199 : // keeps track of the checksum
200 : static void
201 0 : UpdateChecksum(nsICryptoHash* aCrypto, const nsACString& aPrefix)
202 : {
203 0 : MOZ_ASSERT(aCrypto);
204 0 : aCrypto->Update(reinterpret_cast<uint8_t*>(const_cast<char*>(
205 0 : aPrefix.BeginReading())),
206 0 : aPrefix.Length());
207 0 : }
208 :
209 : // Please see https://bug1287058.bmoattachments.org/attachment.cgi?id=8795366
210 : // for detail about partial update algorithm.
211 : nsresult
212 0 : LookupCacheV4::ApplyUpdate(TableUpdateV4* aTableUpdate,
213 : PrefixStringMap& aInputMap,
214 : PrefixStringMap& aOutputMap)
215 : {
216 0 : MOZ_ASSERT(aOutputMap.IsEmpty());
217 :
218 0 : nsCOMPtr<nsICryptoHash> crypto;
219 0 : nsresult rv = InitCrypto(crypto);
220 0 : if (NS_FAILED(rv)) {
221 0 : return rv;
222 : }
223 :
224 : // oldPSet contains prefixes we already have or we just merged last round.
225 : // addPSet contains prefixes stored in tableUpdate which should be merged with oldPSet.
226 0 : VLPrefixSet oldPSet(aInputMap);
227 0 : VLPrefixSet addPSet(aTableUpdate->Prefixes());
228 :
229 : // RemovalIndiceArray is a sorted integer array indicating the index of prefix we should
230 : // remove from the old prefix set(according to lexigraphic order).
231 : // |removalIndex| is the current index of RemovalIndiceArray.
232 : // |numOldPrefixPicked| is used to record how many prefixes we picked from the old map.
233 0 : TableUpdateV4::RemovalIndiceArray& removalArray = aTableUpdate->RemovalIndices();
234 0 : uint32_t removalIndex = 0;
235 0 : int32_t numOldPrefixPicked = -1;
236 :
237 0 : nsDependentCSubstring smallestOldPrefix;
238 0 : nsDependentCSubstring smallestAddPrefix;
239 :
240 0 : bool isOldMapEmpty = false, isAddMapEmpty = false;
241 :
242 : // This is used to avoid infinite loop for partial update algorithm.
243 : // The maximum loops will be the number of old prefixes plus the number of add prefixes.
244 0 : int32_t index = oldPSet.Count() + addPSet.Count() + 1;
245 0 : for(;index > 0; index--) {
246 : // Get smallest prefix from the old prefix set if we don't have one
247 0 : if (smallestOldPrefix.IsEmpty() && !isOldMapEmpty) {
248 0 : isOldMapEmpty = !oldPSet.GetSmallestPrefix(smallestOldPrefix);
249 : }
250 :
251 : // Get smallest prefix from add prefix set if we don't have one
252 0 : if (smallestAddPrefix.IsEmpty() && !isAddMapEmpty) {
253 0 : isAddMapEmpty = !addPSet.GetSmallestPrefix(smallestAddPrefix);
254 : }
255 :
256 : bool pickOld;
257 :
258 : // If both prefix sets are not empty, then compare to find the smaller one.
259 0 : if (!isOldMapEmpty && !isAddMapEmpty) {
260 0 : if (smallestOldPrefix == smallestAddPrefix) {
261 0 : LOG(("Add prefix should not exist in the original prefix set."));
262 0 : return NS_ERROR_UC_UPDATE_DUPLICATE_PREFIX;
263 : }
264 :
265 : // Compare the smallest string in old prefix set and add prefix set,
266 : // merge the smaller one into new map to ensure merged string still
267 : // follows lexigraphic order.
268 0 : pickOld = smallestOldPrefix < smallestAddPrefix;
269 0 : } else if (!isOldMapEmpty && isAddMapEmpty) {
270 0 : pickOld = true;
271 0 : } else if (isOldMapEmpty && !isAddMapEmpty) {
272 0 : pickOld = false;
273 : // If both maps are empty, then partial update is complete.
274 : } else {
275 : break;
276 : }
277 :
278 0 : if (pickOld) {
279 0 : numOldPrefixPicked++;
280 :
281 : // If the number of picks from old map matches the removalIndex, then this prefix
282 : // will be removed by not merging it to new map.
283 0 : if (removalIndex < removalArray.Length() &&
284 0 : numOldPrefixPicked == removalArray[removalIndex]) {
285 0 : removalIndex++;
286 : } else {
287 0 : AppendPrefixToMap(aOutputMap, smallestOldPrefix);
288 0 : UpdateChecksum(crypto, smallestOldPrefix);
289 : }
290 0 : smallestOldPrefix.SetLength(0);
291 : } else {
292 0 : AppendPrefixToMap(aOutputMap, smallestAddPrefix);
293 0 : UpdateChecksum(crypto, smallestAddPrefix);
294 :
295 0 : smallestAddPrefix.SetLength(0);
296 : }
297 : }
298 :
299 : // We expect index will be greater to 0 because max number of runs will be
300 : // the number of original prefix plus add prefix.
301 0 : if (index <= 0) {
302 0 : LOG(("There are still prefixes remaining after reaching maximum runs."));
303 0 : return NS_ERROR_UC_UPDATE_INFINITE_LOOP;
304 : }
305 :
306 0 : if (removalIndex < removalArray.Length()) {
307 0 : LOG(("There are still prefixes to remove after exhausting the old PrefixSet."));
308 0 : return NS_ERROR_UC_UPDATE_WRONG_REMOVAL_INDICES;
309 : }
310 :
311 0 : nsAutoCString checksum;
312 0 : crypto->Finish(false, checksum);
313 0 : if (aTableUpdate->Checksum().IsEmpty()) {
314 0 : LOG(("Update checksum missing."));
315 0 : Telemetry::Accumulate(Telemetry::URLCLASSIFIER_UPDATE_ERROR, mProvider,
316 0 : NS_ERROR_GET_CODE(NS_ERROR_UC_UPDATE_MISSING_CHECKSUM));
317 :
318 : // Generate our own checksum to tableUpdate to ensure there is always
319 : // checksum in .metadata
320 0 : std::string stdChecksum(checksum.BeginReading(), checksum.Length());
321 0 : aTableUpdate->NewChecksum(stdChecksum);
322 0 : } else if (aTableUpdate->Checksum() != checksum){
323 0 : LOG(("Checksum mismatch after applying partial update"));
324 0 : return NS_ERROR_UC_UPDATE_CHECKSUM_MISMATCH;
325 : }
326 :
327 0 : return NS_OK;
328 : }
329 :
330 : nsresult
331 0 : LookupCacheV4::AddFullHashResponseToCache(const FullHashResponseMap& aResponseMap)
332 : {
333 0 : CopyClassHashTable<FullHashResponseMap>(aResponseMap, mFullHashCache);
334 :
335 0 : return NS_OK;
336 : }
337 :
338 : nsresult
339 0 : LookupCacheV4::InitCrypto(nsCOMPtr<nsICryptoHash>& aCrypto)
340 : {
341 : nsresult rv;
342 0 : aCrypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
343 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
344 0 : return rv;
345 : }
346 :
347 0 : rv = aCrypto->Init(nsICryptoHash::SHA256);
348 0 : NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "InitCrypto failed");
349 :
350 0 : return rv;
351 : }
352 :
353 : nsresult
354 0 : LookupCacheV4::VerifyChecksum(const nsACString& aChecksum)
355 : {
356 0 : nsCOMPtr<nsICryptoHash> crypto;
357 0 : nsresult rv = InitCrypto(crypto);
358 0 : if (NS_FAILED(rv)) {
359 0 : return rv;
360 : }
361 :
362 0 : PrefixStringMap map;
363 0 : mVLPrefixSet->GetPrefixes(map);
364 :
365 0 : VLPrefixSet loadPSet(map);
366 0 : uint32_t index = loadPSet.Count() + 1;
367 0 : for(;index > 0; index--) {
368 0 : nsDependentCSubstring prefix;
369 0 : if (!loadPSet.GetSmallestPrefix(prefix)) {
370 0 : break;
371 : }
372 0 : UpdateChecksum(crypto, prefix);
373 : }
374 :
375 0 : nsAutoCString checksum;
376 0 : crypto->Finish(false, checksum);
377 :
378 0 : if (checksum != aChecksum) {
379 0 : LOG(("Checksum mismatch when loading prefixes from file."));
380 0 : return NS_ERROR_FILE_CORRUPTED;
381 : }
382 :
383 0 : return NS_OK;
384 : }
385 :
386 : //////////////////////////////////////////////////////////////////////////
387 : // A set of lightweight functions for reading/writing value from/to file.
388 :
389 : namespace {
390 :
391 : template<typename T>
392 : struct ValueTraits
393 : {
394 0 : static uint32_t Length(const T& aValue) { return sizeof(T); }
395 0 : static char* WritePtr(T& aValue, uint32_t aLength) { return (char*)&aValue; }
396 0 : static const char* ReadPtr(const T& aValue) { return (char*)&aValue; }
397 0 : static bool IsFixedLength() { return true; }
398 : };
399 :
400 : template<>
401 : struct ValueTraits<nsACString>
402 : {
403 0 : static bool IsFixedLength() { return false; }
404 :
405 0 : static uint32_t Length(const nsACString& aValue)
406 : {
407 0 : return aValue.Length();
408 : }
409 :
410 0 : static char* WritePtr(nsACString& aValue, uint32_t aLength)
411 : {
412 0 : aValue.SetLength(aLength);
413 0 : return aValue.BeginWriting();
414 : }
415 :
416 0 : static const char* ReadPtr(const nsACString& aValue)
417 : {
418 0 : return aValue.BeginReading();
419 : }
420 : };
421 :
422 : template<typename T> static nsresult
423 0 : WriteValue(nsIOutputStream *aOutputStream, const T& aValue)
424 : {
425 0 : uint32_t writeLength = ValueTraits<T>::Length(aValue);
426 0 : if (!ValueTraits<T>::IsFixedLength()) {
427 : // We need to write out the variable value length.
428 0 : nsresult rv = WriteValue(aOutputStream, writeLength);
429 0 : NS_ENSURE_SUCCESS(rv, rv);
430 : }
431 :
432 : // Write out the value.
433 0 : auto valueReadPtr = ValueTraits<T>::ReadPtr(aValue);
434 : uint32_t written;
435 0 : nsresult rv = aOutputStream->Write(valueReadPtr, writeLength, &written);
436 0 : if (NS_FAILED(rv) || written != writeLength) {
437 0 : LOG(("Failed to write the value."));
438 0 : return NS_FAILED(rv) ? rv : NS_ERROR_FAILURE;
439 : }
440 :
441 0 : return rv;
442 : }
443 :
444 : template<typename T> static nsresult
445 0 : ReadValue(nsIInputStream* aInputStream, T& aValue)
446 : {
447 : nsresult rv;
448 :
449 : uint32_t readLength;
450 0 : if (ValueTraits<T>::IsFixedLength()) {
451 0 : readLength = ValueTraits<T>::Length(aValue);
452 : } else {
453 : // Read the variable value length from file.
454 0 : nsresult rv = ReadValue(aInputStream, readLength);
455 0 : NS_ENSURE_SUCCESS(rv, rv);
456 : }
457 :
458 : // Read the value.
459 : uint32_t read;
460 0 : auto valueWritePtr = ValueTraits<T>::WritePtr(aValue, readLength);
461 0 : rv = aInputStream->Read(valueWritePtr, readLength, &read);
462 0 : if (NS_FAILED(rv) || read != readLength) {
463 0 : LOG(("Failed to read the value."));
464 0 : return NS_FAILED(rv) ? rv : NS_ERROR_FAILURE;
465 : }
466 :
467 0 : return rv;
468 : }
469 :
470 : } // end of unnamed namespace.
471 : ////////////////////////////////////////////////////////////////////////
472 :
473 : nsresult
474 0 : LookupCacheV4::WriteMetadata(TableUpdateV4* aTableUpdate)
475 : {
476 0 : NS_ENSURE_ARG_POINTER(aTableUpdate);
477 0 : if (nsUrlClassifierDBService::ShutdownHasStarted()) {
478 0 : return NS_ERROR_ABORT;
479 : }
480 :
481 0 : nsCOMPtr<nsIFile> metaFile;
482 0 : nsresult rv = mStoreDirectory->Clone(getter_AddRefs(metaFile));
483 0 : NS_ENSURE_SUCCESS(rv, rv);
484 :
485 0 : rv = metaFile->AppendNative(mTableName + METADATA_SUFFIX);
486 0 : NS_ENSURE_SUCCESS(rv, rv);
487 :
488 0 : nsCOMPtr<nsIOutputStream> outputStream;
489 0 : rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), metaFile,
490 0 : PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE);
491 0 : if (!NS_SUCCEEDED(rv)) {
492 0 : LOG(("Unable to create file to store metadata."));
493 0 : return rv;
494 : }
495 :
496 : // Write the state.
497 0 : rv = WriteValue(outputStream, aTableUpdate->ClientState());
498 0 : if (NS_FAILED(rv)) {
499 0 : LOG(("Failed to write the list state."));
500 0 : return rv;
501 : }
502 :
503 : // Write the checksum.
504 0 : rv = WriteValue(outputStream, aTableUpdate->Checksum());
505 0 : if (NS_FAILED(rv)) {
506 0 : LOG(("Failed to write the list checksum."));
507 0 : return rv;
508 : }
509 :
510 0 : return rv;
511 : }
512 :
513 : nsresult
514 0 : LookupCacheV4::LoadMetadata(nsACString& aState, nsACString& aChecksum)
515 : {
516 0 : nsCOMPtr<nsIFile> metaFile;
517 0 : nsresult rv = mStoreDirectory->Clone(getter_AddRefs(metaFile));
518 0 : NS_ENSURE_SUCCESS(rv, rv);
519 :
520 0 : rv = metaFile->AppendNative(mTableName + METADATA_SUFFIX);
521 0 : NS_ENSURE_SUCCESS(rv, rv);
522 :
523 0 : nsCOMPtr<nsIInputStream> localInFile;
524 0 : rv = NS_NewLocalFileInputStream(getter_AddRefs(localInFile), metaFile,
525 0 : PR_RDONLY | nsIFile::OS_READAHEAD);
526 0 : if (NS_FAILED(rv)) {
527 0 : LOG(("Unable to open metadata file."));
528 0 : return rv;
529 : }
530 :
531 : // Read the list state.
532 0 : rv = ReadValue(localInFile, aState);
533 0 : if (NS_FAILED(rv)) {
534 0 : LOG(("Failed to read state."));
535 0 : return rv;
536 : }
537 :
538 : // Read the checksum.
539 0 : rv = ReadValue(localInFile, aChecksum);
540 0 : if (NS_FAILED(rv)) {
541 0 : LOG(("Failed to read checksum."));
542 0 : return rv;
543 : }
544 :
545 0 : return rv;
546 : }
547 :
548 0 : VLPrefixSet::VLPrefixSet(const PrefixStringMap& aMap)
549 0 : : mCount(0)
550 : {
551 0 : for (auto iter = aMap.ConstIter(); !iter.Done(); iter.Next()) {
552 0 : uint32_t size = iter.Key();
553 0 : mMap.Put(size, new PrefixString(*iter.Data(), size));
554 0 : mCount += iter.Data()->Length() / size;
555 : }
556 0 : }
557 :
558 0 : VLPrefixSet::VLPrefixSet(const TableUpdateV4::PrefixStdStringMap& aMap)
559 0 : : mCount(0)
560 : {
561 0 : for (auto iter = aMap.ConstIter(); !iter.Done(); iter.Next()) {
562 0 : uint32_t size = iter.Key();
563 0 : mMap.Put(size, new PrefixString(iter.Data()->GetPrefixString(), size));
564 0 : mCount += iter.Data()->GetPrefixString().Length() / size;
565 : }
566 0 : }
567 :
568 : void
569 0 : VLPrefixSet::Merge(PrefixStringMap& aPrefixMap) {
570 0 : for (auto iter = mMap.ConstIter(); !iter.Done(); iter.Next()) {
571 0 : nsCString* prefixString = aPrefixMap.LookupOrAdd(iter.Key());
572 0 : PrefixString* str = iter.Data();
573 :
574 0 : if (str->get()) {
575 0 : prefixString->Append(str->get(), str->remaining());
576 : }
577 : }
578 0 : }
579 :
580 : bool
581 0 : VLPrefixSet::GetSmallestPrefix(nsDependentCSubstring& aOutString) {
582 0 : PrefixString* pick = nullptr;
583 0 : for (auto iter = mMap.ConstIter(); !iter.Done(); iter.Next()) {
584 0 : PrefixString* str = iter.Data();
585 :
586 0 : if (!str->get()) {
587 0 : continue;
588 : }
589 :
590 0 : if (aOutString.IsEmpty()) {
591 0 : aOutString.Rebind(str->get(), iter.Key());
592 0 : pick = str;
593 0 : continue;
594 : }
595 :
596 0 : nsDependentCSubstring cur(str->get(), iter.Key());
597 0 : if (cur < aOutString) {
598 0 : aOutString.Rebind(str->get(), iter.Key());
599 0 : pick = str;
600 : }
601 : }
602 :
603 0 : if (pick) {
604 0 : pick->next();
605 : }
606 :
607 0 : return pick != nullptr;
608 : }
609 :
610 : } // namespace safebrowsing
611 : } // namespace mozilla
|