summaryrefslogtreecommitdiffstats
path: root/toolkit/components/url-classifier
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/url-classifier')
-rw-r--r--toolkit/components/url-classifier/ChunkSet.cpp248
-rw-r--r--toolkit/components/url-classifier/ChunkSet.h96
-rw-r--r--toolkit/components/url-classifier/Classifier.cpp1786
-rw-r--r--toolkit/components/url-classifier/Classifier.h258
-rw-r--r--toolkit/components/url-classifier/Entries.h347
-rw-r--r--toolkit/components/url-classifier/HashStore.cpp1175
-rw-r--r--toolkit/components/url-classifier/HashStore.h321
-rw-r--r--toolkit/components/url-classifier/IUrlClassifierUITelemetry.idl48
-rw-r--r--toolkit/components/url-classifier/LookupCache.cpp1107
-rw-r--r--toolkit/components/url-classifier/LookupCache.h356
-rw-r--r--toolkit/components/url-classifier/LookupCacheV4.cpp577
-rw-r--r--toolkit/components/url-classifier/LookupCacheV4.h70
-rw-r--r--toolkit/components/url-classifier/ProtocolParser.cpp1080
-rw-r--r--toolkit/components/url-classifier/ProtocolParser.h215
-rw-r--r--toolkit/components/url-classifier/RiceDeltaDecoder.cpp227
-rw-r--r--toolkit/components/url-classifier/RiceDeltaDecoder.h40
-rw-r--r--toolkit/components/url-classifier/SafeBrowsing.sys.mjs575
-rw-r--r--toolkit/components/url-classifier/UrlClassifierHashCompleter.sys.mjs965
-rw-r--r--toolkit/components/url-classifier/UrlClassifierLib.sys.mjs226
-rw-r--r--toolkit/components/url-classifier/UrlClassifierListManager.sys.mjs864
-rw-r--r--toolkit/components/url-classifier/UrlClassifierRemoteSettingsService.sys.mjs141
-rw-r--r--toolkit/components/url-classifier/UrlClassifierTelemetryUtils.cpp157
-rw-r--r--toolkit/components/url-classifier/UrlClassifierTelemetryUtils.h31
-rw-r--r--toolkit/components/url-classifier/VariableLengthPrefixSet.cpp496
-rw-r--r--toolkit/components/url-classifier/VariableLengthPrefixSet.h73
-rw-r--r--toolkit/components/url-classifier/chromium/README.txt41
-rw-r--r--toolkit/components/url-classifier/chromium/safebrowsing.pb.cc8925
-rw-r--r--toolkit/components/url-classifier/chromium/safebrowsing.pb.h10010
-rw-r--r--toolkit/components/url-classifier/chromium/safebrowsing.proto540
-rw-r--r--toolkit/components/url-classifier/components.conf32
-rw-r--r--toolkit/components/url-classifier/docs/flash-block-lists.rst38
-rw-r--r--toolkit/components/url-classifier/docs/index.rst8
-rw-r--r--toolkit/components/url-classifier/moz.build101
-rw-r--r--toolkit/components/url-classifier/nsCheckSummedOutputStream.cpp87
-rw-r--r--toolkit/components/url-classifier/nsCheckSummedOutputStream.h88
-rw-r--r--toolkit/components/url-classifier/nsIUrlClassifierDBService.idl245
-rw-r--r--toolkit/components/url-classifier/nsIUrlClassifierHashCompleter.idl103
-rw-r--r--toolkit/components/url-classifier/nsIUrlClassifierInfo.idl80
-rw-r--r--toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl27
-rw-r--r--toolkit/components/url-classifier/nsIUrlClassifierRemoteSettingsService.idl32
-rw-r--r--toolkit/components/url-classifier/nsIUrlClassifierStreamUpdater.idl39
-rw-r--r--toolkit/components/url-classifier/nsIUrlClassifierUtils.idl158
-rw-r--r--toolkit/components/url-classifier/nsIUrlListManager.idl94
-rw-r--r--toolkit/components/url-classifier/nsUrlClassifierDBService.cpp2613
-rw-r--r--toolkit/components/url-classifier/nsUrlClassifierDBService.h284
-rw-r--r--toolkit/components/url-classifier/nsUrlClassifierInfo.cpp95
-rw-r--r--toolkit/components/url-classifier/nsUrlClassifierInfo.h67
-rw-r--r--toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp597
-rw-r--r--toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h90
-rw-r--r--toolkit/components/url-classifier/nsUrlClassifierProxies.cpp302
-rw-r--r--toolkit/components/url-classifier/nsUrlClassifierProxies.h359
-rw-r--r--toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp881
-rw-r--r--toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.h129
-rw-r--r--toolkit/components/url-classifier/nsUrlClassifierUtils.cpp1125
-rw-r--r--toolkit/components/url-classifier/nsUrlClassifierUtils.h72
-rw-r--r--toolkit/components/url-classifier/tests/UrlClassifierTestUtils.sys.mjs307
-rw-r--r--toolkit/components/url-classifier/tests/browser/browser.toml7
-rw-r--r--toolkit/components/url-classifier/tests/browser/browser_emailtracking_telemetry.js423
-rw-r--r--toolkit/components/url-classifier/tests/browser/page.html8
-rw-r--r--toolkit/components/url-classifier/tests/browser/raptor.jpgbin0 -> 49629 bytes
-rw-r--r--toolkit/components/url-classifier/tests/gtest/Common.cpp219
-rw-r--r--toolkit/components/url-classifier/tests/gtest/Common.h147
-rw-r--r--toolkit/components/url-classifier/tests/gtest/TestCaching.cpp271
-rw-r--r--toolkit/components/url-classifier/tests/gtest/TestChunkSet.cpp281
-rw-r--r--toolkit/components/url-classifier/tests/gtest/TestClassifier.cpp83
-rw-r--r--toolkit/components/url-classifier/tests/gtest/TestFailUpdate.cpp109
-rw-r--r--toolkit/components/url-classifier/tests/gtest/TestFindFullHash.cpp216
-rw-r--r--toolkit/components/url-classifier/tests/gtest/TestLookupCacheV4.cpp152
-rw-r--r--toolkit/components/url-classifier/tests/gtest/TestPerProviderDirectory.cpp127
-rw-r--r--toolkit/components/url-classifier/tests/gtest/TestPrefixSet.cpp87
-rw-r--r--toolkit/components/url-classifier/tests/gtest/TestProtocolParser.cpp140
-rw-r--r--toolkit/components/url-classifier/tests/gtest/TestRiceDeltaDecoder.cpp173
-rw-r--r--toolkit/components/url-classifier/tests/gtest/TestSafeBrowsingProtobuf.cpp30
-rw-r--r--toolkit/components/url-classifier/tests/gtest/TestSafebrowsingHash.cpp58
-rw-r--r--toolkit/components/url-classifier/tests/gtest/TestTable.cpp59
-rw-r--r--toolkit/components/url-classifier/tests/gtest/TestURLsAndHashing.cpp71
-rw-r--r--toolkit/components/url-classifier/tests/gtest/TestUrlClassifierTableUpdateV4.cpp785
-rw-r--r--toolkit/components/url-classifier/tests/gtest/TestUrlClassifierUtils.cpp254
-rw-r--r--toolkit/components/url-classifier/tests/gtest/TestVariableLengthPrefixSet.cpp486
-rw-r--r--toolkit/components/url-classifier/tests/gtest/moz.build40
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/allowlistAnnotatedFrame.html143
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/bad.css1
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/bad.css^headers^1
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/basic.vtt27
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/basic.vtt^headers^1
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/bug_1281083.html11
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/bug_1580416.html13
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/cache.sjs90
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/chrome.toml83
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedFrame.html154
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html26
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/classifierCommon.js108
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/classifierFrame.html57
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/classifierHelper.js204
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/cleanWorker.js12
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/dnt.html31
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/dnt.sjs9
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/evil.css1
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/evil.css^headers^1
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/evil.js3
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/evil.js^headers^2
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/evilWorker.js5
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/features.js291
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/gethash.sjs86
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/gethashFrame.html61
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/good.js3
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/head.js40
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/import.css3
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/import2.css3
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/mochitest.toml91
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/ping.sjs15
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/raptor.jpgbin0 -> 49629 bytes
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/redirect_tracker.sjs7
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/report.sjs71
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/sandboxed.html8
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/sandboxed.html^headers^1
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/seek.webmbin0 -> 215529 bytes
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/sw_register.html33
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/sw_unregister.html12
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/sw_worker.js10
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_advisory_link.html138
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_allowlisted_annotations.html60
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_annotation_vs_TP.html31
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_bug1254766.html265
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_cachemiss.html167
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classified_annotations.html171
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classifier.html141
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref.html157
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref_bug1395411.html72
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classifier_match.html179
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classifier_worker.html73
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classify_by_default.html156
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classify_ping.html131
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classify_top_sandboxed.html74
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_classify_track.html163
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_cryptomining.html100
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_cryptomining_annotate.html31
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_donottrack.html141
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_emailtracking.html130
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_fingerprinting.html130
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_fingerprinting_annotate.html31
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_gethash.html118
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_privatebrowsing_trackingprotection.html153
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_reporturl.html217
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_safebrowsing_bug1272239.html91
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_socialtracking.html109
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_socialtracking_annotate.html32
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html235
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1157081.html100
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1312515.html164
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1580416.html102
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_whitelist.html165
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/threathit.sjs47
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/track.html7
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/trackerFrame.html91
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs80
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/trackingRequest.html21
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/trackingRequest.js9
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/trackingRequest.js^headers^2
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/unwantedWorker.js5
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/update.sjs71
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/vp9.webmbin0 -> 97465 bytes
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html15
-rw-r--r--toolkit/components/url-classifier/tests/mochitest/workerFrame.html65
-rw-r--r--toolkit/components/url-classifier/tests/moz.build17
-rw-r--r--toolkit/components/url-classifier/tests/unit/data/content-fingerprinting-track-digest256bin0 -> 948 bytes
-rw-r--r--toolkit/components/url-classifier/tests/unit/data/digest1.chunkbin0 -> 939 bytes
-rw-r--r--toolkit/components/url-classifier/tests/unit/data/digest2.chunk2
-rw-r--r--toolkit/components/url-classifier/tests/unit/data/google-trackwhite-digest256bin0 -> 1470328 bytes
-rw-r--r--toolkit/components/url-classifier/tests/unit/data/invalid.chunk2
-rw-r--r--toolkit/components/url-classifier/tests/unit/data/mozplugin-block-digest256bin0 -> 3029 bytes
-rw-r--r--toolkit/components/url-classifier/tests/unit/head_urlclassifier.js570
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_addsub.js329
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_backoff.js92
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_bug1274685_unowned_list.js65
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_canonicalization.js83
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_channelClassifierService.js225
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_dbservice.js329
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_digest256.js143
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_exceptionListService.js285
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_features.js81
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_hashcompleter.js438
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_hashcompleter_v4.js292
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_listmanager.js355
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_malwaretable_pref.js4
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_partial.js607
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_platform_specific_threats.js104
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_pref.js15
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_prefixset.js178
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_provider_url.js32
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_rsListService.js424
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_safebrowsing_protobuf.js29
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_shouldclassify.js166
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_streamupdater.js244
-rw-r--r--toolkit/components/url-classifier/tests/unit/test_threat_type_conversion.js50
-rw-r--r--toolkit/components/url-classifier/tests/unit/xpcshell.toml56
196 files changed, 54649 insertions, 0 deletions
diff --git a/toolkit/components/url-classifier/ChunkSet.cpp b/toolkit/components/url-classifier/ChunkSet.cpp
new file mode 100644
index 0000000000..af488d43d7
--- /dev/null
+++ b/toolkit/components/url-classifier/ChunkSet.cpp
@@ -0,0 +1,248 @@
+//* -*- 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 "ChunkSet.h"
+
+#include "nsReadableUtils.h"
+
+namespace mozilla {
+namespace safebrowsing {
+
+const size_t ChunkSet::IO_BUFFER_SIZE;
+
+nsresult ChunkSet::Serialize(nsACString& aChunkStr) {
+ // Truncate and append rather than assigning because that's more efficient if
+ // aString is an nsAutoCString.
+ aChunkStr.Truncate();
+ StringJoinAppend(aChunkStr, ","_ns, mRanges,
+ [](nsACString& dst, const Range& range) {
+ dst.AppendInt((int32_t)range.Begin());
+ if (range.Begin() != range.End()) {
+ dst.Append('-');
+ dst.AppendInt((int32_t)range.End());
+ }
+ });
+
+ return NS_OK;
+}
+
+nsresult ChunkSet::Set(uint32_t aChunk) {
+ if (!Has(aChunk)) {
+ Range chunkRange(aChunk, aChunk);
+
+ if (mRanges.Length() == 0) {
+ if (!mRanges.AppendElement(chunkRange, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ return NS_OK;
+ }
+
+ if (mRanges.LastElement().Precedes(chunkRange)) {
+ mRanges.LastElement().End(aChunk);
+ } else if (chunkRange.Precedes(mRanges[0])) {
+ mRanges[0].Begin(aChunk);
+ } else {
+ ChunkSet tmp;
+ if (!tmp.mRanges.AppendElement(chunkRange, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return Merge(tmp);
+ }
+ }
+
+ return NS_OK;
+}
+
+bool ChunkSet::Has(uint32_t aChunk) const {
+ size_t idx;
+ return BinarySearchIf(mRanges, 0, mRanges.Length(),
+ Range::IntersectionComparator(Range(aChunk, aChunk)),
+ &idx);
+ // IntersectionComparator works because we create a
+ // single-chunk range.
+}
+
+nsresult ChunkSet::Merge(const ChunkSet& aOther) {
+ size_t oldLen = mRanges.Length();
+
+ for (const Range& mergeRange : aOther.mRanges) {
+ if (!HasSubrange(mergeRange)) {
+ if (!mRanges.InsertElementSorted(mergeRange, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ }
+
+ if (oldLen < mRanges.Length()) {
+ for (size_t i = 1; i < mRanges.Length(); i++) {
+ while (mRanges[i - 1].FoldLeft(mRanges[i])) {
+ mRanges.RemoveElementAt(i);
+
+ if (i == mRanges.Length()) {
+ return NS_OK;
+ }
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+uint32_t ChunkSet::Length() const {
+ uint32_t len = 0;
+ for (const Range& range : mRanges) {
+ len += range.Length();
+ }
+
+ return len;
+}
+
+nsresult ChunkSet::Remove(const ChunkSet& aOther) {
+ for (const Range& removalRange : aOther.mRanges) {
+ if (mRanges.Length() == 0) {
+ return NS_OK;
+ }
+
+ if (mRanges.LastElement().End() < removalRange.Begin() ||
+ aOther.mRanges.LastElement().End() < mRanges[0].Begin()) {
+ return NS_OK;
+ }
+
+ size_t intersectionIdx;
+ while (BinarySearchIf(mRanges, 0, mRanges.Length(),
+ Range::IntersectionComparator(removalRange),
+ &intersectionIdx)) {
+ ChunkSet remains;
+ nsresult rv = mRanges[intersectionIdx].Remove(removalRange, remains);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ mRanges.RemoveElementAt(intersectionIdx);
+ if (!mRanges.InsertElementsAt(intersectionIdx, remains.mRanges,
+ fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+void ChunkSet::Clear() { mRanges.Clear(); }
+
+nsresult ChunkSet::Write(nsIOutputStream* aOut) const {
+ nsTArray<uint32_t> chunks(IO_BUFFER_SIZE);
+
+ for (const Range& range : mRanges) {
+ for (uint32_t chunk = range.Begin(); chunk <= range.End(); chunk++) {
+ chunks.AppendElement(chunk);
+
+ if (chunks.Length() == chunks.Capacity()) {
+ nsresult rv = WriteTArray(aOut, chunks);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ chunks.Clear();
+ }
+ }
+ }
+
+ nsresult rv = WriteTArray(aOut, chunks);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult ChunkSet::Read(nsIInputStream* aIn, uint32_t aNumElements) {
+ nsTArray<uint32_t> chunks(IO_BUFFER_SIZE);
+
+ while (aNumElements != 0) {
+ chunks.Clear();
+
+ uint32_t numToRead =
+ aNumElements > IO_BUFFER_SIZE ? IO_BUFFER_SIZE : aNumElements;
+
+ nsresult rv = ReadTArray(aIn, &chunks, numToRead);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ aNumElements -= numToRead;
+
+ for (uint32_t c : chunks) {
+ rv = Set(c);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+bool ChunkSet::HasSubrange(const Range& aSubrange) const {
+ for (const Range& range : mRanges) {
+ if (range.Contains(aSubrange)) {
+ return true;
+ } else if (!(aSubrange.Begin() > range.End() ||
+ range.Begin() > aSubrange.End())) {
+ // In this case, aSubrange overlaps this range but is not a subrange.
+ // because the ChunkSet implementation ensures that there are no
+ // overlapping ranges, this means that aSubrange cannot be a subrange of
+ // any of the following ranges
+ return false;
+ }
+ }
+
+ return false;
+}
+
+uint32_t ChunkSet::Range::Length() const { return mEnd - mBegin + 1; }
+
+nsresult ChunkSet::Range::Remove(const Range& aRange,
+ ChunkSet& aRemainderSet) const {
+ if (mBegin < aRange.mBegin && aRange.mBegin <= mEnd) {
+ // aRange overlaps & follows this range
+ Range range(mBegin, aRange.mBegin - 1);
+ if (!aRemainderSet.mRanges.AppendElement(range, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ if (mBegin <= aRange.mEnd && aRange.mEnd < mEnd) {
+ // aRange overlaps & precedes this range
+ Range range(aRange.mEnd + 1, mEnd);
+ if (!aRemainderSet.mRanges.AppendElement(range, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ return NS_OK;
+}
+
+bool ChunkSet::Range::FoldLeft(const Range& aRange) {
+ if (Contains(aRange)) {
+ return true;
+ } else if (Precedes(aRange) ||
+ (mBegin <= aRange.mBegin && aRange.mBegin <= mEnd)) {
+ mEnd = aRange.mEnd;
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace safebrowsing
+} // namespace mozilla
diff --git a/toolkit/components/url-classifier/ChunkSet.h b/toolkit/components/url-classifier/ChunkSet.h
new file mode 100644
index 0000000000..d9978b21fd
--- /dev/null
+++ b/toolkit/components/url-classifier/ChunkSet.h
@@ -0,0 +1,96 @@
+//* -*- 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/. */
+
+#ifndef ChunkSet_h__
+#define ChunkSet_h__
+
+#include "Entries.h"
+#include "nsString.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace safebrowsing {
+
+/**
+ * Store the chunk numbers as an array of ranges of uint32_t.
+ * We need chunk numbers in order to ask for incremental updates from the
+ * server.
+ */
+class ChunkSet {
+ public:
+ nsresult Serialize(nsACString& aStr);
+ nsresult Set(uint32_t aChunk);
+ bool Has(uint32_t chunk) const;
+ nsresult Merge(const ChunkSet& aOther);
+ uint32_t Length() const;
+ nsresult Remove(const ChunkSet& aOther);
+ void Clear();
+
+ nsresult Write(nsIOutputStream* aOut) const;
+ nsresult Read(nsIInputStream* aIn, uint32_t aNumElements);
+
+ ChunkSet InfallibleClone() const {
+ ChunkSet result;
+ if (!result.mRanges.Assign(mRanges, fallible)) {
+ MOZ_CRASH("Out of memory");
+ }
+ return result;
+ }
+
+ private:
+ class Range {
+ public:
+ Range(uint32_t aBegin, uint32_t aEnd) : mBegin(aBegin), mEnd(aEnd) {}
+
+ uint32_t Length() const;
+ nsresult Remove(const Range& aRange, ChunkSet& aRemainderSet) const;
+ bool FoldLeft(const Range& aRange);
+
+ bool operator==(const Range& rhs) const { return mBegin == rhs.mBegin; }
+ bool operator<(const Range& rhs) const { return mBegin < rhs.mBegin; }
+
+ uint32_t Begin() const { return mBegin; }
+ void Begin(const uint32_t aBegin) { mBegin = aBegin; }
+ uint32_t End() const { return mEnd; }
+ void End(const uint32_t aEnd) { mEnd = aEnd; }
+
+ bool Contains(const Range& aRange) const {
+ return mBegin <= aRange.mBegin && aRange.mEnd <= mEnd;
+ }
+ bool Precedes(const Range& aRange) const {
+ return mEnd + 1 == aRange.mBegin;
+ }
+
+ struct IntersectionComparator {
+ int operator()(const Range& aRange) const {
+ if (aRange.mBegin > mTarget.mEnd) {
+ return -1;
+ }
+ if (mTarget.mBegin > aRange.mEnd) {
+ return 1;
+ }
+ return 0;
+ }
+
+ explicit IntersectionComparator(const Range& aTarget)
+ : mTarget(aTarget) {}
+ const Range& mTarget;
+ };
+
+ private:
+ uint32_t mBegin;
+ uint32_t mEnd;
+ };
+
+ static const size_t IO_BUFFER_SIZE = 1024;
+ FallibleTArray<Range> mRanges;
+
+ bool HasSubrange(const Range& aSubrange) const;
+};
+
+} // namespace safebrowsing
+} // namespace mozilla
+
+#endif
diff --git a/toolkit/components/url-classifier/Classifier.cpp b/toolkit/components/url-classifier/Classifier.cpp
new file mode 100644
index 0000000000..7a78a59243
--- /dev/null
+++ b/toolkit/components/url-classifier/Classifier.cpp
@@ -0,0 +1,1786 @@
+//* -*- 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 "Classifier.h"
+#include "LookupCacheV4.h"
+#include "nsIFile.h"
+#include "nsNetCID.h"
+#include "nsPrintfCString.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Components.h"
+#include "mozilla/EndianUtils.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/LazyIdleThread.h"
+#include "mozilla/Logging.h"
+#include "mozilla/SyncRunnable.h"
+#include "mozilla/Base64.h"
+#include "mozilla/Unused.h"
+#include "mozilla/UniquePtr.h"
+#include "nsUrlClassifierDBService.h"
+#include "nsUrlClassifierUtils.h"
+
+// MOZ_LOG=UrlClassifierDbService:5
+extern mozilla::LazyLogModule gUrlClassifierDbServiceLog;
+#define LOG(args) \
+ MOZ_LOG(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() \
+ MOZ_LOG_TEST(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug)
+
+#define STORE_DIRECTORY "safebrowsing"_ns
+#define TO_DELETE_DIR_SUFFIX "-to_delete"_ns
+#define BACKUP_DIR_SUFFIX "-backup"_ns
+#define UPDATING_DIR_SUFFIX "-updating"_ns
+
+#define V4_METADATA_SUFFIX ".metadata"_ns
+#define V2_METADATA_SUFFIX ".sbstore"_ns
+
+// The amount of time, in milliseconds, that our IO thread will stay alive after
+// the last event it processes.
+#define DEFAULT_THREAD_TIMEOUT_MS 5000
+
+namespace mozilla {
+namespace safebrowsing {
+
+bool Classifier::OnUpdateThread() const {
+ bool onthread = false;
+ if (mUpdateThread) {
+ mUpdateThread->IsOnCurrentThread(&onthread);
+ }
+ return onthread;
+}
+
+void Classifier::SplitTables(const nsACString& str,
+ nsTArray<nsCString>& tables) {
+ tables.Clear();
+
+ for (const auto& table : str.Split(',')) {
+ if (!table.IsEmpty()) {
+ tables.AppendElement(table);
+ }
+ }
+
+ // Remove duplicates
+ tables.Sort();
+ const auto newEnd = std::unique(tables.begin(), tables.end());
+ tables.TruncateLength(std::distance(tables.begin(), newEnd));
+}
+
+nsresult Classifier::GetPrivateStoreDirectory(
+ nsIFile* aRootStoreDirectory, const nsACString& aTableName,
+ const nsACString& aProvider, nsIFile** aPrivateStoreDirectory) {
+ NS_ENSURE_ARG_POINTER(aPrivateStoreDirectory);
+
+ if (!StringEndsWith(aTableName, "-proto"_ns)) {
+ // Only V4 table names (ends with '-proto') would be stored
+ // to per-provider sub-directory.
+ nsCOMPtr<nsIFile>(aRootStoreDirectory).forget(aPrivateStoreDirectory);
+ return NS_OK;
+ }
+
+ if (aProvider.IsEmpty()) {
+ // When failing to get provider, just store in the root folder.
+ nsCOMPtr<nsIFile>(aRootStoreDirectory).forget(aPrivateStoreDirectory);
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIFile> providerDirectory;
+
+ // Clone first since we are gonna create a new directory.
+ nsresult rv = aRootStoreDirectory->Clone(getter_AddRefs(providerDirectory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Append the provider name to the root store directory.
+ rv = providerDirectory->AppendNative(aProvider);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Ensure existence of the provider directory.
+ bool dirExists;
+ rv = providerDirectory->Exists(&dirExists);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!dirExists) {
+ LOG(("Creating private directory for %s", nsCString(aTableName).get()));
+ rv = providerDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
+ NS_ENSURE_SUCCESS(rv, rv);
+ providerDirectory.forget(aPrivateStoreDirectory);
+ return rv;
+ }
+
+ // Store directory exists. Check if it's a directory.
+ bool isDir;
+ rv = providerDirectory->IsDirectory(&isDir);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!isDir) {
+ return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+ }
+
+ providerDirectory.forget(aPrivateStoreDirectory);
+
+ return NS_OK;
+}
+
+Classifier::Classifier()
+ : mIsTableRequestResultOutdated(true),
+ mUpdateInterrupted(true),
+ mIsClosed(false) {
+ // Make a lazy thread for any IO
+ mUpdateThread =
+ new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS, "Classifier Update",
+ LazyIdleThread::ShutdownMethod::ManualShutdown);
+}
+
+Classifier::~Classifier() {
+ if (mUpdateThread) {
+ mUpdateThread->Shutdown();
+ mUpdateThread = nullptr;
+ }
+
+ Close();
+}
+
+nsresult Classifier::SetupPathNames() {
+ // Get the root directory where to store all the databases.
+ nsresult rv = mCacheDirectory->Clone(getter_AddRefs(mRootStoreDirectory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mRootStoreDirectory->AppendNative(STORE_DIRECTORY);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Make sure LookupCaches (which are persistent and survive updates)
+ // are reading/writing in the right place. We will be moving their
+ // files "underneath" them during backup/restore.
+ for (uint32_t i = 0; i < mLookupCaches.Length(); i++) {
+ mLookupCaches[i]->UpdateRootDirHandle(mRootStoreDirectory);
+ }
+
+ // Directory where to move a backup before an update.
+ rv = mCacheDirectory->Clone(getter_AddRefs(mBackupDirectory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mBackupDirectory->AppendNative(STORE_DIRECTORY + BACKUP_DIR_SUFFIX);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Directory where to be working on the update.
+ rv = mCacheDirectory->Clone(getter_AddRefs(mUpdatingDirectory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mUpdatingDirectory->AppendNative(STORE_DIRECTORY + UPDATING_DIR_SUFFIX);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Directory where to move the backup so we can atomically
+ // delete (really move) it.
+ rv = mCacheDirectory->Clone(getter_AddRefs(mToDeleteDirectory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mToDeleteDirectory->AppendNative(STORE_DIRECTORY + TO_DELETE_DIR_SUFFIX);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult Classifier::CreateStoreDirectory() {
+ if (ShouldAbort()) {
+ return NS_OK; // nothing to do, the classifier is done
+ }
+
+ // Ensure the safebrowsing directory exists.
+ bool storeExists;
+ nsresult rv = mRootStoreDirectory->Exists(&storeExists);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!storeExists) {
+ rv = mRootStoreDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ bool storeIsDir;
+ rv = mRootStoreDirectory->IsDirectory(&storeIsDir);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!storeIsDir) return NS_ERROR_FILE_DESTINATION_NOT_DIR;
+ }
+
+ return NS_OK;
+}
+
+// Testing entries are created directly in LookupCache instead of
+// created via update(Bug 1531354). We can remove unused testing
+// files from profile.
+// TODO: See Bug 723153 to clear old safebrowsing store
+nsresult Classifier::ClearLegacyFiles() {
+ if (ShouldAbort()) {
+ return NS_OK; // nothing to do, the classifier is done
+ }
+
+ nsTArray<nsLiteralCString> tables = {
+ "test-phish-simple"_ns, "test-malware-simple"_ns,
+ "test-unwanted-simple"_ns, "test-harmful-simple"_ns,
+ "test-track-simple"_ns, "test-trackwhite-simple"_ns,
+ "test-block-simple"_ns,
+ };
+
+ const auto fnFindAndRemove = [](nsIFile* aRootDirectory,
+ const nsACString& aFileName) {
+ nsCOMPtr<nsIFile> file;
+ nsresult rv = aRootDirectory->Clone(getter_AddRefs(file));
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ rv = file->AppendNative(aFileName);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ bool exists;
+ rv = file->Exists(&exists);
+ if (NS_FAILED(rv) || !exists) {
+ return false;
+ }
+
+ rv = file->Remove(false);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ return true;
+ };
+
+ for (const auto& table : tables) {
+ // Remove both .sbstore and .vlpse if .sbstore exists
+ if (fnFindAndRemove(mRootStoreDirectory, table + ".sbstore"_ns)) {
+ fnFindAndRemove(mRootStoreDirectory, table + ".vlpset"_ns);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult Classifier::Open(nsIFile& aCacheDirectory) {
+ // Remember the Local profile directory.
+ nsresult rv = aCacheDirectory.Clone(getter_AddRefs(mCacheDirectory));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Create the handles to the update and backup directories.
+ rv = SetupPathNames();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Clean up any to-delete directories that haven't been deleted yet.
+ // This is still required for backward compatibility.
+ rv = CleanToDelete();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // If we met a crash during the previous update, "safebrowsing-updating"
+ // directory will exist and let's remove it.
+ rv = mUpdatingDirectory->Remove(true);
+ if (NS_SUCCEEDED(rv)) {
+ // If the "safebrowsing-updating" exists, it implies a crash occurred
+ // in the previous update.
+ LOG(("We may have hit a crash in the previous update."));
+ }
+
+ // Check whether we have an incomplete update and recover from the
+ // backup if so.
+ rv = RecoverBackups();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Make sure the main store directory exists.
+ rv = CreateStoreDirectory();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ClearLegacyFiles();
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+
+ // Build the list of know urlclassifier lists
+ // XXX: Disk IO potentially on the main thread during startup
+ RegenActiveTables();
+
+ return NS_OK;
+}
+
+void Classifier::Close() {
+ // Close will be called by PreShutdown, so it is important to note that
+ // things put here should not affect an ongoing update thread.
+ mIsClosed = true;
+ DropStores();
+}
+
+void Classifier::Reset() {
+ MOZ_ASSERT(!OnUpdateThread(), "Reset() MUST NOT be called on update thread");
+
+ LOG(("Reset() is called so we interrupt the update."));
+ mUpdateInterrupted = true;
+
+ // We don't pass the ref counted object 'Classifier' to resetFunc because we
+ // don't want to release 'Classifier in the update thread, which triggers an
+ // assertion when LazyIdelUpdate thread is not created and removed by the same
+ // thread (worker thread). Since |resetFuc| is a synchronous call, we can just
+ // pass the reference of Classifier because Classifier's life cycle is
+ // guarantee longer than |resetFunc|.
+ auto resetFunc = [&] {
+ if (this->mIsClosed) {
+ return; // too late to reset, bail
+ }
+ this->DropStores();
+
+ this->mRootStoreDirectory->Remove(true);
+ this->mBackupDirectory->Remove(true);
+ this->mUpdatingDirectory->Remove(true);
+ this->mToDeleteDirectory->Remove(true);
+
+ this->CreateStoreDirectory();
+ this->RegenActiveTables();
+ };
+
+ if (!mUpdateThread) {
+ LOG(("Async update has been disabled. Just Reset() on worker thread."));
+ resetFunc();
+ return;
+ }
+
+ nsCOMPtr<nsIRunnable> r =
+ NS_NewRunnableFunction("safebrowsing::Classifier::Reset", resetFunc);
+ SyncRunnable::DispatchToThread(mUpdateThread, r);
+}
+
+void Classifier::ResetTables(ClearType aType,
+ const nsTArray<nsCString>& aTables) {
+ for (uint32_t i = 0; i < aTables.Length(); i++) {
+ LOG(("Resetting table: %s", aTables[i].get()));
+ RefPtr<LookupCache> cache = GetLookupCache(aTables[i]);
+ if (cache) {
+ // Remove any cached Completes for this table if clear type is Clear_Cache
+ if (aType == Clear_Cache) {
+ cache->ClearCache();
+ } else {
+ cache->ClearAll();
+ }
+ }
+ }
+
+ // Clear on-disk database if clear type is Clear_All
+ if (aType == Clear_All) {
+ DeleteTables(mRootStoreDirectory, aTables);
+
+ RegenActiveTables();
+ }
+}
+
+// |DeleteTables| is used by |GetLookupCache| to remove on-disk data when
+// we detect prefix file corruption. So make sure not to call |GetLookupCache|
+// again in this function to avoid infinite loop.
+void Classifier::DeleteTables(nsIFile* aDirectory,
+ const nsTArray<nsCString>& aTables) {
+ nsCOMPtr<nsIDirectoryEnumerator> entries;
+ nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ nsCOMPtr<nsIFile> file;
+ while (NS_SUCCEEDED(rv = entries->GetNextFile(getter_AddRefs(file))) &&
+ file) {
+ // If |file| is a directory, recurse to find its entries as well.
+ bool isDirectory;
+ if (NS_FAILED(file->IsDirectory(&isDirectory))) {
+ continue;
+ }
+ if (isDirectory) {
+ DeleteTables(file, aTables);
+ continue;
+ }
+
+ nsCString leafName;
+ rv = file->GetNativeLeafName(leafName);
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ // Remove file extension if there's one.
+ int32_t dotPosition = leafName.RFind(".");
+ if (dotPosition >= 0) {
+ leafName.Truncate(dotPosition);
+ }
+
+ if (!leafName.IsEmpty() && aTables.Contains(leafName)) {
+ if (NS_FAILED(file->Remove(false))) {
+ NS_WARNING(nsPrintfCString("Fail to remove file %s from the disk",
+ leafName.get())
+ .get());
+ }
+ }
+ }
+ NS_ENSURE_SUCCESS_VOID(rv);
+}
+
+// This function is I/O intensive. It should only be called before applying
+// an update.
+void Classifier::TableRequest(nsACString& aResult) {
+ MOZ_ASSERT(!NS_IsMainThread(),
+ "TableRequest must be called on the classifier worker thread.");
+
+ // This function and all disk I/O are guaranteed to occur
+ // on the same thread so we don't need to add a lock around.
+ if (!mIsTableRequestResultOutdated) {
+ aResult = mTableRequestResult;
+ return;
+ }
+
+ // We reset tables failed to load here; not just tables are corrupted.
+ // It is because this is a safer way to ensure Safe Browsing databases
+ // can be recovered from any bad situations.
+ nsTArray<nsCString> failedTables;
+
+ // Load meta data from *.sbstore files in the root directory.
+ // Specifically for v4 tables.
+ nsCString v2Metadata;
+ nsresult rv = LoadHashStore(mRootStoreDirectory, v2Metadata, failedTables);
+ if (NS_SUCCEEDED(rv)) {
+ aResult.Append(v2Metadata);
+ }
+
+ // Load meta data from *.metadata files in the root directory.
+ // Specifically for v4 tables.
+ nsCString v4Metadata;
+ rv = LoadMetadata(mRootStoreDirectory, v4Metadata, failedTables);
+ if (NS_SUCCEEDED(rv)) {
+ aResult.Append(v4Metadata);
+ }
+
+ // Clear data for tables that we failed to open, a full update should
+ // be requested for those tables.
+ if (failedTables.Length() != 0) {
+ LOG(("Reset tables failed to open before applying an update"));
+ ResetTables(Clear_All, failedTables);
+ }
+
+ // Update the TableRequest result in-memory cache.
+ mTableRequestResult = aResult;
+ mIsTableRequestResultOutdated = false;
+}
+
+nsresult Classifier::CheckURIFragments(
+ const nsTArray<nsCString>& aSpecFragments, const nsACString& aTable,
+ LookupResultArray& aResults) {
+ // A URL can form up to 30 different fragments
+ MOZ_ASSERT(aSpecFragments.Length() != 0);
+ MOZ_ASSERT(aSpecFragments.Length() <=
+ (MAX_HOST_COMPONENTS * (MAX_PATH_COMPONENTS + 2)));
+
+ if (LOG_ENABLED()) {
+ uint32_t urlIdx = 0;
+ for (uint32_t i = 1; i < aSpecFragments.Length(); i++) {
+ if (aSpecFragments[urlIdx].Length() < aSpecFragments[i].Length()) {
+ urlIdx = i;
+ }
+ }
+ LOG(("Checking table %s, URL is %s", aTable.BeginReading(),
+ aSpecFragments[urlIdx].get()));
+ }
+
+ RefPtr<LookupCache> cache = GetLookupCache(aTable);
+ if (NS_WARN_IF(!cache)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Now check each lookup fragment against the entries in the DB.
+ for (uint32_t i = 0; i < aSpecFragments.Length(); i++) {
+ Completion lookupHash;
+ lookupHash.FromPlaintext(aSpecFragments[i]);
+
+ bool has, confirmed;
+ uint32_t matchLength;
+
+ nsresult rv = cache->Has(lookupHash, &has, &matchLength, &confirmed);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (has) {
+ RefPtr<LookupResult> result = new LookupResult;
+ aResults.AppendElement(result);
+
+ if (LOG_ENABLED()) {
+ nsAutoCString checking;
+ lookupHash.ToHexString(checking);
+ LOG(("Found a result in fragment %s, hash %s (%X)",
+ aSpecFragments[i].get(), checking.get(), lookupHash.ToUint32()));
+ LOG(("Result %s, match %d-bytes prefix",
+ confirmed ? "confirmed." : "Not confirmed.", matchLength));
+ }
+
+ result->hash.complete = lookupHash;
+ result->mConfirmed = confirmed;
+ result->mTableName.Assign(cache->TableName());
+ result->mPartialHashLength = confirmed ? COMPLETE_SIZE : matchLength;
+ result->mProtocolV2 = LookupCache::Cast<LookupCacheV2>(cache);
+ }
+ }
+
+ return NS_OK;
+}
+
+static nsresult SwapDirectoryContent(nsIFile* aDir1, nsIFile* aDir2,
+ nsIFile* aParentDir, nsIFile* aTempDir) {
+ // Pre-condition: |aDir1| and |aDir2| are directory and their parent
+ // are both |aParentDir|.
+ //
+ // Post-condition: The locations where aDir1 and aDir2 point to will not
+ // change but their contents will be exchanged. If we failed
+ // to swap their content, everything will be rolled back.
+
+ nsAutoCString tempDirName;
+ aTempDir->GetNativeLeafName(tempDirName);
+
+ nsresult rv;
+
+ nsAutoCString dirName1, dirName2;
+ aDir1->GetNativeLeafName(dirName1);
+ aDir2->GetNativeLeafName(dirName2);
+
+ LOG(("Swapping directories %s and %s...", dirName1.get(), dirName2.get()));
+
+ // 1. Rename "dirName1" to "temp"
+ rv = aDir1->RenameToNative(nullptr, tempDirName);
+ if (NS_FAILED(rv)) {
+ LOG(("Unable to rename %s to %s", dirName1.get(), tempDirName.get()));
+ return rv; // Nothing to roll back.
+ }
+
+ // 1.1. Create a handle for temp directory. This is required since
+ // |nsIFile.rename| will not change the location where the
+ // object points to.
+ nsCOMPtr<nsIFile> tempDirectory;
+ rv = aParentDir->Clone(getter_AddRefs(tempDirectory));
+ rv = tempDirectory->AppendNative(tempDirName);
+
+ // 2. Rename "dirName2" to "dirName1".
+ rv = aDir2->RenameToNative(nullptr, dirName1);
+ if (NS_FAILED(rv)) {
+ LOG(("Failed to rename %s to %s. Rename temp directory back to %s",
+ dirName2.get(), dirName1.get(), dirName1.get()));
+ nsresult rbrv = tempDirectory->RenameToNative(nullptr, dirName1);
+ NS_ENSURE_SUCCESS(rbrv, rbrv);
+ return rv;
+ }
+
+ // 3. Rename "temp" to "dirName2".
+ rv = tempDirectory->RenameToNative(nullptr, dirName2);
+ if (NS_FAILED(rv)) {
+ LOG(("Failed to rename temp directory to %s. ", dirName2.get()));
+ // We've done (1) renaming "dir1 to temp" and
+ // (2) renaming "dir2 to dir1"
+ // so the rollback is
+ // (1) renaming "dir1 to dir2" and
+ // (2) renaming "temp to dir1"
+ nsresult rbrv; // rollback result
+ rbrv = aDir1->RenameToNative(nullptr, dirName2);
+ NS_ENSURE_SUCCESS(rbrv, rbrv);
+ rbrv = tempDirectory->RenameToNative(nullptr, dirName1);
+ NS_ENSURE_SUCCESS(rbrv, rbrv);
+ return rv;
+ }
+
+ return rv;
+}
+
+void Classifier::RemoveUpdateIntermediaries() {
+ // Remove old LookupCaches.
+ mNewLookupCaches.Clear();
+
+ // Remove the "old" directory. (despite its looking-new name)
+ if (NS_FAILED(mUpdatingDirectory->Remove(true))) {
+ // If the directory is locked from removal for some reason,
+ // we will fail here and it doesn't matter until the next
+ // update. (the next udpate will fail due to the removable
+ // "safebrowsing-udpating" directory.)
+ LOG(("Failed to remove updating directory."));
+ }
+}
+
+void Classifier::CopyAndInvalidateFullHashCache() {
+ MOZ_ASSERT(!OnUpdateThread(),
+ "CopyAndInvalidateFullHashCache cannot be called on update thread "
+ "since it mutates mLookupCaches which is only safe on "
+ "worker thread.");
+
+ // New lookup caches are built from disk, data likes cache which is
+ // generated online won't exist. We have to manually copy cache from
+ // old LookupCache to new LookupCache.
+ for (auto& newCache : mNewLookupCaches) {
+ for (auto& oldCache : mLookupCaches) {
+ if (oldCache->TableName() == newCache->TableName()) {
+ newCache->CopyFullHashCache(oldCache);
+ break;
+ }
+ }
+ }
+
+ // Clear cache when update.
+ // Invalidate cache entries in CopyAndInvalidateFullHashCache because only
+ // at this point we will have cache data in LookupCache.
+ for (auto& newCache : mNewLookupCaches) {
+ newCache->InvalidateExpiredCacheEntries();
+ }
+}
+
+void Classifier::MergeNewLookupCaches() {
+ MOZ_ASSERT(!OnUpdateThread(),
+ "MergeNewLookupCaches cannot be called on update thread "
+ "since it mutates mLookupCaches which is only safe on "
+ "worker thread.");
+
+ for (auto& newCache : mNewLookupCaches) {
+ // For each element in mNewLookCaches, it will be swapped with
+ // - An old cache in mLookupCache with the same table name or
+ // - nullptr (mLookupCache will be expaned) otherwise.
+ size_t swapIndex = 0;
+ for (; swapIndex < mLookupCaches.Length(); swapIndex++) {
+ if (mLookupCaches[swapIndex]->TableName() == newCache->TableName()) {
+ break;
+ }
+ }
+ if (swapIndex == mLookupCaches.Length()) {
+ mLookupCaches.AppendElement(nullptr);
+ }
+
+ std::swap(mLookupCaches[swapIndex], newCache);
+ mLookupCaches[swapIndex]->UpdateRootDirHandle(mRootStoreDirectory);
+ }
+
+ // At this point, mNewLookupCaches's length remains the same but
+ // will contain either old cache (override) or nullptr (append).
+}
+
+nsresult Classifier::SwapInNewTablesAndCleanup() {
+ nsresult rv;
+
+ // Step 1. Swap in on-disk tables. The idea of using "safebrowsing-backup"
+ // as the intermediary directory is we can get databases recovered if
+ // crash occurred in any step of the swap. (We will recover from
+ // "safebrowsing-backup" in OpenDb().)
+ rv = SwapDirectoryContent(mUpdatingDirectory, // contains new tables
+ mRootStoreDirectory, // contains old tables
+ mCacheDirectory, // common parent dir
+ mBackupDirectory); // intermediary dir for swap
+ if (NS_FAILED(rv)) {
+ LOG(("Failed to swap in on-disk tables."));
+ RemoveUpdateIntermediaries();
+ return rv;
+ }
+
+ // Step 2. Merge mNewLookupCaches into mLookupCaches. The outdated
+ // LookupCaches will be stored in mNewLookupCaches and be cleaned
+ // up later.
+ MergeNewLookupCaches();
+
+ // Step 3. Re-generate active tables based on on-disk tables.
+ rv = RegenActiveTables();
+ if (NS_FAILED(rv)) {
+ LOG(("Failed to re-generate active tables!"));
+ }
+
+ // Step 4. Clean up intermediaries for update.
+ RemoveUpdateIntermediaries();
+
+ // Step 5. Invalidate cached tableRequest request.
+ mIsTableRequestResultOutdated = true;
+
+ LOG(("Done swap in updated tables."));
+
+ return rv;
+}
+
+void Classifier::FlushAndDisableAsyncUpdate() {
+ LOG(("Classifier::FlushAndDisableAsyncUpdate [%p, %p]", this,
+ mUpdateThread.get()));
+
+ if (!mUpdateThread) {
+ LOG(("Async update has been disabled."));
+ return;
+ }
+
+ mUpdateThread->Shutdown();
+ mUpdateThread = nullptr;
+}
+
+nsresult Classifier::AsyncApplyUpdates(const TableUpdateArray& aUpdates,
+ const AsyncUpdateCallback& aCallback) {
+ LOG(("Classifier::AsyncApplyUpdates"));
+
+ if (!mUpdateThread) {
+ LOG(("Async update has already been disabled."));
+ return NS_ERROR_FAILURE;
+ }
+
+ // Caller thread | Update thread
+ // --------------------------------------------------------
+ // | ApplyUpdatesBackground
+ // (processing other task) | (bg-update done. ping back to caller
+ // thread) (processing other task) | idle... ApplyUpdatesForeground |
+ // callback |
+
+ MOZ_ASSERT(mNewLookupCaches.IsEmpty(),
+ "There should be no leftovers from a previous update.");
+
+ mUpdateInterrupted = false;
+ nsresult rv =
+ mRootStoreDirectory->Clone(getter_AddRefs(mRootStoreDirectoryForUpdate));
+ if (NS_FAILED(rv)) {
+ LOG(("Failed to clone mRootStoreDirectory for update."));
+ return rv;
+ }
+
+ nsCOMPtr<nsIThread> callerThread = NS_GetCurrentThread();
+ MOZ_ASSERT(!OnUpdateThread());
+
+ RefPtr<Classifier> self = this;
+ nsCOMPtr<nsIRunnable> bgRunnable = NS_NewRunnableFunction(
+ "safebrowsing::Classifier::AsyncApplyUpdates",
+ [self, aUpdates = aUpdates.Clone(), aCallback, callerThread]() mutable {
+ MOZ_ASSERT(self->OnUpdateThread(), "MUST be on update thread");
+
+ nsresult bgRv;
+ nsTArray<nsCString> failedTableNames;
+
+ TableUpdateArray updates;
+
+ // Make a copy of the array since we'll be removing entries as
+ // we process them on the background thread.
+ if (updates.AppendElements(std::move(aUpdates), fallible)) {
+ LOG(("Step 1. ApplyUpdatesBackground on update thread."));
+ bgRv = self->ApplyUpdatesBackground(updates, failedTableNames);
+ } else {
+ LOG(
+ ("Step 1. Not enough memory to run ApplyUpdatesBackground on "
+ "update thread."));
+ bgRv = NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Classifier is created in the worker thread and it has to be released
+ // in the worker thread(because of the constrain that LazyIdelThread has
+ // to be created and released in the same thread). We transfer the
+ // ownership to the caller thread here to gurantee that we don't release
+ // it in the udpate thread.
+ nsCOMPtr<nsIRunnable> fgRunnable = NS_NewRunnableFunction(
+ "safebrowsing::Classifier::AsyncApplyUpdates",
+ [self = std::move(self), aCallback, bgRv,
+ failedTableNames = std::move(failedTableNames),
+ callerThread]() mutable {
+ RefPtr<Classifier> classifier = std::move(self);
+
+ MOZ_ASSERT(NS_GetCurrentThread() == callerThread,
+ "MUST be on caller thread");
+
+ LOG(("Step 2. ApplyUpdatesForeground on caller thread"));
+ nsresult rv =
+ classifier->ApplyUpdatesForeground(bgRv, failedTableNames);
+
+ LOG(("Step 3. Updates applied! Fire callback."));
+ aCallback(rv);
+ });
+
+ callerThread->Dispatch(fgRunnable, NS_DISPATCH_NORMAL);
+ });
+
+ return mUpdateThread->Dispatch(bgRunnable, NS_DISPATCH_NORMAL);
+}
+
+nsresult Classifier::ApplyUpdatesBackground(
+ TableUpdateArray& aUpdates, nsTArray<nsCString>& aFailedTableNames) {
+ // |mUpdateInterrupted| is guaranteed to have been unset.
+ // If |mUpdateInterrupted| is set at any point, Reset() must have
+ // been called then we need to interrupt the update process.
+ // We only add checkpoints for non-trivial tasks.
+
+ if (aUpdates.IsEmpty()) {
+ return NS_OK;
+ }
+
+ nsUrlClassifierUtils* urlUtil = nsUrlClassifierUtils::GetInstance();
+ if (NS_WARN_IF(!urlUtil)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCString provider;
+ // Assume all TableUpdate objects should have the same provider.
+ urlUtil->GetTelemetryProvider(aUpdates[0]->TableName(), provider);
+
+ Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_CL_KEYED_UPDATE_TIME>
+ keyedTimer(provider);
+
+ PRIntervalTime clockStart = 0;
+ if (LOG_ENABLED()) {
+ clockStart = PR_IntervalNow();
+ }
+
+ nsresult rv;
+
+ // Check point 1: Copying files takes time so we check ShouldAbort()
+ // inside CopyInUseDirForUpdate().
+ rv = CopyInUseDirForUpdate(); // i.e. mUpdatingDirectory will be setup.
+ if (NS_FAILED(rv)) {
+ LOG(("Failed to copy in-use directory for update."));
+ return (rv == NS_ERROR_ABORT) ? NS_OK : rv;
+ }
+
+ LOG(("Applying %zu table updates.", aUpdates.Length()));
+
+ for (uint32_t i = 0; i < aUpdates.Length(); i++) {
+ RefPtr<const TableUpdate> update = aUpdates[i];
+ if (!update) {
+ // Previous UpdateHashStore() may have consumed this update..
+ continue;
+ }
+
+ // Run all updates for one table
+ nsAutoCString updateTable(update->TableName());
+
+ // Check point 2: Processing downloaded data takes time.
+ if (ShouldAbort()) {
+ LOG(("Update is interrupted. Stop building new tables."));
+ return NS_OK;
+ }
+
+ // Will update the mirrored in-memory and on-disk databases.
+ if (TableUpdate::Cast<TableUpdateV2>(update)) {
+ rv = UpdateHashStore(aUpdates, updateTable);
+ } else {
+ rv = UpdateTableV4(aUpdates, updateTable);
+ }
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ LOG(("Failed to update table: %s", updateTable.get()));
+ // We don't quit the updating process immediately when we discover
+ // a failure. Instead, we continue to apply updates to the
+ // remaining tables to find other tables which may also fail to
+ // apply an update. This help us reset all the corrupted tables
+ // within a single update.
+ // Note that changes that result from successful updates don't take
+ // effect after the updating process is finished. This is because
+ // when an error occurs during the updating process, we ignore all
+ // changes that have happened during the udpating process.
+ aFailedTableNames.AppendElement(updateTable);
+ continue;
+ }
+ }
+
+ if (!aFailedTableNames.IsEmpty()) {
+ RemoveUpdateIntermediaries();
+ return NS_ERROR_FAILURE;
+ }
+
+ if (LOG_ENABLED()) {
+ PRIntervalTime clockEnd = PR_IntervalNow();
+ LOG(("update took %dms\n",
+ PR_IntervalToMilliseconds(clockEnd - clockStart)));
+ }
+
+ return rv;
+}
+
+nsresult Classifier::ApplyUpdatesForeground(
+ nsresult aBackgroundRv, const nsTArray<nsCString>& aFailedTableNames) {
+ if (ShouldAbort()) {
+ LOG(("Update is interrupted! Just remove update intermediaries."));
+ RemoveUpdateIntermediaries();
+ return NS_OK;
+ }
+ if (NS_SUCCEEDED(aBackgroundRv)) {
+ // Copy and Invalidate fullhash cache here because this call requires
+ // mLookupCaches which is only available on work-thread
+ CopyAndInvalidateFullHashCache();
+
+ return SwapInNewTablesAndCleanup();
+ }
+ if (NS_ERROR_OUT_OF_MEMORY != aBackgroundRv) {
+ ResetTables(Clear_All, aFailedTableNames);
+ }
+ return aBackgroundRv;
+}
+
+nsresult Classifier::ApplyFullHashes(ConstTableUpdateArray& aUpdates) {
+ MOZ_ASSERT(!OnUpdateThread(),
+ "ApplyFullHashes() MUST NOT be called on update thread");
+ MOZ_ASSERT(
+ !NS_IsMainThread(),
+ "ApplyFullHashes() must be called on the classifier worker thread.");
+
+ LOG(("Applying %zu table gethashes.", aUpdates.Length()));
+
+ for (uint32_t i = 0; i < aUpdates.Length(); i++) {
+ nsresult rv = UpdateCache(aUpdates[i]);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aUpdates[i] = nullptr;
+ }
+
+ return NS_OK;
+}
+
+void Classifier::GetCacheInfo(const nsACString& aTable,
+ nsIUrlClassifierCacheInfo** aCache) {
+ RefPtr<const LookupCache> lookupCache = GetLookupCache(aTable);
+ if (!lookupCache) {
+ return;
+ }
+
+ lookupCache->GetCacheInfo(aCache);
+}
+
+void Classifier::DropStores() {
+ // See the comment in Classifier::Close() before adding anything here.
+ mLookupCaches.Clear();
+}
+
+nsresult Classifier::RegenActiveTables() {
+ if (ShouldAbort()) {
+ return NS_OK; // nothing to do, the classifier is done
+ }
+
+ mActiveTablesCache.Clear();
+
+ // The extension of V2 and V4 prefix files is .vlpset
+ // We still check .pset here for legacy load.
+ nsTArray<nsCString> exts = {".vlpset"_ns, ".pset"_ns};
+ nsTArray<nsCString> foundTables;
+ nsresult rv = ScanStoreDir(mRootStoreDirectory, exts, foundTables);
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+
+ // We don't have test tables on disk, add Moz built-in entries here
+ rv = AddMozEntries(foundTables);
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+
+ for (const auto& table : foundTables) {
+ RefPtr<const LookupCache> lookupCache = GetLookupCache(table);
+ if (!lookupCache) {
+ LOG(("Inactive table (no cache): %s", table.get()));
+ continue;
+ }
+
+ if (!lookupCache->IsPrimed()) {
+ LOG(("Inactive table (cache not primed): %s", table.get()));
+ continue;
+ }
+
+ LOG(("Active %s table: %s",
+ LookupCache::Cast<const LookupCacheV4>(lookupCache) ? "v4" : "v2",
+ table.get()));
+
+ mActiveTablesCache.AppendElement(table);
+ }
+
+ return NS_OK;
+}
+
+nsresult Classifier::AddMozEntries(nsTArray<nsCString>& aTables) {
+ nsTArray<nsLiteralCString> tables = {
+ "moztest-phish-simple"_ns, "moztest-malware-simple"_ns,
+ "moztest-unwanted-simple"_ns, "moztest-harmful-simple"_ns,
+ "moztest-track-simple"_ns, "moztest-trackwhite-simple"_ns,
+ "moztest-block-simple"_ns,
+ };
+
+ for (const auto& table : tables) {
+ RefPtr<LookupCache> c = GetLookupCache(table, false);
+ RefPtr<LookupCacheV2> lookupCache = LookupCache::Cast<LookupCacheV2>(c);
+ if (!lookupCache || lookupCache->IsPrimed()) {
+ continue;
+ }
+
+ aTables.AppendElement(table);
+ }
+
+ return NS_OK;
+}
+
+nsresult Classifier::ScanStoreDir(nsIFile* aDirectory,
+ const nsTArray<nsCString>& aExtensions,
+ nsTArray<nsCString>& aTables) {
+ nsCOMPtr<nsIDirectoryEnumerator> entries;
+ nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> file;
+ while (NS_SUCCEEDED(rv = entries->GetNextFile(getter_AddRefs(file))) &&
+ file) {
+ // If |file| is a directory, recurse to find its entries as well.
+ bool isDirectory;
+ if (NS_FAILED(file->IsDirectory(&isDirectory))) {
+ continue;
+ }
+ if (isDirectory) {
+ ScanStoreDir(file, aExtensions, aTables);
+ continue;
+ }
+
+ nsAutoCString leafName;
+ rv = file->GetNativeLeafName(leafName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (const auto& ext : aExtensions) {
+ if (StringEndsWith(leafName, ext)) {
+ aTables.AppendElement(
+ Substring(leafName, 0, leafName.Length() - strlen(ext.get())));
+ break;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult Classifier::ActiveTables(nsTArray<nsCString>& aTables) const {
+ aTables = mActiveTablesCache.Clone();
+ return NS_OK;
+}
+
+nsresult Classifier::CleanToDelete() {
+ bool exists;
+ nsresult rv = mToDeleteDirectory->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (exists) {
+ rv = mToDeleteDirectory->Remove(true);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+#ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+
+already_AddRefed<nsIFile> Classifier::GetFailedUpdateDirectroy() {
+ nsCString failedUpdatekDirName = STORE_DIRECTORY + nsCString("-failedupdate");
+
+ nsCOMPtr<nsIFile> failedUpdatekDirectory;
+ if (NS_FAILED(
+ mCacheDirectory->Clone(getter_AddRefs(failedUpdatekDirectory))) ||
+ NS_FAILED(failedUpdatekDirectory->AppendNative(failedUpdatekDirName))) {
+ LOG(("Failed to init failedUpdatekDirectory."));
+ return nullptr;
+ }
+
+ return failedUpdatekDirectory.forget();
+}
+
+nsresult Classifier::DumpRawTableUpdates(const nsACString& aRawUpdates) {
+ LOG(("Dumping raw table updates..."));
+
+ DumpFailedUpdate();
+
+ nsCOMPtr<nsIFile> failedUpdatekDirectory = GetFailedUpdateDirectroy();
+
+ // Create tableupdate.bin and dump raw table update data.
+ nsCOMPtr<nsIFile> rawTableUpdatesFile;
+ nsCOMPtr<nsIOutputStream> outputStream;
+ if (NS_FAILED(
+ failedUpdatekDirectory->Clone(getter_AddRefs(rawTableUpdatesFile))) ||
+ NS_FAILED(
+ rawTableUpdatesFile->AppendNative(nsCString("tableupdates.bin"))) ||
+ NS_FAILED(NS_NewLocalFileOutputStream(
+ getter_AddRefs(outputStream), rawTableUpdatesFile,
+ PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE))) {
+ LOG(("Failed to create file to dump raw table updates."));
+ return NS_ERROR_FAILURE;
+ }
+
+ // Write out the data.
+ uint32_t written;
+ nsresult rv = outputStream->Write(aRawUpdates.BeginReading(),
+ aRawUpdates.Length(), &written);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(written == aRawUpdates.Length(), NS_ERROR_FAILURE);
+
+ return rv;
+}
+
+nsresult Classifier::DumpFailedUpdate() {
+ LOG(("Dumping failed update..."));
+
+ nsCOMPtr<nsIFile> failedUpdatekDirectory = GetFailedUpdateDirectroy();
+
+ // Remove the "failed update" directory no matter it exists or not.
+ // Failure is fine because the directory may not exist.
+ failedUpdatekDirectory->Remove(true);
+
+ nsCString failedUpdatekDirName;
+ nsresult rv = failedUpdatekDirectory->GetNativeLeafName(failedUpdatekDirName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Copy the in-use directory to a clean "failed update" directory.
+ nsCOMPtr<nsIFile> inUseDirectory;
+ if (NS_FAILED(mRootStoreDirectory->Clone(getter_AddRefs(inUseDirectory))) ||
+ NS_FAILED(inUseDirectory->CopyToNative(nullptr, failedUpdatekDirName))) {
+ LOG(("Failed to move in-use to the \"failed update\" directory %s",
+ failedUpdatekDirName.get()));
+ return NS_ERROR_FAILURE;
+ }
+
+ return rv;
+}
+
+#endif // MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+
+/**
+ * This function copies the files one by one to the destination folder.
+ * Before copying a file, it checks ::ShouldAbort and returns
+ * NS_ERROR_ABORT if the flag is set.
+ */
+nsresult Classifier::CopyDirectoryInterruptible(nsCOMPtr<nsIFile>& aDestDir,
+ nsCOMPtr<nsIFile>& aSourceDir) {
+ nsCOMPtr<nsIDirectoryEnumerator> entries;
+ nsresult rv = aSourceDir->GetDirectoryEntries(getter_AddRefs(entries));
+ NS_ENSURE_SUCCESS(rv, rv);
+ MOZ_ASSERT(entries);
+
+ nsCOMPtr<nsIFile> source;
+ while (NS_SUCCEEDED(rv = entries->GetNextFile(getter_AddRefs(source))) &&
+ source) {
+ if (ShouldAbort()) {
+ LOG(("Update is interrupted. Aborting the directory copy"));
+ return NS_ERROR_ABORT;
+ }
+
+ bool isDirectory;
+ rv = source->IsDirectory(&isDirectory);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (isDirectory) {
+ // If it is a directory, recursively copy the files inside the directory.
+ nsAutoCString leaf;
+ source->GetNativeLeafName(leaf);
+ MOZ_ASSERT(!leaf.IsEmpty());
+
+ nsCOMPtr<nsIFile> dest;
+ aDestDir->Clone(getter_AddRefs(dest));
+ dest->AppendNative(leaf);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = CopyDirectoryInterruptible(dest, source);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ rv = source->CopyToNative(aDestDir, ""_ns);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ // If the destination directory doesn't exist in the end, it means that the
+ // source directory is empty, we should copy the directory here.
+ bool exist;
+ rv = aDestDir->Exists(&exist);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!exist) {
+ rv = aDestDir->Create(nsIFile::DIRECTORY_TYPE, 0755);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+nsresult Classifier::CopyInUseDirForUpdate() {
+ LOG(("Copy in-use directory content for update."));
+ if (ShouldAbort()) {
+ return NS_ERROR_UC_UPDATE_SHUTDOWNING;
+ }
+
+ // We copy everything from in-use directory to a temporary directory
+ // for updating.
+
+ // Remove the destination directory first (just in case) the do the copy.
+ mUpdatingDirectory->Remove(true);
+ if (!mRootStoreDirectoryForUpdate) {
+ LOG(("mRootStoreDirectoryForUpdate is null."));
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ nsresult rv = CopyDirectoryInterruptible(mUpdatingDirectory,
+ mRootStoreDirectoryForUpdate);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult Classifier::RecoverBackups() {
+ bool backupExists;
+ nsresult rv = mBackupDirectory->Exists(&backupExists);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (backupExists) {
+ // Remove the safebrowsing dir if it exists
+ nsCString storeDirName;
+ rv = mRootStoreDirectory->GetNativeLeafName(storeDirName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool storeExists;
+ rv = mRootStoreDirectory->Exists(&storeExists);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (storeExists) {
+ rv = mRootStoreDirectory->Remove(true);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Move the backup to the store location
+ rv = mBackupDirectory->MoveToNative(nullptr, storeDirName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // mBackupDirectory now points to storeDir, fix up.
+ rv = SetupPathNames();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+bool Classifier::CheckValidUpdate(TableUpdateArray& aUpdates,
+ const nsACString& aTable) {
+ // take the quick exit if there is no valid update for us
+ // (common case)
+ uint32_t validupdates = 0;
+
+ for (uint32_t i = 0; i < aUpdates.Length(); i++) {
+ RefPtr<const TableUpdate> update = aUpdates[i];
+ if (!update || !update->TableName().Equals(aTable)) {
+ continue;
+ }
+ if (update->Empty()) {
+ aUpdates[i] = nullptr;
+ continue;
+ }
+ validupdates++;
+ }
+
+ if (!validupdates) {
+ // This can happen if the update was only valid for one table.
+ return false;
+ }
+
+ return true;
+}
+
+nsCString Classifier::GetProvider(const nsACString& aTableName) {
+ nsUrlClassifierUtils* urlUtil = nsUrlClassifierUtils::GetInstance();
+ if (NS_WARN_IF(!urlUtil)) {
+ return ""_ns;
+ }
+
+ nsCString provider;
+ nsresult rv = urlUtil->GetProvider(aTableName, provider);
+
+ return NS_SUCCEEDED(rv) ? provider : ""_ns;
+}
+
+/*
+ * This will consume+delete updates from the passed nsTArray.
+ */
+nsresult Classifier::UpdateHashStore(TableUpdateArray& aUpdates,
+ const nsACString& aTable) {
+ if (ShouldAbort()) {
+ return NS_ERROR_UC_UPDATE_SHUTDOWNING;
+ }
+
+ LOG(("Classifier::UpdateHashStore(%s)", PromiseFlatCString(aTable).get()));
+
+ // moztest- tables don't support update because they are directly created
+ // in LookupCache. To test updates, use tables begin with "test-" instead.
+ // Also, recommend using 'test-' tables while writing testcases because
+ // it is more like the real world scenario.
+ MOZ_ASSERT(!nsUrlClassifierUtils::IsMozTestTable(aTable));
+
+ HashStore store(aTable, GetProvider(aTable), mUpdatingDirectory);
+
+ if (!CheckValidUpdate(aUpdates, store.TableName())) {
+ return NS_OK;
+ }
+
+ nsresult rv = store.Open();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = store.BeginUpdate();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Read the part of the store that is (only) in the cache
+ RefPtr<LookupCacheV2> lookupCacheV2;
+ {
+ RefPtr<LookupCache> lookupCache =
+ GetLookupCacheForUpdate(store.TableName());
+ if (lookupCache) {
+ lookupCacheV2 = LookupCache::Cast<LookupCacheV2>(lookupCache);
+ }
+ }
+ if (!lookupCacheV2) {
+ return NS_ERROR_UC_UPDATE_TABLE_NOT_FOUND;
+ }
+
+ FallibleTArray<uint32_t> AddPrefixHashes;
+ FallibleTArray<nsCString> AddCompletesHashes;
+ rv = lookupCacheV2->GetPrefixes(AddPrefixHashes, AddCompletesHashes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = store.AugmentAdds(AddPrefixHashes, AddCompletesHashes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ AddPrefixHashes.Clear();
+ AddCompletesHashes.Clear();
+
+ uint32_t applied = 0;
+
+ for (uint32_t i = 0; i < aUpdates.Length(); i++) {
+ RefPtr<TableUpdate> update = aUpdates[i];
+ if (!update || !update->TableName().Equals(store.TableName())) {
+ continue;
+ }
+
+ RefPtr<TableUpdateV2> updateV2 = TableUpdate::Cast<TableUpdateV2>(update);
+ NS_ENSURE_TRUE(updateV2, NS_ERROR_UC_UPDATE_UNEXPECTED_VERSION);
+
+ rv = store.ApplyUpdate(updateV2);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ applied++;
+
+ LOG(("Applied update to table %s:", store.TableName().get()));
+ LOG((" %d add chunks", updateV2->AddChunks().Length()));
+ LOG((" %zu add prefixes", updateV2->AddPrefixes().Length()));
+ LOG((" %zu add completions", updateV2->AddCompletes().Length()));
+ LOG((" %d sub chunks", updateV2->SubChunks().Length()));
+ LOG((" %zu sub prefixes", updateV2->SubPrefixes().Length()));
+ LOG((" %zu sub completions", updateV2->SubCompletes().Length()));
+ LOG((" %d add expirations", updateV2->AddExpirations().Length()));
+ LOG((" %d sub expirations", updateV2->SubExpirations().Length()));
+
+ aUpdates[i] = nullptr;
+ }
+
+ LOG(("Applied %d update(s) to %s.", applied, store.TableName().get()));
+
+ rv = store.Rebuild();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ LOG(("Table %s now has:", store.TableName().get()));
+ LOG((" %d add chunks", store.AddChunks().Length()));
+ LOG((" %zu add prefixes", store.AddPrefixes().Length()));
+ LOG((" %zu add completions", store.AddCompletes().Length()));
+ LOG((" %d sub chunks", store.SubChunks().Length()));
+ LOG((" %zu sub prefixes", store.SubPrefixes().Length()));
+ LOG((" %zu sub completions", store.SubCompletes().Length()));
+
+ rv = store.WriteFile();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // At this point the store is updated and written out to disk, but
+ // the data is still in memory. Build our quick-lookup table here.
+ rv = lookupCacheV2->Build(store.AddPrefixes(), store.AddCompletes());
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_UC_UPDATE_BUILD_PREFIX_FAILURE);
+
+ rv = lookupCacheV2->WriteFile();
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_UC_UPDATE_FAIL_TO_WRITE_DISK);
+
+ LOG(("Successfully updated %s", store.TableName().get()));
+
+ return NS_OK;
+}
+
+nsresult Classifier::UpdateTableV4(TableUpdateArray& aUpdates,
+ const nsACString& aTable) {
+ MOZ_ASSERT(!NS_IsMainThread(),
+ "UpdateTableV4 must be called on the classifier worker thread.");
+ if (ShouldAbort()) {
+ return NS_ERROR_UC_UPDATE_SHUTDOWNING;
+ }
+
+ // moztest- tables don't support update, see comment in UpdateHashStore.
+ MOZ_ASSERT(!nsUrlClassifierUtils::IsMozTestTable(aTable));
+
+ LOG(("Classifier::UpdateTableV4(%s)", PromiseFlatCString(aTable).get()));
+
+ if (!CheckValidUpdate(aUpdates, aTable)) {
+ return NS_OK;
+ }
+
+ RefPtr<LookupCacheV4> lookupCacheV4;
+ {
+ RefPtr<LookupCache> lookupCache = GetLookupCacheForUpdate(aTable);
+ if (lookupCache) {
+ lookupCacheV4 = LookupCache::Cast<LookupCacheV4>(lookupCache);
+ }
+ }
+ if (!lookupCacheV4) {
+ return NS_ERROR_UC_UPDATE_TABLE_NOT_FOUND;
+ }
+
+ nsresult rv = NS_OK;
+
+ // If there are multiple updates for the same table, prefixes1 & prefixes2
+ // will act as input and output in turn to reduce memory copy overhead.
+ PrefixStringMap prefixes1, prefixes2;
+ PrefixStringMap* input = &prefixes1;
+ PrefixStringMap* output = &prefixes2;
+
+ RefPtr<const TableUpdateV4> lastAppliedUpdate = nullptr;
+ for (uint32_t i = 0; i < aUpdates.Length(); i++) {
+ RefPtr<TableUpdate> update = aUpdates[i];
+ if (!update || !update->TableName().Equals(aTable)) {
+ continue;
+ }
+
+ RefPtr<TableUpdateV4> updateV4 = TableUpdate::Cast<TableUpdateV4>(update);
+ NS_ENSURE_TRUE(updateV4, NS_ERROR_UC_UPDATE_UNEXPECTED_VERSION);
+
+ if (updateV4->IsFullUpdate()) {
+ input->Clear();
+ output->Clear();
+ rv = lookupCacheV4->ApplyUpdate(updateV4, *input, *output);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ } else {
+ // If both prefix sets are empty, this means we are doing a partial update
+ // without a prior full/partial update in the loop. In this case we should
+ // get prefixes from the lookup cache first.
+ if (prefixes1.IsEmpty() && prefixes2.IsEmpty()) {
+ lookupCacheV4->GetPrefixes(prefixes1);
+ } else {
+ MOZ_ASSERT(prefixes1.IsEmpty() ^ prefixes2.IsEmpty());
+
+ // When there are multiple partial updates, input should always point
+ // to the non-empty prefix set(filled by previous full/partial update).
+ // output should always point to the empty prefix set.
+ input = prefixes1.IsEmpty() ? &prefixes2 : &prefixes1;
+ output = prefixes1.IsEmpty() ? &prefixes1 : &prefixes2;
+ }
+
+ rv = lookupCacheV4->ApplyUpdate(updateV4, *input, *output);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ input->Clear();
+ }
+
+ // Keep track of the last applied update.
+ lastAppliedUpdate = updateV4;
+
+ aUpdates[i] = nullptr;
+ }
+
+ rv = lookupCacheV4->Build(*output);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_UC_UPDATE_BUILD_PREFIX_FAILURE);
+
+ rv = lookupCacheV4->WriteFile();
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_UC_UPDATE_FAIL_TO_WRITE_DISK);
+
+ if (lastAppliedUpdate) {
+ LOG(("Write meta data of the last applied update."));
+ rv = lookupCacheV4->WriteMetadata(lastAppliedUpdate);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_UC_UPDATE_FAIL_TO_WRITE_DISK);
+ }
+
+ LOG(("Successfully updated %s\n", PromiseFlatCString(aTable).get()));
+
+ return NS_OK;
+}
+
+nsresult Classifier::UpdateCache(RefPtr<const TableUpdate> aUpdate) {
+ if (!aUpdate) {
+ return NS_OK;
+ }
+
+ nsAutoCString table(aUpdate->TableName());
+ LOG(("Classifier::UpdateCache(%s)", table.get()));
+
+ RefPtr<LookupCache> lookupCache = GetLookupCache(table);
+ if (!lookupCache) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<LookupCacheV2> lookupV2 =
+ LookupCache::Cast<LookupCacheV2>(lookupCache);
+ if (lookupV2) {
+ RefPtr<const TableUpdateV2> updateV2 =
+ TableUpdate::Cast<TableUpdateV2>(aUpdate);
+ lookupV2->AddGethashResultToCache(updateV2->AddCompletes(),
+ updateV2->MissPrefixes());
+ } else {
+ RefPtr<LookupCacheV4> lookupV4 =
+ LookupCache::Cast<LookupCacheV4>(lookupCache);
+ if (!lookupV4) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<const TableUpdateV4> updateV4 =
+ TableUpdate::Cast<TableUpdateV4>(aUpdate);
+ lookupV4->AddFullHashResponseToCache(updateV4->FullHashResponse());
+ }
+
+#if defined(DEBUG)
+ lookupCache->DumpCache();
+#endif
+
+ return NS_OK;
+}
+
+RefPtr<LookupCache> Classifier::GetLookupCache(const nsACString& aTable,
+ bool aForUpdate) {
+ // GetLookupCache(aForUpdate==true) can only be called on update thread.
+ MOZ_ASSERT_IF(aForUpdate, OnUpdateThread());
+
+ LookupCacheArray& lookupCaches =
+ aForUpdate ? mNewLookupCaches : mLookupCaches;
+ auto& rootStoreDirectory =
+ aForUpdate ? mUpdatingDirectory : mRootStoreDirectory;
+
+ for (auto c : lookupCaches) {
+ if (c->TableName().Equals(aTable)) {
+ return c;
+ }
+ }
+
+ // We don't want to create lookupcache when shutdown is already happening.
+ if (ShouldAbort()) {
+ return nullptr;
+ }
+
+ // TODO : Bug 1302600, It would be better if we have a more general non-main
+ // thread method to convert table name to protocol version. Currently
+ // we can only know this by checking if the table name ends with
+ // '-proto'.
+ RefPtr<LookupCache> cache;
+ nsCString provider = GetProvider(aTable);
+
+ // Google requests SafeBrowsing related feature should only be enabled when
+ // the databases are update-to-date. Since we disable Safe Browsing update in
+ // Safe Mode, ignore tables provided by Google to ensure we don't show
+ // outdated warnings.
+ if (nsUrlClassifierUtils::IsInSafeMode()) {
+ if (provider.EqualsASCII("google") || provider.EqualsASCII("google4")) {
+ return nullptr;
+ }
+ }
+
+ if (StringEndsWith(aTable, "-proto"_ns)) {
+ cache = new LookupCacheV4(aTable, provider, rootStoreDirectory);
+ } else {
+ cache = new LookupCacheV2(aTable, provider, rootStoreDirectory);
+ }
+
+ nsresult rv = cache->Init();
+ if (NS_FAILED(rv)) {
+ return nullptr;
+ }
+ rv = cache->Open();
+ if (NS_SUCCEEDED(rv)) {
+ lookupCaches.AppendElement(cache);
+ return cache;
+ }
+
+ // At this point we failed to open LookupCache.
+ //
+ // GetLookupCache for update and for other usage will run on update thread
+ // and worker thread respectively (Bug 1339760). Removing stuff only in
+ // their own realms potentially increases the concurrency.
+
+ if (aForUpdate) {
+ // Remove intermediaries no matter if it's due to file corruption or not.
+ RemoveUpdateIntermediaries();
+ return nullptr;
+ }
+
+ // Non-update case.
+ if (rv == NS_ERROR_FILE_CORRUPTED) {
+ // Remove all the on-disk data when the table's prefix file is corrupted.
+ LOG(("Failed to get prefixes from file for table %s, delete on-disk data!",
+ aTable.BeginReading()));
+
+ DeleteTables(mRootStoreDirectory, nsTArray<nsCString>{nsCString(aTable)});
+ }
+ return nullptr;
+}
+
+nsresult Classifier::ReadNoiseEntries(const Prefix& aPrefix,
+ const nsACString& aTableName,
+ uint32_t aCount,
+ PrefixArray& aNoiseEntries) {
+ RefPtr<LookupCache> cache = GetLookupCache(aTableName);
+ if (!cache) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<LookupCacheV2> cacheV2 = LookupCache::Cast<LookupCacheV2>(cache);
+ RefPtr<LookupCacheV4> cacheV4 = LookupCache::Cast<LookupCacheV4>(cache);
+ MOZ_ASSERT_IF(cacheV2, !cacheV4);
+
+ if (cache->PrefixLength() == 0) {
+ NS_WARNING("Could not find prefix in PrefixSet during noise lookup");
+ return NS_ERROR_FAILURE;
+ }
+
+ // We do not want to simply pick random prefixes, because this would allow
+ // averaging out the noise by analysing the traffic from Firefox users.
+ // Instead, we ensure the 'noise' is the same for the same prefix by seeding
+ // the random number generator with the prefix. We prefer not to use rand()
+ // which isn't thread safe, and the reseeding of which could trip up other
+ // parts othe code that expect actual random numbers.
+ // Here we use a simple LCG (Linear Congruential Generator) to generate
+ // random numbers. We seed the LCG with the prefix we are generating noise
+ // for.
+ // http://en.wikipedia.org/wiki/Linear_congruential_generator
+
+ uint32_t m = cache->PrefixLength();
+ uint32_t a = aCount % m;
+ uint32_t idx = aPrefix.ToUint32() % m;
+
+ for (size_t i = 0; i < aCount; i++) {
+ idx = (a * idx + a) % m;
+
+ uint32_t hash;
+
+ nsresult rv;
+ if (cacheV2) {
+ rv = cacheV2->GetPrefixByIndex(idx, &hash);
+ } else {
+ // We don't add noises for variable length prefix because of simplicity,
+ // so we will only get fixed length prefix (4 bytes).
+ rv = cacheV4->GetFixedLengthPrefixByIndex(idx, &hash);
+ }
+
+ if (NS_FAILED(rv)) {
+ NS_WARNING(
+ "Could not find the target prefix in PrefixSet during noise lookup");
+ return NS_ERROR_FAILURE;
+ }
+
+ Prefix newPrefix;
+ // In the case V4 little endian, we did swapping endian when converting from
+ // char* to int, should revert endian to make sure we will send hex string
+ // correctly See https://bugzilla.mozilla.org/show_bug.cgi?id=1283007#c23
+ if (!cacheV2 && !bool(MOZ_BIG_ENDIAN())) {
+ hash = NativeEndian::swapFromBigEndian(hash);
+ }
+
+ newPrefix.FromUint32(hash);
+ if (newPrefix != aPrefix) {
+ aNoiseEntries.AppendElement(newPrefix);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult Classifier::LoadHashStore(nsIFile* aDirectory, nsACString& aResult,
+ nsTArray<nsCString>& aFailedTableNames) {
+ nsTArray<nsCString> tables;
+ nsTArray<nsCString> exts = {V2_METADATA_SUFFIX};
+
+ nsresult rv = ScanStoreDir(mRootStoreDirectory, exts, tables);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ for (const auto& table : tables) {
+ HashStore store(table, GetProvider(table), mRootStoreDirectory);
+
+ nsresult rv = store.Open();
+ if (NS_FAILED(rv) || !GetLookupCache(table)) {
+ // TableRequest is called right before applying an update.
+ // If we cannot retrieve metadata for a given table or we fail to
+ // load the prefixes for a table, reset the table to esnure we
+ // apply a full update to the table.
+ LOG(("Failed to get metadata for v2 table %s", table.get()));
+ aFailedTableNames.AppendElement(table);
+ continue;
+ }
+
+ ChunkSet& adds = store.AddChunks();
+ ChunkSet& subs = store.SubChunks();
+
+ // Open HashStore will always succeed even that is not a v2 table.
+ // So exception tables without add and sub chunks.
+ if (adds.Length() == 0 && subs.Length() == 0) {
+ continue;
+ }
+
+ aResult.Append(store.TableName());
+ aResult.Append(';');
+
+ if (adds.Length() > 0) {
+ aResult.AppendLiteral("a:");
+ nsAutoCString addList;
+ adds.Serialize(addList);
+ aResult.Append(addList);
+ }
+
+ if (subs.Length() > 0) {
+ if (adds.Length() > 0) {
+ aResult.Append(':');
+ }
+ aResult.AppendLiteral("s:");
+ nsAutoCString subList;
+ subs.Serialize(subList);
+ aResult.Append(subList);
+ }
+
+ aResult.Append('\n');
+ }
+
+ return rv;
+}
+
+nsresult Classifier::LoadMetadata(nsIFile* aDirectory, nsACString& aResult,
+ nsTArray<nsCString>& aFailedTableNames) {
+ nsTArray<nsCString> tables;
+ nsTArray<nsCString> exts = {V4_METADATA_SUFFIX};
+
+ nsresult rv = ScanStoreDir(mRootStoreDirectory, exts, tables);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ for (const auto& table : tables) {
+ RefPtr<LookupCache> c = GetLookupCache(table);
+ RefPtr<LookupCacheV4> lookupCacheV4 = LookupCache::Cast<LookupCacheV4>(c);
+
+ if (!lookupCacheV4) {
+ aFailedTableNames.AppendElement(table);
+ continue;
+ }
+
+ nsCString state, sha256;
+ rv = lookupCacheV4->LoadMetadata(state, sha256);
+ Telemetry::Accumulate(Telemetry::URLCLASSIFIER_VLPS_METADATA_CORRUPT,
+ rv == NS_ERROR_FILE_CORRUPTED);
+ if (NS_FAILED(rv)) {
+ LOG(("Failed to get metadata for v4 table %s", table.get()));
+ aFailedTableNames.AppendElement(table);
+ continue;
+ }
+
+ // The state might include '\n' so that we have to encode.
+ nsAutoCString stateBase64;
+ rv = Base64Encode(state, stateBase64);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsAutoCString checksumBase64;
+ rv = Base64Encode(sha256, checksumBase64);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ LOG(("Appending state '%s' and checksum '%s' for table %s",
+ stateBase64.get(), checksumBase64.get(), table.get()));
+
+ aResult.AppendPrintf("%s;%s:%s\n", table.get(), stateBase64.get(),
+ checksumBase64.get());
+ }
+
+ return rv;
+}
+
+bool Classifier::ShouldAbort() const {
+ return mIsClosed || nsUrlClassifierDBService::ShutdownHasStarted() ||
+ (mUpdateInterrupted && mUpdateThread->IsOnCurrentThread());
+}
+
+} // namespace safebrowsing
+} // namespace mozilla
diff --git a/toolkit/components/url-classifier/Classifier.h b/toolkit/components/url-classifier/Classifier.h
new file mode 100644
index 0000000000..a9af2736d2
--- /dev/null
+++ b/toolkit/components/url-classifier/Classifier.h
@@ -0,0 +1,258 @@
+//* -*- 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/. */
+
+#ifndef Classifier_h__
+#define Classifier_h__
+
+#include "Entries.h"
+#include "HashStore.h"
+#include "ProtocolParser.h"
+#include "LookupCache.h"
+#include "mozilla/Atomics.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsIFile.h"
+
+namespace mozilla {
+
+class LazyIdleThread;
+
+namespace safebrowsing {
+
+/**
+ * Maintains the stores and LookupCaches for the url classifier.
+ */
+class Classifier {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Classifier);
+
+ Classifier();
+
+ nsresult Open(nsIFile& aCacheDirectory);
+ void Close();
+ void Reset(); // Not including any intermediary for update.
+
+ /**
+ * Clear data for specific tables.
+ * If ClearType is Clear_Cache, this function will only clear cache in lookup
+ * cache, otherwise, it will clear data in lookup cache and data stored on
+ * disk.
+ */
+ enum ClearType {
+ Clear_Cache,
+ Clear_All,
+ };
+ void ResetTables(ClearType aType, const nsTArray<nsCString>& aTables);
+
+ /**
+ * Get the list of active tables and their chunks in a format
+ * suitable for an update request.
+ */
+ void TableRequest(nsACString& aResult);
+
+ /*
+ * Get all tables that we know about.
+ */
+ nsresult ActiveTables(nsTArray<nsCString>& aTables) const;
+
+ /**
+ * Check URL fragments against a specified table.
+ * The fragments should be generated by |LookupCache::GetLookupFragments|
+ */
+ nsresult CheckURIFragments(const nsTArray<nsCString>& aSpecFragments,
+ const nsACString& table,
+ LookupResultArray& aResults);
+
+ /**
+ * Asynchronously apply updates to the in-use databases. When the
+ * update is complete, the caller can be notified by |aCallback|, which
+ * will occur on the caller thread.
+ */
+ using AsyncUpdateCallback = std::function<void(nsresult)>;
+ nsresult AsyncApplyUpdates(const TableUpdateArray& aUpdates,
+ const AsyncUpdateCallback& aCallback);
+
+ /**
+ * Wait until the ongoing async update is finished and callback
+ * is fired. Once this function returns, AsyncApplyUpdates is
+ * no longer available.
+ */
+ void FlushAndDisableAsyncUpdate();
+
+ /**
+ * Apply full hashes retrived from gethash to cache.
+ */
+ nsresult ApplyFullHashes(ConstTableUpdateArray& aUpdates);
+
+ /*
+ * Get a bunch of extra prefixes to query for completion
+ * and mask the real entry being requested
+ */
+ nsresult ReadNoiseEntries(const Prefix& aPrefix, const nsACString& aTableName,
+ uint32_t aCount, PrefixArray& aNoiseEntries);
+
+#ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+ nsresult DumpRawTableUpdates(const nsACString& aRawUpdates);
+#endif
+
+ static void SplitTables(const nsACString& str, nsTArray<nsCString>& tables);
+
+ // Given a root store directory, return a private store directory
+ // based on the table name. To avoid migration issue, the private
+ // store directory is only different from root directory for V4 tables.
+ //
+ // For V4 tables (suffixed by '-proto'), the private directory would
+ // be [root directory path]/[provider]. The provider of V4 tables is
+ // 'google4'.
+ //
+ // Note that if the table name is not owned by any provider, just use
+ // the root directory.
+ static nsresult GetPrivateStoreDirectory(nsIFile* aRootStoreDirectory,
+ const nsACString& aTableName,
+ const nsACString& aProvider,
+ nsIFile** aPrivateStoreDirectory);
+
+ // Swap in in-memory and on-disk database and remove all
+ // update intermediaries.
+ nsresult SwapInNewTablesAndCleanup();
+
+ RefPtr<LookupCache> GetLookupCache(const nsACString& aTable,
+ bool aForUpdate = false);
+
+ void GetCacheInfo(const nsACString& aTable,
+ nsIUrlClassifierCacheInfo** aCache);
+
+ bool OnUpdateThread() const;
+
+ private:
+ ~Classifier();
+
+ void DropStores();
+ void DeleteTables(nsIFile* aDirectory, const nsTArray<nsCString>& aTables);
+
+ nsresult CreateStoreDirectory();
+ nsresult SetupPathNames();
+ nsresult RecoverBackups();
+ nsresult CleanToDelete();
+ nsresult CopyInUseDirForUpdate();
+ nsresult CopyDirectoryInterruptible(nsCOMPtr<nsIFile>& aDestDir,
+ nsCOMPtr<nsIFile>& aSourceDir);
+ nsresult RegenActiveTables();
+
+ void MergeNewLookupCaches(); // Merge mNewLookupCaches into mLookupCaches.
+
+ void CopyAndInvalidateFullHashCache();
+
+ // Remove any intermediary for update, including in-memory
+ // and on-disk data.
+ void RemoveUpdateIntermediaries();
+
+#ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+ already_AddRefed<nsIFile> GetFailedUpdateDirectroy();
+ nsresult DumpFailedUpdate();
+#endif
+
+ nsresult ScanStoreDir(nsIFile* aDirectory,
+ const nsTArray<nsCString>& aExtensions,
+ nsTArray<nsCString>& aTables);
+
+ nsresult UpdateHashStore(TableUpdateArray& aUpdates,
+ const nsACString& aTable);
+
+ nsresult UpdateTableV4(TableUpdateArray& aUpdates, const nsACString& aTable);
+
+ nsresult UpdateCache(RefPtr<const TableUpdate> aUpdates);
+
+ RefPtr<LookupCache> GetLookupCacheForUpdate(const nsACString& aTable) {
+ return GetLookupCache(aTable, true);
+ }
+
+ RefPtr<LookupCache> GetLookupCacheFrom(const nsACString& aTable,
+ LookupCacheArray& aLookupCaches,
+ nsIFile* aRootStoreDirectory);
+
+ bool CheckValidUpdate(TableUpdateArray& aUpdates, const nsACString& aTable);
+
+ nsresult LoadHashStore(nsIFile* aDirectory, nsACString& aResult,
+ nsTArray<nsCString>& aFailedTableNames);
+
+ nsresult LoadMetadata(nsIFile* aDirectory, nsACString& aResult,
+ nsTArray<nsCString>& aFailedTableNames);
+
+ static nsCString GetProvider(const nsACString& aTableName);
+
+ /**
+ * The "background" part of ApplyUpdates. Once the background update
+ * is called, the foreground update has to be called along with the
+ * background result no matter whether the background update is
+ * successful or not.
+ */
+ nsresult ApplyUpdatesBackground(TableUpdateArray& aUpdates,
+ nsTArray<nsCString>& aFailedTableNames);
+
+ /**
+ * The "foreground" part of ApplyUpdates. The in-use data (in-memory and
+ * on-disk) will be touched so this MUST be mutually exclusive to other
+ * member functions.
+ *
+ * If |aBackgroundRv| is successful, the return value is the result of
+ * bringing stuff to the foreground. Otherwise, the foreground table may
+ * be reset according to the background update failed reason and
+ * |aBackgroundRv| will be returned to forward the background update result.
+ */
+ nsresult ApplyUpdatesForeground(nsresult aBackgroundRv,
+ const nsTArray<nsCString>& aFailedTableNames);
+
+ // Used by worker thread and update thread to abort current operation.
+ bool ShouldAbort() const;
+
+ // Add built-in entries for testing.
+ nsresult AddMozEntries(nsTArray<nsCString>& aTables);
+
+ // Remove test files if exist
+ nsresult ClearLegacyFiles();
+
+ // Root dir of the Local profile.
+ nsCOMPtr<nsIFile> mCacheDirectory;
+ // Main directory where to store the databases.
+ nsCOMPtr<nsIFile> mRootStoreDirectory;
+ // Used for atomically updating the other dirs.
+ nsCOMPtr<nsIFile> mBackupDirectory;
+ nsCOMPtr<nsIFile> mUpdatingDirectory; // For update only.
+ nsCOMPtr<nsIFile> mToDeleteDirectory;
+ LookupCacheArray mLookupCaches; // For query only.
+ nsTArray<nsCString> mActiveTablesCache;
+ uint32_t mHashKey;
+
+ // In-memory cache for the result of TableRequest. See
+ // nsIUrlClassifierDBService.getTables for the format.
+ nsCString mTableRequestResult;
+
+ // Whether mTableRequestResult is outdated and needs to
+ // be reloaded from disk.
+ bool mIsTableRequestResultOutdated;
+
+ // The copy of mLookupCaches for update only.
+ LookupCacheArray mNewLookupCaches;
+
+ // True when Reset() is called.
+ bool mUpdateInterrupted;
+
+ // True once CLose() has been called
+ Atomic<bool> mIsClosed;
+
+ RefPtr<LazyIdleThread> mUpdateThread; // For async update.
+
+ // Identical to mRootStoreDirectory but for update only because
+ // nsIFile is not thread safe and mRootStoreDirectory needs to
+ // be accessed in CopyInUseDirForUpdate().
+ // It will be initialized right before update on the worker thread.
+ nsCOMPtr<nsIFile> mRootStoreDirectoryForUpdate;
+};
+
+} // namespace safebrowsing
+} // namespace mozilla
+
+#endif
diff --git a/toolkit/components/url-classifier/Entries.h b/toolkit/components/url-classifier/Entries.h
new file mode 100644
index 0000000000..713867c2b7
--- /dev/null
+++ b/toolkit/components/url-classifier/Entries.h
@@ -0,0 +1,347 @@
+//* -*- 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/. */
+
+// This header file defines the storage types of the actual safebrowsing
+// chunk data, which may be either 32-bit hashes or complete 256-bit hashes.
+// Chunk numbers are represented in ChunkSet.h.
+
+#ifndef SBEntries_h__
+#define SBEntries_h__
+
+#include "nsTArray.h"
+#include "nsString.h"
+#include "nsICryptoHash.h"
+#include "nsNetUtil.h"
+#include "nsIOutputStream.h"
+#include "nsClassHashtable.h"
+#include "nsComponentManagerUtils.h"
+#include "nsTHashMap.h"
+#include "plbase64.h"
+
+namespace mozilla {
+namespace safebrowsing {
+
+#define PREFIX_SIZE 4
+#define COMPLETE_SIZE 32
+
+// This is the struct that contains 4-byte hash prefixes.
+template <uint32_t S, class Comparator>
+struct SafebrowsingHash {
+ static_assert(S >= 4, "The SafebrowsingHash should be at least 4 bytes.");
+
+ static const uint32_t sHashSize = S;
+ typedef SafebrowsingHash<S, Comparator> self_type;
+ uint8_t buf[S];
+
+ nsresult FromPlaintext(const nsACString& aPlainText) {
+ // From the protocol doc:
+ // Each entry in the chunk is composed
+ // of the SHA 256 hash of a suffix/prefix expression.
+ nsresult rv;
+ nsCOMPtr<nsICryptoHash> hash =
+ do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = hash->Init(nsICryptoHash::SHA256);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = hash->Update(
+ reinterpret_cast<const uint8_t*>(aPlainText.BeginReading()),
+ aPlainText.Length());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString hashed;
+ rv = hash->Finish(false, hashed);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ASSERTION(hashed.Length() >= sHashSize,
+ "not enough characters in the hash");
+
+ memcpy(buf, hashed.BeginReading(), sHashSize);
+
+ return NS_OK;
+ }
+
+ void Assign(const nsACString& aStr) {
+ NS_ASSERTION(aStr.Length() >= sHashSize,
+ "string must be at least sHashSize characters long");
+ memcpy(buf, aStr.BeginReading(), sHashSize);
+ }
+
+ int Compare(const self_type& aOther) const {
+ return Comparator::Compare(buf, aOther.buf);
+ }
+
+ bool operator==(const self_type& aOther) const {
+ return Comparator::Compare(buf, aOther.buf) == 0;
+ }
+
+ bool operator!=(const self_type& aOther) const {
+ return Comparator::Compare(buf, aOther.buf) != 0;
+ }
+
+ bool operator<(const self_type& aOther) const {
+ return Comparator::Compare(buf, aOther.buf) < 0;
+ }
+
+ void ToString(nsACString& aStr) const {
+ // Base64 represents 6-bits data as 8-bits output.
+ uint32_t len = ((sHashSize + 2) / 3) * 4;
+
+ aStr.SetLength(len);
+ PL_Base64Encode((char*)buf, sHashSize, aStr.BeginWriting());
+ MOZ_ASSERT(aStr.BeginReading()[len] == '\0');
+ }
+
+ nsCString ToString() const {
+ nsAutoCString str;
+ ToString(str);
+ return std::move(str);
+ }
+
+ void ToHexString(nsACString& aStr) const {
+ static const char* const lut = "0123456789ABCDEF";
+ // 32 bytes is the longest hash
+ size_t len = 32;
+
+ aStr.SetCapacity(2 * len);
+ for (size_t i = 0; i < len; ++i) {
+ const char c = static_cast<char>(buf[i]);
+ aStr.Append(lut[(c >> 4) & 0x0F]);
+ aStr.Append(lut[c & 15]);
+ }
+ }
+
+ uint32_t ToUint32() const {
+ uint32_t n;
+ memcpy(&n, buf, sizeof(n));
+ return n;
+ }
+ void FromUint32(uint32_t aHash) { memcpy(buf, &aHash, sizeof(aHash)); }
+};
+
+class PrefixComparator {
+ public:
+ static int Compare(const uint8_t* a, const uint8_t* b) {
+ uint32_t first;
+ memcpy(&first, a, sizeof(uint32_t));
+
+ uint32_t second;
+ memcpy(&second, b, sizeof(uint32_t));
+
+ if (first > second) {
+ return 1;
+ } else if (first == second) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+};
+// Use this for 4-byte hashes
+typedef SafebrowsingHash<PREFIX_SIZE, PrefixComparator> Prefix;
+typedef nsTArray<Prefix> PrefixArray;
+
+class CompletionComparator {
+ public:
+ static int Compare(const uint8_t* a, const uint8_t* b) {
+ return memcmp(a, b, COMPLETE_SIZE);
+ }
+};
+// Use this for 32-byte hashes
+typedef SafebrowsingHash<COMPLETE_SIZE, CompletionComparator> Completion;
+typedef nsTArray<Completion> CompletionArray;
+
+struct AddPrefix {
+ // The truncated hash.
+ Prefix prefix;
+ // The chunk number to which it belongs.
+ uint32_t addChunk;
+
+ AddPrefix() : addChunk(0) {}
+
+ // Returns the chunk number.
+ uint32_t Chunk() const { return addChunk; }
+ const Prefix& PrefixHash() const { return prefix; }
+
+ template <class T>
+ int Compare(const T& other) const {
+ int cmp = prefix.Compare(other.PrefixHash());
+ if (cmp != 0) {
+ return cmp;
+ }
+ return addChunk - other.addChunk;
+ }
+};
+
+struct AddComplete {
+ Completion complete;
+ uint32_t addChunk;
+
+ AddComplete() : addChunk(0) {}
+
+ uint32_t Chunk() const { return addChunk; }
+ // The 4-byte prefix of the sha256 hash.
+ uint32_t ToUint32() const { return complete.ToUint32(); }
+ // The 32-byte sha256 hash.
+ const Completion& CompleteHash() const { return complete; }
+
+ template <class T>
+ int Compare(const T& other) const {
+ int cmp = complete.Compare(other.CompleteHash());
+ if (cmp != 0) {
+ return cmp;
+ }
+ return addChunk - other.addChunk;
+ }
+
+ bool operator!=(const AddComplete& aOther) const {
+ if (addChunk != aOther.addChunk) {
+ return true;
+ }
+ return complete != aOther.complete;
+ }
+};
+
+struct SubPrefix {
+ // The hash to subtract.
+ Prefix prefix;
+ // The chunk number of the add chunk to which the hash belonged.
+ uint32_t addChunk;
+ // The chunk number of this sub chunk.
+ uint32_t subChunk;
+
+ SubPrefix() : addChunk(0), subChunk(0) {}
+
+ uint32_t Chunk() const { return subChunk; }
+ uint32_t AddChunk() const { return addChunk; }
+ const Prefix& PrefixHash() const { return prefix; }
+
+ template <class T>
+ // Returns 0 if and only if the chunks are the same in every way.
+ int Compare(const T& aOther) const {
+ int cmp = prefix.Compare(aOther.PrefixHash());
+ if (cmp != 0) return cmp;
+ if (addChunk != aOther.addChunk) return addChunk - aOther.addChunk;
+ return subChunk - aOther.subChunk;
+ }
+
+ template <class T>
+ int CompareAlt(const T& aOther) const {
+ Prefix other;
+ other.FromUint32(aOther.ToUint32());
+ int cmp = prefix.Compare(other);
+ if (cmp != 0) return cmp;
+ return addChunk - aOther.addChunk;
+ }
+};
+
+struct SubComplete {
+ Completion complete;
+ uint32_t addChunk;
+ uint32_t subChunk;
+
+ SubComplete() : addChunk(0), subChunk(0) {}
+
+ uint32_t Chunk() const { return subChunk; }
+ uint32_t AddChunk() const { return addChunk; }
+ const Completion& CompleteHash() const { return complete; }
+ // The 4-byte prefix of the sha256 hash.
+ uint32_t ToUint32() const { return complete.ToUint32(); }
+
+ int Compare(const SubComplete& aOther) const {
+ int cmp = complete.Compare(aOther.complete);
+ if (cmp != 0) return cmp;
+ if (addChunk != aOther.addChunk) return addChunk - aOther.addChunk;
+ return subChunk - aOther.subChunk;
+ }
+};
+
+typedef FallibleTArray<AddPrefix> AddPrefixArray;
+typedef FallibleTArray<AddComplete> AddCompleteArray;
+typedef FallibleTArray<SubPrefix> SubPrefixArray;
+typedef FallibleTArray<SubComplete> SubCompleteArray;
+typedef FallibleTArray<Prefix> MissPrefixArray;
+
+/**
+ * Sort an array of store entries.
+ */
+template <class T, class Alloc>
+void EntrySort(nsTArray_Impl<T, Alloc>& aArray) {
+ aArray.Sort([](const T& aA, const T& aB) { return aA.Compare(aB); });
+}
+
+template <class T, class Alloc>
+nsresult ReadTArray(nsIInputStream* aStream, nsTArray_Impl<T, Alloc>* aArray,
+ uint32_t aNumElements) {
+ if (!aArray->SetLength(aNumElements, fallible)) return NS_ERROR_OUT_OF_MEMORY;
+
+ void* buffer = aArray->Elements();
+ nsresult rv =
+ NS_ReadInputStreamToBuffer(aStream, &buffer, (aNumElements * sizeof(T)));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return NS_OK;
+}
+
+template <class T, class Alloc>
+nsresult WriteTArray(nsIOutputStream* aStream,
+ nsTArray_Impl<T, Alloc>& aArray) {
+ uint32_t written;
+ return aStream->Write(reinterpret_cast<char*>(aArray.Elements()),
+ aArray.Length() * sizeof(T), &written);
+}
+
+typedef nsClassHashtable<nsUint32HashKey, nsCString> PrefixStringMap;
+
+typedef nsTHashMap<nsCStringHashKey, int64_t> TableFreshnessMap;
+
+typedef nsCStringHashKey FullHashString;
+
+typedef nsTHashMap<FullHashString, int64_t> FullHashExpiryCache;
+
+struct CachedFullHashResponse {
+ int64_t negativeCacheExpirySec;
+
+ // Map contains all matches found in Fullhash response, this field might be
+ // empty.
+ FullHashExpiryCache fullHashes;
+
+ CachedFullHashResponse& operator=(const CachedFullHashResponse& aOther) {
+ negativeCacheExpirySec = aOther.negativeCacheExpirySec;
+
+ fullHashes = aOther.fullHashes.Clone();
+
+ return *this;
+ }
+
+ bool operator==(const CachedFullHashResponse& aOther) const {
+ if (negativeCacheExpirySec != aOther.negativeCacheExpirySec ||
+ fullHashes.Count() != aOther.fullHashes.Count()) {
+ return false;
+ }
+ for (const auto& entry : fullHashes) {
+ if (entry.GetData() != aOther.fullHashes.Get(entry.GetKey())) {
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+typedef nsClassHashtable<nsUint32HashKey, CachedFullHashResponse>
+ FullHashResponseMap;
+
+template <class T>
+void CopyClassHashTable(const T& aSource, T& aDestination) {
+ for (const auto& entry : aSource) {
+ auto value = aDestination.GetOrInsertNew(entry.GetKey());
+ *value = *(entry.GetData());
+ }
+}
+
+} // namespace safebrowsing
+} // namespace mozilla
+
+#endif // SBEntries_h__
diff --git a/toolkit/components/url-classifier/HashStore.cpp b/toolkit/components/url-classifier/HashStore.cpp
new file mode 100644
index 0000000000..146f8ac104
--- /dev/null
+++ b/toolkit/components/url-classifier/HashStore.cpp
@@ -0,0 +1,1175 @@
+//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// Originally based on Chrome sources:
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "HashStore.h"
+#include "nsICryptoHash.h"
+#include "nsISeekableStream.h"
+#include "nsNetUtil.h"
+#include "nsCheckSummedOutputStream.h"
+#include "prio.h"
+#include "mozilla/Logging.h"
+#include "zlib.h"
+#include "Classifier.h"
+#include "nsUrlClassifierDBService.h"
+#include "mozilla/Telemetry.h"
+
+// Main store for SafeBrowsing protocol data. We store
+// known add/sub chunks, prefixes and completions in memory
+// during an update, and serialize to disk.
+// We do not store the add prefixes, those are retrieved by
+// decompressing the PrefixSet cache whenever we need to apply
+// an update.
+//
+// byte slicing: Many of the 4-byte values stored here are strongly
+// correlated in the upper bytes, and uncorrelated in the lower
+// bytes. Because zlib/DEFLATE requires match lengths of at least
+// 3 to achieve good compression, and we don't get those if only
+// the upper 16-bits are correlated, it is worthwhile to slice 32-bit
+// values into 4 1-byte slices and compress the slices individually.
+// The slices corresponding to MSBs will compress very well, and the
+// slice corresponding to LSB almost nothing. Because of this, we
+// only apply DEFLATE to the 3 most significant bytes, and store the
+// LSB uncompressed.
+//
+// byte sliced (numValues) data format:
+// uint32_t compressed-size
+// compressed-size bytes zlib DEFLATE data
+// 0...numValues byte MSB of 4-byte numValues data
+// uint32_t compressed-size
+// compressed-size bytes zlib DEFLATE data
+// 0...numValues byte 2nd byte of 4-byte numValues data
+// uint32_t compressed-size
+// compressed-size bytes zlib DEFLATE data
+// 0...numValues byte 3rd byte of 4-byte numValues data
+// 0...numValues byte LSB of 4-byte numValues data
+//
+// Store data format:
+// uint32_t magic
+// uint32_t version
+// uint32_t numAddChunks
+// uint32_t numSubChunks
+// uint32_t numAddPrefixes
+// uint32_t numSubPrefixes
+// uint32_t numAddCompletes
+// uint32_t numSubCompletes
+// 0...numAddChunks uint32_t addChunk
+// 0...numSubChunks uint32_t subChunk
+// byte sliced (numAddPrefixes) uint32_t add chunk of AddPrefixes
+// byte sliced (numSubPrefixes) uint32_t add chunk of SubPrefixes
+// byte sliced (numSubPrefixes) uint32_t sub chunk of SubPrefixes
+// byte sliced (numSubPrefixes) uint32_t SubPrefixes
+// byte sliced (numAddCompletes) uint32_t add chunk of AddCompletes
+// 0...numSubCompletes 32-byte Completions + uint32_t addChunk
+// + uint32_t subChunk
+// 16-byte MD5 of all preceding data
+
+// Name of the SafeBrowsing store
+#define STORE_SUFFIX ".sbstore"
+
+// MOZ_LOG=UrlClassifierDbService:5
+extern mozilla::LazyLogModule gUrlClassifierDbServiceLog;
+#define LOG(args) \
+ MOZ_LOG(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() \
+ MOZ_LOG_TEST(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug)
+
+namespace mozilla::safebrowsing {
+
+const uint32_t STORE_MAGIC = 0x1231af3b;
+const uint32_t CURRENT_VERSION = 4;
+
+nsresult TableUpdateV2::NewAddPrefix(uint32_t aAddChunk, const Prefix& aHash) {
+ AddPrefix* add = mAddPrefixes.AppendElement(fallible);
+ if (!add) return NS_ERROR_OUT_OF_MEMORY;
+ add->addChunk = aAddChunk;
+ add->prefix = aHash;
+ return NS_OK;
+}
+
+nsresult TableUpdateV2::NewSubPrefix(uint32_t aAddChunk, const Prefix& aHash,
+ uint32_t aSubChunk) {
+ SubPrefix* sub = mSubPrefixes.AppendElement(fallible);
+ if (!sub) return NS_ERROR_OUT_OF_MEMORY;
+ sub->addChunk = aAddChunk;
+ sub->prefix = aHash;
+ sub->subChunk = aSubChunk;
+ return NS_OK;
+}
+
+nsresult TableUpdateV2::NewAddComplete(uint32_t aAddChunk,
+ const Completion& aHash) {
+ AddComplete* add = mAddCompletes.AppendElement(fallible);
+ if (!add) return NS_ERROR_OUT_OF_MEMORY;
+ add->addChunk = aAddChunk;
+ add->complete = aHash;
+ return NS_OK;
+}
+
+nsresult TableUpdateV2::NewSubComplete(uint32_t aAddChunk,
+ const Completion& aHash,
+ uint32_t aSubChunk) {
+ SubComplete* sub = mSubCompletes.AppendElement(fallible);
+ if (!sub) return NS_ERROR_OUT_OF_MEMORY;
+ sub->addChunk = aAddChunk;
+ sub->complete = aHash;
+ sub->subChunk = aSubChunk;
+ return NS_OK;
+}
+
+nsresult TableUpdateV2::NewMissPrefix(const Prefix& aPrefix) {
+ Prefix* prefix = mMissPrefixes.AppendElement(aPrefix, fallible);
+ if (!prefix) return NS_ERROR_OUT_OF_MEMORY;
+ return NS_OK;
+}
+
+void TableUpdateV4::NewPrefixes(int32_t aSize, const nsACString& aPrefixes) {
+ NS_ENSURE_TRUE_VOID(aSize >= 4 && aSize <= COMPLETE_SIZE);
+ NS_ENSURE_TRUE_VOID(aPrefixes.Length() % aSize == 0);
+ NS_ENSURE_TRUE_VOID(!mPrefixesMap.Contains(aSize));
+
+ int numOfPrefixes = aPrefixes.Length() / aSize;
+
+ if (aSize > 4) {
+ // TODO Bug 1364043 we may have a better API to record multiple samples into
+ // histograms with one call
+#ifdef NIGHTLY_BUILD
+ for (int i = 0; i < std::min(20, numOfPrefixes); i++) {
+ Telemetry::Accumulate(Telemetry::URLCLASSIFIER_VLPS_LONG_PREFIXES, aSize);
+ }
+#endif
+ } else if (LOG_ENABLED()) {
+ const uint32_t* p =
+ reinterpret_cast<const uint32_t*>(ToNewCString(aPrefixes));
+
+ // Dump the first/last 10 fixed-length prefixes for debugging.
+ LOG(("* The first 10 (maximum) fixed-length prefixes: "));
+ for (int i = 0; i < std::min(10, numOfPrefixes); i++) {
+ const uint8_t* c = reinterpret_cast<const uint8_t*>(&p[i]);
+ LOG(("%.2X%.2X%.2X%.2X", c[0], c[1], c[2], c[3]));
+ }
+
+ LOG(("* The last 10 (maximum) fixed-length prefixes: "));
+ for (int i = std::max(0, numOfPrefixes - 10); i < numOfPrefixes; i++) {
+ const uint8_t* c = reinterpret_cast<const uint8_t*>(&p[i]);
+ LOG(("%.2X%.2X%.2X%.2X", c[0], c[1], c[2], c[3]));
+ }
+
+ LOG(("---- %zu fixed-length prefixes in total.",
+ aPrefixes.Length() / aSize));
+ }
+
+ mPrefixesMap.InsertOrUpdate(aSize, MakeUnique<nsCString>(aPrefixes));
+}
+
+nsresult TableUpdateV4::NewRemovalIndices(const uint32_t* aIndices,
+ size_t aNumOfIndices) {
+ MOZ_ASSERT(mRemovalIndiceArray.IsEmpty(),
+ "mRemovalIndiceArray must be empty");
+
+ if (!mRemovalIndiceArray.SetCapacity(aNumOfIndices, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ for (size_t i = 0; i < aNumOfIndices; i++) {
+ mRemovalIndiceArray.AppendElement(aIndices[i]);
+ }
+ return NS_OK;
+}
+
+void TableUpdateV4::SetSHA256(const std::string& aSHA256) {
+ mSHA256.Assign(aSHA256.data(), aSHA256.size());
+}
+
+nsresult TableUpdateV4::NewFullHashResponse(
+ const Prefix& aPrefix, const CachedFullHashResponse& aResponse) {
+ CachedFullHashResponse* response =
+ mFullHashResponseMap.GetOrInsertNew(aPrefix.ToUint32());
+ if (!response) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ *response = aResponse;
+ return NS_OK;
+}
+
+void TableUpdateV4::Clear() {
+ mPrefixesMap.Clear();
+ mRemovalIndiceArray.Clear();
+}
+
+HashStore::HashStore(const nsACString& aTableName, const nsACString& aProvider,
+ nsIFile* aRootStoreDir)
+ : mTableName(aTableName), mInUpdate(false), mFileSize(0) {
+ nsresult rv = Classifier::GetPrivateStoreDirectory(
+ aRootStoreDir, aTableName, aProvider, getter_AddRefs(mStoreDirectory));
+ if (NS_FAILED(rv)) {
+ LOG(("Failed to get private store directory for %s", mTableName.get()));
+ mStoreDirectory = aRootStoreDir;
+ }
+}
+
+HashStore::~HashStore() = default;
+
+nsresult HashStore::Reset() {
+ LOG(("HashStore resetting"));
+
+ // Close InputStream before removing the file
+ if (mInputStream) {
+ nsresult rv = mInputStream->Close();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mInputStream = nullptr;
+ }
+
+ nsCOMPtr<nsIFile> storeFile;
+ nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = storeFile->AppendNative(mTableName + nsLiteralCString(STORE_SUFFIX));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = storeFile->Remove(false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mFileSize = 0;
+
+ return NS_OK;
+}
+
+nsresult HashStore::CheckChecksum(uint32_t aFileSize) {
+ if (!mInputStream) {
+ return NS_OK;
+ }
+
+ // Check for file corruption by
+ // comparing the stored checksum to actual checksum of data
+ nsAutoCString hash;
+ nsAutoCString compareHash;
+ uint32_t read;
+
+ nsresult rv = CalculateChecksum(hash, aFileSize, true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ compareHash.SetLength(hash.Length());
+
+ if (hash.Length() > aFileSize) {
+ NS_WARNING("SafeBrowsing file not long enough to store its hash");
+ return NS_ERROR_FAILURE;
+ }
+ nsCOMPtr<nsISeekableStream> seekIn = do_QueryInterface(mInputStream);
+ rv = seekIn->Seek(nsISeekableStream::NS_SEEK_SET, aFileSize - hash.Length());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mInputStream->Read(compareHash.BeginWriting(), hash.Length(), &read);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ASSERTION(read == hash.Length(), "Could not read hash bytes");
+
+ if (!hash.Equals(compareHash)) {
+ NS_WARNING("SafeBrowsing file failed checksum.");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult HashStore::Open(uint32_t aVersion) {
+ nsCOMPtr<nsIFile> storeFile;
+ nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = storeFile->AppendNative(mTableName + ".sbstore"_ns);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIInputStream> origStream;
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(origStream), storeFile,
+ PR_RDONLY | nsIFile::OS_READAHEAD);
+
+ if (rv == NS_ERROR_FILE_NOT_FOUND) {
+ UpdateHeader();
+ return NS_OK;
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int64_t fileSize;
+ rv = storeFile->GetFileSize(&fileSize);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (fileSize < 0 || fileSize > UINT32_MAX) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mFileSize = static_cast<uint32_t>(fileSize);
+ rv = NS_NewBufferedInputStream(getter_AddRefs(mInputStream),
+ origStream.forget(), mFileSize);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ReadHeader();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ LOG(("Failed to read header for %s", mTableName.get()));
+ return NS_ERROR_FILE_CORRUPTED;
+ }
+
+ rv = SanityCheck(aVersion);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult HashStore::ReadHeader() {
+ if (!mInputStream) {
+ UpdateHeader();
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
+ nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ void* buffer = &mHeader;
+ rv = NS_ReadInputStreamToBuffer(mInputStream, &buffer, sizeof(Header));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult HashStore::SanityCheck(uint32_t aVersion) const {
+ const uint32_t VER = aVersion == 0 ? CURRENT_VERSION : aVersion;
+ if (mHeader.magic != STORE_MAGIC || mHeader.version != VER) {
+ NS_WARNING("Unexpected header data in the store.");
+ // Version mismatch is also considered file corrupted,
+ // We need this error code to know if we should remove the file.
+ return NS_ERROR_FILE_CORRUPTED;
+ }
+
+ return NS_OK;
+}
+
+nsresult HashStore::CalculateChecksum(nsAutoCString& aChecksum,
+ uint32_t aFileSize,
+ bool aChecksumPresent) {
+ aChecksum.Truncate();
+
+ // Reset mInputStream to start
+ nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
+ nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+
+ nsCOMPtr<nsICryptoHash> hash =
+ do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Size of MD5 hash in bytes
+ const uint32_t CHECKSUM_SIZE = 16;
+
+ // MD5 is not a secure hash function, but since this is a filesystem integrity
+ // check, this usage is ok.
+ rv = hash->Init(nsICryptoHash::MD5);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!aChecksumPresent) {
+ // Hash entire file
+ rv = hash->UpdateFromStream(mInputStream, UINT32_MAX);
+ } else {
+ // Hash everything but last checksum bytes
+ if (aFileSize < CHECKSUM_SIZE) {
+ NS_WARNING("SafeBrowsing file isn't long enough to store its checksum");
+ return NS_ERROR_FAILURE;
+ }
+ rv = hash->UpdateFromStream(mInputStream, aFileSize - CHECKSUM_SIZE);
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = hash->Finish(false, aChecksum);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+void HashStore::UpdateHeader() {
+ mHeader.magic = STORE_MAGIC;
+ mHeader.version = CURRENT_VERSION;
+
+ mHeader.numAddChunks = mAddChunks.Length();
+ mHeader.numSubChunks = mSubChunks.Length();
+ mHeader.numAddPrefixes = mAddPrefixes.Length();
+ mHeader.numSubPrefixes = mSubPrefixes.Length();
+ mHeader.numAddCompletes = mAddCompletes.Length();
+ mHeader.numSubCompletes = mSubCompletes.Length();
+}
+
+nsresult HashStore::ReadChunkNumbers() {
+ if (!mInputStream) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
+ nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, sizeof(Header));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mAddChunks.Read(mInputStream, mHeader.numAddChunks);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ASSERTION(mAddChunks.Length() == mHeader.numAddChunks,
+ "Read the right amount of add chunks.");
+
+ rv = mSubChunks.Read(mInputStream, mHeader.numSubChunks);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ASSERTION(mSubChunks.Length() == mHeader.numSubChunks,
+ "Read the right amount of sub chunks.");
+
+ return NS_OK;
+}
+
+nsresult HashStore::ReadHashes() {
+ if (!mInputStream) {
+ // BeginUpdate has been called but Open hasn't initialized mInputStream,
+ // because the existing HashStore is empty.
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
+
+ uint32_t offset = sizeof(Header);
+ offset += (mHeader.numAddChunks + mHeader.numSubChunks) * sizeof(uint32_t);
+ nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ReadAddPrefixes();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ReadSubPrefixes();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ReadAddCompletes();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ReadTArray(mInputStream, &mSubCompletes, mHeader.numSubCompletes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult HashStore::PrepareForUpdate() {
+ nsresult rv = CheckChecksum(mFileSize);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ReadChunkNumbers();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ReadHashes();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult HashStore::BeginUpdate() {
+ // Check wether the file is corrupted and read the rest of the store
+ // in memory.
+ nsresult rv = PrepareForUpdate();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Close input stream, won't be needed any more and
+ // we will rewrite ourselves.
+ if (mInputStream) {
+ rv = mInputStream->Close();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ mInUpdate = true;
+
+ return NS_OK;
+}
+
+template <class T>
+static nsresult Merge(ChunkSet* aStoreChunks, FallibleTArray<T>* aStorePrefixes,
+ const ChunkSet& aUpdateChunks,
+ FallibleTArray<T>& aUpdatePrefixes,
+ bool aAllowMerging = false) {
+ EntrySort(aUpdatePrefixes);
+
+ auto storeIter = aStorePrefixes->begin();
+ auto storeEnd = aStorePrefixes->end();
+
+ // use a separate array so we can keep the iterators valid
+ // if the nsTArray grows
+ nsTArray<T> adds;
+
+ for (const auto& updatePrefix : aUpdatePrefixes) {
+ // skip this chunk if we already have it, unless we're
+ // merging completions, in which case we'll always already
+ // have the chunk from the original prefix
+ if (aStoreChunks->Has(updatePrefix.Chunk()))
+ if (!aAllowMerging) continue;
+ // XXX: binary search for insertion point might be faster in common
+ // case?
+ while (storeIter < storeEnd && (storeIter->Compare(updatePrefix) < 0)) {
+ // skip forward to matching element (or not...)
+ storeIter++;
+ }
+ // no match, add
+ if (storeIter == storeEnd || storeIter->Compare(updatePrefix) != 0) {
+ if (!adds.AppendElement(updatePrefix, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ }
+
+ // Chunks can be empty, but we should still report we have them
+ // to make the chunkranges continuous.
+ aStoreChunks->Merge(aUpdateChunks);
+
+ if (!aStorePrefixes->AppendElements(adds, fallible))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ EntrySort(*aStorePrefixes);
+
+ return NS_OK;
+}
+
+nsresult HashStore::ApplyUpdate(RefPtr<TableUpdateV2> aUpdate) {
+ MOZ_ASSERT(mTableName.Equals(aUpdate->TableName()));
+
+ nsresult rv = mAddExpirations.Merge(aUpdate->AddExpirations());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mSubExpirations.Merge(aUpdate->SubExpirations());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = Expire();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = Merge(&mAddChunks, &mAddPrefixes, aUpdate->AddChunks(),
+ aUpdate->AddPrefixes());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = Merge(&mAddChunks, &mAddCompletes, aUpdate->AddChunks(),
+ aUpdate->AddCompletes(), true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = Merge(&mSubChunks, &mSubPrefixes, aUpdate->SubChunks(),
+ aUpdate->SubPrefixes());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = Merge(&mSubChunks, &mSubCompletes, aUpdate->SubChunks(),
+ aUpdate->SubCompletes(), true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult HashStore::Rebuild() {
+ NS_ASSERTION(mInUpdate, "Must be in update to rebuild.");
+
+ nsresult rv = ProcessSubs();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ UpdateHeader();
+
+ return NS_OK;
+}
+
+template <class T>
+static void ExpireEntries(FallibleTArray<T>* aEntries, ChunkSet& aExpirations) {
+ auto addIter = aEntries->begin();
+
+ for (const auto& entry : *aEntries) {
+ if (!aExpirations.Has(entry.Chunk())) {
+ *addIter = entry;
+ addIter++;
+ }
+ }
+
+ aEntries->TruncateLength(addIter - aEntries->begin());
+}
+
+nsresult HashStore::Expire() {
+ ExpireEntries(&mAddPrefixes, mAddExpirations);
+ ExpireEntries(&mAddCompletes, mAddExpirations);
+ ExpireEntries(&mSubPrefixes, mSubExpirations);
+ ExpireEntries(&mSubCompletes, mSubExpirations);
+
+ mAddChunks.Remove(mAddExpirations);
+ mSubChunks.Remove(mSubExpirations);
+
+ mAddExpirations.Clear();
+ mSubExpirations.Clear();
+
+ return NS_OK;
+}
+
+template <class T>
+nsresult DeflateWriteTArray(nsIOutputStream* aStream, nsTArray<T>& aIn) {
+ uLongf insize = aIn.Length() * sizeof(T);
+ uLongf outsize = compressBound(insize);
+ FallibleTArray<char> outBuff;
+ if (!outBuff.SetLength(outsize, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ int zerr = compress(reinterpret_cast<Bytef*>(outBuff.Elements()), &outsize,
+ reinterpret_cast<const Bytef*>(aIn.Elements()), insize);
+ if (zerr != Z_OK) {
+ return NS_ERROR_FAILURE;
+ }
+ LOG(("DeflateWriteTArray: %lu in %lu out", insize, outsize));
+
+ outBuff.TruncateLength(outsize);
+
+ // Length of compressed data stream
+ uint32_t dataLen = outBuff.Length();
+ uint32_t written;
+ nsresult rv = aStream->Write(reinterpret_cast<char*>(&dataLen),
+ sizeof(dataLen), &written);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ASSERTION(written == sizeof(dataLen), "Error writing deflate length");
+
+ // Store to stream
+ rv = WriteTArray(aStream, outBuff);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+template <class T>
+nsresult InflateReadTArray(nsIInputStream* aStream, FallibleTArray<T>* aOut,
+ uint32_t aExpectedSize) {
+ uint32_t inLen;
+ uint32_t read;
+ nsresult rv =
+ aStream->Read(reinterpret_cast<char*>(&inLen), sizeof(inLen), &read);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ASSERTION(read == sizeof(inLen), "Error reading inflate length");
+
+ FallibleTArray<char> inBuff;
+ if (!inBuff.SetLength(inLen, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ rv = ReadTArray(aStream, &inBuff, inLen);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uLongf insize = inLen;
+ uLongf outsize = aExpectedSize * sizeof(T);
+ if (!aOut->SetLength(aExpectedSize, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ int zerr =
+ uncompress(reinterpret_cast<Bytef*>(aOut->Elements()), &outsize,
+ reinterpret_cast<const Bytef*>(inBuff.Elements()), insize);
+ if (zerr != Z_OK) {
+ return NS_ERROR_FAILURE;
+ }
+ LOG(("InflateReadTArray: %lu in %lu out", insize, outsize));
+
+ NS_ASSERTION(outsize == aExpectedSize * sizeof(T),
+ "Decompression size mismatch");
+
+ return NS_OK;
+}
+
+static nsresult ByteSliceWrite(nsIOutputStream* aOut,
+ nsTArray<uint32_t>& aData) {
+ nsTArray<uint8_t> slice;
+ uint32_t count = aData.Length();
+
+ // Only process one slice at a time to avoid using too much memory.
+ if (!slice.SetLength(count, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Process slice 1.
+ for (uint32_t i = 0; i < count; i++) {
+ slice[i] = (aData[i] >> 24);
+ }
+
+ nsresult rv = DeflateWriteTArray(aOut, slice);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Process slice 2.
+ for (uint32_t i = 0; i < count; i++) {
+ slice[i] = ((aData[i] >> 16) & 0xFF);
+ }
+
+ rv = DeflateWriteTArray(aOut, slice);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Process slice 3.
+ for (uint32_t i = 0; i < count; i++) {
+ slice[i] = ((aData[i] >> 8) & 0xFF);
+ }
+
+ rv = DeflateWriteTArray(aOut, slice);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Process slice 4.
+ for (uint32_t i = 0; i < count; i++) {
+ slice[i] = (aData[i] & 0xFF);
+ }
+
+ // The LSB slice is generally uncompressible, don't bother
+ // compressing it.
+ rv = WriteTArray(aOut, slice);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+static nsresult ByteSliceRead(nsIInputStream* aInStream,
+ FallibleTArray<uint32_t>* aData, uint32_t count) {
+ FallibleTArray<uint8_t> slice1;
+ FallibleTArray<uint8_t> slice2;
+ FallibleTArray<uint8_t> slice3;
+ FallibleTArray<uint8_t> slice4;
+
+ nsresult rv = InflateReadTArray(aInStream, &slice1, count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = InflateReadTArray(aInStream, &slice2, count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = InflateReadTArray(aInStream, &slice3, count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ReadTArray(aInStream, &slice4, count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!aData->SetCapacity(count, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ for (uint32_t i = 0; i < count; i++) {
+ // SetCapacity was just called, these cannot fail.
+ (void)aData->AppendElement(
+ (slice1[i] << 24) | (slice2[i] << 16) | (slice3[i] << 8) | (slice4[i]),
+ fallible);
+ }
+
+ return NS_OK;
+}
+
+nsresult HashStore::ReadAddPrefixes() {
+ FallibleTArray<uint32_t> chunks;
+ uint32_t count = mHeader.numAddPrefixes;
+
+ nsresult rv = ByteSliceRead(mInputStream, &chunks, count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!mAddPrefixes.SetCapacity(count, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ for (uint32_t i = 0; i < count; i++) {
+ AddPrefix* add = mAddPrefixes.AppendElement(fallible);
+ add->prefix.FromUint32(0);
+ add->addChunk = chunks[i];
+ }
+
+ return NS_OK;
+}
+
+nsresult HashStore::ReadAddCompletes() {
+ FallibleTArray<uint32_t> chunks;
+ uint32_t count = mHeader.numAddCompletes;
+
+ nsresult rv = ByteSliceRead(mInputStream, &chunks, count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!mAddCompletes.SetCapacity(count, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ for (uint32_t i = 0; i < count; i++) {
+ AddComplete* add = mAddCompletes.AppendElement(fallible);
+ add->addChunk = chunks[i];
+ }
+
+ return NS_OK;
+}
+
+nsresult HashStore::ReadSubPrefixes() {
+ FallibleTArray<uint32_t> addchunks;
+ FallibleTArray<uint32_t> subchunks;
+ FallibleTArray<uint32_t> prefixes;
+ uint32_t count = mHeader.numSubPrefixes;
+
+ nsresult rv = ByteSliceRead(mInputStream, &addchunks, count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ByteSliceRead(mInputStream, &subchunks, count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ByteSliceRead(mInputStream, &prefixes, count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!mSubPrefixes.SetCapacity(count, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ for (uint32_t i = 0; i < count; i++) {
+ SubPrefix* sub = mSubPrefixes.AppendElement(fallible);
+ sub->addChunk = addchunks[i];
+ sub->prefix.FromUint32(prefixes[i]);
+ sub->subChunk = subchunks[i];
+ }
+
+ return NS_OK;
+}
+
+// Split up PrefixArray back into the constituents
+nsresult HashStore::WriteAddPrefixChunks(nsIOutputStream* aOut) {
+ nsTArray<uint32_t> chunks;
+ uint32_t count = mAddPrefixes.Length();
+ if (!chunks.SetCapacity(count, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ for (uint32_t i = 0; i < count; i++) {
+ chunks.AppendElement(mAddPrefixes[i].Chunk());
+ }
+
+ nsresult rv = ByteSliceWrite(aOut, chunks);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult HashStore::WriteAddCompleteChunks(nsIOutputStream* aOut) {
+ nsTArray<uint32_t> chunks;
+ uint32_t count = mAddCompletes.Length();
+ if (!chunks.SetCapacity(count, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ for (uint32_t i = 0; i < count; i++) {
+ chunks.AppendElement(mAddCompletes[i].Chunk());
+ }
+
+ nsresult rv = ByteSliceWrite(aOut, chunks);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult HashStore::WriteSubPrefixes(nsIOutputStream* aOut) {
+ nsTArray<uint32_t> addchunks;
+ nsTArray<uint32_t> subchunks;
+ nsTArray<uint32_t> prefixes;
+ uint32_t count = mSubPrefixes.Length();
+ if (!addchunks.SetCapacity(count, fallible) ||
+ !subchunks.SetCapacity(count, fallible) ||
+ !prefixes.SetCapacity(count, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ for (uint32_t i = 0; i < count; i++) {
+ addchunks.AppendElement(mSubPrefixes[i].AddChunk());
+ prefixes.AppendElement(mSubPrefixes[i].PrefixHash().ToUint32());
+ subchunks.AppendElement(mSubPrefixes[i].Chunk());
+ }
+
+ nsresult rv = ByteSliceWrite(aOut, addchunks);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ByteSliceWrite(aOut, subchunks);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ByteSliceWrite(aOut, prefixes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult HashStore::WriteFile() {
+ NS_ASSERTION(mInUpdate, "Must be in update to write database.");
+ if (nsUrlClassifierDBService::ShutdownHasStarted()) {
+ return NS_ERROR_ABORT;
+ }
+
+ nsCOMPtr<nsIFile> storeFile;
+ nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = storeFile->AppendNative(mTableName + ".sbstore"_ns);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIOutputStream> out;
+ rv = NS_NewCheckSummedOutputStream(getter_AddRefs(out), storeFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t written;
+ rv = out->Write(reinterpret_cast<char*>(&mHeader), sizeof(mHeader), &written);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Write chunk numbers.
+ rv = mAddChunks.Write(out);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mSubChunks.Write(out);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Write hashes.
+ rv = WriteAddPrefixChunks(out);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = WriteSubPrefixes(out);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = WriteAddCompleteChunks(out);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = WriteTArray(out, mSubCompletes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISafeOutputStream> safeOut = do_QueryInterface(out, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = safeOut->Finish();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult HashStore::ReadCompletionsLegacyV3(AddCompleteArray& aCompletes) {
+ if (mHeader.version != 3) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIFile> storeFile;
+ nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = storeFile->AppendNative(mTableName + nsLiteralCString(STORE_SUFFIX));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t offset = mFileSize -
+ sizeof(struct AddComplete) * mHeader.numAddCompletes -
+ sizeof(struct SubComplete) * mHeader.numSubCompletes -
+ nsCheckSummedOutputStream::CHECKSUM_SIZE;
+
+ nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = ReadTArray(mInputStream, &aCompletes, mHeader.numAddCompletes);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+template <class T>
+static void Erase(FallibleTArray<T>* array,
+ typename FallibleTArray<T>::iterator& iterStart,
+ typename FallibleTArray<T>::iterator& iterEnd) {
+ array->RemoveElementsRange(iterStart, iterEnd);
+}
+
+// Find items matching between |subs| and |adds|, and remove them,
+// recording the item from |adds| in |adds_removed|. To minimize
+// copies, the inputs are processing in parallel, so |subs| and |adds|
+// should be compatibly ordered (either by SBAddPrefixLess or
+// SBAddPrefixHashLess).
+//
+// |predAS| provides add < sub, |predSA| provides sub < add, for the
+// tightest compare appropriate (see calls in SBProcessSubs).
+template <class TSub, class TAdd>
+static void KnockoutSubs(FallibleTArray<TSub>* aSubs,
+ FallibleTArray<TAdd>* aAdds) {
+ // Keep a pair of output iterators for writing kept items. Due to
+ // deletions, these may lag the main iterators. Using erase() on
+ // individual items would result in O(N^2) copies. Using a list
+ // would work around that, at double or triple the memory cost.
+ auto addOut = aAdds->begin();
+ auto addIter = aAdds->begin();
+
+ auto subOut = aSubs->begin();
+ auto subIter = aSubs->begin();
+
+ auto addEnd = aAdds->end();
+ auto subEnd = aSubs->end();
+
+ while (addIter != addEnd && subIter != subEnd) {
+ // additer compare, so it compares on add chunk
+ int32_t cmp = addIter->Compare(*subIter);
+ if (cmp > 0) {
+ // If |*sub_iter| < |*add_iter|, retain the sub.
+ *subOut = *subIter;
+ ++subOut;
+ ++subIter;
+ } else if (cmp < 0) {
+ // If |*add_iter| < |*sub_iter|, retain the add.
+ *addOut = *addIter;
+ ++addOut;
+ ++addIter;
+ } else {
+ // Drop equal items
+ ++addIter;
+ ++subIter;
+ }
+ }
+
+ Erase(aAdds, addOut, addIter);
+ Erase(aSubs, subOut, subIter);
+}
+
+// Remove items in |removes| from |fullHashes|. |fullHashes| and
+// |removes| should be ordered by SBAddPrefix component.
+template <class T>
+static void RemoveMatchingPrefixes(const SubPrefixArray& aSubs,
+ FallibleTArray<T>* aFullHashes) {
+ // Where to store kept items.
+ auto out = aFullHashes->begin();
+ auto hashIter = aFullHashes->begin();
+ auto hashEnd = aFullHashes->end();
+
+ auto removeIter = aSubs.begin();
+ auto removeEnd = aSubs.end();
+
+ while (hashIter != hashEnd && removeIter != removeEnd) {
+ int32_t cmp = removeIter->CompareAlt(*hashIter);
+ if (cmp > 0) {
+ // Keep items less than |*removeIter|.
+ *out = *hashIter;
+ ++out;
+ ++hashIter;
+ } else if (cmp < 0) {
+ // No hit for |*removeIter|, bump it forward.
+ ++removeIter;
+ } else {
+ // Drop equal items, there may be multiple hits.
+ do {
+ ++hashIter;
+ } while (hashIter != hashEnd && !(removeIter->CompareAlt(*hashIter) < 0));
+ ++removeIter;
+ }
+ }
+ Erase(aFullHashes, out, hashIter);
+}
+
+static void RemoveDeadSubPrefixes(SubPrefixArray& aSubs, ChunkSet& aAddChunks) {
+ auto subIter = aSubs.begin();
+
+ for (const auto& sub : aSubs) {
+ bool hasChunk = aAddChunks.Has(sub.AddChunk());
+ // Keep the subprefix if the chunk it refers to is one
+ // we haven't seen it yet.
+ if (!hasChunk) {
+ *subIter = sub;
+ subIter++;
+ }
+ }
+
+ LOG(("Removed %" PRId64 " dead SubPrefix entries.",
+ static_cast<int64_t>(aSubs.end() - subIter)));
+ aSubs.TruncateLength(subIter - aSubs.begin());
+}
+
+#ifdef DEBUG
+template <class T>
+static void EnsureSorted(FallibleTArray<T>* aArray) {
+ auto start = aArray->begin();
+ auto end = aArray->end();
+ auto iter = start;
+ auto previous = start;
+
+ while (iter != end) {
+ previous = iter;
+ ++iter;
+ if (iter != end) {
+ MOZ_ASSERT(iter->Compare(*previous) >= 0);
+ }
+ }
+}
+#endif
+
+nsresult HashStore::ProcessSubs() {
+#ifdef DEBUG
+ EnsureSorted(&mAddPrefixes);
+ EnsureSorted(&mSubPrefixes);
+ EnsureSorted(&mAddCompletes);
+ EnsureSorted(&mSubCompletes);
+ LOG(("All databases seem to have a consistent sort order."));
+#endif
+
+ RemoveMatchingPrefixes(mSubPrefixes, &mAddCompletes);
+ RemoveMatchingPrefixes(mSubPrefixes, &mSubCompletes);
+
+ // Remove any remaining subbed prefixes from both addprefixes
+ // and addcompletes.
+ KnockoutSubs(&mSubPrefixes, &mAddPrefixes);
+ KnockoutSubs(&mSubCompletes, &mAddCompletes);
+
+ // Remove any remaining subprefixes referring to addchunks that
+ // we have (and hence have been processed above).
+ RemoveDeadSubPrefixes(mSubPrefixes, mAddChunks);
+
+#ifdef DEBUG
+ EnsureSorted(&mAddPrefixes);
+ EnsureSorted(&mSubPrefixes);
+ EnsureSorted(&mAddCompletes);
+ EnsureSorted(&mSubCompletes);
+ LOG(("All databases seem to have a consistent sort order."));
+#endif
+
+ return NS_OK;
+}
+
+nsresult HashStore::AugmentAdds(const nsTArray<uint32_t>& aPrefixes,
+ const nsTArray<nsCString>& aCompletes) {
+ if (aPrefixes.Length() != mAddPrefixes.Length() ||
+ aCompletes.Length() != mAddCompletes.Length()) {
+ LOG((
+ "Amount of prefixes/completes in cache not consistent with store prefixes(%zu vs %zu), \
+ store completes(%zu vs %zu)",
+ aPrefixes.Length(), mAddPrefixes.Length(), aCompletes.Length(),
+ mAddCompletes.Length()));
+ return NS_ERROR_FAILURE;
+ }
+
+ for (size_t i = 0; i < mAddPrefixes.Length(); i++) {
+ mAddPrefixes[i].prefix.FromUint32(aPrefixes[i]);
+ }
+
+ for (size_t i = 0; i < mAddCompletes.Length(); i++) {
+ mAddCompletes[i].complete.Assign(aCompletes[i]);
+ }
+
+ return NS_OK;
+}
+
+ChunkSet& HashStore::AddChunks() {
+ ReadChunkNumbers();
+
+ return mAddChunks;
+}
+
+ChunkSet& HashStore::SubChunks() {
+ ReadChunkNumbers();
+
+ return mSubChunks;
+}
+
+} // namespace mozilla::safebrowsing
diff --git a/toolkit/components/url-classifier/HashStore.h b/toolkit/components/url-classifier/HashStore.h
new file mode 100644
index 0000000000..ccb665efa7
--- /dev/null
+++ b/toolkit/components/url-classifier/HashStore.h
@@ -0,0 +1,321 @@
+/* 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/. */
+
+#ifndef HashStore_h__
+#define HashStore_h__
+
+#include "Entries.h"
+#include "ChunkSet.h"
+
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsIFile.h"
+#include "nsISupports.h"
+#include "nsCOMPtr.h"
+#include <string>
+
+namespace mozilla {
+namespace safebrowsing {
+
+// The abstract class of TableUpdateV2 and TableUpdateV4. This
+// is convenient for passing the TableUpdate* around associated
+// with v2 and v4 instance.
+class TableUpdate {
+ public:
+ TableUpdate(const nsACString& aTable) : mTable(aTable) {}
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TableUpdate);
+
+ // To be overriden.
+ virtual bool Empty() const = 0;
+
+ // Common interfaces.
+ const nsCString& TableName() const { return mTable; }
+
+ template <typename T>
+ static T* Cast(TableUpdate* aThat) {
+ return (T::TAG == aThat->Tag() ? reinterpret_cast<T*>(aThat) : nullptr);
+ }
+ template <typename T>
+ static const T* Cast(const TableUpdate* aThat) {
+ return (T::TAG == aThat->Tag() ? reinterpret_cast<const T*>(aThat)
+ : nullptr);
+ }
+
+ protected:
+ virtual ~TableUpdate() = default;
+
+ private:
+ virtual int Tag() const = 0;
+
+ const nsCString mTable;
+};
+
+typedef nsTArray<RefPtr<TableUpdate>> TableUpdateArray;
+typedef nsTArray<RefPtr<const TableUpdate>> ConstTableUpdateArray;
+
+// A table update is built from a single update chunk from the server. As the
+// protocol parser processes each chunk, it constructs a table update with the
+// new hashes.
+class TableUpdateV2 : public TableUpdate {
+ public:
+ explicit TableUpdateV2(const nsACString& aTable) : TableUpdate(aTable) {}
+
+ bool Empty() const override {
+ return mAddChunks.Length() == 0 && mSubChunks.Length() == 0 &&
+ mAddExpirations.Length() == 0 && mSubExpirations.Length() == 0 &&
+ mAddPrefixes.Length() == 0 && mSubPrefixes.Length() == 0 &&
+ mAddCompletes.Length() == 0 && mSubCompletes.Length() == 0 &&
+ mMissPrefixes.Length() == 0;
+ }
+
+ // Throughout, uint32_t aChunk refers only to the chunk number. Chunk data is
+ // stored in the Prefix structures.
+ [[nodiscard]] nsresult NewAddChunk(uint32_t aChunk) {
+ return mAddChunks.Set(aChunk);
+ };
+ [[nodiscard]] nsresult NewSubChunk(uint32_t aChunk) {
+ return mSubChunks.Set(aChunk);
+ };
+ [[nodiscard]] nsresult NewAddExpiration(uint32_t aChunk) {
+ return mAddExpirations.Set(aChunk);
+ };
+ [[nodiscard]] nsresult NewSubExpiration(uint32_t aChunk) {
+ return mSubExpirations.Set(aChunk);
+ };
+ [[nodiscard]] nsresult NewAddPrefix(uint32_t aAddChunk,
+ const Prefix& aPrefix);
+ [[nodiscard]] nsresult NewSubPrefix(uint32_t aAddChunk, const Prefix& aPrefix,
+ uint32_t aSubChunk);
+ [[nodiscard]] nsresult NewAddComplete(uint32_t aChunk,
+ const Completion& aCompletion);
+ [[nodiscard]] nsresult NewSubComplete(uint32_t aAddChunk,
+ const Completion& aCompletion,
+ uint32_t aSubChunk);
+ [[nodiscard]] nsresult NewMissPrefix(const Prefix& aPrefix);
+
+ const ChunkSet& AddChunks() const { return mAddChunks; }
+ const ChunkSet& SubChunks() const { return mSubChunks; }
+
+ // Expirations for chunks.
+ const ChunkSet& AddExpirations() const { return mAddExpirations; }
+ const ChunkSet& SubExpirations() const { return mSubExpirations; }
+
+ // Hashes associated with this chunk.
+ AddPrefixArray& AddPrefixes() { return mAddPrefixes; }
+ SubPrefixArray& SubPrefixes() { return mSubPrefixes; }
+ const AddCompleteArray& AddCompletes() const { return mAddCompletes; }
+ AddCompleteArray& AddCompletes() { return mAddCompletes; }
+ SubCompleteArray& SubCompletes() { return mSubCompletes; }
+
+ // Entries that cannot be completed.
+ const MissPrefixArray& MissPrefixes() const { return mMissPrefixes; }
+
+ // For downcasting.
+ static const int TAG = 2;
+
+ private:
+ // The list of chunk numbers that we have for each of the type of chunks.
+ ChunkSet mAddChunks;
+ ChunkSet mSubChunks;
+ ChunkSet mAddExpirations;
+ ChunkSet mSubExpirations;
+
+ // 4-byte sha256 prefixes.
+ AddPrefixArray mAddPrefixes;
+ SubPrefixArray mSubPrefixes;
+
+ // This is only used by gethash so don't add this to Header.
+ MissPrefixArray mMissPrefixes;
+
+ // 32-byte hashes.
+ AddCompleteArray mAddCompletes;
+ SubCompleteArray mSubCompletes;
+
+ virtual int Tag() const override { return TAG; }
+};
+
+// Structure for DBService/HashStore/Classifiers to update.
+// It would contain the prefixes (both fixed and variable length)
+// for addition and indices to removal. See Bug 1283009.
+class TableUpdateV4 : public TableUpdate {
+ public:
+ typedef nsTArray<int32_t> RemovalIndiceArray;
+
+ public:
+ explicit TableUpdateV4(const nsACString& aTable)
+ : TableUpdate(aTable), mFullUpdate(false) {}
+
+ bool Empty() const override {
+ return mPrefixesMap.IsEmpty() && mRemovalIndiceArray.IsEmpty() &&
+ mFullHashResponseMap.IsEmpty();
+ }
+
+ bool IsFullUpdate() const { return mFullUpdate; }
+ const PrefixStringMap& Prefixes() const { return mPrefixesMap; }
+ const RemovalIndiceArray& RemovalIndices() const {
+ return mRemovalIndiceArray;
+ }
+ const nsACString& ClientState() const { return mClientState; }
+ const nsACString& SHA256() const { return mSHA256; }
+ const FullHashResponseMap& FullHashResponse() const {
+ return mFullHashResponseMap;
+ }
+
+ // For downcasting.
+ static const int TAG = 4;
+
+ void SetFullUpdate(bool aIsFullUpdate) { mFullUpdate = aIsFullUpdate; }
+ void NewPrefixes(int32_t aSize, const nsACString& aPrefixes);
+ void SetNewClientState(const nsACString& aState) { mClientState = aState; }
+ void SetSHA256(const std::string& aSHA256);
+
+ nsresult NewRemovalIndices(const uint32_t* aIndices, size_t aNumOfIndices);
+ nsresult NewFullHashResponse(const Prefix& aPrefix,
+ const CachedFullHashResponse& aResponse);
+
+ // Clear Prefixes & Removal indice.
+ void Clear();
+
+ private:
+ virtual int Tag() const override { return TAG; }
+
+ bool mFullUpdate;
+ PrefixStringMap mPrefixesMap;
+ RemovalIndiceArray mRemovalIndiceArray;
+ nsCString mClientState;
+ nsCString mSHA256;
+
+ // This is used to store response from fullHashes.find.
+ FullHashResponseMap mFullHashResponseMap;
+};
+
+// There is one hash store per table.
+class HashStore {
+ public:
+ HashStore(const nsACString& aTableName, const nsACString& aProvider,
+ nsIFile* aRootStoreFile);
+ ~HashStore();
+
+ const nsCString& TableName() const { return mTableName; }
+
+ // Version is set to 0 by default, it is only used when we want to open
+ // a specific version of HashStore. Note that the intention of aVersion
+ // is only to pass SanityCheck, reading data from older version should
+ // be handled additionally.
+ nsresult Open(uint32_t aVersion = 0);
+
+ // Add Prefixes/Completes are stored partly in the PrefixSet (contains the
+ // Prefix data organized for fast lookup/low RAM usage) and partly in the
+ // HashStore (Add Chunk numbers - only used for updates, slow retrieval).
+ // AugmentAdds function joins the separate datasets into one complete
+ // prefixes+chunknumbers dataset.
+ nsresult AugmentAdds(const nsTArray<uint32_t>& aPrefixes,
+ const nsTArray<nsCString>& aCompletes);
+
+ ChunkSet& AddChunks();
+ ChunkSet& SubChunks();
+ AddPrefixArray& AddPrefixes() { return mAddPrefixes; }
+ SubPrefixArray& SubPrefixes() { return mSubPrefixes; }
+ AddCompleteArray& AddCompletes() { return mAddCompletes; }
+ SubCompleteArray& SubCompletes() { return mSubCompletes; }
+
+ // =======
+ // Updates
+ // =======
+ // Begin the update process. Reads the store into memory.
+ nsresult BeginUpdate();
+
+ // Imports the data from a TableUpdate.
+ nsresult ApplyUpdate(RefPtr<TableUpdateV2> aUpdate);
+
+ // Process expired chunks
+ nsresult Expire();
+
+ // Rebuild the store, Incorporating all the applied updates.
+ nsresult Rebuild();
+
+ // Write the current state of the store to disk.
+ // If you call between ApplyUpdate() and Rebuild(), you'll
+ // have a mess on your hands.
+ nsresult WriteFile();
+
+ nsresult ReadCompletionsLegacyV3(AddCompleteArray& aCompletes);
+
+ nsresult Reset();
+
+ private:
+ nsresult ReadHeader();
+ nsresult SanityCheck(uint32_t aVersion = 0) const;
+ nsresult CalculateChecksum(nsAutoCString& aChecksum, uint32_t aFileSize,
+ bool aChecksumPresent);
+ nsresult CheckChecksum(uint32_t aFileSize);
+ void UpdateHeader();
+
+ nsresult ReadCompletions();
+ nsresult ReadChunkNumbers();
+ nsresult ReadHashes();
+
+ nsresult ReadAddPrefixes();
+ nsresult ReadSubPrefixes();
+ nsresult ReadAddCompletes();
+
+ nsresult WriteAddPrefixChunks(nsIOutputStream* aOut);
+ nsresult WriteSubPrefixes(nsIOutputStream* aOut);
+ nsresult WriteAddCompleteChunks(nsIOutputStream* aOut);
+
+ nsresult ProcessSubs();
+
+ nsresult PrepareForUpdate();
+
+ // This is used for checking that the database is correct and for figuring out
+ // the number of chunks, etc. to read from disk on restart.
+ struct Header {
+ uint32_t magic;
+ uint32_t version;
+ uint32_t numAddChunks;
+ uint32_t numSubChunks;
+ uint32_t numAddPrefixes;
+ uint32_t numSubPrefixes;
+ uint32_t numAddCompletes;
+ uint32_t numSubCompletes;
+ };
+
+ Header mHeader;
+
+ // The name of the table (must end in -shavar or -digest256, or evidently
+ // -simple for unittesting.
+ const nsCString mTableName;
+ nsCOMPtr<nsIFile> mStoreDirectory;
+
+ bool mInUpdate;
+
+ nsCOMPtr<nsIInputStream> mInputStream;
+
+ // Chunk numbers, stored as uint32_t arrays.
+ ChunkSet mAddChunks;
+ ChunkSet mSubChunks;
+
+ ChunkSet mAddExpirations;
+ ChunkSet mSubExpirations;
+
+ // Chunk data for shavar tables. See Entries.h for format.
+ AddPrefixArray mAddPrefixes;
+ SubPrefixArray mSubPrefixes;
+
+ // See bug 806422 for background. We must be able to distinguish between
+ // updates from the completion server and updates from the regular server.
+ AddCompleteArray mAddCompletes;
+ SubCompleteArray mSubCompletes;
+
+ uint32_t mFileSize;
+
+ // For gtest to inspect private members.
+ friend class PerProviderDirectoryTestUtils;
+};
+
+} // namespace safebrowsing
+} // namespace mozilla
+
+#endif
diff --git a/toolkit/components/url-classifier/IUrlClassifierUITelemetry.idl b/toolkit/components/url-classifier/IUrlClassifierUITelemetry.idl
new file mode 100644
index 0000000000..12ca91659a
--- /dev/null
+++ b/toolkit/components/url-classifier/IUrlClassifierUITelemetry.idl
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 2; 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 "nsISupports.idl"
+
+[scriptable, uuid(a6c62ce5-3a95-41bb-b0f1-8cd4f4ca00e3)]
+interface IUrlClassifierUITelemetry : nsISupports {
+
+const uint32_t WARNING_MALWARE_PAGE_TOP = 1;
+const uint32_t WARNING_MALWARE_PAGE_TOP_WHY_BLOCKED = 2;
+const uint32_t WARNING_MALWARE_PAGE_TOP_GET_ME_OUT_OF_HERE = 3;
+const uint32_t WARNING_MALWARE_PAGE_TOP_IGNORE_WARNING = 4;
+const uint32_t WARNING_MALWARE_PAGE_FRAME = 5;
+const uint32_t WARNING_MALWARE_PAGE_FRAME_WHY_BLOCKED = 6;
+const uint32_t WARNING_MALWARE_PAGE_FRAME_GET_ME_OUT_OF_HERE = 7;
+const uint32_t WARNING_MALWARE_PAGE_FRAME_IGNORE_WARNING = 8;
+
+const uint32_t WARNING_PHISHING_PAGE_TOP = 9;
+const uint32_t WARNING_PHISHING_PAGE_TOP_WHY_BLOCKED = 10;
+const uint32_t WARNING_PHISHING_PAGE_TOP_GET_ME_OUT_OF_HERE = 11;
+const uint32_t WARNING_PHISHING_PAGE_TOP_IGNORE_WARNING = 12;
+const uint32_t WARNING_PHISHING_PAGE_FRAME = 13;
+const uint32_t WARNING_PHISHING_PAGE_FRAME_WHY_BLOCKED = 14;
+const uint32_t WARNING_PHISHING_PAGE_FRAME_GET_ME_OUT_OF_HERE = 15;
+const uint32_t WARNING_PHISHING_PAGE_FRAME_IGNORE_WARNING = 16;
+
+const uint32_t WARNING_UNWANTED_PAGE_TOP = 17;
+const uint32_t WARNING_UNWANTED_PAGE_TOP_WHY_BLOCKED = 18;
+const uint32_t WARNING_UNWANTED_PAGE_TOP_GET_ME_OUT_OF_HERE = 19;
+const uint32_t WARNING_UNWANTED_PAGE_TOP_IGNORE_WARNING = 20;
+const uint32_t WARNING_UNWANTED_PAGE_FRAME = 21;
+const uint32_t WARNING_UNWANTED_PAGE_FRAME_WHY_BLOCKED = 22;
+const uint32_t WARNING_UNWANTED_PAGE_FRAME_GET_ME_OUT_OF_HERE = 23;
+const uint32_t WARNING_UNWANTED_PAGE_FRAME_IGNORE_WARNING = 24;
+
+const uint32_t WARNING_HARMFUL_PAGE_TOP = 25;
+const uint32_t WARNING_HARMFUL_PAGE_TOP_WHY_BLOCKED = 26;
+const uint32_t WARNING_HARMFUL_PAGE_TOP_GET_ME_OUT_OF_HERE = 27;
+const uint32_t WARNING_HARMFUL_PAGE_TOP_IGNORE_WARNING = 28;
+const uint32_t WARNING_HARMFUL_PAGE_FRAME = 29;
+const uint32_t WARNING_HARMFUL_PAGE_FRAME_WHY_BLOCKED = 30;
+const uint32_t WARNING_HARMFUL_PAGE_FRAME_GET_ME_OUT_OF_HERE = 31;
+const uint32_t WARNING_HARMFUL_PAGE_FRAME_IGNORE_WARNING = 32;
+
+};
diff --git a/toolkit/components/url-classifier/LookupCache.cpp b/toolkit/components/url-classifier/LookupCache.cpp
new file mode 100644
index 0000000000..3cb7b2f9eb
--- /dev/null
+++ b/toolkit/components/url-classifier/LookupCache.cpp
@@ -0,0 +1,1107 @@
+//* -*- 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 "LookupCacheV4.h"
+#include "HashStore.h"
+#include "nsIFileStreams.h"
+#include "nsISeekableStream.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Logging.h"
+#include "nsNetUtil.h"
+#include "nsCheckSummedOutputStream.h"
+#include "crc32c.h"
+#include "prprf.h"
+#include "Classifier.h"
+#include "nsUrlClassifierInfo.h"
+#include "nsUrlClassifierUtils.h"
+#include "nsUrlClassifierDBService.h"
+
+#ifdef DEBUG
+# include "nsPrintfCString.h"
+#endif
+
+// 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
+// provide a classification as long as the data is up to date.
+
+// PrefixSet stores and provides lookups for 4-byte prefixes.
+// mUpdateCompletions contains 32-byte completions which were
+// contained in updates. They are retrieved from HashStore/.sbtore
+// on startup.
+// mGetHashCache contains 32-byte completions which were
+// returned from the gethash server. They are not serialized,
+// only cached until the next update.
+
+// MOZ_LOG=UrlClassifierDbService:5
+extern mozilla::LazyLogModule gUrlClassifierDbServiceLog;
+#define LOG(args) \
+ MOZ_LOG(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() \
+ MOZ_LOG_TEST(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug)
+
+namespace mozilla {
+namespace safebrowsing {
+
+const uint32_t LookupCache::MAX_BUFFER_SIZE = 64 * 1024;
+
+const int CacheResultV2::VER = CacheResult::V2;
+const int CacheResultV4::VER = CacheResult::V4;
+
+const int LookupCacheV2::VER = 2;
+const uint32_t LookupCacheV2::VLPSET_MAGIC = 0xe5b862e7;
+const uint32_t LookupCacheV2::VLPSET_VERSION = 1;
+
+namespace {
+
+//////////////////////////////////////////////////////////////////////////
+// A set of lightweight functions for reading/writing value from/to file.
+template <typename T>
+struct ValueTraits {
+ static_assert(sizeof(T) <= LookupCacheV4::MAX_METADATA_VALUE_LENGTH,
+ "LookupCacheV4::MAX_METADATA_VALUE_LENGTH is too small.");
+ static uint32_t Length(const T& aValue) { return sizeof(T); }
+ static char* WritePtr(T& aValue, uint32_t aLength) { return (char*)&aValue; }
+ static const char* ReadPtr(const T& aValue) { return (char*)&aValue; }
+ static bool IsFixedLength() { return true; }
+};
+
+template <>
+struct ValueTraits<nsACString> {
+ static bool IsFixedLength() { return false; }
+
+ static uint32_t Length(const nsACString& aValue) { return aValue.Length(); }
+
+ static char* WritePtr(nsACString& aValue, uint32_t aLength) {
+ aValue.SetLength(aLength);
+ return aValue.BeginWriting();
+ }
+
+ static const char* ReadPtr(const nsACString& aValue) {
+ return aValue.BeginReading();
+ }
+};
+
+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<char>(aIn[i]);
+ aOut.Append(lut[(c >> 4) & 0x0F]);
+ aOut.Append(lut[c & 15]);
+ }
+}
+
+#ifdef DEBUG
+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, pret.tm_hour,
+ pret.tm_min, pret.tm_sec);
+}
+#endif
+
+} // end of unnamed namespace.
+////////////////////////////////////////////////////////////////////////
+
+template <typename T>
+nsresult LookupCache::WriteValue(nsIOutputStream* aOutputStream,
+ const T& aValue) {
+ uint32_t writeLength = ValueTraits<T>::Length(aValue);
+ MOZ_ASSERT(writeLength <= LookupCacheV4::MAX_METADATA_VALUE_LENGTH,
+ "LookupCacheV4::MAX_METADATA_VALUE_LENGTH is too small.");
+ if (!ValueTraits<T>::IsFixedLength()) {
+ // We need to write out the variable value length.
+ nsresult rv = WriteValue(aOutputStream, writeLength);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Write out the value.
+ auto valueReadPtr = ValueTraits<T>::ReadPtr(aValue);
+ uint32_t written;
+ nsresult rv = aOutputStream->Write(valueReadPtr, writeLength, &written);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (NS_WARN_IF(written != writeLength)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return rv;
+}
+
+template <typename T>
+nsresult LookupCache::ReadValue(nsIInputStream* aInputStream, T& aValue) {
+ nsresult rv;
+
+ uint32_t readLength;
+ if (ValueTraits<T>::IsFixedLength()) {
+ readLength = ValueTraits<T>::Length(aValue);
+ } else {
+ // Read the variable value length from file.
+ nsresult rv = ReadValue(aInputStream, readLength);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Sanity-check the readLength in case of disk corruption
+ // (see bug 1433636).
+ if (readLength > LookupCacheV4::MAX_METADATA_VALUE_LENGTH) {
+ return NS_ERROR_FILE_CORRUPTED;
+ }
+
+ // Read the value.
+ uint32_t read;
+ auto valueWritePtr = ValueTraits<T>::WritePtr(aValue, readLength);
+ rv = aInputStream->Read(valueWritePtr, readLength, &read);
+ if (NS_FAILED(rv) || read != readLength) {
+ LOG(("Failed to read the value."));
+ return NS_FAILED(rv) ? rv : NS_ERROR_FAILURE;
+ }
+
+ return rv;
+}
+
+// These symbols are referenced from another compilation unit, but their
+// implementation depends on local symbols. Workaround this by forcing their
+// instantiation there.
+template nsresult mozilla::safebrowsing::LookupCache::WriteValue(
+ nsIOutputStream*, nsTSubstring<char> const&);
+template nsresult mozilla::safebrowsing::LookupCache::ReadValue(
+ nsIInputStream*, nsTSubstring<char>&);
+
+LookupCache::LookupCache(const nsACString& aTableName,
+ const nsACString& aProvider,
+ nsCOMPtr<nsIFile>& aRootStoreDir)
+ : mPrimed(false),
+ mTableName(aTableName),
+ mProvider(aProvider),
+ mRootStoreDirectory(aRootStoreDir),
+ mVLPrefixSet(nullptr) {
+ UpdateRootDirHandle(mRootStoreDirectory);
+}
+
+nsresult LookupCache::Open() {
+ LOG(("Loading PrefixSet for %s", mTableName.get()));
+ nsresult rv;
+ if (nsUrlClassifierUtils::IsMozTestTable(mTableName)) {
+ // For built-in test table, we don't load it from disk,
+ // test entries are directly added in memory.
+ rv = LoadMozEntries();
+ } else {
+ rv = LoadPrefixSet();
+ }
+
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+
+ return rv;
+}
+
+nsresult LookupCache::Init() {
+ MOZ_ASSERT(!mVLPrefixSet);
+
+ mVLPrefixSet = new VariableLengthPrefixSet();
+ nsresult rv = mVLPrefixSet->Init(mTableName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+nsresult LookupCache::UpdateRootDirHandle(
+ nsCOMPtr<nsIFile>& aNewRootStoreDirectory) {
+ nsresult rv;
+
+ if (aNewRootStoreDirectory != mRootStoreDirectory) {
+ rv = aNewRootStoreDirectory->Clone(getter_AddRefs(mRootStoreDirectory));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = Classifier::GetPrivateStoreDirectory(mRootStoreDirectory, mTableName,
+ mProvider,
+ getter_AddRefs(mStoreDirectory));
+
+ if (NS_FAILED(rv)) {
+ LOG(("Failed to get private store directory for %s", mTableName.get()));
+ mStoreDirectory = mRootStoreDirectory;
+ }
+
+ if (LOG_ENABLED()) {
+ nsString path;
+ mStoreDirectory->GetPath(path);
+ LOG(("Private store directory for %s is %s", mTableName.get(),
+ NS_ConvertUTF16toUTF8(path).get()));
+ }
+
+ return rv;
+}
+
+nsresult LookupCache::WriteFile() {
+ if (nsUrlClassifierDBService::ShutdownHasStarted()) {
+ return NS_ERROR_ABORT;
+ }
+
+ nsCOMPtr<nsIFile> psFile;
+ nsresult rv = mStoreDirectory->Clone(getter_AddRefs(psFile));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = psFile->AppendNative(mTableName + GetPrefixSetSuffix());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = StoreToFile(psFile);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ LOG(("Failed to store the prefixset for table %s", mTableName.get()));
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult LookupCache::CheckCache(const Completion& aCompletion, bool* aHas,
+ bool* aConfirmed) {
+ // Shouldn't call this function if prefix is not in the database.
+ MOZ_ASSERT(*aHas);
+
+ *aConfirmed = false;
+
+ uint32_t prefix = aCompletion.ToUint32();
+
+ CachedFullHashResponse* fullHashResponse = mFullHashCache.Get(prefix);
+ if (!fullHashResponse) {
+ return NS_OK;
+ }
+
+ int64_t nowSec = PR_Now() / PR_USEC_PER_SEC;
+ int64_t expiryTimeSec;
+
+ FullHashExpiryCache& fullHashes = fullHashResponse->fullHashes;
+ nsDependentCSubstring completion(
+ reinterpret_cast<const char*>(aCompletion.buf), COMPLETE_SIZE);
+
+ // Check if we can find the fullhash in positive cache
+ if (fullHashes.Get(completion, &expiryTimeSec)) {
+ if (nowSec <= expiryTimeSec) {
+ // Url is NOT safe.
+ *aConfirmed = true;
+ LOG(("Found a valid fullhash in the positive cache"));
+ } else {
+ // Trigger a gethash request in this case(aConfirmed is false).
+ LOG(("Found an expired fullhash in the positive cache"));
+
+ // Remove fullhash entry from the cache when the negative cache
+ // is also expired because whether or not the fullhash is cached
+ // locally, we will need to consult the server next time we
+ // lookup this hash. We may as well remove it from our cache.
+ if (fullHashResponse->negativeCacheExpirySec < expiryTimeSec) {
+ fullHashes.Remove(completion);
+ if (fullHashes.Count() == 0 &&
+ fullHashResponse->negativeCacheExpirySec < nowSec) {
+ mFullHashCache.Remove(prefix);
+ }
+ }
+ }
+ return NS_OK;
+ }
+
+ // Check negative cache.
+ if (fullHashResponse->negativeCacheExpirySec >= nowSec) {
+ // Url is safe.
+ LOG(("Found a valid prefix in the negative cache"));
+ *aHas = false;
+ } else {
+ LOG(("Found an expired prefix in the negative cache"));
+ if (fullHashes.Count() == 0) {
+ mFullHashCache.Remove(prefix);
+ }
+ }
+
+ return NS_OK;
+}
+
+// This function remove cache entries whose negative cache time is expired.
+// It is possible that a cache entry whose positive cache time is not yet
+// expired but still being removed after calling this API. Right now we call
+// this on every update.
+void LookupCache::InvalidateExpiredCacheEntries() {
+ int64_t nowSec = PR_Now() / PR_USEC_PER_SEC;
+
+ for (auto iter = mFullHashCache.Iter(); !iter.Done(); iter.Next()) {
+ CachedFullHashResponse* response = iter.UserData();
+ if (response->negativeCacheExpirySec < nowSec) {
+ iter.Remove();
+ }
+ }
+}
+
+void LookupCache::CopyFullHashCache(const LookupCache* aSource) {
+ if (!aSource) {
+ return;
+ }
+
+ CopyClassHashTable<FullHashResponseMap>(aSource->mFullHashCache,
+ mFullHashCache);
+}
+
+void LookupCache::ClearCache() { mFullHashCache.Clear(); }
+
+void LookupCache::ClearAll() {
+ ClearCache();
+ ClearPrefixes();
+ mPrimed = false;
+}
+
+nsresult LookupCache::ClearPrefixes() {
+ // Clear by seting a empty map
+ PrefixStringMap map;
+ return mVLPrefixSet->SetPrefixes(map);
+}
+
+bool LookupCache::IsEmpty() const {
+ bool isEmpty;
+ mVLPrefixSet->IsEmpty(&isEmpty);
+ return isEmpty;
+}
+
+void LookupCache::GetCacheInfo(nsIUrlClassifierCacheInfo** aCache) const {
+ MOZ_ASSERT(aCache);
+
+ RefPtr<nsUrlClassifierCacheInfo> info = new nsUrlClassifierCacheInfo;
+ info->table = mTableName;
+
+ for (const auto& cacheEntry : mFullHashCache) {
+ RefPtr<nsUrlClassifierCacheEntry> entry = new nsUrlClassifierCacheEntry;
+
+ // Set prefix of the cache entry.
+ nsAutoCString prefix(reinterpret_cast<const char*>(&cacheEntry.GetKey()),
+ PREFIX_SIZE);
+ CStringToHexString(prefix, entry->prefix);
+
+ // Set expiry of the cache entry.
+ CachedFullHashResponse* response = cacheEntry.GetWeak();
+ entry->expirySec = response->negativeCacheExpirySec;
+
+ // Set positive cache.
+ FullHashExpiryCache& fullHashes = response->fullHashes;
+ for (const auto& fullHashEntry : fullHashes) {
+ RefPtr<nsUrlClassifierPositiveCacheEntry> match =
+ new nsUrlClassifierPositiveCacheEntry;
+
+ // Set fullhash of positive cache entry.
+ CStringToHexString(fullHashEntry.GetKey(), match->fullhash);
+
+ // Set expiry of positive cache entry.
+ match->expirySec = fullHashEntry.GetData();
+
+ entry->matches.AppendElement(
+ static_cast<nsIUrlClassifierPositiveCacheEntry*>(match));
+ }
+
+ info->entries.AppendElement(
+ static_cast<nsIUrlClassifierCacheEntry*>(entry));
+ }
+
+ info.forget(aCache);
+}
+
+/* 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", &i1, &i2, &i3,
+ &i4, &c) == 4) {
+ return (i1 <= 0xFF && i2 <= 0xFF && i3 <= 0xFF && i4 <= 0xFF);
+ }
+
+ return false;
+}
+
+// This is used when the URL is created by CreatePairwiseEntityListURI(),
+// which returns an URI like "toplevel.page/?resource=third.party.domain"
+// The fragment rule for the hostname(toplevel.page) is still the same
+// as Safe Browsing protocol.
+// The difference is that we always keep the path and query string and
+// generate an additional fragment by removing the leading component of
+// third.party.domain. This is to make sure we can find a match when a
+// exceptionlisted domain is eTLD.
+/* static */
+nsresult LookupCache::GetLookupEntitylistFragments(
+ const nsACString& aSpec, nsTArray<nsCString>* aFragments) {
+ aFragments->Clear();
+
+ nsACString::const_iterator begin, end, iter, iter_end;
+ aSpec.BeginReading(begin);
+ aSpec.EndReading(end);
+
+ iter = begin;
+ iter_end = end;
+
+ // Fallback to use default fragment rule when the URL doesn't contain
+ // "/?resoruce=" because this means the URL is not generated in
+ // CreatePairwiseEntityListURI()
+ if (!FindInReadable("/?resource="_ns, iter, iter_end)) {
+ return GetLookupFragments(aSpec, aFragments);
+ }
+
+ const nsACString& topLevelURL = Substring(begin, iter++);
+ const nsACString& thirdPartyURL = Substring(iter_end, end);
+
+ /**
+ * For the top-level URL, we follow the host fragment rule defined
+ * in the Safe Browsing protocol.
+ */
+ nsTArray<nsCString> topLevelURLs;
+ topLevelURLs.AppendElement(topLevelURL);
+
+ if (!IsCanonicalizedIP(topLevelURL)) {
+ topLevelURL.BeginReading(begin);
+ topLevelURL.EndReading(end);
+ int numTopLevelURLComponents = 0;
+ while (RFindInReadable("."_ns, begin, end) &&
+ numTopLevelURLComponents < MAX_HOST_COMPONENTS) {
+ // don't bother checking toplevel domains
+ if (++numTopLevelURLComponents >= 2) {
+ topLevelURL.EndReading(iter);
+ topLevelURLs.AppendElement(Substring(end, iter));
+ }
+ end = begin;
+ topLevelURL.BeginReading(begin);
+ }
+ }
+
+ /**
+ * The whiltelisted domain in the entity list may be eTLD or eTLD+1.
+ * Since the number of the domain name part in the third-party URL searching
+ * is always less than or equal to eTLD+1, we remove the leading
+ * component from the third-party domain to make sure we can find a match
+ * if the exceptionlisted domain stoed in the entity list is eTLD.
+ */
+ nsTArray<nsCString> thirdPartyURLs;
+ thirdPartyURLs.AppendElement(thirdPartyURL);
+
+ if (!IsCanonicalizedIP(thirdPartyURL)) {
+ thirdPartyURL.BeginReading(iter);
+ thirdPartyURL.EndReading(end);
+ if (FindCharInReadable('.', iter, end)) {
+ iter++;
+ nsAutoCString thirdPartyURLToAdd;
+ thirdPartyURLToAdd.Assign(Substring(iter++, end));
+
+ // don't bother checking toplevel domains
+ if (FindCharInReadable('.', iter, end)) {
+ thirdPartyURLs.AppendElement(thirdPartyURLToAdd);
+ }
+ }
+ }
+
+ for (size_t i = 0; i < topLevelURLs.Length(); i++) {
+ for (size_t j = 0; j < thirdPartyURLs.Length(); j++) {
+ nsAutoCString key;
+ key.Assign(topLevelURLs[i]);
+ key.Append("/?resource=");
+ key.Append(thirdPartyURLs[j]);
+
+ aFragments->AppendElement(key);
+ }
+ }
+
+ return NS_OK;
+}
+
+/* static */
+nsresult LookupCache::GetLookupFragments(const nsACString& aSpec,
+ nsTArray<nsCString>* aFragments)
+
+{
+ aFragments->Clear();
+
+ nsACString::const_iterator begin, end, iter;
+ aSpec.BeginReading(begin);
+ aSpec.EndReading(end);
+
+ iter = begin;
+ if (!FindCharInReadable('/', iter, end)) {
+ return NS_OK;
+ }
+
+ const nsACString& host = Substring(begin, iter++);
+ nsAutoCString path;
+ path.Assign(Substring(iter, end));
+
+ /**
+ * From the protocol doc:
+ * For the hostname, the client will try at most 5 different strings. They
+ * are:
+ * a) The exact hostname of the url
+ * b) The 4 hostnames formed by starting with the last 5 components and
+ * successivly removing the leading component. The top-level component
+ * can be skipped. This is not done if the hostname is a numerical IP.
+ */
+ nsTArray<nsCString> hosts;
+ hosts.AppendElement(host);
+
+ if (!IsCanonicalizedIP(host)) {
+ host.BeginReading(begin);
+ host.EndReading(end);
+ int numHostComponents = 0;
+ while (RFindInReadable("."_ns, begin, end) &&
+ numHostComponents < MAX_HOST_COMPONENTS) {
+ // don't bother checking toplevel domains
+ if (++numHostComponents >= 2) {
+ host.EndReading(iter);
+ hosts.AppendElement(Substring(end, iter));
+ }
+ end = begin;
+ host.BeginReading(begin);
+ }
+ }
+
+ /**
+ * From the protocol doc:
+ * For the path, the client will also try at most 6 different strings.
+ * They are:
+ * a) the exact path of the url, including query parameters
+ * b) the exact path of the url, without query parameters
+ * c) the 4 paths formed by starting at the root (/) and
+ * successively appending path components, including a trailing
+ * slash. This behavior should only extend up to the next-to-last
+ * path component, that is, a trailing slash should never be
+ * appended that was not present in the original url.
+ */
+ nsTArray<nsCString> paths;
+ nsAutoCString pathToAdd;
+
+ path.BeginReading(begin);
+ path.EndReading(end);
+ iter = begin;
+ if (FindCharInReadable('?', iter, end)) {
+ pathToAdd = Substring(begin, iter);
+ paths.AppendElement(pathToAdd);
+ end = iter;
+ }
+
+ int numPathComponents = 1;
+ iter = begin;
+ while (FindCharInReadable('/', iter, end) &&
+ numPathComponents < MAX_PATH_COMPONENTS) {
+ iter++;
+ pathToAdd.Assign(Substring(begin, iter));
+ paths.AppendElement(pathToAdd);
+ numPathComponents++;
+ }
+
+ // If we haven't already done so, add the full path
+ if (!pathToAdd.Equals(path)) {
+ paths.AppendElement(path);
+ }
+ // Check an empty path (for whole-domain blocklist entries)
+ if (!paths.Contains(""_ns)) {
+ paths.AppendElement(""_ns);
+ }
+
+ for (uint32_t hostIndex = 0; hostIndex < hosts.Length(); hostIndex++) {
+ for (uint32_t pathIndex = 0; pathIndex < paths.Length(); pathIndex++) {
+ nsCString key;
+ key.Assign(hosts[hostIndex]);
+ key.Append('/');
+ key.Append(paths[pathIndex]);
+
+ aFragments->AppendElement(key);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult LookupCache::LoadPrefixSet() {
+ nsCOMPtr<nsIFile> psFile;
+ nsresult rv = mStoreDirectory->Clone(getter_AddRefs(psFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = psFile->AppendNative(mTableName + GetPrefixSetSuffix());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool exists;
+ rv = psFile->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (exists) {
+ LOG(("stored PrefixSet exists, loading from disk"));
+ rv = LoadFromFile(psFile);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ mPrimed = true;
+ } else {
+ // The only scenario we load the old .pset file is when we haven't received
+ // a SafeBrowsng update before. After receiving an update, new .vlpset will
+ // be stored while old .pset will be removed.
+ if (NS_SUCCEEDED(LoadLegacyFile())) {
+ mPrimed = true;
+ } else {
+ LOG(("no (usable) stored PrefixSet found"));
+ }
+ }
+
+#ifdef DEBUG
+ if (mPrimed) {
+ uint32_t size = SizeOfPrefixSet();
+ LOG(("SB tree done, size = %d bytes\n", size));
+ }
+#endif
+
+ return NS_OK;
+}
+
+size_t LookupCache::SizeOfPrefixSet() const {
+ return mVLPrefixSet->SizeOfIncludingThis(moz_malloc_size_of);
+}
+
+#if defined(DEBUG)
+void LookupCache::DumpCache() const {
+ if (!LOG_ENABLED()) {
+ return;
+ }
+
+ for (const auto& cacheEntry : mFullHashCache) {
+ CachedFullHashResponse* response = cacheEntry.GetWeak();
+
+ nsAutoCString prefix;
+ CStringToHexString(
+ nsCString(reinterpret_cast<const char*>(&cacheEntry.GetKey()),
+ PREFIX_SIZE),
+ prefix);
+ LOG(("Cache prefix(%s): %s, Expiry: %s", mTableName.get(), prefix.get(),
+ GetFormattedTimeString(response->negativeCacheExpirySec).get()));
+
+ FullHashExpiryCache& fullHashes = response->fullHashes;
+ for (const auto& fullHashEntry : fullHashes) {
+ nsAutoCString fullhash;
+ CStringToHexString(fullHashEntry.GetKey(), fullhash);
+ LOG((" - %s, Expiry: %s", fullhash.get(),
+ GetFormattedTimeString(fullHashEntry.GetData()).get()));
+ }
+ }
+}
+#endif
+
+nsresult LookupCache::StoreToFile(nsCOMPtr<nsIFile>& aFile) {
+ NS_ENSURE_ARG_POINTER(aFile);
+
+ uint32_t fileSize = sizeof(Header) +
+ mVLPrefixSet->CalculatePreallocateSize() +
+ nsCrc32CheckSumedOutputStream::CHECKSUM_SIZE;
+
+ nsCOMPtr<nsIOutputStream> localOutFile;
+ nsresult rv =
+ NS_NewSafeLocalFileOutputStream(getter_AddRefs(localOutFile), aFile,
+ PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Preallocate the file storage
+ {
+ nsCOMPtr<nsIFileOutputStream> fos(do_QueryInterface(localOutFile));
+ Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_VLPS_FALLOCATE_TIME> timer;
+
+ Unused << fos->Preallocate(fileSize);
+ }
+
+ nsCOMPtr<nsIOutputStream> out;
+ rv = NS_NewCrc32OutputStream(getter_AddRefs(out), localOutFile.forget(),
+ std::min(fileSize, MAX_BUFFER_SIZE));
+
+ // Write header
+ Header header;
+ GetHeader(header);
+
+ rv = WriteValue(out, header);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Write prefixes
+ rv = mVLPrefixSet->WritePrefixes(out);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Write checksum
+ nsCOMPtr<nsISafeOutputStream> safeOut = do_QueryInterface(out, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = safeOut->Finish();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ LOG(("[%s] Storing PrefixSet successful", mTableName.get()));
+
+ // This is to remove old ".pset" files if exist
+ Unused << ClearLegacyFile();
+ return NS_OK;
+}
+
+nsresult LookupCache::LoadFromFile(nsCOMPtr<nsIFile>& aFile) {
+ NS_ENSURE_ARG_POINTER(aFile);
+
+ Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_VLPS_FILELOAD_TIME> timer;
+
+ nsCOMPtr<nsIInputStream> localInFile;
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(localInFile), aFile,
+ PR_RDONLY | nsIFile::OS_READAHEAD);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Calculate how big the file is, make sure our read buffer isn't bigger
+ // than the file itself which is just wasting memory.
+ int64_t fileSize;
+ rv = aFile->GetFileSize(&fileSize);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (fileSize < 0 || fileSize > UINT32_MAX) {
+ return NS_ERROR_FAILURE;
+ }
+
+ uint32_t bufferSize =
+ std::min<uint32_t>(static_cast<uint32_t>(fileSize), MAX_BUFFER_SIZE);
+
+ // Convert to buffered stream
+ nsCOMPtr<nsIInputStream> in;
+ rv = NS_NewBufferedInputStream(getter_AddRefs(in), localInFile.forget(),
+ bufferSize);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Load header
+ Header header;
+ rv = ReadValue(in, header);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ LOG(("Failed to read header for %s", mTableName.get()));
+ return NS_ERROR_FILE_CORRUPTED;
+ }
+
+ rv = SanityCheck(header);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Load data
+ rv = mVLPrefixSet->LoadPrefixes(in);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Load crc32 checksum and verify
+ rv = VerifyCRC32(in);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mPrimed = true;
+
+ LOG(("[%s] Loading PrefixSet successful", mTableName.get()));
+ return NS_OK;
+}
+
+// This function assumes CRC32 checksum is in the end of the input stream
+nsresult LookupCache::VerifyCRC32(nsCOMPtr<nsIInputStream>& aIn) {
+ nsCOMPtr<nsISeekableStream> seekIn = do_QueryInterface(aIn);
+ nsresult rv = seekIn->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ uint64_t len;
+ rv = aIn->Available(&len);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ uint32_t calculateCrc32 = ~0;
+
+ // We don't want to include the checksum itself
+ len = len - nsCrc32CheckSumedOutputStream::CHECKSUM_SIZE;
+
+ static const uint64_t STREAM_BUFFER_SIZE = 4096;
+ char buffer[STREAM_BUFFER_SIZE];
+ while (len) {
+ uint32_t read;
+ uint64_t readLimit = std::min<uint64_t>(STREAM_BUFFER_SIZE, len);
+
+ rv = aIn->Read(buffer, readLimit, &read);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ calculateCrc32 = ComputeCrc32c(
+ calculateCrc32, reinterpret_cast<const uint8_t*>(buffer), read);
+
+ len -= read;
+ }
+
+ // Now read the CRC32
+ uint32_t crc32;
+ ReadValue(aIn, crc32);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (crc32 != calculateCrc32) {
+ return NS_ERROR_FILE_CORRUPTED;
+ }
+
+ return NS_OK;
+}
+
+nsresult LookupCacheV2::Has(const Completion& aCompletion, bool* aHas,
+ uint32_t* aMatchLength, bool* aConfirmed) {
+ *aHas = *aConfirmed = false;
+ *aMatchLength = 0;
+
+ uint32_t length = 0;
+ nsDependentCSubstring fullhash;
+ fullhash.Rebind((const char*)aCompletion.buf, COMPLETE_SIZE);
+
+ uint32_t prefix = aCompletion.ToUint32();
+
+ nsresult rv = mVLPrefixSet->Matches(prefix, fullhash, &length);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (length == 0) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(length == PREFIX_SIZE || length == COMPLETE_SIZE);
+
+ *aHas = true;
+ *aMatchLength = length;
+ *aConfirmed = length == COMPLETE_SIZE;
+
+ if (!(*aConfirmed)) {
+ rv = CheckCache(aCompletion, aHas, aConfirmed);
+ }
+
+ return rv;
+}
+
+nsresult LookupCacheV2::Build(AddPrefixArray& aAddPrefixes,
+ AddCompleteArray& aAddCompletes) {
+ nsresult rv = mVLPrefixSet->SetPrefixes(aAddPrefixes, aAddCompletes);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ mPrimed = true;
+
+ return NS_OK;
+}
+
+nsresult LookupCacheV2::GetPrefixes(FallibleTArray<uint32_t>& aAddPrefixes) {
+ if (!mPrimed) {
+ // This can happen if its a new table, so no error.
+ LOG(("GetPrefixes from empty LookupCache"));
+ return NS_OK;
+ }
+
+ return mVLPrefixSet->GetFixedLengthPrefixes(&aAddPrefixes, nullptr);
+}
+
+nsresult LookupCacheV2::GetPrefixes(FallibleTArray<uint32_t>& aAddPrefixes,
+ FallibleTArray<nsCString>& aAddCompletes) {
+ if (!mPrimed) {
+ // This can happen if its a new table, so no error.
+ LOG(("GetHashes from empty LookupCache"));
+ return NS_OK;
+ }
+
+ return mVLPrefixSet->GetFixedLengthPrefixes(&aAddPrefixes, &aAddCompletes);
+}
+
+nsresult LookupCacheV2::GetPrefixByIndex(uint32_t aIndex,
+ uint32_t* aOutPrefix) const {
+ NS_ENSURE_ARG_POINTER(aOutPrefix);
+
+ return mVLPrefixSet->GetFixedLengthPrefixByIndex(aIndex, aOutPrefix);
+}
+
+void LookupCacheV2::AddGethashResultToCache(
+ const AddCompleteArray& aAddCompletes, const MissPrefixArray& aMissPrefixes,
+ int64_t aExpirySec) {
+ static const int64_t CACHE_DURATION_SEC = 15 * 60;
+ int64_t defaultExpirySec = PR_Now() / PR_USEC_PER_SEC + CACHE_DURATION_SEC;
+ if (aExpirySec != 0) {
+ defaultExpirySec = aExpirySec;
+ }
+
+ for (const AddComplete& add : aAddCompletes) {
+ nsDependentCSubstring fullhash(
+ reinterpret_cast<const char*>(add.CompleteHash().buf), COMPLETE_SIZE);
+
+ CachedFullHashResponse* response =
+ mFullHashCache.GetOrInsertNew(add.ToUint32());
+ response->negativeCacheExpirySec = defaultExpirySec;
+
+ FullHashExpiryCache& fullHashes = response->fullHashes;
+ fullHashes.InsertOrUpdate(fullhash, defaultExpirySec);
+ }
+
+ for (const Prefix& prefix : aMissPrefixes) {
+ CachedFullHashResponse* response =
+ mFullHashCache.GetOrInsertNew(prefix.ToUint32());
+
+ response->negativeCacheExpirySec = defaultExpirySec;
+ }
+}
+
+void LookupCacheV2::GetHeader(Header& aHeader) {
+ aHeader.magic = LookupCacheV2::VLPSET_MAGIC;
+ aHeader.version = LookupCacheV2::VLPSET_VERSION;
+}
+
+nsresult LookupCacheV2::SanityCheck(const Header& aHeader) {
+ if (aHeader.magic != LookupCacheV2::VLPSET_MAGIC) {
+ return NS_ERROR_FILE_CORRUPTED;
+ }
+
+ if (aHeader.version != LookupCacheV2::VLPSET_VERSION) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult LookupCacheV2::LoadLegacyFile() {
+ // Because mozilla Safe Browsing v2 server only includes completions
+ // in the update, we can simplify this function by only loading .sbtore
+ if (!mProvider.EqualsLiteral("mozilla")) {
+ return NS_OK;
+ }
+
+ HashStore store(mTableName, mProvider, mRootStoreDirectory);
+
+ // Support loading version 3 HashStore.
+ nsresult rv = store.Open(3);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (store.AddChunks().Length() == 0 && store.SubChunks().Length() == 0) {
+ // Return when file doesn't exist
+ return NS_OK;
+ }
+
+ AddPrefixArray prefix;
+ AddCompleteArray addComplete;
+
+ rv = store.ReadCompletionsLegacyV3(addComplete);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return Build(prefix, addComplete);
+}
+
+nsresult LookupCacheV2::ClearLegacyFile() {
+ nsCOMPtr<nsIFile> file;
+ nsresult rv = mStoreDirectory->Clone(getter_AddRefs(file));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = file->AppendNative(mTableName + ".pset"_ns);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ bool exists;
+ rv = file->Exists(&exists);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (exists) {
+ rv = file->Remove(false);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ LOG(("[%s]Old PrefixSet is successfully removed!", mTableName.get()));
+ }
+
+ return NS_OK;
+}
+
+nsCString LookupCacheV2::GetPrefixSetSuffix() const { return ".vlpset"_ns; }
+
+// Support creating built-in entries for phsihing, malware, unwanted, harmful,
+// tracking/tracking exceptionlist and flash block tables.
+//
+nsresult LookupCacheV2::LoadMozEntries() {
+ // We already have the entries, return
+ if (!IsEmpty() || IsPrimed()) {
+ return NS_OK;
+ }
+
+ nsTArray<nsLiteralCString> entries;
+
+ if (mTableName.EqualsLiteral("moztest-phish-simple")) {
+ // Entries for phishing table
+ entries.AppendElement("itisatrap.org/firefox/its-a-trap.html"_ns);
+ } else if (mTableName.EqualsLiteral("moztest-malware-simple")) {
+ // Entries for malware table
+ entries.AppendElement("itisatrap.org/firefox/its-an-attack.html"_ns);
+ } else if (mTableName.EqualsLiteral("moztest-unwanted-simple")) {
+ // Entries for unwanted table
+ entries.AppendElement("itisatrap.org/firefox/unwanted.html"_ns);
+ } else if (mTableName.EqualsLiteral("moztest-harmful-simple")) {
+ // Entries for harmfule tables
+ entries.AppendElement("itisatrap.org/firefox/harmful.html"_ns);
+ } else if (mTableName.EqualsLiteral("moztest-track-simple")) {
+ // Entries for tracking table
+ entries.AppendElement("trackertest.org/"_ns);
+ entries.AppendElement("itisatracker.org/"_ns);
+ } else if (mTableName.EqualsLiteral("moztest-trackwhite-simple")) {
+ // Entries for tracking entitylist table
+ entries.AppendElement("itisatrap.org/?resource=itisatracker.org"_ns);
+ } else if (mTableName.EqualsLiteral("moztest-block-simple")) {
+ // Entries for flash block table
+ entries.AppendElement("itisatrap.org/firefox/blocked.html"_ns);
+ } else {
+ MOZ_ASSERT_UNREACHABLE();
+ }
+
+ AddPrefixArray prefix;
+ AddCompleteArray completes;
+ for (const auto& entry : entries) {
+ AddComplete add;
+ if (NS_FAILED(add.complete.FromPlaintext(entry))) {
+ continue;
+ }
+ if (!completes.AppendElement(add, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ return Build(prefix, completes);
+}
+
+} // namespace safebrowsing
+} // namespace mozilla
diff --git a/toolkit/components/url-classifier/LookupCache.h b/toolkit/components/url-classifier/LookupCache.h
new file mode 100644
index 0000000000..090f8eb733
--- /dev/null
+++ b/toolkit/components/url-classifier/LookupCache.h
@@ -0,0 +1,356 @@
+//* -*- 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/. */
+
+#ifndef LookupCache_h__
+#define LookupCache_h__
+
+#include "Entries.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsCOMPtr.h"
+#include "nsIFile.h"
+#include "mozilla/RefPtr.h"
+#include "nsUrlClassifierPrefixSet.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 {
+ public:
+ LookupResult()
+ : mNoise(false),
+ mProtocolConfirmed(false),
+ mPartialHashLength(0),
+ mConfirmed(false),
+ mProtocolV2(true) {}
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LookupResult);
+
+ // The fragment that matched in the LookupCache
+ union {
+ Prefix fixedLengthPrefix;
+ Completion complete;
+ } hash;
+
+ const Completion& CompleteHash() const {
+ MOZ_ASSERT(!mNoise);
+ return hash.complete;
+ }
+
+ nsCString PartialHash() const {
+ MOZ_ASSERT(mPartialHashLength <= COMPLETE_SIZE);
+ if (mNoise) {
+ return nsCString(
+ reinterpret_cast<const char*>(hash.fixedLengthPrefix.buf),
+ PREFIX_SIZE);
+ } else {
+ return nsCString(reinterpret_cast<const char*>(hash.complete.buf),
+ mPartialHashLength);
+ }
+ }
+
+ nsAutoCString PartialHashHex() const {
+ nsAutoCString hex;
+ for (size_t i = 0; i < mPartialHashLength; i++) {
+ hex.AppendPrintf("%.2X", hash.complete.buf[i]);
+ }
+ return hex;
+ }
+
+ bool Confirmed() const { return mConfirmed || mProtocolConfirmed; }
+
+ // True if we have a complete match for this hash in the table.
+ bool Complete() const { return mPartialHashLength == COMPLETE_SIZE; }
+
+ // True if this is a noise entry, i.e. an extra entry
+ // that is inserted to mask the true URL we are requesting.
+ // Noise entries will not have a complete 256-bit hash as
+ // they are fetched from the local 32-bit database and we
+ // don't know the corresponding full URL.
+ bool mNoise;
+
+ bool mProtocolConfirmed;
+
+ nsCString mTableName;
+
+ uint32_t mPartialHashLength;
+
+ // True as long as this lookup is complete and hasn't expired.
+ bool mConfirmed;
+
+ bool mProtocolV2;
+
+ private:
+ ~LookupResult() = default;
+};
+
+typedef nsTArray<RefPtr<LookupResult>> LookupResultArray;
+
+class CacheResult {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CacheResult);
+
+ enum { V2, V4 };
+
+ virtual int Ver() const = 0;
+ virtual bool findCompletion(const Completion& aCompletion) const = 0;
+
+ template <typename T>
+ static const T* Cast(const CacheResult* aThat) {
+ return ((aThat && T::VER == aThat->Ver())
+ ? reinterpret_cast<const T*>(aThat)
+ : nullptr);
+ }
+
+ nsCString table;
+ Prefix prefix;
+
+ protected:
+ virtual ~CacheResult() = default;
+};
+
+class CacheResultV2 final : public CacheResult {
+ public:
+ static const int VER;
+
+ // True when 'prefix' in CacheResult indicates a prefix that
+ // cannot be completed.
+ bool miss = false;
+
+ // 'completion' and 'addChunk' are used when 'miss' field is false.
+ Completion completion;
+ uint32_t addChunk;
+
+ bool operator==(const CacheResultV2& aOther) const {
+ if (table != aOther.table || prefix != aOther.prefix ||
+ miss != aOther.miss) {
+ return false;
+ }
+
+ if (miss) {
+ return true;
+ }
+ return completion == aOther.completion && addChunk == aOther.addChunk;
+ }
+
+ bool findCompletion(const Completion& aCompletion) const override {
+ return completion == aCompletion;
+ }
+
+ virtual int Ver() const override { return VER; }
+};
+
+class CacheResultV4 final : public CacheResult {
+ public:
+ static const int VER;
+
+ CachedFullHashResponse response;
+
+ bool operator==(const CacheResultV4& aOther) const {
+ return table == aOther.table && prefix == aOther.prefix &&
+ response == aOther.response;
+ }
+
+ bool findCompletion(const Completion& aCompletion) const override {
+ nsDependentCSubstring completion(
+ reinterpret_cast<const char*>(aCompletion.buf), COMPLETE_SIZE);
+ return response.fullHashes.Contains(completion);
+ }
+
+ virtual int Ver() const override { return VER; }
+};
+
+typedef nsTArray<RefPtr<const CacheResult>> ConstCacheResultArray;
+
+class LookupCache {
+ public:
+ // Check for a canonicalized IP address.
+ static bool IsCanonicalizedIP(const nsACString& aHost);
+
+ // take a lookup string (www.hostname.com/path/to/resource.html) and
+ // expand it into the set of fragments that should be searched for in an
+ // entry
+ static nsresult GetLookupFragments(const nsACString& aSpec,
+ nsTArray<nsCString>* aFragments);
+
+ static nsresult GetLookupEntitylistFragments(const nsACString& aSpec,
+ nsTArray<nsCString>* aFragments);
+
+ LookupCache(const nsACString& aTableName, const nsACString& aProvider,
+ nsCOMPtr<nsIFile>& aStoreFile);
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LookupCache);
+
+ const nsCString& TableName() const { return mTableName; }
+
+ // The directory handle where we operate will
+ // be moved away when a backup is made.
+ nsresult UpdateRootDirHandle(nsCOMPtr<nsIFile>& aRootStoreDirectory);
+
+ // Write data stored in lookup cache to disk.
+ nsresult WriteFile();
+
+ bool IsPrimed() const { return mPrimed; };
+
+ // Called when update to clear expired entries.
+ void InvalidateExpiredCacheEntries();
+
+ // Copy fullhash cache from another LookupCache.
+ void CopyFullHashCache(const LookupCache* aSource);
+
+ // Clear fullhash cache from fullhash/gethash response.
+ void ClearCache();
+
+ // Check if completions can be found in cache.
+ // Currently this is only used by testcase.
+ bool IsInCache(uint32_t key) const { return mFullHashCache.Get(key); };
+
+ uint32_t PrefixLength() const {
+ return mVLPrefixSet->FixedLengthPrefixLength();
+ }
+
+#if DEBUG
+ void DumpCache() const;
+#endif
+
+ void GetCacheInfo(nsIUrlClassifierCacheInfo** aCache) const;
+
+ nsresult VerifyCRC32(nsCOMPtr<nsIInputStream>& aIn);
+
+ virtual nsresult Open();
+ virtual nsresult Init();
+ ;
+ virtual nsresult ClearPrefixes();
+ virtual nsresult Has(const Completion& aCompletion, bool* aHas,
+ uint32_t* aMatchLength, bool* aConfirmed) = 0;
+
+ // Prefix files file header
+ struct Header {
+ uint32_t magic;
+ uint32_t version;
+ };
+
+ virtual nsresult StoreToFile(nsCOMPtr<nsIFile>& aFile);
+ virtual nsresult LoadFromFile(nsCOMPtr<nsIFile>& aFile);
+
+ virtual bool IsEmpty() const;
+
+ virtual void ClearAll();
+
+ virtual nsresult LoadMozEntries() = 0;
+
+ template <typename T>
+ static T* Cast(LookupCache* aThat) {
+ return ((aThat && T::VER == aThat->Ver()) ? reinterpret_cast<T*>(aThat)
+ : nullptr);
+ }
+ template <typename T>
+ static const T* Cast(const LookupCache* aThat) {
+ return ((aThat && T::VER == aThat->Ver())
+ ? reinterpret_cast<const T*>(aThat)
+ : nullptr);
+ }
+
+ private:
+ nsresult LoadPrefixSet();
+
+ virtual size_t SizeOfPrefixSet() const;
+ virtual nsCString GetPrefixSetSuffix() const = 0;
+
+ virtual int Ver() const = 0;
+
+ virtual void GetHeader(Header& aHeader) = 0;
+ virtual nsresult SanityCheck(const Header& aHeader) = 0;
+ virtual nsresult LoadLegacyFile() = 0;
+ virtual nsresult ClearLegacyFile() = 0;
+
+ protected:
+ virtual ~LookupCache() = default;
+
+ // Buffer size for file read/write
+ static const uint32_t MAX_BUFFER_SIZE;
+
+ // Check completions in positive cache and prefix in negative cache.
+ // 'aHas' and 'aConfirmed' are output parameters.
+ nsresult CheckCache(const Completion& aCompletion, bool* aHas,
+ bool* aConfirmed);
+
+ bool mPrimed; // true when the PrefixSet has been loaded (or constructed)
+ const nsCString mTableName;
+ const nsCString mProvider;
+ nsCOMPtr<nsIFile> mRootStoreDirectory;
+ nsCOMPtr<nsIFile> mStoreDirectory;
+
+ // For gtest to inspect private members.
+ friend class PerProviderDirectoryTestUtils;
+
+ // Cache stores fullhash response(V4)/gethash response(V2)
+ FullHashResponseMap mFullHashCache;
+
+ RefPtr<VariableLengthPrefixSet> mVLPrefixSet;
+
+ template <typename T>
+ static nsresult WriteValue(nsIOutputStream* aOutputStream, const T& aValue);
+ template <typename T>
+ static nsresult ReadValue(nsIInputStream* aInputStream, T& aValue);
+};
+
+typedef nsTArray<RefPtr<LookupCache>> LookupCacheArray;
+
+class LookupCacheV2 final : public LookupCache {
+ public:
+ explicit LookupCacheV2(const nsACString& aTableName,
+ const nsACString& aProvider,
+ nsCOMPtr<nsIFile>& aStoreFile)
+ : LookupCache(aTableName, aProvider, aStoreFile) {}
+
+ virtual nsresult Has(const Completion& aCompletion, bool* aHas,
+ uint32_t* aMatchLength, bool* aConfirmed) override;
+
+ nsresult Build(AddPrefixArray& aAddPrefixes, AddCompleteArray& aAddCompletes);
+
+ nsresult GetPrefixes(FallibleTArray<uint32_t>& aAddPrefixes);
+ nsresult GetPrefixes(FallibleTArray<uint32_t>& aAddPrefixes,
+ FallibleTArray<nsCString>& aAddCompletes);
+ nsresult GetPrefixByIndex(uint32_t aIndex, uint32_t* aOutPrefix) const;
+
+ // This will Clear() the passed arrays when done.
+ // 'aExpirySec' is used by testcase to config an expired time.
+ void AddGethashResultToCache(const AddCompleteArray& aAddCompletes,
+ const MissPrefixArray& aMissPrefixes,
+ int64_t aExpirySec = 0);
+
+ virtual nsresult LoadMozEntries() override;
+
+ static const int VER;
+ static const uint32_t VLPSET_MAGIC;
+ static const uint32_t VLPSET_VERSION;
+
+ protected:
+ virtual nsCString GetPrefixSetSuffix() const override;
+
+ private:
+ ~LookupCacheV2() = default;
+
+ virtual int Ver() const override { return VER; }
+
+ virtual void GetHeader(Header& aHeader) override;
+ virtual nsresult SanityCheck(const Header& aHeader) override;
+
+ virtual nsresult LoadLegacyFile() override;
+ virtual nsresult ClearLegacyFile() override;
+};
+
+} // namespace safebrowsing
+} // namespace mozilla
+
+#endif
diff --git a/toolkit/components/url-classifier/LookupCacheV4.cpp b/toolkit/components/url-classifier/LookupCacheV4.cpp
new file mode 100644
index 0000000000..14ebc890d4
--- /dev/null
+++ b/toolkit/components/url-classifier/LookupCacheV4.cpp
@@ -0,0 +1,577 @@
+//* -*- 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 "LookupCacheV4.h"
+#include "HashStore.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Unused.h"
+#include "nsCheckSummedOutputStream.h"
+#include "nsUrlClassifierDBService.h"
+#include "crc32c.h"
+#include <string>
+
+// MOZ_LOG=UrlClassifierDbService:5
+extern mozilla::LazyLogModule gUrlClassifierDbServiceLog;
+#define LOG(args) \
+ MOZ_LOG(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() \
+ MOZ_LOG_TEST(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug)
+
+#define METADATA_SUFFIX ".metadata"_ns
+
+namespace mozilla {
+namespace safebrowsing {
+
+////////////////////////////////////////////////////////////////////////
+
+// Prefixes coming from updates and VLPrefixSet are both stored in the HashTable
+// where the (key, value) pair is a prefix size and a lexicographic-sorted
+// string. The difference is prefixes from updates use std:string(to avoid
+// additional copies) and prefixes from VLPrefixSet use nsCString. This class
+// provides a common interface for the partial update algorithm to make it
+// easier to operate on two different kind prefix string map..
+class VLPrefixSet {
+ public:
+ explicit VLPrefixSet(const PrefixStringMap& aMap);
+
+ // This function will merge the prefix map in VLPrefixSet to aPrefixMap.
+ void Merge(PrefixStringMap& aPrefixMap);
+
+ // Find the smallest string from the map in VLPrefixSet.
+ bool GetSmallestPrefix(nsACString& aOutString) const;
+
+ // Return the number of prefixes in the map
+ uint32_t Count() const { return mCount; }
+
+ private:
+ // PrefixString structure contains a lexicographic-sorted string with
+ // a |pos| variable to indicate which substring we are pointing to right now.
+ // |pos| increases each time GetSmallestPrefix finds the smallest string.
+ struct PrefixString {
+ PrefixString(const nsACString& aStr, uint32_t aSize)
+ : data(aStr), pos(0), size(aSize) {
+ MOZ_ASSERT(data.Length() % size == 0,
+ "PrefixString length must be a multiple of the prefix size.");
+ }
+
+ void getRemainingString(nsACString& out) {
+ MOZ_ASSERT(out.IsEmpty());
+ if (remaining() > 0) {
+ out = Substring(data, pos);
+ }
+ }
+ void getPrefix(nsACString& out) {
+ MOZ_ASSERT(out.IsEmpty());
+ if (remaining() >= size) {
+ out = Substring(data, pos, size);
+ } else {
+ MOZ_ASSERT(remaining() == 0,
+ "Remaining bytes but not enough for a (size)-byte prefix.");
+ }
+ }
+ void next() {
+ pos += size;
+ MOZ_ASSERT(pos <= data.Length());
+ }
+ uint32_t remaining() {
+ return data.Length() - pos;
+ MOZ_ASSERT(pos <= data.Length());
+ }
+
+ nsCString data;
+ uint32_t pos;
+ uint32_t size;
+ };
+
+ nsClassHashtable<nsUint32HashKey, PrefixString> mMap;
+ uint32_t mCount;
+};
+
+nsresult LookupCacheV4::Has(const Completion& aCompletion, bool* aHas,
+ uint32_t* aMatchLength, bool* aConfirmed) {
+ *aHas = *aConfirmed = false;
+ *aMatchLength = 0;
+
+ uint32_t length = 0;
+ nsDependentCSubstring fullhash;
+ fullhash.Rebind((const char*)aCompletion.buf, COMPLETE_SIZE);
+
+ // It is tricky that we use BigEndian read for V4 while use
+ // Completion.ToUint32 for V2. This is because in V2, prefixes are converted
+ // to integers and then sorted internally. In V4, prefixes recieved are
+ // already lexicographical order sorted, so when we manipulate these prefixes
+ // with integer form, we always use big endian so prefixes remain the same
+ // order.
+ uint32_t prefix = BigEndian::readUint32(
+ reinterpret_cast<const uint32_t*>(fullhash.BeginReading()));
+
+ nsresult rv = mVLPrefixSet->Matches(prefix, fullhash, &length);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (length == 0) {
+ return NS_OK;
+ }
+
+ MOZ_ASSERT(length >= PREFIX_SIZE && length <= COMPLETE_SIZE);
+
+ // For V4, We don't set |aConfirmed| to true even if we found a match
+ // for 32-bytes prefix. |aConfirmed| is only set if a match is found in cache.
+ *aHas = true;
+ *aMatchLength = length;
+
+ // Even though V4 supports variable-length prefix, we always send 4-bytes for
+ // completion (Bug 1323953). This means cached prefix length is also 4-bytes.
+ return CheckCache(aCompletion, aHas, aConfirmed);
+}
+
+nsresult LookupCacheV4::Build(PrefixStringMap& aPrefixMap) {
+ Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_VLPS_CONSTRUCT_TIME> timer;
+
+ nsresult rv = mVLPrefixSet->SetPrefixes(aPrefixMap);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ mPrimed = true;
+
+ return NS_OK;
+}
+
+nsresult LookupCacheV4::GetPrefixes(PrefixStringMap& aPrefixMap) {
+ if (!mPrimed) {
+ // This can happen if its a new table, so no error.
+ LOG(("GetPrefixes from empty LookupCache"));
+ return NS_OK;
+ }
+ return mVLPrefixSet->GetPrefixes(aPrefixMap);
+}
+
+nsresult LookupCacheV4::GetFixedLengthPrefixes(
+ FallibleTArray<uint32_t>& aPrefixes) {
+ return mVLPrefixSet->GetFixedLengthPrefixes(&aPrefixes, nullptr);
+}
+
+nsresult LookupCacheV4::GetFixedLengthPrefixByIndex(
+ uint32_t aIndex, uint32_t* aOutPrefix) const {
+ NS_ENSURE_ARG_POINTER(aOutPrefix);
+
+ return mVLPrefixSet->GetFixedLengthPrefixByIndex(aIndex, aOutPrefix);
+}
+
+nsresult LookupCacheV4::ClearLegacyFile() {
+ nsCOMPtr<nsIFile> file;
+ nsresult rv = mStoreDirectory->Clone(getter_AddRefs(file));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = file->AppendNative(mTableName + ".pset"_ns);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ bool exists;
+ rv = file->Exists(&exists);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (exists) {
+ rv = file->Remove(false);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ LOG(("[%s] Old PrefixSet is successfully removed!", mTableName.get()));
+ }
+
+ return NS_OK;
+}
+
+nsresult LookupCacheV4::LoadLegacyFile() {
+ nsCOMPtr<nsIFile> file;
+ nsresult rv = mStoreDirectory->Clone(getter_AddRefs(file));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = file->AppendNative(mTableName + ".pset"_ns);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ bool exists;
+ rv = file->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!exists) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIInputStream> localInFile;
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(localInFile), file,
+ PR_RDONLY | nsIFile::OS_READAHEAD);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Calculate how big the file is, make sure our read buffer isn't bigger
+ // than the file itself which is just wasting memory.
+ int64_t fileSize;
+ rv = file->GetFileSize(&fileSize);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (fileSize < 0 || fileSize > UINT32_MAX) {
+ return NS_ERROR_FAILURE;
+ }
+
+ uint32_t bufferSize =
+ std::min<uint32_t>(static_cast<uint32_t>(fileSize), MAX_BUFFER_SIZE);
+
+ // Convert to buffered stream
+ nsCOMPtr<nsIInputStream> in;
+ rv = NS_NewBufferedInputStream(getter_AddRefs(in), localInFile.forget(),
+ bufferSize);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Load data
+ rv = mVLPrefixSet->LoadPrefixes(in);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ mPrimed = true;
+
+ LOG(("[%s] Loading Legacy PrefixSet successful", mTableName.get()));
+ return NS_OK;
+}
+
+void LookupCacheV4::GetHeader(Header& aHeader) {
+ aHeader.magic = LookupCacheV4::VLPSET_MAGIC;
+ aHeader.version = LookupCacheV4::VLPSET_VERSION;
+}
+
+nsresult LookupCacheV4::SanityCheck(const Header& aHeader) {
+ if (aHeader.magic != LookupCacheV4::VLPSET_MAGIC) {
+ return NS_ERROR_FILE_CORRUPTED;
+ }
+
+ if (aHeader.version != LookupCacheV4::VLPSET_VERSION) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsCString LookupCacheV4::GetPrefixSetSuffix() const { return ".vlpset"_ns; }
+
+static nsresult AppendPrefixToMap(PrefixStringMap& prefixes,
+ const nsACString& prefix) {
+ uint32_t len = prefix.Length();
+ MOZ_ASSERT(len >= PREFIX_SIZE && len <= COMPLETE_SIZE);
+ if (!len) {
+ return NS_OK;
+ }
+
+ nsCString* prefixString = prefixes.GetOrInsertNew(len);
+ if (!prefixString->Append(prefix, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+static nsresult InitCrypto(nsCOMPtr<nsICryptoHash>& aCrypto) {
+ nsresult rv;
+ aCrypto = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = aCrypto->Init(nsICryptoHash::SHA256);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "InitCrypto failed");
+
+ return rv;
+}
+
+// Read prefix into a buffer and also update the hash which
+// keeps track of the sha256 hash
+static void UpdateSHA256(nsICryptoHash* aCrypto, const nsACString& aPrefix) {
+ MOZ_ASSERT(aCrypto);
+ aCrypto->Update(
+ reinterpret_cast<uint8_t*>(const_cast<char*>(aPrefix.BeginReading())),
+ aPrefix.Length());
+}
+
+// Please see https://bug1287058.bmoattachments.org/attachment.cgi?id=8795366
+// for detail about partial update algorithm.
+nsresult LookupCacheV4::ApplyUpdate(RefPtr<TableUpdateV4> aTableUpdate,
+ PrefixStringMap& aInputMap,
+ PrefixStringMap& aOutputMap) {
+ MOZ_ASSERT(aOutputMap.IsEmpty());
+
+ nsCOMPtr<nsICryptoHash> crypto;
+ nsresult rv = InitCrypto(crypto);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // oldPSet contains prefixes we already have or we just merged last round.
+ // addPSet contains prefixes stored in tableUpdate which should be merged with
+ // oldPSet.
+ VLPrefixSet oldPSet(aInputMap);
+ VLPrefixSet addPSet(aTableUpdate->Prefixes());
+
+ // RemovalIndiceArray is a sorted integer array indicating the index of prefix
+ // we should remove from the old prefix set(according to lexigraphic order).
+ // |removalIndex| is the current index of RemovalIndiceArray.
+ // |numOldPrefixPicked| is used to record how many prefixes we picked from the
+ // old map.
+ const TableUpdateV4::RemovalIndiceArray& removalArray =
+ aTableUpdate->RemovalIndices();
+ uint32_t removalIndex = 0;
+ int32_t numOldPrefixPicked = -1;
+
+ nsAutoCString smallestOldPrefix;
+ nsAutoCString smallestAddPrefix;
+
+ bool isOldMapEmpty = false, isAddMapEmpty = false;
+
+ // This is used to avoid infinite loop for partial update algorithm.
+ // The maximum loops will be the number of old prefixes plus the number of add
+ // prefixes.
+ int32_t index = oldPSet.Count() + addPSet.Count() + 1;
+ for (; index > 0; index--) {
+ // Get smallest prefix from the old prefix set if we don't have one
+ if (smallestOldPrefix.IsEmpty() && !isOldMapEmpty) {
+ isOldMapEmpty = !oldPSet.GetSmallestPrefix(smallestOldPrefix);
+ }
+
+ // Get smallest prefix from add prefix set if we don't have one
+ if (smallestAddPrefix.IsEmpty() && !isAddMapEmpty) {
+ isAddMapEmpty = !addPSet.GetSmallestPrefix(smallestAddPrefix);
+ }
+
+ bool pickOld;
+
+ // If both prefix sets are not empty, then compare to find the smaller one.
+ if (!isOldMapEmpty && !isAddMapEmpty) {
+ if (smallestOldPrefix == smallestAddPrefix) {
+ LOG(("Add prefix should not exist in the original prefix set."));
+ return NS_ERROR_UC_UPDATE_DUPLICATE_PREFIX;
+ }
+
+ // Compare the smallest string in old prefix set and add prefix set,
+ // merge the smaller one into new map to ensure merged string still
+ // follows lexigraphic order.
+ pickOld = smallestOldPrefix < smallestAddPrefix;
+ } else if (!isOldMapEmpty && isAddMapEmpty) {
+ pickOld = true;
+ } else if (isOldMapEmpty && !isAddMapEmpty) {
+ pickOld = false;
+ // If both maps are empty, then partial update is complete.
+ } else {
+ break;
+ }
+
+ if (pickOld) {
+ numOldPrefixPicked++;
+
+ // If the number of picks from old map matches the removalIndex, then this
+ // prefix will be removed by not merging it to new map.
+ if (removalIndex < removalArray.Length() &&
+ numOldPrefixPicked == removalArray[removalIndex]) {
+ removalIndex++;
+ } else {
+ rv = AppendPrefixToMap(aOutputMap, smallestOldPrefix);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ UpdateSHA256(crypto, smallestOldPrefix);
+ }
+ smallestOldPrefix.SetLength(0);
+ } else {
+ rv = AppendPrefixToMap(aOutputMap, smallestAddPrefix);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ UpdateSHA256(crypto, smallestAddPrefix);
+ smallestAddPrefix.SetLength(0);
+ }
+ }
+
+ // We expect index will be greater to 0 because max number of runs will be
+ // the number of original prefix plus add prefix.
+ if (index <= 0) {
+ LOG(("There are still prefixes remaining after reaching maximum runs."));
+ return NS_ERROR_UC_UPDATE_INFINITE_LOOP;
+ }
+
+ if (removalIndex < removalArray.Length()) {
+ LOG(
+ ("There are still prefixes to remove after exhausting the old "
+ "PrefixSet."));
+ return NS_ERROR_UC_UPDATE_WRONG_REMOVAL_INDICES;
+ }
+
+ // Prefixes and removal indice from update is no longer required
+ // after merging the data with local prefixes.
+ aTableUpdate->Clear();
+
+ nsAutoCString sha256;
+ crypto->Finish(false, sha256);
+ if (aTableUpdate->SHA256().IsEmpty()) {
+ LOG(("Update sha256 hash missing."));
+ Telemetry::Accumulate(
+ Telemetry::URLCLASSIFIER_UPDATE_ERROR, mProvider,
+ NS_ERROR_GET_CODE(NS_ERROR_UC_UPDATE_MISSING_CHECKSUM));
+
+ // Generate our own sha256 to tableUpdate to ensure there is always
+ // checksum in .metadata
+ std::string stdSha256(sha256.BeginReading(), sha256.Length());
+ aTableUpdate->SetSHA256(stdSha256);
+ } else if (aTableUpdate->SHA256() != sha256) {
+ LOG(("SHA256 hash mismatch after applying partial update"));
+ return NS_ERROR_UC_UPDATE_CHECKSUM_MISMATCH;
+ }
+
+ return NS_OK;
+}
+
+nsresult LookupCacheV4::AddFullHashResponseToCache(
+ const FullHashResponseMap& aResponseMap) {
+ CopyClassHashTable<FullHashResponseMap>(aResponseMap, mFullHashCache);
+
+ return NS_OK;
+}
+
+nsresult LookupCacheV4::WriteMetadata(
+ RefPtr<const TableUpdateV4> aTableUpdate) {
+ NS_ENSURE_ARG_POINTER(aTableUpdate);
+ if (nsUrlClassifierDBService::ShutdownHasStarted()) {
+ return NS_ERROR_ABORT;
+ }
+
+ nsCOMPtr<nsIFile> metaFile;
+ nsresult rv = mStoreDirectory->Clone(getter_AddRefs(metaFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = metaFile->AppendNative(mTableName + METADATA_SUFFIX);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIOutputStream> outputStream;
+ rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), metaFile,
+ PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Write the state.
+ rv = WriteValue(outputStream, aTableUpdate->ClientState());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Write the SHA256 hash.
+ rv = WriteValue(outputStream, aTableUpdate->SHA256());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return rv;
+}
+
+nsresult LookupCacheV4::LoadMetadata(nsACString& aState, nsACString& aSHA256) {
+ nsCOMPtr<nsIFile> metaFile;
+ nsresult rv = mStoreDirectory->Clone(getter_AddRefs(metaFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = metaFile->AppendNative(mTableName + METADATA_SUFFIX);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIInputStream> localInFile;
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(localInFile), metaFile,
+ PR_RDONLY | nsIFile::OS_READAHEAD);
+ if (NS_FAILED(rv)) {
+ LOG(("Unable to open metadata file."));
+ return rv;
+ }
+
+ // Read the list state.
+ rv = ReadValue(localInFile, aState);
+ if (NS_FAILED(rv)) {
+ LOG(("Failed to read state."));
+ return rv;
+ }
+
+ // Read the SHA256 hash.
+ rv = ReadValue(localInFile, aSHA256);
+ if (NS_FAILED(rv)) {
+ LOG(("Failed to read SHA256 hash."));
+ return rv;
+ }
+
+ return rv;
+}
+
+VLPrefixSet::VLPrefixSet(const PrefixStringMap& aMap) : mCount(0) {
+ for (const auto& entry : aMap) {
+ uint32_t size = entry.GetKey();
+ MOZ_ASSERT(entry.GetData()->Length() % size == 0,
+ "PrefixString must be a multiple of the prefix size.");
+ mMap.InsertOrUpdate(size, MakeUnique<PrefixString>(*entry.GetData(), size));
+ mCount += entry.GetData()->Length() / size;
+ }
+}
+
+void VLPrefixSet::Merge(PrefixStringMap& aPrefixMap) {
+ for (const auto& entry : mMap) {
+ nsCString* prefixString = aPrefixMap.GetOrInsertNew(entry.GetKey());
+ PrefixString* str = entry.GetWeak();
+
+ nsAutoCString remainingString;
+ str->getRemainingString(remainingString);
+ if (!remainingString.IsEmpty()) {
+ MOZ_ASSERT(remainingString.Length() == str->remaining());
+ prefixString->Append(remainingString);
+ }
+ }
+}
+
+bool VLPrefixSet::GetSmallestPrefix(nsACString& aOutString) const {
+ PrefixString* pick = nullptr;
+ for (const auto& entry : mMap) {
+ PrefixString* str = entry.GetWeak();
+
+ if (str->remaining() <= 0) {
+ continue;
+ }
+
+ if (aOutString.IsEmpty()) {
+ str->getPrefix(aOutString);
+ MOZ_ASSERT(aOutString.Length() == entry.GetKey());
+ pick = str;
+ continue;
+ }
+
+ nsAutoCString cur;
+ str->getPrefix(cur);
+ if (!cur.IsEmpty() && cur < aOutString) {
+ aOutString.Assign(cur);
+ MOZ_ASSERT(aOutString.Length() == entry.GetKey());
+ pick = str;
+ }
+ }
+
+ if (pick) {
+ pick->next();
+ }
+
+ return pick != nullptr;
+}
+
+nsresult LookupCacheV4::LoadMozEntries() { return NS_ERROR_NOT_IMPLEMENTED; }
+
+} // namespace safebrowsing
+} // namespace mozilla
diff --git a/toolkit/components/url-classifier/LookupCacheV4.h b/toolkit/components/url-classifier/LookupCacheV4.h
new file mode 100644
index 0000000000..b0bfbadcc3
--- /dev/null
+++ b/toolkit/components/url-classifier/LookupCacheV4.h
@@ -0,0 +1,70 @@
+//* -*- 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/. */
+
+#ifndef LookupCacheV4_h__
+#define LookupCacheV4_h__
+
+#include "LookupCache.h"
+
+namespace mozilla {
+namespace safebrowsing {
+
+// Forward declaration.
+class TableUpdateV4;
+
+class LookupCacheV4 final : public LookupCache {
+ public:
+ explicit LookupCacheV4(const nsACString& aTableName,
+ const nsACString& aProvider,
+ nsCOMPtr<nsIFile>& aStoreFile)
+ : LookupCache(aTableName, aProvider, aStoreFile) {}
+
+ virtual nsresult Has(const Completion& aCompletion, bool* aHas,
+ uint32_t* aMatchLength, bool* aConfirmed) override;
+
+ nsresult Build(PrefixStringMap& aPrefixMap);
+
+ nsresult GetPrefixes(PrefixStringMap& aPrefixMap);
+ nsresult GetFixedLengthPrefixes(FallibleTArray<uint32_t>& aPrefixes);
+ nsresult GetFixedLengthPrefixByIndex(uint32_t aIndex,
+ uint32_t* aOutPrefix) const;
+
+ // ApplyUpdate will merge data stored in aTableUpdate with prefixes in
+ // aInputMap.
+ nsresult ApplyUpdate(RefPtr<TableUpdateV4> aTableUpdate,
+ PrefixStringMap& aInputMap, PrefixStringMap& aOutputMap);
+
+ nsresult AddFullHashResponseToCache(const FullHashResponseMap& aResponseMap);
+
+ nsresult WriteMetadata(RefPtr<const TableUpdateV4> aTableUpdate);
+ nsresult LoadMetadata(nsACString& aState, nsACString& aChecksum);
+
+ virtual nsresult LoadMozEntries() override;
+
+ static constexpr int VER = 4;
+ static constexpr uint32_t MAX_METADATA_VALUE_LENGTH = 256;
+ static constexpr uint32_t VLPSET_MAGIC = 1;
+ static constexpr uint32_t VLPSET_VERSION = 0x36044a35;
+
+ protected:
+ virtual nsCString GetPrefixSetSuffix() const override;
+ nsCString GetMetadataSuffix() const;
+
+ private:
+ ~LookupCacheV4() = default;
+
+ virtual int Ver() const override { return VER; }
+
+ virtual nsresult LoadLegacyFile() override;
+ virtual nsresult ClearLegacyFile() override;
+
+ virtual void GetHeader(Header& aHeader) override;
+ virtual nsresult SanityCheck(const Header& aHeader) override;
+};
+
+} // namespace safebrowsing
+} // namespace mozilla
+
+#endif
diff --git a/toolkit/components/url-classifier/ProtocolParser.cpp b/toolkit/components/url-classifier/ProtocolParser.cpp
new file mode 100644
index 0000000000..2c9c919c58
--- /dev/null
+++ b/toolkit/components/url-classifier/ProtocolParser.cpp
@@ -0,0 +1,1080 @@
+//* -*- 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 "ProtocolParser.h"
+#include "LookupCache.h"
+#include "nsNetCID.h"
+#include "mozilla/Components.h"
+#include "mozilla/Logging.h"
+#include "prnetdb.h"
+#include "prprf.h"
+#include "Classifier.h"
+
+#include "nsUrlClassifierDBService.h"
+#include "nsUrlClassifierUtils.h"
+#include "nsPrintfCString.h"
+#include "mozilla/Base64.h"
+#include "RiceDeltaDecoder.h"
+#include "mozilla/EndianUtils.h"
+#include "mozilla/ErrorNames.h"
+#include "mozilla/IntegerPrintfMacros.h"
+
+// MOZ_LOG=UrlClassifierProtocolParser:5
+extern mozilla::LazyLogModule gUrlClassifierDbServiceLog;
+mozilla::LazyLogModule gUrlClassifierProtocolParserLog(
+ "UrlClassifierProtocolParser");
+#define PARSER_LOG(args) \
+ MOZ_LOG(gUrlClassifierProtocolParserLog, mozilla::LogLevel::Debug, args)
+
+#define LOG_ENABLED() \
+ MOZ_LOG_TEST(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug)
+
+namespace mozilla {
+namespace safebrowsing {
+
+// Updates will fail if fed chunks larger than this
+const uint32_t MAX_CHUNK_SIZE = (4 * 1024 * 1024);
+// Updates will fail if the total number of tocuhed chunks is larger than this
+const uint32_t MAX_CHUNK_RANGE = 1000000;
+
+const uint32_t DOMAIN_SIZE = 4;
+
+// Parse one stringified range of chunks of the form "n" or "n-m" from a
+// comma-separated list of chunks. Upon return, 'begin' will point to the
+// next range of chunks in the list of chunks.
+static bool ParseChunkRange(const nsAutoCString& string, uint32_t* aFirst,
+ uint32_t* aLast) {
+ uint32_t numRead = PR_sscanf(string.get(), "%u-%u", aFirst, aLast);
+ if (numRead == 2) {
+ if (*aFirst > *aLast) {
+ uint32_t tmp = *aFirst;
+ *aFirst = *aLast;
+ *aLast = tmp;
+ }
+ return true;
+ }
+
+ if (numRead == 1) {
+ *aLast = *aFirst;
+ return true;
+ }
+
+ return false;
+}
+
+///////////////////////////////////////////////////////////////
+// ProtocolParser implementation
+
+ProtocolParser::ProtocolParser() : mUpdateStatus(NS_OK), mUpdateWaitSec(0) {}
+
+ProtocolParser::~ProtocolParser() = default;
+
+nsresult ProtocolParser::Begin(const nsACString& aTable,
+ const nsTArray<nsCString>& aUpdateTables) {
+ // ProtocolParser objects should never be reused.
+ MOZ_ASSERT(mPending.IsEmpty());
+ MOZ_ASSERT(mTableUpdates.IsEmpty());
+ MOZ_ASSERT(mForwards.IsEmpty());
+ MOZ_ASSERT(mRequestedTables.IsEmpty());
+ MOZ_ASSERT(mTablesToReset.IsEmpty());
+
+ if (!aTable.IsEmpty()) {
+ SetCurrentTable(aTable);
+ }
+ SetRequestedTables(aUpdateTables);
+
+ return NS_OK;
+}
+
+RefPtr<TableUpdate> ProtocolParser::GetTableUpdate(const nsACString& aTable) {
+ for (uint32_t i = 0; i < mTableUpdates.Length(); i++) {
+ if (aTable.Equals(mTableUpdates[i]->TableName())) {
+ return mTableUpdates[i];
+ }
+ }
+
+ // We free automatically on destruction, ownership of these
+ // updates can be transferred to DBServiceWorker, which passes
+ // them back to Classifier when doing the updates, and that
+ // will free them.
+ RefPtr<TableUpdate> update = CreateTableUpdate(aTable);
+ mTableUpdates.AppendElement(update);
+ return update;
+}
+
+///////////////////////////////////////////////////////////////////////
+// ProtocolParserV2
+
+ProtocolParserV2::ProtocolParserV2()
+ : mState(PROTOCOL_STATE_CONTROL), mTableUpdate(nullptr) {}
+
+ProtocolParserV2::~ProtocolParserV2() = default;
+
+void ProtocolParserV2::SetCurrentTable(const nsACString& aTable) {
+ RefPtr<TableUpdate> update = GetTableUpdate(aTable);
+ mTableUpdate = TableUpdate::Cast<TableUpdateV2>(update);
+}
+
+nsresult ProtocolParserV2::AppendStream(const nsACString& aData) {
+ if (NS_FAILED(mUpdateStatus)) return mUpdateStatus;
+
+ nsresult rv;
+ if (!mPending.Append(aData, mozilla::fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+#ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+ mRawUpdate.Append(aData);
+#endif
+
+ bool done = false;
+ while (!done) {
+ if (nsUrlClassifierDBService::ShutdownHasStarted()) {
+ return NS_ERROR_ABORT;
+ }
+
+ if (mState == PROTOCOL_STATE_CONTROL) {
+ rv = ProcessControl(&done);
+ } else if (mState == PROTOCOL_STATE_CHUNK) {
+ rv = ProcessChunk(&done);
+ } else {
+ NS_ERROR("Unexpected protocol state");
+ rv = NS_ERROR_FAILURE;
+ }
+ if (NS_FAILED(rv)) {
+ mUpdateStatus = rv;
+ return rv;
+ }
+ }
+ return NS_OK;
+}
+
+void ProtocolParserV2::End() {
+ // Inbound data has already been processed in every AppendStream() call.
+ mTableUpdate = nullptr;
+}
+
+nsresult ProtocolParserV2::ProcessControl(bool* aDone) {
+ nsresult rv;
+
+ nsAutoCString line;
+ *aDone = true;
+ while (NextLine(line)) {
+ PARSER_LOG(("Processing %s\n", line.get()));
+
+ if (StringBeginsWith(line, "i:"_ns)) {
+ // Set the table name from the table header line.
+ SetCurrentTable(Substring(line, 2));
+ } else if (StringBeginsWith(line, "n:"_ns)) {
+ if (PR_sscanf(line.get(), "n:%d", &mUpdateWaitSec) != 1) {
+ PARSER_LOG(("Error parsing n: '%s' (%d)", line.get(), mUpdateWaitSec));
+ return NS_ERROR_FAILURE;
+ }
+ } else if (line.EqualsLiteral("r:pleasereset")) {
+ PARSER_LOG(("All tables will be reset."));
+ mTablesToReset = mRequestedTables.Clone();
+ } else if (StringBeginsWith(line, "u:"_ns)) {
+ rv = ProcessForward(line);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (StringBeginsWith(line, "a:"_ns) ||
+ StringBeginsWith(line, "s:"_ns)) {
+ rv = ProcessChunkControl(line);
+ NS_ENSURE_SUCCESS(rv, rv);
+ *aDone = false;
+ return NS_OK;
+ } else if (StringBeginsWith(line, "ad:"_ns) ||
+ StringBeginsWith(line, "sd:"_ns)) {
+ rv = ProcessExpirations(line);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ *aDone = true;
+ return NS_OK;
+}
+
+nsresult ProtocolParserV2::ProcessExpirations(const nsCString& aLine) {
+ if (!mTableUpdate) {
+ NS_WARNING("Got an expiration without a table.");
+ return NS_ERROR_FAILURE;
+ }
+ const nsACString& list = Substring(aLine, 3);
+ for (const auto& str : list.Split(',')) {
+ uint32_t first, last;
+ if (ParseChunkRange(nsAutoCString(str), &first, &last)) {
+ if (last < first) return NS_ERROR_FAILURE;
+ if (last - first > MAX_CHUNK_RANGE) return NS_ERROR_FAILURE;
+ for (uint32_t num = first; num <= last; num++) {
+ if (aLine[0] == 'a') {
+ nsresult rv = mTableUpdate->NewAddExpiration(num);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ } else {
+ nsresult rv = mTableUpdate->NewSubExpiration(num);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ }
+ } else {
+ return NS_ERROR_FAILURE;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult ProtocolParserV2::ProcessChunkControl(const nsCString& aLine) {
+ if (!mTableUpdate) {
+ NS_WARNING("Got a chunk before getting a table.");
+ return NS_ERROR_FAILURE;
+ }
+
+ mState = PROTOCOL_STATE_CHUNK;
+ char command;
+
+ mChunkState.Clear();
+
+ if (PR_sscanf(aLine.get(), "%c:%d:%d:%d", &command, &mChunkState.num,
+ &mChunkState.hashSize, &mChunkState.length) != 4) {
+ NS_WARNING(("PR_sscanf failed"));
+ return NS_ERROR_FAILURE;
+ }
+
+ if (mChunkState.length > MAX_CHUNK_SIZE) {
+ NS_WARNING("Invalid length specified in update.");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!(mChunkState.hashSize == PREFIX_SIZE ||
+ mChunkState.hashSize == COMPLETE_SIZE)) {
+ NS_WARNING("Invalid hash size specified in update.");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (StringEndsWith(mTableUpdate->TableName(), "-shavar"_ns) ||
+ StringEndsWith(mTableUpdate->TableName(), "-simple"_ns)) {
+ // Accommodate test tables ending in -simple for now.
+ mChunkState.type = (command == 'a') ? CHUNK_ADD : CHUNK_SUB;
+ } else if (StringEndsWith(mTableUpdate->TableName(), "-digest256"_ns)) {
+ mChunkState.type = (command == 'a') ? CHUNK_ADD_DIGEST : CHUNK_SUB_DIGEST;
+ }
+ nsresult rv;
+ switch (mChunkState.type) {
+ case CHUNK_ADD:
+ rv = mTableUpdate->NewAddChunk(mChunkState.num);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ break;
+ case CHUNK_SUB:
+ rv = mTableUpdate->NewSubChunk(mChunkState.num);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ break;
+ case CHUNK_ADD_DIGEST:
+ rv = mTableUpdate->NewAddChunk(mChunkState.num);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ break;
+ case CHUNK_SUB_DIGEST:
+ rv = mTableUpdate->NewSubChunk(mChunkState.num);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ break;
+ }
+
+ return NS_OK;
+}
+
+nsresult ProtocolParserV2::ProcessForward(const nsCString& aLine) {
+ const nsACString& forward = Substring(aLine, 2);
+ return AddForward(forward);
+}
+
+nsresult ProtocolParserV2::AddForward(const nsACString& aUrl) {
+ if (!mTableUpdate) {
+ NS_WARNING("Forward without a table name.");
+ return NS_ERROR_FAILURE;
+ }
+
+ ForwardedUpdate* forward = mForwards.AppendElement();
+ forward->table = mTableUpdate->TableName();
+ forward->url.Assign(aUrl);
+
+ return NS_OK;
+}
+
+nsresult ProtocolParserV2::ProcessChunk(bool* aDone) {
+ if (!mTableUpdate) {
+ NS_WARNING("Processing chunk without an active table.");
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_ASSERTION(mChunkState.num != 0, "Must have a chunk number.");
+
+ if (mPending.Length() < mChunkState.length) {
+ *aDone = true;
+ return NS_OK;
+ }
+
+ // Pull the chunk out of the pending stream data.
+ nsAutoCString chunk;
+ chunk.Assign(Substring(mPending, 0, mChunkState.length));
+ mPending.Cut(0, mChunkState.length);
+
+ *aDone = false;
+ mState = PROTOCOL_STATE_CONTROL;
+
+ if (StringEndsWith(mTableUpdate->TableName(), "-shavar"_ns)) {
+ return ProcessShaChunk(chunk);
+ }
+ if (StringEndsWith(mTableUpdate->TableName(), "-digest256"_ns)) {
+ return ProcessDigestChunk(chunk);
+ }
+ return ProcessPlaintextChunk(chunk);
+}
+
+/**
+ * Process a plaintext chunk (currently only used in unit tests).
+ */
+nsresult ProtocolParserV2::ProcessPlaintextChunk(const nsACString& aChunk) {
+ if (!mTableUpdate) {
+ NS_WARNING("Chunk received with no table.");
+ return NS_ERROR_FAILURE;
+ }
+
+ PARSER_LOG(("Handling a %zd-byte simple chunk", aChunk.Length()));
+
+ nsTArray<nsCString> lines;
+ ParseString(PromiseFlatCString(aChunk), '\n', lines);
+
+ // non-hashed tables need to be hashed
+ for (uint32_t i = 0; i < lines.Length(); i++) {
+ nsCString& line = lines[i];
+
+ if (mChunkState.type == CHUNK_ADD) {
+ if (mChunkState.hashSize == COMPLETE_SIZE) {
+ Completion hash;
+ hash.FromPlaintext(line);
+ nsresult rv = mTableUpdate->NewAddComplete(mChunkState.num, hash);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ } else {
+ NS_ASSERTION(mChunkState.hashSize == 4,
+ "Only 32- or 4-byte hashes can be used for add chunks.");
+ Prefix hash;
+ hash.FromPlaintext(line);
+ nsresult rv = mTableUpdate->NewAddPrefix(mChunkState.num, hash);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ } else {
+ nsCString::const_iterator begin, iter, end;
+ line.BeginReading(begin);
+ line.EndReading(end);
+ iter = begin;
+ uint32_t addChunk;
+ if (!FindCharInReadable(':', iter, end) ||
+ PR_sscanf(lines[i].get(), "%d:", &addChunk) != 1) {
+ NS_WARNING("Received sub chunk without associated add chunk.");
+ return NS_ERROR_FAILURE;
+ }
+ iter++;
+
+ if (mChunkState.hashSize == COMPLETE_SIZE) {
+ Completion hash;
+ hash.FromPlaintext(Substring(iter, end));
+ nsresult rv =
+ mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ } else {
+ NS_ASSERTION(mChunkState.hashSize == 4,
+ "Only 32- or 4-byte hashes can be used for add chunks.");
+ Prefix hash;
+ hash.FromPlaintext(Substring(iter, end));
+ nsresult rv =
+ mTableUpdate->NewSubPrefix(addChunk, hash, mChunkState.num);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult ProtocolParserV2::ProcessShaChunk(const nsACString& aChunk) {
+ uint32_t start = 0;
+ while (start < aChunk.Length()) {
+ // First four bytes are the domain key.
+ Prefix domain;
+ domain.Assign(Substring(aChunk, start, DOMAIN_SIZE));
+ start += DOMAIN_SIZE;
+
+ // Then a count of entries.
+ uint8_t numEntries = static_cast<uint8_t>(aChunk[start]);
+ start++;
+
+ PARSER_LOG(
+ ("Handling a %zd-byte shavar chunk containing %u entries"
+ " for domain %X",
+ aChunk.Length(), numEntries, domain.ToUint32()));
+
+ nsresult rv;
+ if (mChunkState.type == CHUNK_ADD && mChunkState.hashSize == PREFIX_SIZE) {
+ rv = ProcessHostAdd(domain, numEntries, aChunk, &start);
+ } else if (mChunkState.type == CHUNK_ADD &&
+ mChunkState.hashSize == COMPLETE_SIZE) {
+ rv = ProcessHostAddComplete(numEntries, aChunk, &start);
+ } else if (mChunkState.type == CHUNK_SUB &&
+ mChunkState.hashSize == PREFIX_SIZE) {
+ rv = ProcessHostSub(domain, numEntries, aChunk, &start);
+ } else if (mChunkState.type == CHUNK_SUB &&
+ mChunkState.hashSize == COMPLETE_SIZE) {
+ rv = ProcessHostSubComplete(numEntries, aChunk, &start);
+ } else {
+ NS_WARNING("Unexpected chunk type/hash size!");
+ PARSER_LOG(("Got an unexpected chunk type/hash size: %s:%d",
+ mChunkState.type == CHUNK_ADD ? "add" : "sub",
+ mChunkState.hashSize));
+ return NS_ERROR_FAILURE;
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+nsresult ProtocolParserV2::ProcessDigestChunk(const nsACString& aChunk) {
+ PARSER_LOG(("Handling a %zd-byte digest256 chunk", aChunk.Length()));
+
+ if (mChunkState.type == CHUNK_ADD_DIGEST) {
+ return ProcessDigestAdd(aChunk);
+ }
+ if (mChunkState.type == CHUNK_SUB_DIGEST) {
+ return ProcessDigestSub(aChunk);
+ }
+ return NS_ERROR_UNEXPECTED;
+}
+
+nsresult ProtocolParserV2::ProcessDigestAdd(const nsACString& aChunk) {
+ MOZ_ASSERT(mTableUpdate);
+ // The ABNF format for add chunks is (HASH)+, where HASH is 32 bytes.
+ MOZ_ASSERT(aChunk.Length() % 32 == 0,
+ "Chunk length in bytes must be divisible by 4");
+ uint32_t start = 0;
+ while (start < aChunk.Length()) {
+ Completion hash;
+ hash.Assign(Substring(aChunk, start, COMPLETE_SIZE));
+ start += COMPLETE_SIZE;
+ nsresult rv = mTableUpdate->NewAddComplete(mChunkState.num, hash);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult ProtocolParserV2::ProcessDigestSub(const nsACString& aChunk) {
+ MOZ_ASSERT(mTableUpdate);
+ // The ABNF format for sub chunks is (ADDCHUNKNUM HASH)+, where ADDCHUNKNUM
+ // is a 4 byte chunk number, and HASH is 32 bytes.
+ MOZ_ASSERT(aChunk.Length() % 36 == 0,
+ "Chunk length in bytes must be divisible by 36");
+ uint32_t start = 0;
+ while (start < aChunk.Length()) {
+ // Read ADDCHUNKNUM
+ const nsACString& addChunkStr = Substring(aChunk, start, 4);
+ start += 4;
+
+ uint32_t addChunk;
+ memcpy(&addChunk, addChunkStr.BeginReading(), 4);
+ addChunk = PR_ntohl(addChunk);
+
+ // Read the hash
+ Completion hash;
+ hash.Assign(Substring(aChunk, start, COMPLETE_SIZE));
+ start += COMPLETE_SIZE;
+
+ nsresult rv = mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult ProtocolParserV2::ProcessHostAdd(const Prefix& aDomain,
+ uint8_t aNumEntries,
+ const nsACString& aChunk,
+ uint32_t* aStart) {
+ MOZ_ASSERT(mTableUpdate);
+ NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE,
+ "ProcessHostAdd should only be called for prefix hashes.");
+
+ if (aNumEntries == 0) {
+ nsresult rv = mTableUpdate->NewAddPrefix(mChunkState.num, aDomain);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ return NS_OK;
+ }
+
+ if (*aStart + (PREFIX_SIZE * aNumEntries) > aChunk.Length()) {
+ NS_WARNING("Chunk is not long enough to contain the expected entries.");
+ return NS_ERROR_FAILURE;
+ }
+
+ for (uint8_t i = 0; i < aNumEntries; i++) {
+ Prefix hash;
+ hash.Assign(Substring(aChunk, *aStart, PREFIX_SIZE));
+ PARSER_LOG(("Add prefix %X", hash.ToUint32()));
+ nsresult rv = mTableUpdate->NewAddPrefix(mChunkState.num, hash);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ *aStart += PREFIX_SIZE;
+ }
+
+ return NS_OK;
+}
+
+nsresult ProtocolParserV2::ProcessHostSub(const Prefix& aDomain,
+ uint8_t aNumEntries,
+ const nsACString& aChunk,
+ uint32_t* aStart) {
+ MOZ_ASSERT(mTableUpdate);
+ NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE,
+ "ProcessHostSub should only be called for prefix hashes.");
+
+ if (aNumEntries == 0) {
+ if ((*aStart) + 4 > aChunk.Length()) {
+ NS_WARNING("Received a zero-entry sub chunk without an associated add.");
+ return NS_ERROR_FAILURE;
+ }
+
+ const nsACString& addChunkStr = Substring(aChunk, *aStart, 4);
+ *aStart += 4;
+
+ uint32_t addChunk;
+ memcpy(&addChunk, addChunkStr.BeginReading(), 4);
+ addChunk = PR_ntohl(addChunk);
+
+ PARSER_LOG(("Sub prefix (addchunk=%u)", addChunk));
+ nsresult rv =
+ mTableUpdate->NewSubPrefix(addChunk, aDomain, mChunkState.num);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ return NS_OK;
+ }
+
+ if (*aStart + ((PREFIX_SIZE + 4) * aNumEntries) > aChunk.Length()) {
+ NS_WARNING("Chunk is not long enough to contain the expected entries.");
+ return NS_ERROR_FAILURE;
+ }
+
+ for (uint8_t i = 0; i < aNumEntries; i++) {
+ const nsACString& addChunkStr = Substring(aChunk, *aStart, 4);
+ *aStart += 4;
+
+ uint32_t addChunk;
+ memcpy(&addChunk, addChunkStr.BeginReading(), 4);
+ addChunk = PR_ntohl(addChunk);
+
+ Prefix prefix;
+ prefix.Assign(Substring(aChunk, *aStart, PREFIX_SIZE));
+ *aStart += PREFIX_SIZE;
+
+ PARSER_LOG(("Sub prefix %X (addchunk=%u)", prefix.ToUint32(), addChunk));
+ nsresult rv = mTableUpdate->NewSubPrefix(addChunk, prefix, mChunkState.num);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult ProtocolParserV2::ProcessHostAddComplete(uint8_t aNumEntries,
+ const nsACString& aChunk,
+ uint32_t* aStart) {
+ MOZ_ASSERT(mTableUpdate);
+ NS_ASSERTION(
+ mChunkState.hashSize == COMPLETE_SIZE,
+ "ProcessHostAddComplete should only be called for complete hashes.");
+
+ if (aNumEntries == 0) {
+ // this is totally comprehensible.
+ // My sarcasm detector is going off!
+ NS_WARNING("Expected > 0 entries for a 32-byte hash add.");
+ return NS_OK;
+ }
+
+ if (*aStart + (COMPLETE_SIZE * aNumEntries) > aChunk.Length()) {
+ NS_WARNING("Chunk is not long enough to contain the expected entries.");
+ return NS_ERROR_FAILURE;
+ }
+
+ for (uint8_t i = 0; i < aNumEntries; i++) {
+ Completion hash;
+ hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE));
+ nsresult rv = mTableUpdate->NewAddComplete(mChunkState.num, hash);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ *aStart += COMPLETE_SIZE;
+ }
+
+ return NS_OK;
+}
+
+nsresult ProtocolParserV2::ProcessHostSubComplete(uint8_t aNumEntries,
+ const nsACString& aChunk,
+ uint32_t* aStart) {
+ MOZ_ASSERT(mTableUpdate);
+ NS_ASSERTION(
+ mChunkState.hashSize == COMPLETE_SIZE,
+ "ProcessHostSubComplete should only be called for complete hashes.");
+
+ if (aNumEntries == 0) {
+ // this is totally comprehensible.
+ NS_WARNING("Expected > 0 entries for a 32-byte hash sub.");
+ return NS_OK;
+ }
+
+ if (*aStart + ((COMPLETE_SIZE + 4) * aNumEntries) > aChunk.Length()) {
+ NS_WARNING("Chunk is not long enough to contain the expected entries.");
+ return NS_ERROR_FAILURE;
+ }
+
+ for (uint8_t i = 0; i < aNumEntries; i++) {
+ const nsACString& addChunkStr = Substring(aChunk, *aStart, 4);
+ *aStart += 4;
+
+ uint32_t addChunk;
+ memcpy(&addChunk, addChunkStr.BeginReading(), 4);
+ addChunk = PR_ntohl(addChunk);
+
+ Completion hash;
+ hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE));
+ *aStart += COMPLETE_SIZE;
+
+ nsresult rv = mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ return NS_OK;
+}
+
+bool ProtocolParserV2::NextLine(nsACString& aLine) {
+ int32_t newline = mPending.FindChar('\n');
+ if (newline == kNotFound) {
+ return false;
+ }
+ aLine.Assign(Substring(mPending, 0, newline));
+ mPending.Cut(0, newline + 1);
+ return true;
+}
+
+RefPtr<TableUpdate> ProtocolParserV2::CreateTableUpdate(
+ const nsACString& aTableName) const {
+ return new TableUpdateV2(aTableName);
+}
+
+///////////////////////////////////////////////////////////////////////
+// ProtocolParserProtobuf
+
+ProtocolParserProtobuf::ProtocolParserProtobuf() = default;
+
+ProtocolParserProtobuf::~ProtocolParserProtobuf() = default;
+
+void ProtocolParserProtobuf::SetCurrentTable(const nsACString& aTable) {
+ // Should never occur.
+ MOZ_ASSERT_UNREACHABLE("SetCurrentTable shouldn't be called");
+}
+
+RefPtr<TableUpdate> ProtocolParserProtobuf::CreateTableUpdate(
+ const nsACString& aTableName) const {
+ return new TableUpdateV4(aTableName);
+}
+
+nsresult ProtocolParserProtobuf::AppendStream(const nsACString& aData) {
+ // Protobuf data cannot be parsed progressively. Just save the incoming data.
+ if (!mPending.Append(aData, mozilla::fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ return NS_OK;
+}
+
+void ProtocolParserProtobuf::End() {
+ // mUpdateStatus will be updated to success as long as not all
+ // the responses are invalid.
+ mUpdateStatus = NS_ERROR_FAILURE;
+
+ FetchThreatListUpdatesResponse response;
+ if (!response.ParseFromArray(mPending.get(), mPending.Length())) {
+ NS_WARNING("ProtocolParserProtobuf failed parsing data.");
+ return;
+ }
+
+ auto minWaitDuration = response.minimum_wait_duration();
+ mUpdateWaitSec =
+ minWaitDuration.seconds() + minWaitDuration.nanos() / 1000000000;
+
+ for (int i = 0; i < response.list_update_responses_size(); i++) {
+ auto r = response.list_update_responses(i);
+ nsAutoCString listName;
+ nsresult rv = ProcessOneResponse(r, listName);
+ if (NS_SUCCEEDED(rv)) {
+ mUpdateStatus = rv;
+ } else {
+ nsAutoCString errorName;
+ mozilla::GetErrorName(rv, errorName);
+ NS_WARNING(nsPrintfCString("Failed to process one response for '%s': %s",
+ listName.get(), errorName.get())
+ .get());
+ if (!listName.IsEmpty()) {
+ PARSER_LOG(("Table %s will be reset.", listName.get()));
+ mTablesToReset.AppendElement(listName);
+ }
+ }
+ }
+}
+
+nsresult ProtocolParserProtobuf::ProcessOneResponse(
+ const ListUpdateResponse& aResponse, nsACString& aListName) {
+ MOZ_ASSERT(aListName.IsEmpty());
+
+ // A response must have a threat type.
+ if (!aResponse.has_threat_type()) {
+ NS_WARNING(
+ "Threat type not initialized. This seems to be an invalid response.");
+ return NS_ERROR_UC_PARSER_MISSING_PARAM;
+ }
+
+ nsUrlClassifierUtils* urlUtil = nsUrlClassifierUtils::GetInstance();
+ if (NS_WARN_IF(!urlUtil)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Convert threat type to list name.
+ nsCString possibleListNames;
+ nsresult rv = urlUtil->ConvertThreatTypeToListNames(aResponse.threat_type(),
+ possibleListNames);
+ if (NS_FAILED(rv)) {
+ PARSER_LOG(("Threat type to list name conversion error: %d",
+ aResponse.threat_type()));
+ return NS_ERROR_UC_PARSER_UNKNOWN_THREAT;
+ }
+
+ // Match the table name we received with one of the ones we requested.
+ // We ignore the case where a threat type matches more than one list
+ // per provider and return the first one. See bug 1287059."
+ nsTArray<nsCString> possibleListNameArray;
+ Classifier::SplitTables(possibleListNames, possibleListNameArray);
+ for (auto possibleName : possibleListNameArray) {
+ if (mRequestedTables.Contains(possibleName)) {
+ aListName = possibleName;
+ break;
+ }
+ }
+
+ if (aListName.IsEmpty()) {
+ PARSER_LOG(
+ ("We received an update for a list we didn't ask for. Ignoring it."));
+ return NS_ERROR_FAILURE;
+ }
+
+ // Test if this is a full update.
+ bool isFullUpdate = false;
+ if (aResponse.has_response_type()) {
+ isFullUpdate = aResponse.response_type() == ListUpdateResponse::FULL_UPDATE;
+ } else {
+ NS_WARNING("Response type not initialized.");
+ return NS_ERROR_UC_PARSER_MISSING_PARAM;
+ }
+
+ // Warn if there's no new state.
+ if (!aResponse.has_new_client_state()) {
+ NS_WARNING("New state not initialized.");
+ return NS_ERROR_UC_PARSER_MISSING_PARAM;
+ }
+
+ auto tu = GetTableUpdate(aListName);
+ auto tuV4 = TableUpdate::Cast<TableUpdateV4>(tu);
+ NS_ENSURE_TRUE(tuV4, NS_ERROR_FAILURE);
+
+ nsCString state(aResponse.new_client_state().c_str(),
+ aResponse.new_client_state().size());
+ tuV4->SetNewClientState(state);
+
+ if (aResponse.has_checksum()) {
+ tuV4->SetSHA256(aResponse.checksum().sha256());
+ }
+
+ PARSER_LOG(
+ ("==== Update for threat type '%d' ====", aResponse.threat_type()));
+ PARSER_LOG(("* aListName: %s\n", PromiseFlatCString(aListName).get()));
+ PARSER_LOG(("* newState: %s\n", aResponse.new_client_state().c_str()));
+ PARSER_LOG(("* isFullUpdate: %s\n", (isFullUpdate ? "yes" : "no")));
+ PARSER_LOG(
+ ("* hasChecksum: %s\n", (aResponse.has_checksum() ? "yes" : "no")));
+ PARSER_LOG(("* additions: %d\n", aResponse.additions().size()));
+ PARSER_LOG(("* removals: %d\n", aResponse.removals().size()));
+
+ tuV4->SetFullUpdate(isFullUpdate);
+
+ rv = ProcessAdditionOrRemoval(*tuV4, aResponse.additions(),
+ true /*aIsAddition*/);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = ProcessAdditionOrRemoval(*tuV4, aResponse.removals(), false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PARSER_LOG(("\n\n"));
+
+ return NS_OK;
+}
+
+nsresult ProtocolParserProtobuf::ProcessAdditionOrRemoval(
+ TableUpdateV4& aTableUpdate, const ThreatEntrySetList& aUpdate,
+ bool aIsAddition) {
+ nsresult ret = NS_OK;
+
+ for (int i = 0; i < aUpdate.size(); i++) {
+ auto update = aUpdate.Get(i);
+ if (!update.has_compression_type()) {
+ NS_WARNING(nsPrintfCString("%s with no compression type.",
+ aIsAddition ? "Addition" : "Removal")
+ .get());
+ continue;
+ }
+
+ switch (update.compression_type()) {
+ case COMPRESSION_TYPE_UNSPECIFIED:
+ NS_WARNING("Unspecified compression type.");
+ break;
+
+ case RAW:
+ ret = (aIsAddition ? ProcessRawAddition(aTableUpdate, update)
+ : ProcessRawRemoval(aTableUpdate, update));
+ break;
+
+ case RICE:
+ ret = (aIsAddition ? ProcessEncodedAddition(aTableUpdate, update)
+ : ProcessEncodedRemoval(aTableUpdate, update));
+ break;
+ }
+ }
+
+ return ret;
+}
+
+nsresult ProtocolParserProtobuf::ProcessRawAddition(
+ TableUpdateV4& aTableUpdate, const ThreatEntrySet& aAddition) {
+ if (!aAddition.has_raw_hashes()) {
+ PARSER_LOG(("* No raw addition."));
+ return NS_OK;
+ }
+
+ auto rawHashes = aAddition.raw_hashes();
+ if (!rawHashes.has_prefix_size()) {
+ NS_WARNING("Raw hash has no prefix size");
+ return NS_OK;
+ }
+
+ uint32_t prefixSize = rawHashes.prefix_size();
+ MOZ_ASSERT(prefixSize >= PREFIX_SIZE && prefixSize <= COMPLETE_SIZE);
+
+ nsCString prefixes;
+ if (!prefixes.Assign(rawHashes.raw_hashes().c_str(),
+ rawHashes.raw_hashes().size(), mozilla::fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ MOZ_ASSERT(prefixes.Length() % prefixSize == 0,
+ "PrefixString length must be a multiple of the prefix size.");
+
+ if (LOG_ENABLED()) {
+ PARSER_LOG((" Raw addition (%d-byte prefixes)", prefixSize));
+ PARSER_LOG((" - # of prefixes: %zu", prefixes.Length() / prefixSize));
+ if (4 == prefixSize) {
+ uint32_t* fixedLengthPrefixes = (uint32_t*)prefixes.get();
+ PARSER_LOG((" - Memory address: 0x%p", fixedLengthPrefixes));
+ }
+ }
+
+ aTableUpdate.NewPrefixes(prefixSize, prefixes);
+ return NS_OK;
+}
+
+nsresult ProtocolParserProtobuf::ProcessRawRemoval(
+ TableUpdateV4& aTableUpdate, const ThreatEntrySet& aRemoval) {
+ if (!aRemoval.has_raw_indices()) {
+ NS_WARNING("A removal has no indices.");
+ return NS_OK;
+ }
+
+ // indices is an array of int32.
+ auto indices = aRemoval.raw_indices().indices();
+ PARSER_LOG(("* Raw removal"));
+ PARSER_LOG((" - # of removal: %d", indices.size()));
+
+ nsresult rv = aTableUpdate.NewRemovalIndices((const uint32_t*)indices.data(),
+ indices.size());
+ if (NS_FAILED(rv)) {
+ PARSER_LOG(("Failed to create new removal indices."));
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+static nsresult DoRiceDeltaDecode(const RiceDeltaEncoding& aEncoding,
+ nsTArray<uint32_t>& aDecoded) {
+ if (aEncoding.num_entries() > 0 &&
+ (!aEncoding.has_rice_parameter() || !aEncoding.has_encoded_data())) {
+ PARSER_LOG(("Rice parameter or encoded data is missing."));
+ return NS_ERROR_UC_PARSER_MISSING_PARAM;
+ } else if (aEncoding.num_entries() == 0 && !aEncoding.has_first_value()) {
+ PARSER_LOG(("Missing first_value for an single-integer Rice encoding."));
+ return NS_ERROR_UC_PARSER_MISSING_VALUE;
+ }
+
+ auto first_value = aEncoding.has_first_value() ? aEncoding.first_value() : 0;
+
+ PARSER_LOG(("* Encoding info:"));
+ PARSER_LOG((" - First value: %" PRId64, first_value));
+ PARSER_LOG((" - Num of entries: %d", aEncoding.num_entries()));
+ PARSER_LOG((" - Rice parameter: %d", aEncoding.rice_parameter()));
+
+ // Set up the input buffer. Note that the bits should be read
+ // from LSB to MSB so that we in-place reverse the bits before
+ // feeding to the decoder.
+ auto encoded =
+ const_cast<RiceDeltaEncoding&>(aEncoding).mutable_encoded_data();
+ RiceDeltaDecoder decoder((uint8_t*)encoded->c_str(), encoded->size());
+
+ // Setup the output buffer. The "first value" is included in
+ // the output buffer.
+ if (!aDecoded.SetLength(aEncoding.num_entries() + 1, mozilla::fallible)) {
+ NS_WARNING("Not enough memory to decode the RiceDelta input.");
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Decode!
+ bool rv = decoder.Decode(
+ aEncoding.rice_parameter(), first_value,
+ aEncoding.num_entries(), // # of entries (first value not included).
+ &aDecoded[0]);
+
+ NS_ENSURE_TRUE(rv, NS_ERROR_UC_PARSER_DECODE_FAILURE);
+
+ return NS_OK;
+}
+
+nsresult ProtocolParserProtobuf::ProcessEncodedAddition(
+ TableUpdateV4& aTableUpdate, const ThreatEntrySet& aAddition) {
+ if (!aAddition.has_rice_hashes()) {
+ PARSER_LOG(("* No rice encoded addition."));
+ return NS_OK;
+ }
+
+ nsTArray<uint32_t> decoded;
+ nsresult rv = DoRiceDeltaDecode(aAddition.rice_hashes(), decoded);
+ if (NS_FAILED(rv)) {
+ PARSER_LOG(("Failed to parse encoded prefixes."));
+ return rv;
+ }
+
+ // Say we have the following raw prefixes
+ // BE LE
+ // 00 00 00 01 1 16777216
+ // 00 00 02 00 512 131072
+ // 00 03 00 00 196608 768
+ // 04 00 00 00 67108864 4
+ //
+ // which can be treated as uint32 (big-endian) sorted in increasing order:
+ //
+ // [1, 512, 196608, 67108864]
+ //
+ // According to https://developers.google.com/safe-browsing/v4/compression,
+ // the following should be done prior to compression:
+ //
+ // 1) re-interpret in little-endian ==> [16777216, 131072, 768, 4]
+ // 2) sort in increasing order ==> [4, 768, 131072, 16777216]
+ //
+ // In order to get the original byte stream from |decoded|
+ // ([4, 768, 131072, 16777216] in this case), we have to:
+ //
+ // 1) sort in big-endian order ==> [16777216, 131072, 768, 4]
+ // 2) copy each uint32 in little-endian to the result string
+ //
+
+ // The 4-byte prefixes have to be re-sorted in Big-endian increasing order.
+ struct CompareBigEndian {
+ bool Equals(const uint32_t& aA, const uint32_t& aB) const {
+ return aA == aB;
+ }
+
+ bool LessThan(const uint32_t& aA, const uint32_t& aB) const {
+ return NativeEndian::swapToBigEndian(aA) <
+ NativeEndian::swapToBigEndian(aB);
+ }
+ };
+ decoded.Sort(CompareBigEndian());
+
+ // The encoded prefixes are always 4 bytes.
+ nsCString prefixes;
+ if (!prefixes.SetCapacity(decoded.Length() * 4, mozilla::fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ for (size_t i = 0; i < decoded.Length(); i++) {
+ // Note that the third argument is the number of elements we want
+ // to copy (and swap) but not the number of bytes we want to copy.
+ char p[4];
+ NativeEndian::copyAndSwapToLittleEndian(p, &decoded[i], 1);
+ prefixes.Append(p, 4);
+ }
+
+ aTableUpdate.NewPrefixes(4, prefixes);
+ return NS_OK;
+}
+
+nsresult ProtocolParserProtobuf::ProcessEncodedRemoval(
+ TableUpdateV4& aTableUpdate, const ThreatEntrySet& aRemoval) {
+ if (!aRemoval.has_rice_indices()) {
+ PARSER_LOG(("* No rice encoded removal."));
+ return NS_OK;
+ }
+
+ nsTArray<uint32_t> decoded;
+ nsresult rv = DoRiceDeltaDecode(aRemoval.rice_indices(), decoded);
+ if (NS_FAILED(rv)) {
+ PARSER_LOG(("Failed to decode encoded removal indices."));
+ return rv;
+ }
+
+ // The encoded prefixes are always 4 bytes.
+ rv = aTableUpdate.NewRemovalIndices(&decoded[0], decoded.Length());
+ if (NS_FAILED(rv)) {
+ PARSER_LOG(("Failed to create new removal indices."));
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+} // namespace safebrowsing
+} // namespace mozilla
diff --git a/toolkit/components/url-classifier/ProtocolParser.h b/toolkit/components/url-classifier/ProtocolParser.h
new file mode 100644
index 0000000000..125255f10c
--- /dev/null
+++ b/toolkit/components/url-classifier/ProtocolParser.h
@@ -0,0 +1,215 @@
+//* -*- 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/. */
+
+#ifndef ProtocolParser_h__
+#define ProtocolParser_h__
+
+#include "HashStore.h"
+#include "chromium/safebrowsing.pb.h"
+
+namespace mozilla {
+namespace safebrowsing {
+
+/**
+ * Abstract base class for parsing update data in multiple formats.
+ */
+class ProtocolParser {
+ public:
+ struct ForwardedUpdate {
+ nsCString table;
+ nsCString url;
+ };
+
+ ProtocolParser();
+ virtual ~ProtocolParser();
+
+ nsresult Status() const { return mUpdateStatus; }
+
+#ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+ virtual nsCString GetRawTableUpdates() const { return mPending; }
+#endif
+
+ virtual void SetCurrentTable(const nsACString& aTable) = 0;
+
+ void SetRequestedTables(const nsTArray<nsCString>& aRequestTables) {
+ mRequestedTables = aRequestTables.Clone();
+ }
+
+ nsresult Begin(const nsACString& aTable,
+ const nsTArray<nsCString>& aUpdateTables);
+ virtual nsresult AppendStream(const nsACString& aData) = 0;
+
+ uint32_t UpdateWaitSec() { return mUpdateWaitSec; }
+
+ // Notify that the inbound data is ready for parsing if progressive
+ // parsing is not supported, for example in V4.
+ virtual void End() = 0;
+
+ RefPtr<TableUpdate> GetTableUpdate(const nsACString& aTable);
+ void ForgetTableUpdates() { mTableUpdates.Clear(); }
+ const TableUpdateArray& GetTableUpdates() { return mTableUpdates; }
+
+ // These are only meaningful to V2. Since they were originally public,
+ // moving them to ProtocolParserV2 requires a dymamic cast in the call
+ // sites. As a result, we will leave them until we remove support
+ // for V2 entirely..
+ virtual const nsTArray<ForwardedUpdate>& Forwards() const {
+ return mForwards;
+ }
+ bool ResetRequested() const { return !mTablesToReset.IsEmpty(); }
+ const nsTArray<nsCString>& TablesToReset() const { return mTablesToReset; }
+
+ protected:
+ virtual RefPtr<TableUpdate> CreateTableUpdate(
+ const nsACString& aTableName) const = 0;
+
+ nsCString mPending;
+ nsresult mUpdateStatus;
+
+ // Keep track of updates to apply before passing them to the DBServiceWorkers.
+ TableUpdateArray mTableUpdates;
+
+ nsTArray<ForwardedUpdate> mForwards;
+
+ // The table names that were requested from the client.
+ nsTArray<nsCString> mRequestedTables;
+
+ // The table names that failed to update and need to be reset.
+ nsTArray<nsCString> mTablesToReset;
+
+ // How long we should wait until the next update.
+ uint32_t mUpdateWaitSec;
+};
+
+/**
+ * Helpers to parse the "shavar", "digest256" and "simple" list formats.
+ */
+class ProtocolParserV2 final : public ProtocolParser {
+ public:
+ ProtocolParserV2();
+ virtual ~ProtocolParserV2();
+
+ virtual void SetCurrentTable(const nsACString& aTable) override;
+ virtual nsresult AppendStream(const nsACString& aData) override;
+ virtual void End() override;
+
+ // Update information.
+ virtual const nsTArray<ForwardedUpdate>& Forwards() const override {
+ return mForwards;
+ }
+
+#ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+ // Unfortunately we have to override to return mRawUpdate which
+ // will not be modified during the parsing, unlike mPending.
+ virtual nsCString GetRawTableUpdates() const override { return mRawUpdate; }
+#endif
+
+ private:
+ virtual RefPtr<TableUpdate> CreateTableUpdate(
+ const nsACString& aTableName) const override;
+
+ nsresult ProcessControl(bool* aDone);
+ nsresult ProcessExpirations(const nsCString& aLine);
+ nsresult ProcessChunkControl(const nsCString& aLine);
+ nsresult ProcessForward(const nsCString& aLine);
+ nsresult AddForward(const nsACString& aUrl);
+ nsresult ProcessChunk(bool* done);
+ // Remove this, it's only used for testing
+ nsresult ProcessPlaintextChunk(const nsACString& aChunk);
+ nsresult ProcessShaChunk(const nsACString& aChunk);
+ nsresult ProcessHostAdd(const Prefix& aDomain, uint8_t aNumEntries,
+ const nsACString& aChunk, uint32_t* aStart);
+ nsresult ProcessHostSub(const Prefix& aDomain, uint8_t aNumEntries,
+ const nsACString& aChunk, uint32_t* aStart);
+ nsresult ProcessHostAddComplete(uint8_t aNumEntries, const nsACString& aChunk,
+ uint32_t* aStart);
+ nsresult ProcessHostSubComplete(uint8_t numEntries, const nsACString& aChunk,
+ uint32_t* start);
+ // Digest chunks are very similar to shavar chunks, except digest chunks
+ // always contain the full hash, so there is no need for chunk data to
+ // contain prefix sizes.
+ nsresult ProcessDigestChunk(const nsACString& aChunk);
+ nsresult ProcessDigestAdd(const nsACString& aChunk);
+ nsresult ProcessDigestSub(const nsACString& aChunk);
+ bool NextLine(nsACString& aLine);
+
+ enum ParserState { PROTOCOL_STATE_CONTROL, PROTOCOL_STATE_CHUNK };
+ ParserState mState;
+
+ enum ChunkType {
+ // Types for shavar tables.
+ CHUNK_ADD,
+ CHUNK_SUB,
+ // Types for digest256 tables. digest256 tables differ in format from
+ // shavar tables since they only contain complete hashes.
+ CHUNK_ADD_DIGEST,
+ CHUNK_SUB_DIGEST
+ };
+
+ struct ChunkState {
+ ChunkType type;
+ uint32_t num;
+ uint32_t hashSize;
+ uint32_t length;
+ void Clear() {
+ num = 0;
+ hashSize = 0;
+ length = 0;
+ }
+ };
+ ChunkState mChunkState;
+
+ // Updates to apply to the current table being parsed.
+ RefPtr<TableUpdateV2> mTableUpdate;
+
+#ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+ nsCString mRawUpdate; // Keep a copy of mPending before it's processed.
+#endif
+};
+
+// Helpers to parse the "proto" list format.
+class ProtocolParserProtobuf final : public ProtocolParser {
+ public:
+ typedef FetchThreatListUpdatesResponse_ListUpdateResponse ListUpdateResponse;
+ typedef google::protobuf::RepeatedPtrField<ThreatEntrySet> ThreatEntrySetList;
+
+ public:
+ ProtocolParserProtobuf();
+
+ virtual void SetCurrentTable(const nsACString& aTable) override;
+ virtual nsresult AppendStream(const nsACString& aData) override;
+ virtual void End() override;
+
+ private:
+ virtual ~ProtocolParserProtobuf();
+
+ virtual RefPtr<TableUpdate> CreateTableUpdate(
+ const nsACString& aTableName) const override;
+
+ // For parsing update info.
+ nsresult ProcessOneResponse(const ListUpdateResponse& aResponse,
+ nsACString& aListName);
+
+ nsresult ProcessAdditionOrRemoval(TableUpdateV4& aTableUpdate,
+ const ThreatEntrySetList& aUpdate,
+ bool aIsAddition);
+
+ nsresult ProcessRawAddition(TableUpdateV4& aTableUpdate,
+ const ThreatEntrySet& aAddition);
+
+ nsresult ProcessRawRemoval(TableUpdateV4& aTableUpdate,
+ const ThreatEntrySet& aRemoval);
+
+ nsresult ProcessEncodedAddition(TableUpdateV4& aTableUpdate,
+ const ThreatEntrySet& aAddition);
+
+ nsresult ProcessEncodedRemoval(TableUpdateV4& aTableUpdate,
+ const ThreatEntrySet& aRemoval);
+};
+
+} // namespace safebrowsing
+} // namespace mozilla
+
+#endif
diff --git a/toolkit/components/url-classifier/RiceDeltaDecoder.cpp b/toolkit/components/url-classifier/RiceDeltaDecoder.cpp
new file mode 100644
index 0000000000..ea16888fdb
--- /dev/null
+++ b/toolkit/components/url-classifier/RiceDeltaDecoder.cpp
@@ -0,0 +1,227 @@
+/* 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 "RiceDeltaDecoder.h"
+#include "mozilla/Logging.h"
+
+#include <limits>
+
+extern mozilla::LazyLogModule gUrlClassifierDbServiceLog;
+#define LOG(args) \
+ MOZ_LOG(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug, args)
+
+namespace {
+
+////////////////////////////////////////////////////////////////////////
+// BitBuffer is copied and modified from webrtc/base/bitbuffer.h
+//
+
+/*
+ * Copyright 2015 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree (webrtc/base/bitbuffer.h/cc). An additional intellectual property
+ * rights grant can be found in the file PATENTS. All contributing
+ * project authors may be found in the AUTHORS file in the root of
+ * the source tree.
+ */
+
+class BitBuffer {
+ public:
+ BitBuffer(const uint8_t* bytes, size_t byte_count);
+
+ // The remaining bits in the byte buffer.
+ uint64_t RemainingBitCount() const;
+
+ // Reads bit-sized values from the buffer. Returns false if there isn't enough
+ // data left for the specified bit count..
+ bool ReadBits(uint32_t* val, size_t bit_count);
+
+ // Peeks bit-sized values from the buffer. Returns false if there isn't enough
+ // data left for the specified number of bits. Doesn't move the current
+ // offset.
+ bool PeekBits(uint32_t* val, size_t bit_count);
+
+ // Reads the exponential golomb encoded value at the current offset.
+ // Exponential golomb values are encoded as:
+ // 1) x = source val + 1
+ // 2) In binary, write [countbits(x) - 1] 1s, then x
+ // To decode, we count the number of leading 1 bits, read that many + 1 bits,
+ // and increment the result by 1.
+ // Returns false if there isn't enough data left for the specified type, or if
+ // the value wouldn't fit in a uint32_t.
+ bool ReadExponentialGolomb(uint32_t* val);
+
+ // Moves current position |bit_count| bits forward. Returns false if
+ // there aren't enough bits left in the buffer.
+ bool ConsumeBits(size_t bit_count);
+
+ protected:
+ const uint8_t* const bytes_;
+ // The total size of |bytes_|.
+ size_t byte_count_;
+ // The current offset, in bytes, from the start of |bytes_|.
+ size_t byte_offset_;
+ // The current offset, in bits, into the current byte.
+ size_t bit_offset_;
+};
+
+} // end of unnamed namespace
+
+static void ReverseByte(uint8_t& b) {
+ b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
+ b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
+ b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
+}
+
+namespace mozilla {
+namespace safebrowsing {
+
+RiceDeltaDecoder::RiceDeltaDecoder(uint8_t* aEncodedData,
+ size_t aEncodedDataSize)
+ : mEncodedData(aEncodedData), mEncodedDataSize(aEncodedDataSize) {}
+
+bool RiceDeltaDecoder::Decode(uint32_t aRiceParameter, uint32_t aFirstValue,
+ uint32_t aNumEntries, uint32_t* aDecodedData) {
+ // Reverse each byte before reading bits from the byte buffer.
+ for (size_t i = 0; i < mEncodedDataSize; i++) {
+ ReverseByte(mEncodedData[i]);
+ }
+
+ BitBuffer bitBuffer(mEncodedData, mEncodedDataSize);
+
+ // q = quotient
+ // r = remainder
+ // k = RICE parameter
+ const uint32_t k = aRiceParameter;
+ aDecodedData[0] = aFirstValue;
+ for (uint32_t i = 0; i < aNumEntries; i++) {
+ // Read the quotient of N.
+ uint32_t q;
+ if (!bitBuffer.ReadExponentialGolomb(&q)) {
+ LOG(("Encoded data underflow!"));
+ return false;
+ }
+
+ // Read the remainder of N, one bit at a time.
+ uint32_t r = 0;
+ for (uint32_t j = 0; j < k; j++) {
+ uint32_t b = 0;
+ if (!bitBuffer.ReadBits(&b, 1)) {
+ // Insufficient bits. Just leave them as zeros.
+ break;
+ }
+ // Add the bit to the right position so that it's in Little Endian order.
+ r |= b << j;
+ }
+
+ // Caculate N from q,r,k.
+ uint32_t N = (q << k) + r;
+
+ // We start filling aDecodedData from [1].
+ aDecodedData[i + 1] = N + aDecodedData[i];
+ }
+
+ return true;
+}
+
+} // namespace safebrowsing
+} // namespace mozilla
+
+namespace {
+//////////////////////////////////////////////////////////////////////////
+// The BitBuffer impl is copied and modified from webrtc/base/bitbuffer.cc
+//
+
+// Returns the lowest (right-most) |bit_count| bits in |byte|.
+uint8_t LowestBits(uint8_t byte, size_t bit_count) {
+ return byte & ((1 << bit_count) - 1);
+}
+
+// Returns the highest (left-most) |bit_count| bits in |byte|, shifted to the
+// lowest bits (to the right).
+uint8_t HighestBits(uint8_t byte, size_t bit_count) {
+ MOZ_ASSERT(bit_count < 8u);
+ uint8_t shift = 8 - static_cast<uint8_t>(bit_count);
+ uint8_t mask = 0xFF << shift;
+ return (byte & mask) >> shift;
+}
+
+BitBuffer::BitBuffer(const uint8_t* bytes, size_t byte_count)
+ : bytes_(bytes), byte_count_(byte_count), byte_offset_(), bit_offset_() {
+ MOZ_ASSERT(static_cast<uint64_t>(byte_count_) <=
+ std::numeric_limits<uint32_t>::max());
+}
+
+uint64_t BitBuffer::RemainingBitCount() const {
+ return (static_cast<uint64_t>(byte_count_) - byte_offset_) * 8 - bit_offset_;
+}
+
+bool BitBuffer::PeekBits(uint32_t* val, size_t bit_count) {
+ if (!val || bit_count > RemainingBitCount() || bit_count > 32) {
+ return false;
+ }
+ const uint8_t* bytes = bytes_ + byte_offset_;
+ size_t remaining_bits_in_current_byte = 8 - bit_offset_;
+ uint32_t bits = LowestBits(*bytes++, remaining_bits_in_current_byte);
+ // If we're reading fewer bits than what's left in the current byte, just
+ // return the portion of this byte that we need.
+ if (bit_count < remaining_bits_in_current_byte) {
+ *val = HighestBits(bits, bit_offset_ + bit_count);
+ return true;
+ }
+ // Otherwise, subtract what we've read from the bit count and read as many
+ // full bytes as we can into bits.
+ bit_count -= remaining_bits_in_current_byte;
+ while (bit_count >= 8) {
+ bits = (bits << 8) | *bytes++;
+ bit_count -= 8;
+ }
+ // Whatever we have left is smaller than a byte, so grab just the bits we need
+ // and shift them into the lowest bits.
+ if (bit_count > 0) {
+ bits <<= bit_count;
+ bits |= HighestBits(*bytes, bit_count);
+ }
+ *val = bits;
+ return true;
+}
+
+bool BitBuffer::ReadBits(uint32_t* val, size_t bit_count) {
+ return PeekBits(val, bit_count) && ConsumeBits(bit_count);
+}
+
+bool BitBuffer::ConsumeBits(size_t bit_count) {
+ if (bit_count > RemainingBitCount()) {
+ return false;
+ }
+
+ byte_offset_ += (bit_offset_ + bit_count) / 8;
+ bit_offset_ = (bit_offset_ + bit_count) % 8;
+ return true;
+}
+
+bool BitBuffer::ReadExponentialGolomb(uint32_t* val) {
+ if (!val) {
+ return false;
+ }
+
+ *val = 0;
+
+ // Count the number of leading 0 bits by peeking/consuming them one at a time.
+ size_t one_bit_count = 0;
+ uint32_t peeked_bit;
+ while (PeekBits(&peeked_bit, 1) && peeked_bit == 1) {
+ one_bit_count++;
+ ConsumeBits(1);
+ }
+ if (!ConsumeBits(1)) {
+ return false; // The stream is incorrectly terminated at '1'.
+ }
+
+ *val = one_bit_count;
+ return true;
+}
+} // namespace
diff --git a/toolkit/components/url-classifier/RiceDeltaDecoder.h b/toolkit/components/url-classifier/RiceDeltaDecoder.h
new file mode 100644
index 0000000000..6aa3313fbe
--- /dev/null
+++ b/toolkit/components/url-classifier/RiceDeltaDecoder.h
@@ -0,0 +1,40 @@
+/* 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/. */
+
+#ifndef RICE_DELTA_DECODER_H
+#define RICE_DELTA_DECODER_H
+
+#include <cstddef>
+#include <cstdint>
+
+namespace mozilla {
+namespace safebrowsing {
+
+class RiceDeltaDecoder {
+ public:
+ // This decoder is tailored for safebrowsing v4, including the
+ // bit reading order and how the remainder part is interpreted.
+ // The caller just needs to feed the byte stream received from
+ // network directly. Note that the input buffer must be mutable
+ // since the decoder will do some pre-processing before decoding.
+ RiceDeltaDecoder(uint8_t* aEncodedData, size_t aEncodedDataSize);
+
+ // @param aNumEntries The number of values to be decoded, not including
+ // the first value.
+ // @param aDecodedData A pre-allocated output buffer. Note that
+ // aDecodedData[0] will be filled with |aFirstValue|
+ // and the buffer length (in byte) should be
+ // ((aNumEntries + 1) * sizeof(uint32_t)).
+ bool Decode(uint32_t aRiceParameter, uint32_t aFirstValue,
+ uint32_t aNumEntries, uint32_t* aDecodedData);
+
+ private:
+ uint8_t* mEncodedData;
+ size_t mEncodedDataSize;
+};
+
+} // namespace safebrowsing
+} // namespace mozilla
+
+#endif // UPDATE_V4_DECODER_H
diff --git a/toolkit/components/url-classifier/SafeBrowsing.sys.mjs b/toolkit/components/url-classifier/SafeBrowsing.sys.mjs
new file mode 100644
index 0000000000..5f0b5faec8
--- /dev/null
+++ b/toolkit/components/url-classifier/SafeBrowsing.sys.mjs
@@ -0,0 +1,575 @@
+/* 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/. */
+
+const PREF_DEBUG_ENABLED = "browser.safebrowsing.debug";
+let loggingEnabled = false;
+
+// Log only if browser.safebrowsing.debug is true
+function log(...stuff) {
+ if (!loggingEnabled) {
+ return;
+ }
+
+ var d = new Date();
+ let msg = "SafeBrowsing: " + d.toTimeString() + ": " + stuff.join(" ");
+ dump(Services.urlFormatter.trimSensitiveURLs(msg) + "\n");
+}
+
+function getLists(prefName) {
+ log("getLists: " + prefName);
+ let pref = Services.prefs.getCharPref(prefName, "");
+
+ // Splitting an empty string returns [''], we really want an empty array.
+ if (!pref) {
+ return [];
+ }
+
+ return pref.split(",").map(value => value.trim());
+}
+
+const FEATURES = [
+ {
+ name: "phishing",
+ list: ["urlclassifier.phishTable"],
+ enabled() {
+ return Services.prefs.getBoolPref(
+ "browser.safebrowsing.phishing.enabled"
+ );
+ },
+ update() {
+ return Services.prefs.getBoolPref(
+ "browser.safebrowsing.features.phishing.update",
+ this.enabled()
+ );
+ },
+ },
+ {
+ name: "malware",
+ list: ["urlclassifier.malwareTable"],
+ enabled() {
+ return Services.prefs.getBoolPref("browser.safebrowsing.malware.enabled");
+ },
+ update() {
+ return Services.prefs.getBoolPref(
+ "browser.safebrowsing.features.malware.update",
+ this.enabled()
+ );
+ },
+ },
+ {
+ name: "blockedURIs",
+ list: ["urlclassifier.blockedTable"],
+ enabled() {
+ return Services.prefs.getBoolPref(
+ "browser.safebrowsing.blockedURIs.enabled"
+ );
+ },
+ update() {
+ return Services.prefs.getBoolPref(
+ "browser.safebrowsing.features.blockedURIs.update",
+ this.enabled()
+ );
+ },
+ },
+ {
+ name: "downloads",
+ list: [
+ "urlclassifier.downloadBlockTable",
+ "urlclassifier.downloadAllowTable",
+ ],
+ enabled() {
+ return (
+ Services.prefs.getBoolPref("browser.safebrowsing.downloads.enabled") &&
+ Services.prefs.getBoolPref("browser.safebrowsing.malware.enabled")
+ );
+ },
+ update() {
+ return Services.prefs.getBoolPref(
+ "browser.safebrowsing.features.downloads.update",
+ this.enabled()
+ );
+ },
+ },
+ {
+ name: "trackingAnnotation",
+ list: [
+ "urlclassifier.trackingAnnotationTable",
+ "urlclassifier.trackingAnnotationWhitelistTable",
+ ],
+ enabled() {
+ return Services.prefs.getBoolPref(
+ "privacy.trackingprotection.annotate_channels"
+ );
+ },
+ update() {
+ return Services.prefs.getBoolPref(
+ "browser.safebrowsing.features.trackingAnnotation.update",
+ this.enabled()
+ );
+ },
+ },
+ {
+ name: "trackingProtection",
+ list: [
+ "urlclassifier.trackingTable",
+ "urlclassifier.trackingWhitelistTable",
+ ],
+ enabled() {
+ return (
+ Services.prefs.getBoolPref("privacy.trackingprotection.enabled") ||
+ Services.prefs.getBoolPref("privacy.trackingprotection.pbmode.enabled")
+ );
+ },
+ update() {
+ return Services.prefs.getBoolPref(
+ "browser.safebrowsing.features.trackingProtection.update",
+ this.enabled()
+ );
+ },
+ },
+ {
+ name: "fingerprinting-annotation",
+ list: [
+ "urlclassifier.features.fingerprinting.annotate.blacklistTables",
+ "urlclassifier.features.fingerprinting.annotate.whitelistTables",
+ ],
+ enabled() {
+ // Annotation features are enabled by default.
+ return true;
+ },
+ update() {
+ return Services.prefs.getBoolPref(
+ "browser.safebrowsing.features.fingerprinting.annotate.update",
+ this.enabled()
+ );
+ },
+ },
+ {
+ name: "fingerprinting-protection",
+ list: [
+ "urlclassifier.features.fingerprinting.blacklistTables",
+ "urlclassifier.features.fingerprinting.whitelistTables",
+ ],
+ enabled() {
+ return Services.prefs.getBoolPref(
+ "privacy.trackingprotection.fingerprinting.enabled",
+ false
+ );
+ },
+ update() {
+ return Services.prefs.getBoolPref(
+ "browser.safebrowsing.features.fingerprinting.update",
+ this.enabled()
+ );
+ },
+ },
+ {
+ name: "cryptomining-annotation",
+ list: [
+ "urlclassifier.features.cryptomining.annotate.blacklistTables",
+ "urlclassifier.features.cryptomining.annotate.whitelistTables",
+ ],
+ enabled() {
+ // Annotation features are enabled by default.
+ return true;
+ },
+ update() {
+ return Services.prefs.getBoolPref(
+ "browser.safebrowsing.features.cryptomining.annotate.update",
+ this.enabled()
+ );
+ },
+ },
+ {
+ name: "cryptomining-protection",
+ list: [
+ "urlclassifier.features.cryptomining.blacklistTables",
+ "urlclassifier.features.cryptomining.whitelistTables",
+ ],
+ enabled() {
+ return Services.prefs.getBoolPref(
+ "privacy.trackingprotection.cryptomining.enabled",
+ false
+ );
+ },
+ update() {
+ return Services.prefs.getBoolPref(
+ "browser.safebrowsing.features.cryptomining.update",
+ this.enabled()
+ );
+ },
+ },
+ {
+ name: "socialtracking-annotation",
+ list: [
+ "urlclassifier.features.socialtracking.annotate.blacklistTables",
+ "urlclassifier.features.socialtracking.annotate.whitelistTables",
+ ],
+ enabled() {
+ // Annotation features are enabled by default.
+ return true;
+ },
+ update() {
+ return Services.prefs.getBoolPref(
+ "browser.safebrowsing.features.socialtracking.annotate.update",
+ this.enabled()
+ );
+ },
+ },
+ {
+ name: "socialtracking-protection",
+ list: [
+ "urlclassifier.features.socialtracking.blacklistTables",
+ "urlclassifier.features.socialtracking.whitelistTables",
+ ],
+ enabled() {
+ return Services.prefs.getBoolPref(
+ "privacy.trackingprotection.socialtracking.enabled",
+ false
+ );
+ },
+ update() {
+ return Services.prefs.getBoolPref(
+ "browser.safebrowsing.features.socialtracking.update",
+ this.enabled()
+ );
+ },
+ },
+ {
+ name: "emailtracking-protection",
+ list: [
+ "urlclassifier.features.emailtracking.blocklistTables",
+ "urlclassifier.features.emailtracking.allowlistTables",
+ ],
+ enabled() {
+ return (
+ Services.prefs.getBoolPref(
+ "privacy.trackingprotection.emailtracking.enabled",
+ false
+ ) ||
+ Services.prefs.getBoolPref(
+ "privacy.trackingprotection.emailtracking.pbmode.enabled",
+ false
+ )
+ );
+ },
+ update() {
+ return Services.prefs.getBoolPref(
+ "browser.safebrowsing.features.emailtracking.update",
+ this.enabled()
+ );
+ },
+ },
+ {
+ name: "emailtracking-data-collection",
+ list: [
+ "urlclassifier.features.emailtracking.datacollection.blocklistTables",
+ "urlclassifier.features.emailtracking.datacollection.allowlistTables",
+ ],
+ enabled() {
+ // Data collection features are enabled by default.
+ return true;
+ },
+ update() {
+ return Services.prefs.getBoolPref(
+ "browser.safebrowsing.features.emailtracking.datacollection.update",
+ this.enabled()
+ );
+ },
+ },
+];
+
+export var SafeBrowsing = {
+ init() {
+ if (this.initialized) {
+ log("Already initialized");
+ return;
+ }
+
+ Services.prefs.addObserver("browser.safebrowsing", this);
+ Services.prefs.addObserver("privacy.trackingprotection", this);
+ Services.prefs.addObserver("urlclassifier", this);
+
+ this.readPrefs();
+
+ this.controlUpdateChecking();
+ this.initialized = true;
+
+ log("init() finished");
+ },
+
+ registerTableWithURLs(listname) {
+ let listManager = Cc[
+ "@mozilla.org/url-classifier/listmanager;1"
+ ].getService(Ci.nsIUrlListManager);
+
+ let providerName = this.listToProvider[listname];
+ let provider = this.providers[providerName];
+
+ if (!providerName || !provider) {
+ log("No provider info found for " + listname);
+ log("Check browser.safebrowsing.provider.[google/mozilla].lists");
+ return;
+ }
+
+ if (!provider.updateURL) {
+ log("Invalid update url " + listname);
+ return;
+ }
+
+ listManager.registerTable(
+ listname,
+ providerName,
+ provider.updateURL,
+ provider.gethashURL
+ );
+ },
+
+ registerTables() {
+ this.features.forEach(feature => {
+ feature.list.forEach(table => {
+ this.registerTableWithURLs(table);
+ });
+ });
+ },
+
+ unregisterTables(obsoleteLists) {
+ let listManager = Cc[
+ "@mozilla.org/url-classifier/listmanager;1"
+ ].getService(Ci.nsIUrlListManager);
+
+ obsoleteLists.forEach(list => {
+ list.forEach(table => {
+ listManager.unregisterTable(table);
+ });
+ });
+ },
+
+ initialized: false,
+
+ features: [],
+
+ updateURL: null,
+ gethashURL: null,
+ reportURL: null,
+
+ getReportURL(kind, info) {
+ let pref;
+ switch (kind) {
+ case "Phish":
+ pref = "browser.safebrowsing.reportPhishURL";
+ break;
+
+ case "PhishMistake":
+ case "MalwareMistake":
+ pref =
+ "browser.safebrowsing.provider." +
+ info.provider +
+ ".report" +
+ kind +
+ "URL";
+ break;
+
+ default:
+ let err =
+ "SafeBrowsing getReportURL() called with unknown kind: " + kind;
+ console.error(err);
+ throw err;
+ }
+
+ // The "Phish" reports are about submitting new phishing URLs to Google so
+ // they don't have an associated list URL
+ if (kind != "Phish" && (!info.list || !info.uri)) {
+ return null;
+ }
+
+ let reportUrl = Services.urlFormatter.formatURLPref(pref);
+ // formatURLPref might return "about:blank" if getting the pref fails
+ if (reportUrl == "about:blank") {
+ reportUrl = null;
+ }
+
+ if (reportUrl) {
+ reportUrl += encodeURIComponent(info.uri);
+ }
+ return reportUrl;
+ },
+
+ observe(aSubject, aTopic, aData) {
+ // skip nextupdatetime and lastupdatetime
+ if (aData.includes("lastupdatetime") || aData.includes("nextupdatetime")) {
+ return;
+ }
+
+ if (aData == PREF_DEBUG_ENABLED) {
+ loggingEnabled = Services.prefs.getBoolPref(PREF_DEBUG_ENABLED);
+ return;
+ }
+
+ this.readPrefs();
+ },
+
+ readPrefs() {
+ loggingEnabled = Services.prefs.getBoolPref(PREF_DEBUG_ENABLED);
+ log("reading prefs");
+
+ let obsoleteLists = [];
+ // Make a copy of the original lists before we re-read the prefs.
+ if (this.initialized) {
+ obsoleteLists = this.features.map(feature => {
+ return feature.list;
+ });
+ }
+
+ // Allow to disable all feature updates with a single preference for tests.
+ let update = Services.prefs.getBoolPref(
+ "browser.safebrowsing.update.enabled",
+ true
+ );
+
+ this.features = [];
+ for (let i = 0; i < FEATURES.length; ++i) {
+ this.features[i] = {
+ name: FEATURES[i].name,
+ list: [],
+ enabled: FEATURES[i].enabled(),
+ update: FEATURES[i].update() && update,
+ };
+
+ FEATURES[i].list.forEach(pref => {
+ this.features[i].list.push(...getLists(pref));
+ });
+ }
+
+ for (let i = 0; i < obsoleteLists.length; ++i) {
+ obsoleteLists[i] = obsoleteLists[i].filter(
+ list => !this.features[i].list.includes(list)
+ );
+ }
+
+ this.updateProviderURLs();
+ this.registerTables();
+ if (obsoleteLists) {
+ this.unregisterTables(obsoleteLists);
+ }
+
+ // XXX The listManager backend gets confused if this is called before the
+ // lists are registered. So only call it here when a pref changes, and not
+ // when doing initialization. I expect to refactor this later, so pardon the hack.
+ if (this.initialized) {
+ this.controlUpdateChecking();
+ }
+ },
+
+ updateProviderURLs() {
+ try {
+ var clientID = Services.prefs.getCharPref("browser.safebrowsing.id");
+ } catch (e) {
+ clientID = Services.appinfo.name;
+ }
+
+ log("initializing safe browsing URLs, client id", clientID);
+
+ // Get the different providers
+ let branch = Services.prefs.getBranch("browser.safebrowsing.provider.");
+ let children = branch.getChildList("");
+ this.providers = {};
+ this.listToProvider = {};
+
+ for (let child of children) {
+ log("Child: " + child);
+ let prefComponents = child.split(".");
+ let providerName = prefComponents[0];
+ this.providers[providerName] = {};
+ }
+
+ if (loggingEnabled) {
+ let providerStr = "";
+ Object.keys(this.providers).forEach(function (provider) {
+ if (providerStr === "") {
+ providerStr = provider;
+ } else {
+ providerStr += ", " + provider;
+ }
+ });
+ log("Providers: " + providerStr);
+ }
+
+ Object.keys(this.providers).forEach(function (provider) {
+ if (provider == "test") {
+ return; // skip
+ }
+ let updateURL = Services.urlFormatter.formatURLPref(
+ "browser.safebrowsing.provider." + provider + ".updateURL"
+ );
+ let gethashURL = Services.urlFormatter.formatURLPref(
+ "browser.safebrowsing.provider." + provider + ".gethashURL"
+ );
+ updateURL = updateURL.replace("SAFEBROWSING_ID", clientID);
+ gethashURL = gethashURL.replace("SAFEBROWSING_ID", clientID);
+
+ // Disable updates and gethash if the Google API key is missing.
+ let googleSafebrowsingKey = Services.urlFormatter
+ .formatURL("%GOOGLE_SAFEBROWSING_API_KEY%")
+ .trim();
+ if (
+ (provider == "google" || provider == "google4") &&
+ (!googleSafebrowsingKey ||
+ googleSafebrowsingKey == "no-google-safebrowsing-api-key")
+ ) {
+ log(
+ "Missing Google SafeBrowsing API key, clearing updateURL and gethashURL."
+ );
+ updateURL = "";
+ gethashURL = "";
+ }
+
+ log("Provider: " + provider + " updateURL=" + updateURL);
+ log("Provider: " + provider + " gethashURL=" + gethashURL);
+
+ // Urls used to update DB
+ this.providers[provider].updateURL = updateURL;
+ this.providers[provider].gethashURL = gethashURL;
+
+ // Get lists this provider manages
+ let lists = getLists(
+ "browser.safebrowsing.provider." + provider + ".lists"
+ );
+ if (lists) {
+ lists.forEach(function (list) {
+ this.listToProvider[list] = provider;
+ }, this);
+ } else {
+ log("Update URL given but no lists managed for provider: " + provider);
+ }
+ }, this);
+ },
+
+ controlUpdateChecking() {
+ if (loggingEnabled) {
+ this.features.forEach(feature => {
+ log("feature " + feature.name + ":");
+ log(" enabled:" + feature.enabled);
+ log(" update:" + feature.update);
+ log(" tables:" + feature.list);
+ });
+ }
+
+ let listManager = Cc[
+ "@mozilla.org/url-classifier/listmanager;1"
+ ].getService(Ci.nsIUrlListManager);
+
+ listManager.disableAllUpdates();
+
+ this.features.forEach(feature => {
+ if (feature.update) {
+ feature.list.forEach(table => {
+ listManager.enableUpdate(table);
+ });
+ }
+ });
+
+ listManager.maybeToggleUpdateChecking();
+ },
+};
diff --git a/toolkit/components/url-classifier/UrlClassifierHashCompleter.sys.mjs b/toolkit/components/url-classifier/UrlClassifierHashCompleter.sys.mjs
new file mode 100644
index 0000000000..48a4c432fb
--- /dev/null
+++ b/toolkit/components/url-classifier/UrlClassifierHashCompleter.sys.mjs
@@ -0,0 +1,965 @@
+/* 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/. */
+
+// COMPLETE_LENGTH and PARTIAL_LENGTH copied from nsUrlClassifierDBService.h,
+// they correspond to the length, in bytes, of a hash prefix and the total
+// hash.
+const COMPLETE_LENGTH = 32;
+const PARTIAL_LENGTH = 4;
+
+// Upper limit on the server response minimumWaitDuration
+const MIN_WAIT_DURATION_MAX_VALUE = 24 * 60 * 60 * 1000;
+const PREF_DEBUG_ENABLED = "browser.safebrowsing.debug";
+
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+
+import { NetUtil } from "resource://gre/modules/NetUtil.sys.mjs";
+
+const lazy = {};
+
+XPCOMUtils.defineLazyServiceGetter(
+ lazy,
+ "gDbService",
+ "@mozilla.org/url-classifier/dbservice;1",
+ "nsIUrlClassifierDBService"
+);
+
+XPCOMUtils.defineLazyServiceGetter(
+ lazy,
+ "gUrlUtil",
+ "@mozilla.org/url-classifier/utils;1",
+ "nsIUrlClassifierUtils"
+);
+
+let loggingEnabled = false;
+
+// Log only if browser.safebrowsing.debug is true
+function log(...stuff) {
+ if (!loggingEnabled) {
+ return;
+ }
+
+ var d = new Date();
+ let msg = "hashcompleter: " + d.toTimeString() + ": " + stuff.join(" ");
+ dump(Services.urlFormatter.trimSensitiveURLs(msg) + "\n");
+}
+
+// Map the HTTP response code to a Telemetry bucket
+// https://developers.google.com/safe-browsing/developers_guide_v2?hl=en
+// eslint-disable-next-line complexity
+function httpStatusToBucket(httpStatus) {
+ var statusBucket;
+ switch (httpStatus) {
+ case 100:
+ case 101:
+ // Unexpected 1xx return code
+ statusBucket = 0;
+ break;
+ case 200:
+ // OK - Data is available in the HTTP response body.
+ statusBucket = 1;
+ break;
+ case 201:
+ case 202:
+ case 203:
+ case 205:
+ case 206:
+ // Unexpected 2xx return code
+ statusBucket = 2;
+ break;
+ case 204:
+ // No Content - There are no full-length hashes with the requested prefix.
+ statusBucket = 3;
+ break;
+ case 300:
+ case 301:
+ case 302:
+ case 303:
+ case 304:
+ case 305:
+ case 307:
+ case 308:
+ // Unexpected 3xx return code
+ statusBucket = 4;
+ break;
+ case 400:
+ // Bad Request - The HTTP request was not correctly formed.
+ // The client did not provide all required CGI parameters.
+ statusBucket = 5;
+ break;
+ case 401:
+ case 402:
+ case 405:
+ case 406:
+ case 407:
+ case 409:
+ case 410:
+ case 411:
+ case 412:
+ case 414:
+ case 415:
+ case 416:
+ case 417:
+ case 421:
+ case 426:
+ case 428:
+ case 429:
+ case 431:
+ case 451:
+ // Unexpected 4xx return code
+ statusBucket = 6;
+ break;
+ case 403:
+ // Forbidden - The client id is invalid.
+ statusBucket = 7;
+ break;
+ case 404:
+ // Not Found
+ statusBucket = 8;
+ break;
+ case 408:
+ // Request Timeout
+ statusBucket = 9;
+ break;
+ case 413:
+ // Request Entity Too Large - Bug 1150334
+ statusBucket = 10;
+ break;
+ case 500:
+ case 501:
+ case 510:
+ // Unexpected 5xx return code
+ statusBucket = 11;
+ break;
+ case 502:
+ case 504:
+ case 511:
+ // Local network errors, we'll ignore these.
+ statusBucket = 12;
+ break;
+ case 503:
+ // Service Unavailable - The server cannot handle the request.
+ // Clients MUST follow the backoff behavior specified in the
+ // Request Frequency section.
+ statusBucket = 13;
+ break;
+ case 505:
+ // HTTP Version Not Supported - The server CANNOT handle the requested
+ // protocol major version.
+ statusBucket = 14;
+ break;
+ default:
+ statusBucket = 15;
+ }
+ return statusBucket;
+}
+
+function FullHashMatch(table, hash, duration) {
+ this.tableName = table;
+ this.fullHash = hash;
+ this.cacheDuration = duration;
+}
+
+FullHashMatch.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsIFullHashMatch"]),
+
+ tableName: null,
+ fullHash: null,
+ cacheDuration: null,
+};
+
+export function HashCompleter() {
+ // The current HashCompleterRequest in flight. Once it is started, it is set
+ // to null. It may be used by multiple calls to |complete| in succession to
+ // avoid creating multiple requests to the same gethash URL.
+ this._currentRequest = null;
+ // An Array of ongoing gethash requests which is used to find requests for
+ // the same hash prefix.
+ this._ongoingRequests = [];
+ // A map of gethashUrls to HashCompleterRequests that haven't yet begun.
+ this._pendingRequests = {};
+
+ // A map of gethash URLs to RequestBackoff objects.
+ this._backoffs = {};
+
+ // Whether we have been informed of a shutdown by the shutdown event.
+ this._shuttingDown = false;
+
+ // A map of gethash URLs to next gethash time in miliseconds
+ this._nextGethashTimeMs = {};
+
+ Services.obs.addObserver(this, "quit-application");
+ Services.prefs.addObserver(PREF_DEBUG_ENABLED, this);
+
+ loggingEnabled = Services.prefs.getBoolPref(PREF_DEBUG_ENABLED);
+}
+
+HashCompleter.prototype = {
+ classID: Components.ID("{9111de73-9322-4bfc-8b65-2b727f3e6ec8}"),
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIUrlClassifierHashCompleter",
+ "nsIRunnable",
+ "nsIObserver",
+ "nsISupportsWeakReference",
+ "nsITimerCallback",
+ ]),
+
+ // This is mainly how the HashCompleter interacts with other components.
+ // Even though it only takes one partial hash and callback, subsequent
+ // calls are made into the same HTTP request by using a thread dispatch.
+ complete: function HC_complete(
+ aPartialHash,
+ aGethashUrl,
+ aTableName,
+ aCallback
+ ) {
+ if (!aGethashUrl) {
+ throw Components.Exception("", Cr.NS_ERROR_NOT_INITIALIZED);
+ }
+
+ // Check ongoing requests before creating a new HashCompleteRequest
+ for (let r of this._ongoingRequests) {
+ if (r.find(aPartialHash, aGethashUrl, aTableName)) {
+ log(
+ "Merge gethash request in " +
+ aTableName +
+ " for prefix : " +
+ btoa(aPartialHash)
+ );
+ r.add(aPartialHash, aCallback, aTableName);
+ return;
+ }
+ }
+
+ if (!this._currentRequest) {
+ this._currentRequest = new HashCompleterRequest(this, aGethashUrl);
+ }
+ if (this._currentRequest.gethashUrl == aGethashUrl) {
+ this._currentRequest.add(aPartialHash, aCallback, aTableName);
+ } else {
+ if (!this._pendingRequests[aGethashUrl]) {
+ this._pendingRequests[aGethashUrl] = new HashCompleterRequest(
+ this,
+ aGethashUrl
+ );
+ }
+ this._pendingRequests[aGethashUrl].add(
+ aPartialHash,
+ aCallback,
+ aTableName
+ );
+ }
+
+ if (!this._backoffs[aGethashUrl]) {
+ // Initialize request backoffs separately, since requests are deleted
+ // after they are dispatched.
+ var jslib =
+ Cc["@mozilla.org/url-classifier/jslib;1"].getService().wrappedJSObject;
+
+ // Using the V4 backoff algorithm for both V2 and V4. See bug 1273398.
+ this._backoffs[aGethashUrl] = new jslib.RequestBackoffV4(
+ 10 /* keep track of max requests */,
+ 0 /* don't throttle on successful requests per time period */,
+ lazy.gUrlUtil.getProvider(aTableName) /* used by testcase */
+ );
+ }
+
+ if (!this._nextGethashTimeMs[aGethashUrl]) {
+ this._nextGethashTimeMs[aGethashUrl] = 0;
+ }
+
+ // Start off this request. Without dispatching to a thread, every call to
+ // complete makes an individual HTTP request.
+ Services.tm.dispatchToMainThread(this);
+ },
+
+ // This is called after several calls to |complete|, or after the
+ // currentRequest has finished. It starts off the HTTP request by making a
+ // |begin| call to the HashCompleterRequest.
+ run() {
+ // Clear everything on shutdown
+ if (this._shuttingDown) {
+ this._currentRequest = null;
+ this._pendingRequests = null;
+ this._nextGethashTimeMs = null;
+
+ for (var url in this._backoffs) {
+ this._backoffs[url] = null;
+ }
+ throw Components.Exception("", Cr.NS_ERROR_NOT_INITIALIZED);
+ }
+
+ // If we don't have an in-flight request, make one
+ let pendingUrls = Object.keys(this._pendingRequests);
+ if (!this._currentRequest && pendingUrls.length) {
+ let nextUrl = pendingUrls[0];
+ this._currentRequest = this._pendingRequests[nextUrl];
+ delete this._pendingRequests[nextUrl];
+ }
+
+ if (this._currentRequest) {
+ try {
+ if (this._currentRequest.begin()) {
+ this._ongoingRequests.push(this._currentRequest);
+ }
+ } finally {
+ // If |begin| fails, we should get rid of our request.
+ this._currentRequest = null;
+ }
+ }
+ },
+
+ // Pass the server response status to the RequestBackoff for the given
+ // gethashUrl and fetch the next pending request, if there is one.
+ finishRequest(aRequest, aStatus) {
+ this._ongoingRequests = this._ongoingRequests.filter(v => v != aRequest);
+
+ this._backoffs[aRequest.gethashUrl].noteServerResponse(aStatus);
+ Services.tm.dispatchToMainThread(this);
+ },
+
+ // Returns true if we can make a request from the given url, false otherwise.
+ canMakeRequest(aGethashUrl) {
+ return (
+ this._backoffs[aGethashUrl].canMakeRequest() &&
+ Date.now() >= this._nextGethashTimeMs[aGethashUrl]
+ );
+ },
+
+ // Notifies the RequestBackoff of a new request so we can throttle based on
+ // max requests/time period. This must be called before a channel is opened,
+ // and finishRequest must be called once the response is received.
+ noteRequest(aGethashUrl) {
+ return this._backoffs[aGethashUrl].noteRequest();
+ },
+
+ observe: function HC_observe(aSubject, aTopic, aData) {
+ switch (aTopic) {
+ case "quit-application":
+ this._shuttingDown = true;
+ Services.obs.removeObserver(this, "quit-application");
+ break;
+ case "nsPref:changed":
+ if (aData == PREF_DEBUG_ENABLED) {
+ loggingEnabled = Services.prefs.getBoolPref(PREF_DEBUG_ENABLED);
+ }
+ break;
+ }
+ },
+};
+
+function HashCompleterRequest(aCompleter, aGethashUrl) {
+ // HashCompleter object that created this HashCompleterRequest.
+ this._completer = aCompleter;
+ // The internal set of hashes and callbacks that this request corresponds to.
+ this._requests = [];
+ // nsIChannel that the hash completion query is transmitted over.
+ this._channel = null;
+ // Response body of hash completion. Created in onDataAvailable.
+ this._response = "";
+ // Whether we have been informed of a shutdown by the quit-application event.
+ this._shuttingDown = false;
+ this.gethashUrl = aGethashUrl;
+
+ this.provider = "";
+ // Multiple partial hashes can be associated with the same tables
+ // so we use a map here.
+ this.tableNames = new Map();
+
+ this.telemetryProvider = "";
+ this.telemetryClockStart = 0;
+}
+HashCompleterRequest.prototype = {
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIRequestObserver",
+ "nsIStreamListener",
+ "nsIObserver",
+ ]),
+
+ // This is called by the HashCompleter to add a hash and callback to the
+ // HashCompleterRequest. It must be called before calling |begin|.
+ add: function HCR_add(aPartialHash, aCallback, aTableName) {
+ this._requests.push({
+ partialHash: aPartialHash,
+ callback: aCallback,
+ tableName: aTableName,
+ response: { matches: [] },
+ });
+
+ if (aTableName) {
+ let isTableNameV4 = aTableName.endsWith("-proto");
+ if (0 === this.tableNames.size) {
+ // Decide if this request is v4 by the first added partial hash.
+ this.isV4 = isTableNameV4;
+ } else if (this.isV4 !== isTableNameV4) {
+ log(
+ 'ERROR: Cannot mix "proto" tables with other types within ' +
+ "the same gethash URL."
+ );
+ }
+ if (!this.tableNames.has(aTableName)) {
+ this.tableNames.set(aTableName);
+ }
+
+ // Assuming all tables with the same gethash URL have the same provider
+ if (this.provider == "") {
+ this.provider = lazy.gUrlUtil.getProvider(aTableName);
+ }
+
+ if (this.telemetryProvider == "") {
+ this.telemetryProvider = lazy.gUrlUtil.getTelemetryProvider(aTableName);
+ }
+ }
+ },
+
+ find: function HCR_find(aPartialHash, aGetHashUrl, aTableName) {
+ if (this.gethashUrl != aGetHashUrl || !this.tableNames.has(aTableName)) {
+ return false;
+ }
+
+ return this._requests.find(function (r) {
+ return r.partialHash === aPartialHash;
+ });
+ },
+
+ fillTableStatesBase64: function HCR_fillTableStatesBase64(aCallback) {
+ lazy.gDbService.getTables(aTableData => {
+ aTableData.split("\n").forEach(line => {
+ let p = line.indexOf(";");
+ if (-1 === p) {
+ return;
+ }
+ // [tableName];[stateBase64]:[checksumBase64]
+ let tableName = line.substring(0, p);
+ if (this.tableNames.has(tableName)) {
+ let metadata = line.substring(p + 1).split(":");
+ let stateBase64 = metadata[0];
+ this.tableNames.set(tableName, stateBase64);
+ }
+ });
+
+ aCallback();
+ });
+ },
+
+ // This initiates the HTTP request. It can fail due to backoff timings and
+ // will notify all callbacks as necessary. We notify the backoff object on
+ // begin.
+ begin: function HCR_begin() {
+ if (!this._completer.canMakeRequest(this.gethashUrl)) {
+ log("Can't make request to " + this.gethashUrl + "\n");
+ this.notifyFailure(Cr.NS_ERROR_ABORT);
+ return false;
+ }
+
+ Services.obs.addObserver(this, "quit-application");
+
+ // V4 requires table states to build the request so we need
+ // a async call to retrieve the table states from disk.
+ // Note that |HCR_begin| is fine to be sync because
+ // it doesn't appear in a sync call chain.
+ this.fillTableStatesBase64(() => {
+ try {
+ this.openChannel();
+ // Notify the RequestBackoff if opening the channel succeeded. At this
+ // point, finishRequest must be called.
+ this._completer.noteRequest(this.gethashUrl);
+ } catch (err) {
+ this._completer._ongoingRequests =
+ this._completer._ongoingRequests.filter(v => v != this);
+ this.notifyFailure(err);
+ throw err;
+ }
+ });
+
+ return true;
+ },
+
+ notify: function HCR_notify() {
+ // If we haven't gotten onStopRequest, just cancel. This will call us
+ // with onStopRequest since we implement nsIStreamListener on the
+ // channel.
+ if (this._channel && this._channel.isPending()) {
+ log("cancelling request to " + this.gethashUrl + " (timeout)\n");
+ Services.telemetry
+ .getKeyedHistogramById("URLCLASSIFIER_COMPLETE_TIMEOUT2")
+ .add(this.telemetryProvider, 1);
+ this._channel.cancel(Cr.NS_BINDING_ABORTED);
+ }
+ },
+
+ // Creates an nsIChannel for the request and fills the body.
+ // Enforce bypassing URL Classifier check because if the request is
+ // blocked, it means SafeBrowsing is malfunction.
+ openChannel: function HCR_openChannel() {
+ let loadFlags =
+ Ci.nsIChannel.INHIBIT_CACHING |
+ Ci.nsIChannel.LOAD_BYPASS_CACHE |
+ Ci.nsIChannel.LOAD_BYPASS_URL_CLASSIFIER;
+
+ this.request = {
+ url: this.gethashUrl,
+ body: "",
+ };
+
+ if (this.isV4) {
+ // As per spec, we add the request payload to the gethash url.
+ this.request.url += "&$req=" + this.buildRequestV4();
+ }
+
+ log("actualGethashUrl: " + this.request.url);
+
+ let channel = NetUtil.newChannel({
+ uri: this.request.url,
+ loadUsingSystemPrincipal: true,
+ });
+ channel.loadFlags = loadFlags;
+ channel.loadInfo.originAttributes = {
+ // The firstPartyDomain value should sync with NECKO_SAFEBROWSING_FIRST_PARTY_DOMAIN
+ // defined in nsNetUtil.h.
+ firstPartyDomain:
+ "safebrowsing.86868755-6b82-4842-b301-72671a0db32e.mozilla",
+ };
+
+ // Disable keepalive.
+ let httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
+ httpChannel.setRequestHeader("Connection", "close", false);
+
+ this._channel = channel;
+
+ if (this.isV4) {
+ httpChannel.setRequestHeader("X-HTTP-Method-Override", "POST", false);
+ } else {
+ let body = this.buildRequest();
+ this.addRequestBody(body);
+ }
+
+ // Set a timer that cancels the channel after timeout_ms in case we
+ // don't get a gethash response.
+ this.timer_ = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ // Ask the timer to use nsITimerCallback (.notify()) when ready
+ let timeout = Services.prefs.getIntPref("urlclassifier.gethash.timeout_ms");
+ this.timer_.initWithCallback(this, timeout, this.timer_.TYPE_ONE_SHOT);
+ channel.asyncOpen(this);
+ this.telemetryClockStart = Date.now();
+ },
+
+ buildRequestV4: function HCR_buildRequestV4() {
+ // Convert the "name to state" mapping to two equal-length arrays.
+ let tableNameArray = [];
+ let stateArray = [];
+ this.tableNames.forEach((state, name) => {
+ // We skip the table which is not associated with a state.
+ if (state) {
+ tableNameArray.push(name);
+ stateArray.push(state);
+ }
+ });
+
+ // Build the "distinct" prefix array.
+ // The array is sorted to make sure the entries are arbitrary mixed in a
+ // deterministic way
+ let prefixSet = new Set();
+ this._requests.forEach(r => prefixSet.add(btoa(r.partialHash)));
+ let prefixArray = Array.from(prefixSet).sort();
+
+ log(
+ "Build v4 gethash request with " +
+ JSON.stringify(tableNameArray) +
+ ", " +
+ JSON.stringify(stateArray) +
+ ", " +
+ JSON.stringify(prefixArray)
+ );
+
+ return lazy.gUrlUtil.makeFindFullHashRequestV4(
+ tableNameArray,
+ stateArray,
+ prefixArray
+ );
+ },
+
+ // Returns a string for the request body based on the contents of
+ // this._requests.
+ buildRequest: function HCR_buildRequest() {
+ // Sometimes duplicate entries are sent to HashCompleter but we do not need
+ // to propagate these to the server. (bug 633644)
+ let prefixes = [];
+
+ for (let i = 0; i < this._requests.length; i++) {
+ let request = this._requests[i];
+ if (!prefixes.includes(request.partialHash)) {
+ prefixes.push(request.partialHash);
+ }
+ }
+
+ // Sort to make sure the entries are arbitrary mixed in a deterministic way
+ prefixes.sort();
+
+ let body;
+ body =
+ PARTIAL_LENGTH +
+ ":" +
+ PARTIAL_LENGTH * prefixes.length +
+ "\n" +
+ prefixes.join("");
+
+ log(
+ "Requesting completions for " +
+ prefixes.length +
+ " " +
+ PARTIAL_LENGTH +
+ "-byte prefixes: " +
+ body
+ );
+ return body;
+ },
+
+ // Sets the request body of this._channel.
+ addRequestBody: function HCR_addRequestBody(aBody) {
+ let inputStream = Cc[
+ "@mozilla.org/io/string-input-stream;1"
+ ].createInstance(Ci.nsIStringInputStream);
+
+ inputStream.setData(aBody, aBody.length);
+
+ let uploadChannel = this._channel.QueryInterface(Ci.nsIUploadChannel);
+ uploadChannel.setUploadStream(inputStream, "text/plain", -1);
+
+ let httpChannel = this._channel.QueryInterface(Ci.nsIHttpChannel);
+ httpChannel.requestMethod = "POST";
+ },
+
+ // Parses the response body and eventually adds items to the |response.matches| array
+ // for elements of |this._requests|.
+ handleResponse: function HCR_handleResponse() {
+ if (this._response == "") {
+ return;
+ }
+
+ if (this.isV4) {
+ this.handleResponseV4();
+ return;
+ }
+
+ let start = 0;
+
+ let length = this._response.length;
+ while (start != length) {
+ start = this.handleTable(start);
+ }
+ },
+
+ handleResponseV4: function HCR_handleResponseV4() {
+ let callback = {
+ // onCompleteHashFound will be called for each fullhash found in
+ // FullHashResponse.
+ onCompleteHashFound: (
+ aCompleteHash,
+ aTableNames,
+ aPerHashCacheDuration
+ ) => {
+ log(
+ "V4 fullhash response complete hash found callback: " +
+ aTableNames +
+ ", CacheDuration(" +
+ aPerHashCacheDuration +
+ ")"
+ );
+
+ // Filter table names which we didn't requested.
+ let filteredTables = aTableNames.split(",").filter(name => {
+ return this.tableNames.get(name);
+ });
+ if (0 === filteredTables.length) {
+ log("ERROR: Got complete hash which is from unknown table.");
+ return;
+ }
+ if (filteredTables.length > 1) {
+ log("WARNING: Got complete hash which has ambigious threat type.");
+ }
+
+ this.handleItem({
+ completeHash: aCompleteHash,
+ tableName: filteredTables[0],
+ cacheDuration: aPerHashCacheDuration,
+ });
+ },
+
+ // onResponseParsed will be called no matter if there is match in
+ // FullHashResponse, the callback is mainly used to pass negative cache
+ // duration and minimum wait duration.
+ onResponseParsed: (aMinWaitDuration, aNegCacheDuration) => {
+ log(
+ "V4 fullhash response parsed callback: " +
+ "MinWaitDuration(" +
+ aMinWaitDuration +
+ "), " +
+ "NegativeCacheDuration(" +
+ aNegCacheDuration +
+ ")"
+ );
+
+ let minWaitDuration = aMinWaitDuration;
+
+ if (aMinWaitDuration > MIN_WAIT_DURATION_MAX_VALUE) {
+ log(
+ "WARNING: Minimum wait duration too large, clamping it down " +
+ "to a reasonable value."
+ );
+ minWaitDuration = MIN_WAIT_DURATION_MAX_VALUE;
+ } else if (aMinWaitDuration < 0) {
+ log("WARNING: Minimum wait duration is negative, reset it to 0");
+ minWaitDuration = 0;
+ }
+
+ this._completer._nextGethashTimeMs[this.gethashUrl] =
+ Date.now() + minWaitDuration;
+
+ // A fullhash request may contain more than one prefix, so the negative
+ // cache duration should be set for all the prefixes in the request.
+ this._requests.forEach(request => {
+ request.response.negCacheDuration = aNegCacheDuration;
+ });
+ },
+ };
+
+ lazy.gUrlUtil.parseFindFullHashResponseV4(this._response, callback);
+ },
+
+ // This parses a table entry in the response body and calls |handleItem|
+ // for complete hash in the table entry.
+ handleTable: function HCR_handleTable(aStart) {
+ let body = this._response.substring(aStart);
+
+ // deal with new line indexes as there could be
+ // new line characters in the data parts.
+ let newlineIndex = body.indexOf("\n");
+ if (newlineIndex == -1) {
+ throw errorWithStack();
+ }
+ let header = body.substring(0, newlineIndex);
+ let entries = header.split(":");
+ if (entries.length != 3) {
+ throw errorWithStack();
+ }
+
+ let list = entries[0];
+ let addChunk = parseInt(entries[1]);
+ let dataLength = parseInt(entries[2]);
+
+ log("Response includes add chunks for " + list + ": " + addChunk);
+ if (
+ dataLength % COMPLETE_LENGTH != 0 ||
+ dataLength == 0 ||
+ dataLength > body.length - (newlineIndex + 1)
+ ) {
+ throw errorWithStack();
+ }
+
+ let data = body.substr(newlineIndex + 1, dataLength);
+ for (let i = 0; i < dataLength / COMPLETE_LENGTH; i++) {
+ this.handleItem({
+ completeHash: data.substr(i * COMPLETE_LENGTH, COMPLETE_LENGTH),
+ tableName: list,
+ chunkId: addChunk,
+ });
+ }
+
+ return aStart + newlineIndex + 1 + dataLength;
+ },
+
+ // This adds a complete hash to any entry in |this._requests| that matches
+ // the hash.
+ handleItem: function HCR_handleItem(aData) {
+ let provider = lazy.gUrlUtil.getProvider(aData.tableName);
+ if (provider != this.provider) {
+ log(
+ "Ignoring table " +
+ aData.tableName +
+ " since it belongs to " +
+ provider +
+ " while the response came from " +
+ this.provider +
+ "."
+ );
+ return;
+ }
+
+ for (let i = 0; i < this._requests.length; i++) {
+ let request = this._requests[i];
+ if (aData.completeHash.startsWith(request.partialHash)) {
+ request.response.matches.push(aData);
+ }
+ }
+ },
+
+ // notifySuccess and notifyFailure are used to alert the callbacks with
+ // results. notifySuccess makes |completion| and |completionFinished| calls
+ // while notifyFailure only makes a |completionFinished| call with the error
+ // code.
+ notifySuccess: function HCR_notifySuccess() {
+ // V2 completion handler
+ let completionV2 = req => {
+ req.response.matches.forEach(m => {
+ req.callback.completionV2(m.completeHash, m.tableName, m.chunkId);
+ });
+
+ req.callback.completionFinished(Cr.NS_OK);
+ };
+
+ // V4 completion handler
+ let completionV4 = req => {
+ let matches = Cc["@mozilla.org/array;1"].createInstance(
+ Ci.nsIMutableArray
+ );
+
+ req.response.matches.forEach(m => {
+ matches.appendElement(
+ new FullHashMatch(m.tableName, m.completeHash, m.cacheDuration)
+ );
+ });
+
+ req.callback.completionV4(
+ req.partialHash,
+ req.tableName,
+ req.response.negCacheDuration,
+ matches
+ );
+
+ req.callback.completionFinished(Cr.NS_OK);
+ };
+
+ let completion = this.isV4 ? completionV4 : completionV2;
+ this._requests.forEach(req => {
+ completion(req);
+ });
+ },
+
+ notifyFailure: function HCR_notifyFailure(aStatus) {
+ log("notifying failure\n");
+ for (let i = 0; i < this._requests.length; i++) {
+ let request = this._requests[i];
+ request.callback.completionFinished(aStatus);
+ }
+ },
+
+ onDataAvailable: function HCR_onDataAvailable(
+ aRequest,
+ aInputStream,
+ aOffset,
+ aCount
+ ) {
+ let sis = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(
+ Ci.nsIScriptableInputStream
+ );
+ sis.init(aInputStream);
+ this._response += sis.readBytes(aCount);
+ },
+
+ onStartRequest: function HCR_onStartRequest(aRequest) {
+ // At this point no data is available for us and we have no reason to
+ // terminate the connection, so we do nothing until |onStopRequest|.
+ this._completer._nextGethashTimeMs[this.gethashUrl] = 0;
+
+ if (this.telemetryClockStart > 0) {
+ let msecs = Date.now() - this.telemetryClockStart;
+ Services.telemetry
+ .getKeyedHistogramById("URLCLASSIFIER_COMPLETE_SERVER_RESPONSE_TIME")
+ .add(this.telemetryProvider, msecs);
+ }
+ },
+
+ onStopRequest: function HCR_onStopRequest(aRequest, aStatusCode) {
+ Services.obs.removeObserver(this, "quit-application");
+
+ if (this.timer_) {
+ this.timer_.cancel();
+ this.timer_ = null;
+ }
+
+ this.telemetryClockStart = 0;
+
+ if (this._shuttingDown) {
+ throw Components.Exception("", Cr.NS_ERROR_ABORT);
+ }
+
+ // Default HTTP status to service unavailable, in case we can't retrieve
+ // the true status from the channel.
+ let httpStatus = 503;
+ if (Components.isSuccessCode(aStatusCode)) {
+ let channel = aRequest.QueryInterface(Ci.nsIHttpChannel);
+ let success = channel.requestSucceeded;
+ httpStatus = channel.responseStatus;
+ if (!success) {
+ aStatusCode = Cr.NS_ERROR_ABORT;
+ }
+ }
+ let success = Components.isSuccessCode(aStatusCode);
+ log(
+ "Received a " +
+ httpStatus +
+ " status code from the " +
+ this.provider +
+ " gethash server (success=" +
+ success +
+ "): " +
+ btoa(this._response)
+ );
+
+ Services.telemetry
+ .getKeyedHistogramById("URLCLASSIFIER_COMPLETE_REMOTE_STATUS2")
+ .add(this.telemetryProvider, httpStatusToBucket(httpStatus));
+ if (httpStatus == 400) {
+ dump(
+ "Safe Browsing server returned a 400 during completion: request= " +
+ this.request.url +
+ ",payload= " +
+ this.request.body +
+ "\n"
+ );
+ }
+
+ Services.telemetry
+ .getKeyedHistogramById("URLCLASSIFIER_COMPLETE_TIMEOUT2")
+ .add(this.telemetryProvider, 0);
+
+ // Notify the RequestBackoff once a response is received.
+ this._completer.finishRequest(this, httpStatus);
+
+ if (success) {
+ try {
+ this.handleResponse();
+ } catch (err) {
+ log(err.stack);
+ aStatusCode = err.value;
+ success = false;
+ }
+ }
+
+ if (success) {
+ this.notifySuccess();
+ } else {
+ this.notifyFailure(aStatusCode);
+ }
+ },
+
+ observe: function HCR_observe(aSubject, aTopic, aData) {
+ if (aTopic == "quit-application") {
+ this._shuttingDown = true;
+ if (this._channel) {
+ this._channel.cancel(Cr.NS_ERROR_ABORT);
+ this.telemetryClockStart = 0;
+ }
+
+ Services.obs.removeObserver(this, "quit-application");
+ }
+ },
+};
+
+function errorWithStack() {
+ let err = new Error();
+ err.value = Cr.NS_ERROR_FAILURE;
+ return err;
+}
diff --git a/toolkit/components/url-classifier/UrlClassifierLib.sys.mjs b/toolkit/components/url-classifier/UrlClassifierLib.sys.mjs
new file mode 100644
index 0000000000..aad054ce15
--- /dev/null
+++ b/toolkit/components/url-classifier/UrlClassifierLib.sys.mjs
@@ -0,0 +1,226 @@
+/* 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/. */
+
+// We wastefully reload the same JS files across components. This puts all
+// the common JS files used by safebrowsing and url-classifier into a
+// single component.
+
+const PREF_DISABLE_TEST_BACKOFF =
+ "browser.safebrowsing.provider.test.disableBackoff";
+
+/**
+ * Partially applies a function to a particular "this object" and zero or
+ * more arguments. The result is a new function with some arguments of the first
+ * function pre-filled and the value of |this| "pre-specified".
+ *
+ * Remaining arguments specified at call-time are appended to the pre-
+ * specified ones.
+ *
+ * Usage:
+ * var barMethBound = BindToObject(myFunction, myObj, "arg1", "arg2");
+ * barMethBound("arg3", "arg4");
+ *
+ * @param fn {string} Reference to the function to be bound
+ *
+ * @param self {object} Specifies the object which |this| should point to
+ * when the function is run. If the value is null or undefined, it will default
+ * to the global object.
+ *
+ * @returns {function} A partially-applied form of the speficied function.
+ */
+export function BindToObject(fn, self, opt_args) {
+ var boundargs = fn.boundArgs_ || [];
+ boundargs = boundargs.concat(
+ Array.prototype.slice.call(arguments, 2, arguments.length)
+ );
+
+ if (fn.boundSelf_) {
+ self = fn.boundSelf_;
+ }
+ if (fn.boundFn_) {
+ fn = fn.boundFn_;
+ }
+
+ var newfn = function () {
+ // Combine the static args and the new args into one big array
+ var args = boundargs.concat(Array.prototype.slice.call(arguments));
+ return fn.apply(self, args);
+ };
+
+ newfn.boundArgs_ = boundargs;
+ newfn.boundSelf_ = self;
+ newfn.boundFn_ = fn;
+
+ return newfn;
+}
+
+// This implements logic for stopping requests if the server starts to return
+// too many errors. If we get MAX_ERRORS errors in ERROR_PERIOD minutes, we
+// back off for TIMEOUT_INCREMENT minutes. If we get another error
+// immediately after we restart, we double the timeout and add
+// TIMEOUT_INCREMENT minutes, etc.
+//
+// This is similar to the logic used by the search suggestion service.
+
+// HTTP responses that count as an error. We also include any 5xx response
+// as an error.
+const HTTP_FOUND = 302;
+const HTTP_SEE_OTHER = 303;
+const HTTP_TEMPORARY_REDIRECT = 307;
+
+/**
+ * @param maxErrors Number of times to request before backing off.
+ * @param retryIncrement Time (ms) for each retry before backing off.
+ * @param maxRequests Number the number of requests needed to trigger backoff
+ * @param requestPeriod Number time (ms) in which maxRequests have to occur to
+ * trigger the backoff behavior (0 to disable maxRequests)
+ * @param timeoutIncrement Number time (ms) the starting timeout period
+ * we double this time for consecutive errors
+ * @param maxTimeout Number time (ms) maximum timeout period
+ * @param tolerance Checking next request tolerance.
+ */
+function RequestBackoff(
+ maxErrors,
+ retryIncrement,
+ maxRequests,
+ requestPeriod,
+ timeoutIncrement,
+ maxTimeout,
+ tolerance,
+ provider = null
+) {
+ this.MAX_ERRORS_ = maxErrors;
+ this.RETRY_INCREMENT_ = retryIncrement;
+ this.MAX_REQUESTS_ = maxRequests;
+ this.REQUEST_PERIOD_ = requestPeriod;
+ this.TIMEOUT_INCREMENT_ = timeoutIncrement;
+ this.MAX_TIMEOUT_ = maxTimeout;
+ this.TOLERANCE_ = tolerance;
+
+ // Queue of ints keeping the time of all requests
+ this.requestTimes_ = [];
+
+ this.numErrors_ = 0;
+ this.errorTimeout_ = 0;
+ this.nextRequestTime_ = 0;
+
+ // For test provider, we will disable backoff if preference is set to false.
+ if (provider === "test") {
+ this.canMakeRequestDefault = this.canMakeRequest;
+ this.canMakeRequest = function () {
+ if (Services.prefs.getBoolPref(PREF_DISABLE_TEST_BACKOFF, true)) {
+ return true;
+ }
+ return this.canMakeRequestDefault();
+ };
+ }
+}
+
+/**
+ * Reset the object for reuse. This deliberately doesn't clear requestTimes_.
+ */
+RequestBackoff.prototype.reset = function () {
+ this.numErrors_ = 0;
+ this.errorTimeout_ = 0;
+ this.nextRequestTime_ = 0;
+};
+
+/**
+ * Check to see if we can make a request.
+ */
+RequestBackoff.prototype.canMakeRequest = function () {
+ var now = Date.now();
+ // Note that nsITimer delay is approximate: the timer can be fired before the
+ // requested time has elapsed. So, give it a tolerance
+ if (now + this.TOLERANCE_ < this.nextRequestTime_) {
+ return false;
+ }
+
+ return (
+ this.requestTimes_.length < this.MAX_REQUESTS_ ||
+ now - this.requestTimes_[0] > this.REQUEST_PERIOD_
+ );
+};
+
+RequestBackoff.prototype.noteRequest = function () {
+ var now = Date.now();
+ this.requestTimes_.push(now);
+
+ // We only care about keeping track of MAX_REQUESTS
+ if (this.requestTimes_.length > this.MAX_REQUESTS_) {
+ this.requestTimes_.shift();
+ }
+};
+
+RequestBackoff.prototype.nextRequestDelay = function () {
+ return Math.max(0, this.nextRequestTime_ - Date.now());
+};
+
+/**
+ * Notify this object of the last server response. If it's an error,
+ */
+RequestBackoff.prototype.noteServerResponse = function (status) {
+ if (this.isErrorStatus(status)) {
+ this.numErrors_++;
+
+ if (this.numErrors_ < this.MAX_ERRORS_) {
+ this.errorTimeout_ = this.RETRY_INCREMENT_;
+ } else if (this.numErrors_ == this.MAX_ERRORS_) {
+ this.errorTimeout_ = this.TIMEOUT_INCREMENT_;
+ } else {
+ this.errorTimeout_ *= 2;
+ }
+
+ this.errorTimeout_ = Math.min(this.errorTimeout_, this.MAX_TIMEOUT_);
+ this.nextRequestTime_ = Date.now() + this.errorTimeout_;
+ } else {
+ // Reset error timeout, allow requests to go through.
+ this.reset();
+ }
+};
+
+/**
+ * We consider 302, 303, 307, 4xx, and 5xx http responses to be errors.
+ * @param status Number http status
+ * @return Boolean true if we consider this http status an error
+ */
+RequestBackoff.prototype.isErrorStatus = function (status) {
+ return (
+ (400 <= status && status <= 599) ||
+ HTTP_FOUND == status ||
+ HTTP_SEE_OTHER == status ||
+ HTTP_TEMPORARY_REDIRECT == status
+ );
+};
+
+// Wrap a general-purpose |RequestBackoff| to a v4-specific one
+// since both listmanager and hashcompleter would use it.
+// Note that |maxRequests| and |requestPeriod| is still configurable
+// to throttle pending requests.
+function RequestBackoffV4(maxRequests, requestPeriod, provider = null) {
+ let rand = Math.random();
+ let retryInterval = Math.floor(15 * 60 * 1000 * (rand + 1)); // 15 ~ 30 min.
+ let backoffInterval = Math.floor(30 * 60 * 1000 * (rand + 1)); // 30 ~ 60 min.
+
+ return new RequestBackoff(
+ 2 /* max errors */,
+ retryInterval /* retry interval, 15~30 min */,
+ maxRequests /* num requests */,
+ requestPeriod /* request time, 60 min */,
+ backoffInterval /* backoff interval, 60 min */,
+ 24 * 60 * 60 * 1000 /* max backoff, 24hr */,
+ 1000 /* tolerance of 1 sec */,
+ provider /* provider name */
+ );
+}
+
+export function UrlClassifierLib() {
+ this.wrappedJSObject = {
+ RequestBackoff,
+ RequestBackoffV4,
+ BindToObject,
+ };
+}
+
+UrlClassifierLib.prototype.QueryInterface = ChromeUtils.generateQI([]);
diff --git a/toolkit/components/url-classifier/UrlClassifierListManager.sys.mjs b/toolkit/components/url-classifier/UrlClassifierListManager.sys.mjs
new file mode 100644
index 0000000000..410e203672
--- /dev/null
+++ b/toolkit/components/url-classifier/UrlClassifierListManager.sys.mjs
@@ -0,0 +1,864 @@
+/* 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/. */
+
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+
+// This is the only implementation of nsIUrlListManager.
+// A class that manages lists, namely exception and block lists for
+// phishing or malware protection. The ListManager knows how to fetch,
+// update, and store lists.
+//
+// There is a single listmanager for the whole application.
+//
+// TODO more comprehensive update tests, for example add unittest check
+// that the listmanagers tables are properly written on updates
+
+// Lower and upper limits on the server-provided polling frequency
+const minDelayMs = 5 * 60 * 1000;
+const maxDelayMs = 24 * 60 * 60 * 1000;
+const defaultUpdateIntervalMs = 30 * 60 * 1000;
+// The threshold to check if the browser is idle. We will defer the update in
+// order to save the power consumption if the browser has been idle for one hour
+// because it's likely that the browser will keep idle for a longer period.
+const browserIdleThresholdMs = 60 * 60 * 1000;
+const PREF_DEBUG_ENABLED = "browser.safebrowsing.debug";
+const PREF_TEST_NOTIFICATIONS =
+ "browser.safebrowsing.test-notifications.enabled";
+
+let loggingEnabled = false;
+
+// Variables imported from library.
+let BindToObject, RequestBackoffV4;
+
+// Log only if browser.safebrowsing.debug is true
+function log(...stuff) {
+ if (!loggingEnabled) {
+ return;
+ }
+
+ var d = new Date();
+ let msg = "listmanager: " + d.toTimeString() + ": " + stuff.join(" ");
+ msg = Services.urlFormatter.trimSensitiveURLs(msg);
+ Services.console.logStringMessage(msg);
+ dump(msg + "\n");
+}
+
+/**
+ * A ListManager keeps track of exception and block lists and knows
+ * how to update them.
+ *
+ * @constructor
+ */
+function PROT_ListManager() {
+ loggingEnabled = Services.prefs.getBoolPref(PREF_DEBUG_ENABLED);
+
+ log("Initializing list manager");
+
+ // A map of tableNames to objects of type
+ // { updateUrl: <updateUrl>, gethashUrl: <gethashUrl> }
+ this.tablesData = {};
+ // A map of updateUrls to maps of tables requiring updates, e.g.
+ // { safebrowsing-update-url: { goog-phish-shavar: true,
+ // goog-malware-shavar: true }
+ this.needsUpdate_ = {};
+
+ // A map of updateUrls to single-use nsITimer. An entry exists if and only if
+ // there is at least one table with updates enabled for that url. nsITimers
+ // are reset when enabling/disabling updates or on update callbacks (update
+ // success, update failure, download error).
+ this.updateCheckers_ = {};
+ this.requestBackoffs_ = {};
+
+ // This is only used by testcases to ensure SafeBrowsing.jsm is inited
+ this.registered = false;
+
+ this.dbService_ = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(
+ Ci.nsIUrlClassifierDBService
+ );
+
+ this.idleService_ = Cc["@mozilla.org/widget/useridleservice;1"].getService(
+ Ci.nsIUserIdleService
+ );
+
+ Services.obs.addObserver(this, "quit-application");
+ Services.prefs.addObserver(PREF_DEBUG_ENABLED, this);
+}
+
+/**
+ * Register a new table table
+ * @param tableName - the name of the table
+ * @param updateUrl - the url for updating the table
+ * @param gethashUrl - the url for fetching hash completions
+ * @returns true if the table could be created; false otherwise
+ */
+PROT_ListManager.prototype.registerTable = function (
+ tableName,
+ providerName,
+ updateUrl,
+ gethashUrl
+) {
+ this.registered = true;
+
+ this.tablesData[tableName] = {};
+ if (!updateUrl) {
+ log("Can't register table " + tableName + " without updateUrl");
+ return false;
+ }
+ log("registering " + tableName + " with " + updateUrl);
+ this.tablesData[tableName].updateUrl = updateUrl;
+ this.tablesData[tableName].gethashUrl = gethashUrl;
+ this.tablesData[tableName].provider = providerName;
+
+ // Keep track of all of our update URLs.
+ if (!this.needsUpdate_[updateUrl]) {
+ this.needsUpdate_[updateUrl] = {};
+
+ // Using the V4 backoff algorithm for both V2 and V4. See bug 1273398.
+ this.requestBackoffs_[updateUrl] = new RequestBackoffV4(
+ 4 /* num requests */,
+ 60 * 60 * 1000 /* request time, 60 min */,
+ providerName /* used by testcase */
+ );
+ }
+ this.needsUpdate_[updateUrl][tableName] = false;
+
+ return true;
+};
+
+/**
+ * Unregister a table table from list
+ */
+PROT_ListManager.prototype.unregisterTable = function (tableName) {
+ log("unregistering " + tableName);
+ var table = this.tablesData[tableName];
+ if (table) {
+ if (
+ !this.updatesNeeded_(table.updateUrl) &&
+ this.updateCheckers_[table.updateUrl]
+ ) {
+ this.updateCheckers_[table.updateUrl].cancel();
+ this.updateCheckers_[table.updateUrl] = null;
+ }
+ delete this.needsUpdate_[table.updateUrl][tableName];
+ }
+ delete this.tablesData[tableName];
+};
+
+/**
+ * Delete all of our data tables which seem to leak otherwise.
+ * Remove observers
+ */
+PROT_ListManager.prototype.shutdown_ = function () {
+ this.stopUpdateCheckers();
+ for (var name in this.tablesData) {
+ delete this.tablesData[name];
+ }
+ Services.obs.removeObserver(this, "quit-application");
+ Services.prefs.removeObserver(PREF_DEBUG_ENABLED, this);
+};
+
+/**
+ * xpcom-shutdown callback
+ */
+PROT_ListManager.prototype.observe = function (aSubject, aTopic, aData) {
+ switch (aTopic) {
+ case "quit-application":
+ this.shutdown_();
+ break;
+ case "nsPref:changed":
+ if (aData == PREF_DEBUG_ENABLED) {
+ loggingEnabled = Services.prefs.getBoolPref(PREF_DEBUG_ENABLED);
+ }
+ break;
+ }
+};
+
+PROT_ListManager.prototype.getGethashUrl = function (tableName) {
+ if (this.tablesData[tableName] && this.tablesData[tableName].gethashUrl) {
+ return this.tablesData[tableName].gethashUrl;
+ }
+ return "";
+};
+
+PROT_ListManager.prototype.getUpdateUrl = function (tableName) {
+ if (this.tablesData[tableName] && this.tablesData[tableName].updateUrl) {
+ return this.tablesData[tableName].updateUrl;
+ }
+ return "";
+};
+
+/**
+ * Enable updates for a single table.
+ */
+PROT_ListManager.prototype.enableUpdate = function (tableName) {
+ var table = this.tablesData[tableName];
+ if (table) {
+ log("Enabling table updates for " + tableName);
+ this.needsUpdate_[table.updateUrl][tableName] = true;
+ }
+};
+
+PROT_ListManager.prototype.isRegistered = function () {
+ return this.registered;
+};
+
+/**
+ * Returns true if any table associated with the updateUrl requires updates.
+ * @param updateUrl - the updateUrl
+ */
+PROT_ListManager.prototype.updatesNeeded_ = function (updateUrl) {
+ let updatesNeeded = false;
+ for (var tableName in this.needsUpdate_[updateUrl]) {
+ if (this.needsUpdate_[updateUrl][tableName]) {
+ updatesNeeded = true;
+ }
+ }
+ return updatesNeeded;
+};
+
+/**
+ * Disable updates for all tables.
+ */
+PROT_ListManager.prototype.disableAllUpdates = function () {
+ for (const tableName of Object.keys(this.tablesData)) {
+ this.disableUpdate(tableName);
+ }
+};
+
+/**
+ * Disables updates for a single table. Avoid this internal function
+ * and use disableAllUpdates() instead.
+ */
+PROT_ListManager.prototype.disableUpdate = function (tableName) {
+ var table = this.tablesData[tableName];
+ if (table) {
+ log("Disabling table updates for " + tableName);
+ this.needsUpdate_[table.updateUrl][tableName] = false;
+ if (
+ !this.updatesNeeded_(table.updateUrl) &&
+ this.updateCheckers_[table.updateUrl]
+ ) {
+ this.updateCheckers_[table.updateUrl].cancel();
+ this.updateCheckers_[table.updateUrl] = null;
+ }
+ }
+};
+
+/**
+ * Determine if we have some tables that need updating.
+ */
+PROT_ListManager.prototype.requireTableUpdates = function () {
+ for (var name in this.tablesData) {
+ // Tables that need updating even if other tables don't require it
+ if (this.needsUpdate_[this.tablesData[name].updateUrl][name]) {
+ return true;
+ }
+ }
+
+ return false;
+};
+
+/**
+ * Set timer to check update after delay
+ */
+PROT_ListManager.prototype.setUpdateCheckTimer = function (updateUrl, delay) {
+ this.updateCheckers_[updateUrl] = Cc["@mozilla.org/timer;1"].createInstance(
+ Ci.nsITimer
+ );
+
+ // A helper function to trigger the table update.
+ let update = function () {
+ if (!this.checkForUpdates(updateUrl)) {
+ // Make another attempt later.
+ this.setUpdateCheckTimer(updateUrl, defaultUpdateIntervalMs);
+ }
+ }.bind(this);
+
+ this.updateCheckers_[updateUrl].initWithCallback(
+ () => {
+ this.updateCheckers_[updateUrl] = null;
+ // Check if we are in the idle mode. We will stop the current update and
+ // defer it to the next user interaction active if the browser is
+ // considered in idle mode.
+ if (this.idleService_.idleTime > browserIdleThresholdMs) {
+ let observer = function () {
+ Services.obs.removeObserver(observer, "user-interaction-active");
+ update();
+ };
+
+ Services.obs.addObserver(observer, "user-interaction-active");
+ return;
+ }
+
+ update();
+ },
+ delay,
+ Ci.nsITimer.TYPE_ONE_SHOT
+ );
+};
+/**
+ * Acts as a nsIUrlClassifierCallback for getTables.
+ */
+PROT_ListManager.prototype.kickoffUpdate_ = function () {
+ this.startingUpdate_ = false;
+ var initialUpdateDelay = 3000;
+ // Add a fuzz of 0-1 minutes for both v2 and v4 according to Bug 1305478.
+ initialUpdateDelay += Math.floor(Math.random() * (1 * 60 * 1000));
+
+ // If the user has never downloaded tables, do the check now.
+ log("needsUpdate: " + JSON.stringify(this.needsUpdate_, undefined, 2));
+ for (var updateUrl in this.needsUpdate_) {
+ // If we haven't already kicked off updates for this updateUrl, set a
+ // non-repeating timer for it. The timer delay will be reset either on
+ // updateSuccess to the default update interval, or backed off on
+ // downloadError. Don't set the updateChecker unless at least one table has
+ // updates enabled.
+ if (this.updatesNeeded_(updateUrl) && !this.updateCheckers_[updateUrl]) {
+ let provider = null;
+ Object.keys(this.tablesData).forEach(function (table) {
+ if (this.tablesData[table].updateUrl === updateUrl) {
+ let newProvider = this.tablesData[table].provider;
+ if (provider) {
+ if (newProvider !== provider) {
+ log(
+ "Multiple tables for the same updateURL have a different provider?!"
+ );
+ }
+ } else {
+ provider = newProvider;
+ }
+ }
+ }, this);
+ log(
+ "Initializing update checker for " +
+ updateUrl +
+ " provided by " +
+ provider
+ );
+
+ // Use the initialUpdateDelay + fuzz unless we had previous updates
+ // and the server told us when to try again.
+ let updateDelay = initialUpdateDelay;
+ let nextUpdatePref =
+ "browser.safebrowsing.provider." + provider + ".nextupdatetime";
+ let nextUpdate = Services.prefs.getCharPref(nextUpdatePref, "");
+
+ if (nextUpdate) {
+ updateDelay = Math.min(
+ maxDelayMs,
+ Math.max(0, nextUpdate - Date.now())
+ );
+ log("Next update at " + nextUpdate);
+ }
+ log("Next update " + Math.round(updateDelay / 60000) + "min from now");
+
+ this.setUpdateCheckTimer(updateUrl, updateDelay);
+ } else {
+ log("No updates needed or already initialized for " + updateUrl);
+ }
+ }
+};
+
+PROT_ListManager.prototype.stopUpdateCheckers = function () {
+ log("Stopping updates");
+ for (var updateUrl in this.updateCheckers_) {
+ if (this.updateCheckers_[updateUrl]) {
+ this.updateCheckers_[updateUrl].cancel();
+ this.updateCheckers_[updateUrl] = null;
+ }
+ }
+};
+
+/**
+ * Determine if we have any tables that require updating. Different
+ * Wardens may call us with new tables that need to be updated.
+ */
+PROT_ListManager.prototype.maybeToggleUpdateChecking = function () {
+ // We update tables if we have some tables that want updates. If there
+ // are no tables that want to be updated - we dont need to check anything.
+ if (this.requireTableUpdates()) {
+ log("Starting managing lists");
+
+ // Get the list of existing tables from the DBService before making any
+ // update requests.
+ if (!this.startingUpdate_) {
+ this.startingUpdate_ = true;
+ // check the current state of tables in the database
+ this.kickoffUpdate_();
+ }
+ } else {
+ log("Stopping managing lists (if currently active)");
+ this.stopUpdateCheckers(); // Cancel pending updates
+ }
+};
+
+/**
+ * Force updates for the given tables. This API may trigger more than one update
+ * if the table lists provided belong to multiple updateurl (multiple provider).
+ * Return false when any update is fail due to back-off algorithm.
+ */
+PROT_ListManager.prototype.forceUpdates = function (tables) {
+ log("forceUpdates with " + tables);
+ if (!tables) {
+ return false;
+ }
+
+ let updateUrls = new Set();
+ tables.split(",").forEach(table => {
+ if (this.tablesData[table]) {
+ updateUrls.add(this.tablesData[table].updateUrl);
+ }
+ });
+
+ let ret = true;
+
+ updateUrls.forEach(url => {
+ // Cancel current update timer for the url because we are forcing an update.
+ if (this.updateCheckers_[url]) {
+ this.updateCheckers_[url].cancel();
+ this.updateCheckers_[url] = null;
+ }
+
+ // Trigger an update for the given url.
+ if (!this.checkForUpdates(url, true)) {
+ ret = false;
+ }
+ });
+
+ return ret;
+};
+
+/**
+ * Updates our internal tables from the update server
+ *
+ * @param updateUrl: request updates for tables associated with that url, or
+ * for all tables if the url is empty.
+ * @param manual: the update is triggered manually
+ */
+PROT_ListManager.prototype.checkForUpdates = function (
+ updateUrl,
+ manual = false
+) {
+ log("checkForUpdates with " + updateUrl);
+ // See if we've triggered the request backoff logic.
+ if (!updateUrl) {
+ return false;
+ }
+
+ // Disable SafeBrowsing updates in Safe Mode, but still allow manually
+ // triggering an update for debugging.
+ if (Services.appinfo.inSafeMode && !manual) {
+ log("update is disabled in Safe Mode");
+ return false;
+ }
+
+ if (lazy.enableTestNotifications) {
+ Services.obs.notifyObservers(
+ null,
+ "safebrowsing-update-attempt",
+ updateUrl
+ );
+ }
+
+ if (
+ !this.requestBackoffs_[updateUrl] ||
+ !this.requestBackoffs_[updateUrl].canMakeRequest()
+ ) {
+ log("Can't make update request");
+ return false;
+ }
+ // Grab the current state of the tables from the database
+ this.dbService_.getTables(
+ BindToObject(this.makeUpdateRequest_, this, updateUrl)
+ );
+ return true;
+};
+
+/**
+ * Method that fires the actual HTTP update request.
+ * First we reset any tables that have disappeared.
+ * @param tableData List of table data already in the database, in the form
+ * tablename;<chunk ranges>\n
+ */
+PROT_ListManager.prototype.makeUpdateRequest_ = function (
+ updateUrl,
+ tableData
+) {
+ log("this.tablesData: " + JSON.stringify(this.tablesData, undefined, 2));
+ log("existing chunks: " + tableData + "\n");
+ // Disallow blank updateUrls
+ if (!updateUrl) {
+ return;
+ }
+ // An object of the form
+ // { tableList: comma-separated list of tables to request,
+ // tableNames: map of tables that need updating,
+ // request: list of tables and existing chunk ranges from tableData
+ // }
+ var streamerMap = {
+ tableList: null,
+ tableNames: {},
+ requestPayload: "",
+ isPostRequest: true,
+ };
+
+ let useProtobuf = false;
+ let onceThru = false;
+ for (var tableName in this.tablesData) {
+ // Skip tables not matching this update url
+ if (this.tablesData[tableName].updateUrl != updateUrl) {
+ continue;
+ }
+
+ // Check if |updateURL| is for 'proto'. (only v4 uses protobuf for now.)
+ // We use the table name 'goog-*-proto' and an additional provider "google4"
+ // to describe the v4 settings.
+ let isCurTableProto = tableName.endsWith("-proto");
+ if (!onceThru) {
+ useProtobuf = isCurTableProto;
+ onceThru = true;
+ } else if (useProtobuf !== isCurTableProto) {
+ log(
+ 'ERROR: Cannot mix "proto" tables with other types ' +
+ "within the same provider."
+ );
+ }
+
+ if (this.needsUpdate_[this.tablesData[tableName].updateUrl][tableName]) {
+ streamerMap.tableNames[tableName] = true;
+ }
+ if (!streamerMap.tableList) {
+ streamerMap.tableList = tableName;
+ } else {
+ streamerMap.tableList += "," + tableName;
+ }
+ }
+
+ if (useProtobuf) {
+ let tableArray = [];
+ Object.keys(streamerMap.tableNames).forEach(aTableName => {
+ if (streamerMap.tableNames[aTableName]) {
+ tableArray.push(aTableName);
+ }
+ });
+
+ // Build the <tablename, stateBase64> mapping.
+ let tableState = {};
+ tableData.split("\n").forEach(line => {
+ let p = line.indexOf(";");
+ if (-1 === p) {
+ return;
+ }
+ let tableName = line.substring(0, p);
+ if (tableName in streamerMap.tableNames) {
+ let metadata = line.substring(p + 1).split(":");
+ let stateBase64 = metadata[0];
+ log(tableName + " ==> " + stateBase64);
+ tableState[tableName] = stateBase64;
+ }
+ });
+
+ // The state is a byte stream which server told us from the
+ // last table update. The state would be used to do the partial
+ // update and the empty string means the table has
+ // never been downloaded. See Bug 1287058 for supporting
+ // partial update.
+ let stateArray = [];
+ tableArray.forEach(listName => {
+ stateArray.push(tableState[listName] || "");
+ });
+
+ log("stateArray: " + stateArray);
+
+ let urlUtils = Cc["@mozilla.org/url-classifier/utils;1"].getService(
+ Ci.nsIUrlClassifierUtils
+ );
+
+ streamerMap.requestPayload = urlUtils.makeUpdateRequestV4(
+ tableArray,
+ stateArray
+ );
+ streamerMap.isPostRequest = false;
+ } else {
+ // Build the request. For each table already in the database, include the
+ // chunk data from the database
+ var lines = tableData.split("\n");
+ for (var i = 0; i < lines.length; i++) {
+ var fields = lines[i].split(";");
+ var name = fields[0];
+ if (streamerMap.tableNames[name]) {
+ streamerMap.requestPayload += lines[i] + "\n";
+ delete streamerMap.tableNames[name];
+ }
+ }
+ // For each requested table that didn't have chunk data in the database,
+ // request it fresh
+ for (let tableName in streamerMap.tableNames) {
+ streamerMap.requestPayload += tableName + ";\n";
+ }
+
+ streamerMap.isPostRequest = true;
+ }
+
+ log("update request: " + JSON.stringify(streamerMap, undefined, 2) + "\n");
+
+ // Don't send an empty request.
+ if (streamerMap.requestPayload.length) {
+ this.makeUpdateRequestForEntry_(
+ updateUrl,
+ streamerMap.tableList,
+ streamerMap.requestPayload,
+ streamerMap.isPostRequest
+ );
+ } else {
+ // We were disabled between kicking off getTables and now.
+ log("Not sending empty request");
+ }
+};
+
+PROT_ListManager.prototype.makeUpdateRequestForEntry_ = function (
+ updateUrl,
+ tableList,
+ requestPayload,
+ isPostRequest
+) {
+ log(
+ "makeUpdateRequestForEntry_: requestPayload " +
+ requestPayload +
+ " update: " +
+ updateUrl +
+ " tablelist: " +
+ tableList +
+ "\n"
+ );
+ var streamer = Cc["@mozilla.org/url-classifier/streamupdater;1"].getService(
+ Ci.nsIUrlClassifierStreamUpdater
+ );
+
+ this.requestBackoffs_[updateUrl].noteRequest();
+
+ if (
+ !streamer.downloadUpdates(
+ tableList,
+ requestPayload,
+ isPostRequest,
+ updateUrl,
+ BindToObject(this.updateSuccess_, this, tableList, updateUrl),
+ BindToObject(this.updateError_, this, tableList, updateUrl),
+ BindToObject(this.downloadError_, this, tableList, updateUrl)
+ )
+ ) {
+ // Our alarm gets reset in one of the 3 callbacks.
+ log("pending update, queued request until later");
+ } else {
+ let table = Object.keys(this.tablesData).find(key => {
+ return this.tablesData[key].updateUrl === updateUrl;
+ });
+ let provider = this.tablesData[table].provider;
+ Services.obs.notifyObservers(null, "safebrowsing-update-begin", provider);
+ }
+};
+
+/**
+ * Callback function if the update request succeeded.
+ * @param waitForUpdate String The number of seconds that the client should
+ * wait before requesting again.
+ */
+PROT_ListManager.prototype.updateSuccess_ = function (
+ tableList,
+ updateUrl,
+ waitForUpdateSec
+) {
+ log(
+ "update success for " +
+ tableList +
+ " from " +
+ updateUrl +
+ ": " +
+ waitForUpdateSec +
+ "\n"
+ );
+
+ // The time unit below are all milliseconds if not specified.
+
+ var delay = 0;
+ if (waitForUpdateSec) {
+ delay = parseInt(waitForUpdateSec, 10) * 1000;
+ }
+ // As long as the delay is something sane (5 min to 1 day), update
+ // our delay time for requesting updates. We always use a non-repeating
+ // timer since the delay is set differently at every callback.
+ if (delay > maxDelayMs) {
+ log(
+ "Ignoring delay from server (too long), waiting " +
+ Math.round(maxDelayMs / 60000) +
+ "min"
+ );
+ delay = maxDelayMs;
+ } else if (delay < minDelayMs) {
+ log(
+ "Ignoring delay from server (too short), waiting " +
+ Math.round(defaultUpdateIntervalMs / 60000) +
+ "min"
+ );
+ delay = defaultUpdateIntervalMs;
+ } else {
+ log("Waiting " + Math.round(delay / 60000) + "min");
+ }
+
+ this.setUpdateCheckTimer(updateUrl, delay);
+
+ // Let the backoff object know that we completed successfully.
+ this.requestBackoffs_[updateUrl].noteServerResponse(200);
+
+ // Set last update time for provider
+ // Get the provider for these tables, check for consistency
+ let tables = tableList.split(",");
+ let provider = null;
+ for (let table of tables) {
+ let newProvider = this.tablesData[table].provider;
+ if (provider) {
+ if (newProvider !== provider) {
+ log(
+ "Multiple tables for the same updateURL have a different provider?!"
+ );
+ }
+ } else {
+ provider = newProvider;
+ }
+ }
+
+ // Store the last update time (needed to know if the table is "fresh")
+ // and the next update time (to know when to update next).
+ let lastUpdatePref =
+ "browser.safebrowsing.provider." + provider + ".lastupdatetime";
+ let now = Date.now();
+ log("Setting last update of " + provider + " to " + now);
+ Services.prefs.setCharPref(lastUpdatePref, now.toString());
+
+ let nextUpdatePref =
+ "browser.safebrowsing.provider." + provider + ".nextupdatetime";
+ let targetTime = now + delay;
+ log(
+ "Setting next update of " +
+ provider +
+ " to " +
+ targetTime +
+ " (" +
+ Math.round(delay / 60000) +
+ "min from now)"
+ );
+ Services.prefs.setCharPref(nextUpdatePref, targetTime.toString());
+
+ Services.obs.notifyObservers(null, "safebrowsing-update-finished", "success");
+};
+
+/**
+ * Callback function if the update request succeeded.
+ * @param result String The error code of the failure
+ */
+PROT_ListManager.prototype.updateError_ = function (table, updateUrl, result) {
+ log(
+ "update error for " + table + " from " + updateUrl + ": " + result + "\n"
+ );
+ // There was some trouble applying the updates. Don't try again for at least
+ // updateInterval milliseconds.
+ this.setUpdateCheckTimer(updateUrl, defaultUpdateIntervalMs);
+
+ Services.obs.notifyObservers(
+ null,
+ "safebrowsing-update-finished",
+ "update error: " + result
+ );
+};
+
+/**
+ * Callback function when the download failed
+ * @param status String http status or an empty string if connection refused.
+ */
+PROT_ListManager.prototype.downloadError_ = function (
+ table,
+ updateUrl,
+ status
+) {
+ log("download error for " + table + ": " + status + "\n");
+ // If status is empty, then we assume that we got an NS_CONNECTION_REFUSED
+ // error. In this case, we treat this is a http 500 error.
+ if (!status) {
+ status = 500;
+ }
+ status = parseInt(status, 10);
+ this.requestBackoffs_[updateUrl].noteServerResponse(status);
+ let delay = defaultUpdateIntervalMs;
+ if (this.requestBackoffs_[updateUrl].isErrorStatus(status)) {
+ // Schedule an update for when our backoff is complete
+ delay = this.requestBackoffs_[updateUrl].nextRequestDelay();
+ } else {
+ log("Got non error status for error callback?!");
+ }
+
+ this.setUpdateCheckTimer(updateUrl, delay);
+
+ Services.obs.notifyObservers(
+ null,
+ "safebrowsing-update-finished",
+ "download error: " + status
+ );
+};
+
+/**
+ * Get back-off time for the given provider.
+ * Return 0 if we are not in back-off mode.
+ */
+PROT_ListManager.prototype.getBackOffTime = function (provider) {
+ let updateUrl = "";
+ for (var table in this.tablesData) {
+ if (this.tablesData[table].provider == provider) {
+ updateUrl = this.tablesData[table].updateUrl;
+ break;
+ }
+ }
+
+ if (!updateUrl || !this.requestBackoffs_[updateUrl]) {
+ return 0;
+ }
+
+ let delay = this.requestBackoffs_[updateUrl].nextRequestDelay();
+ return delay == 0 ? 0 : Date.now() + delay;
+};
+
+PROT_ListManager.prototype.QueryInterface = ChromeUtils.generateQI([
+ "nsIUrlListManager",
+ "nsIObserver",
+ "nsITimerCallback",
+]);
+
+let initialized = false;
+function Init() {
+ if (initialized) {
+ return;
+ }
+
+ // Pull the library in.
+ var jslib =
+ Cc["@mozilla.org/url-classifier/jslib;1"].getService().wrappedJSObject;
+ BindToObject = jslib.BindToObject;
+ RequestBackoffV4 = jslib.RequestBackoffV4;
+
+ initialized = true;
+}
+
+export function RegistrationData() {
+ Init();
+ return new PROT_ListManager();
+}
+
+const lazy = {};
+
+XPCOMUtils.defineLazyPreferenceGetter(
+ lazy,
+ "enableTestNotifications",
+ PREF_TEST_NOTIFICATIONS,
+ false
+);
diff --git a/toolkit/components/url-classifier/UrlClassifierRemoteSettingsService.sys.mjs b/toolkit/components/url-classifier/UrlClassifierRemoteSettingsService.sys.mjs
new file mode 100644
index 0000000000..42f8cf272a
--- /dev/null
+++ b/toolkit/components/url-classifier/UrlClassifierRemoteSettingsService.sys.mjs
@@ -0,0 +1,141 @@
+/* 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/. */
+
+const lazy = {};
+ChromeUtils.defineESModuleGetters(lazy, {
+ RemoteSettings: "resource://services-settings/remote-settings.sys.mjs",
+});
+
+const COLLECTION_NAME = "tracking-protection-lists";
+
+// SafeBrowsing protocol parameters.
+export const SBRS_UPDATE_MINIMUM_DELAY = 21600; // Minimum delay before polling again in seconds
+
+export function UrlClassifierRemoteSettingsService() {}
+UrlClassifierRemoteSettingsService.prototype = {
+ classID: Components.ID("{1980624c-c50b-4b46-a91c-dfaba7792706}"),
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIUrlClassifierRemoteSettingsService",
+ ]),
+
+ _initialized: false,
+
+ // Entries that are retrieved from RemoteSettings.get(). keyed by the table name.
+ _entries: {},
+
+ async lazyInit() {
+ if (this._initialized) {
+ return;
+ }
+
+ let rs = lazy.RemoteSettings(COLLECTION_NAME);
+ // Bug 1750191: Notify listmanager to trigger an update instead
+ // of polling data periodically.
+ rs.on("sync", async event => {
+ let {
+ data: { current },
+ } = event;
+ this._onUpdateEntries(current);
+ });
+
+ this._initialized = true;
+
+ let entries;
+ try {
+ entries = await rs.get();
+ } catch (e) {}
+
+ this._onUpdateEntries(entries || []);
+ },
+
+ _onUpdateEntries(aEntries) {
+ aEntries.map(entry => {
+ this._entries[entry.Name] = entry;
+ });
+ },
+
+ // Parse the update request. See UrlClassifierListManager.jsm makeUpdateRequest
+ // for more details about how we build the update request.
+ //
+ // @param aRequest the request payload of the update request
+ // @return array The array of requested tables. Each item in the array is composed
+ // with [table name, chunk numner]
+ _parseRequest(aRequest) {
+ let lines = aRequest.split("\n");
+ let requests = [];
+ for (let line of lines) {
+ let fields = line.split(";");
+ let chunkNum = fields[1]?.match(/(?<=a:).*/);
+ requests.push([fields[0], chunkNum]);
+ }
+ return requests;
+ },
+
+ async _getLists(aRequest, aListener) {
+ await this.lazyInit();
+
+ let rs = lazy.RemoteSettings(COLLECTION_NAME);
+ let payload = "n:" + SBRS_UPDATE_MINIMUM_DELAY + "\n";
+
+ let requests = this._parseRequest(aRequest);
+ for (let request of requests) {
+ let [reqTableName, reqChunkNum] = request;
+ let entry = this._entries[reqTableName];
+ if (!entry?.attachment) {
+ continue;
+ }
+
+ // If the request version is the same as what we have in Remote Settings,
+ // we are up-to-date now.
+ if (entry.Version == reqChunkNum) {
+ continue;
+ }
+
+ let downloadError = false;
+ try {
+ // SafeBrowsing maintains its own files, so we can remove the downloaded
+ // files after SafeBrowsing processes the data.
+ let buffer = await rs.attachments.downloadAsBytes(entry);
+ let bytes = new Uint8Array(buffer);
+ let strData = "";
+ for (let i = 0; i < bytes.length; i++) {
+ strData += String.fromCharCode(bytes[i]);
+ }
+
+ // Construct the payload
+ payload += "i:" + reqTableName + "\n";
+ payload += strData;
+ } catch (e) {
+ downloadError = true;
+ }
+
+ if (downloadError) {
+ try {
+ aListener.onStartRequest(null);
+ aListener.onStopRequest(null, Cr.NS_ERROR_FAILURE);
+ } catch (e) {}
+ return;
+ }
+ }
+
+ // Send the update response over stream listener interface
+ let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
+ Ci.nsIStringInputStream
+ );
+ stream.setData(payload, payload.length);
+
+ aListener.onStartRequest(null);
+ aListener.onDataAvailable(null, stream, 0, payload.length);
+ aListener.onStopRequest(null, Cr.NS_OK);
+ },
+
+ fetchList(aPayload, aListener) {
+ this._getLists(aPayload, aListener);
+ },
+
+ clear() {
+ this._initialized = false;
+ this._entries = {};
+ },
+};
diff --git a/toolkit/components/url-classifier/UrlClassifierTelemetryUtils.cpp b/toolkit/components/url-classifier/UrlClassifierTelemetryUtils.cpp
new file mode 100644
index 0000000000..c6fe9d6ca6
--- /dev/null
+++ b/toolkit/components/url-classifier/UrlClassifierTelemetryUtils.cpp
@@ -0,0 +1,157 @@
+/* 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 "UrlClassifierTelemetryUtils.h"
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+namespace safebrowsing {
+
+uint8_t NetworkErrorToBucket(nsresult rv) {
+ switch (rv) {
+ // Connection errors
+ case NS_ERROR_ALREADY_CONNECTED:
+ return 2;
+ case NS_ERROR_NOT_CONNECTED:
+ return 3;
+ case NS_ERROR_CONNECTION_REFUSED:
+ return 4;
+ case NS_ERROR_NET_TIMEOUT:
+ return 5;
+ case NS_ERROR_OFFLINE:
+ return 6;
+ case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:
+ return 7;
+ case NS_ERROR_NET_RESET:
+ return 8;
+ case NS_ERROR_NET_INTERRUPT:
+ return 9;
+ case NS_ERROR_PROXY_CONNECTION_REFUSED:
+ return 10;
+ case NS_ERROR_NET_PARTIAL_TRANSFER:
+ return 11;
+ case NS_ERROR_NET_INADEQUATE_SECURITY:
+ return 12;
+ // DNS errors
+ case NS_ERROR_UNKNOWN_HOST:
+ return 13;
+ case NS_ERROR_DNS_LOOKUP_QUEUE_FULL:
+ return 14;
+ case NS_ERROR_UNKNOWN_PROXY_HOST:
+ return 15;
+ // Others
+ default:
+ return 1;
+ }
+}
+
+uint32_t HTTPStatusToBucket(uint32_t status) {
+ uint32_t statusBucket;
+ switch (status) {
+ case 100:
+ case 101:
+ // Unexpected 1xx return code
+ statusBucket = 0;
+ break;
+ case 200:
+ // OK - Data is available in the HTTP response body.
+ statusBucket = 1;
+ break;
+ case 201:
+ case 202:
+ case 203:
+ case 205:
+ case 206:
+ // Unexpected 2xx return code
+ statusBucket = 2;
+ break;
+ case 204:
+ // No Content
+ statusBucket = 3;
+ break;
+ case 300:
+ case 301:
+ case 302:
+ case 303:
+ case 304:
+ case 305:
+ case 307:
+ case 308:
+ // Unexpected 3xx return code
+ statusBucket = 4;
+ break;
+ case 400:
+ // Bad Request - The HTTP request was not correctly formed.
+ // The client did not provide all required CGI parameters.
+ statusBucket = 5;
+ break;
+ case 401:
+ case 402:
+ case 405:
+ case 406:
+ case 407:
+ case 409:
+ case 410:
+ case 411:
+ case 412:
+ case 414:
+ case 415:
+ case 416:
+ case 417:
+ case 421:
+ case 426:
+ case 428:
+ case 429:
+ case 431:
+ case 451:
+ // Unexpected 4xx return code
+ statusBucket = 6;
+ break;
+ case 403:
+ // Forbidden - The client id is invalid.
+ statusBucket = 7;
+ break;
+ case 404:
+ // Not Found
+ statusBucket = 8;
+ break;
+ case 408:
+ // Request Timeout
+ statusBucket = 9;
+ break;
+ case 413:
+ // Request Entity Too Large - Bug 1150334
+ statusBucket = 10;
+ break;
+ case 500:
+ case 501:
+ case 510:
+ // Unexpected 5xx return code
+ statusBucket = 11;
+ break;
+ case 502:
+ case 504:
+ case 511:
+ // Local network errors, we'll ignore these.
+ statusBucket = 12;
+ break;
+ case 503:
+ // Service Unavailable - The server cannot handle the request.
+ // Clients MUST follow the backoff behavior specified in the
+ // Request Frequency section.
+ statusBucket = 13;
+ break;
+ case 505:
+ // HTTP Version Not Supported - The server CANNOT handle the requested
+ // protocol major version.
+ statusBucket = 14;
+ break;
+ default:
+ statusBucket = 15;
+ };
+ return statusBucket;
+}
+
+} // namespace safebrowsing
+} // namespace mozilla
diff --git a/toolkit/components/url-classifier/UrlClassifierTelemetryUtils.h b/toolkit/components/url-classifier/UrlClassifierTelemetryUtils.h
new file mode 100644
index 0000000000..a0c2d181df
--- /dev/null
+++ b/toolkit/components/url-classifier/UrlClassifierTelemetryUtils.h
@@ -0,0 +1,31 @@
+//* -*- 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/. */
+
+#ifndef UrlClassifierTelemetryUtils_h__
+#define UrlClassifierTelemetryUtils_h__
+
+#include "mozilla/TypedEnumBits.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+namespace safebrowsing {
+
+// We might need to expand the bucket here if telemetry shows lots of errors
+// are neither connection errors nor DNS errors.
+uint8_t NetworkErrorToBucket(nsresult rv);
+
+// Map the HTTP response code to a Telemetry bucket
+uint32_t HTTPStatusToBucket(uint32_t status);
+
+enum UpdateTimeout {
+ eNoTimeout = 0,
+ eResponseTimeout = 1,
+ eDownloadTimeout = 2,
+};
+
+} // namespace safebrowsing
+} // namespace mozilla
+
+#endif // UrlClassifierTelemetryUtils_h__
diff --git a/toolkit/components/url-classifier/VariableLengthPrefixSet.cpp b/toolkit/components/url-classifier/VariableLengthPrefixSet.cpp
new file mode 100644
index 0000000000..40ea436845
--- /dev/null
+++ b/toolkit/components/url-classifier/VariableLengthPrefixSet.cpp
@@ -0,0 +1,496 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "VariableLengthPrefixSet.h"
+#include "nsIInputStream.h"
+#include "nsUrlClassifierPrefixSet.h"
+#include "nsPrintfCString.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/EndianUtils.h"
+#include "mozilla/Logging.h"
+#include "mozilla/UniquePtr.h"
+#include <algorithm>
+
+// MOZ_LOG=UrlClassifierPrefixSet:5
+static mozilla::LazyLogModule gUrlClassifierPrefixSetLog(
+ "UrlClassifierPrefixSet");
+#define LOG(args) \
+ MOZ_LOG(gUrlClassifierPrefixSetLog, mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() \
+ MOZ_LOG_TEST(gUrlClassifierPrefixSetLog, mozilla::LogLevel::Debug)
+
+namespace mozilla::safebrowsing {
+
+#define PREFIX_SIZE_FIXED 4
+
+#ifdef DEBUG
+namespace {
+
+template <class T>
+void EnsureSorted(T* aArray) {
+ MOZ_ASSERT(std::is_sorted(aArray->Elements(),
+ aArray->Elements() + aArray->Length()));
+}
+
+} // namespace
+#endif
+
+NS_IMPL_ISUPPORTS(VariableLengthPrefixSet, nsIMemoryReporter)
+
+// This class will process prefix size between 4~32. But for 4 bytes prefixes,
+// they will be passed to nsUrlClassifierPrefixSet because of better
+// optimization.
+VariableLengthPrefixSet::VariableLengthPrefixSet()
+ : mLock("VariableLengthPrefixSet.mLock"),
+ mFixedPrefixSet(new nsUrlClassifierPrefixSet) {}
+
+nsresult VariableLengthPrefixSet::Init(const nsACString& aName) {
+ mName = aName;
+ mMemoryReportPath = nsPrintfCString(
+ "explicit/storage/prefix-set/%s",
+ (!aName.IsEmpty() ? PromiseFlatCString(aName).get() : "?!"));
+
+ RegisterWeakMemoryReporter(this);
+
+ return mFixedPrefixSet->Init(aName);
+}
+
+VariableLengthPrefixSet::~VariableLengthPrefixSet() {
+ UnregisterWeakMemoryReporter(this);
+}
+
+nsresult VariableLengthPrefixSet::SetPrefixes(AddPrefixArray& aAddPrefixes,
+ AddCompleteArray& aAddCompletes) {
+ MutexAutoLock lock(mLock);
+
+ // We may modify the prefix string in this function, clear this data
+ // before returning to ensure no one use the data after this API.
+ auto scopeExit = MakeScopeExit([&]() {
+ aAddPrefixes.Clear();
+ aAddCompletes.Clear();
+ });
+
+ // Clear old prefixSet before setting new one.
+ mFixedPrefixSet->SetPrefixes(nullptr, 0);
+ mVLPrefixSet.Clear();
+
+ // Build fixed-length prefix set
+ nsTArray<uint32_t> array;
+ if (!array.SetCapacity(aAddPrefixes.Length(), fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ for (size_t i = 0; i < aAddPrefixes.Length(); i++) {
+ array.AppendElement(aAddPrefixes[i].PrefixHash().ToUint32());
+ }
+
+#ifdef DEBUG
+ // PrefixSet requires sorted order
+ EnsureSorted(&array);
+#endif
+
+ nsresult rv = mFixedPrefixSet->SetPrefixes(array.Elements(), array.Length());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+#ifdef DEBUG
+ uint32_t size;
+ size = mFixedPrefixSet->SizeOfIncludingThis(moz_malloc_size_of);
+ LOG(("SB tree done, size = %d bytes\n", size));
+#endif
+
+ CompletionArray completions;
+ for (size_t i = 0; i < aAddCompletes.Length(); i++) {
+ completions.AppendElement(aAddCompletes[i].CompleteHash());
+ }
+ completions.Sort();
+
+ UniquePtr<nsCString> completionStr(new nsCString);
+ completionStr->SetCapacity(completions.Length() * COMPLETE_SIZE);
+ for (size_t i = 0; i < completions.Length(); i++) {
+ const char* buf = reinterpret_cast<const char*>(completions[i].buf);
+ completionStr->Append(buf, COMPLETE_SIZE);
+ }
+ mVLPrefixSet.InsertOrUpdate(COMPLETE_SIZE, std::move(completionStr));
+
+ return NS_OK;
+}
+
+nsresult VariableLengthPrefixSet::SetPrefixes(PrefixStringMap& aPrefixMap) {
+ MutexAutoLock lock(mLock);
+
+ // We may modify the prefix string in this function, clear this data
+ // before returning to ensure no one use the data after this API.
+ auto scopeExit = MakeScopeExit([&]() { aPrefixMap.Clear(); });
+
+ // Prefix size should not less than 4-bytes or greater than 32-bytes
+ for (const auto& key : aPrefixMap.Keys()) {
+ if (key < PREFIX_SIZE_FIXED || key > COMPLETE_SIZE) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ // Clear old prefixSet before setting new one.
+ mFixedPrefixSet->SetPrefixes(nullptr, 0);
+ mVLPrefixSet.Clear();
+
+ // 4-bytes prefixes are handled by nsUrlClassifierPrefixSet.
+ nsCString* prefixes = aPrefixMap.Get(PREFIX_SIZE_FIXED);
+ if (prefixes) {
+ NS_ENSURE_TRUE(prefixes->Length() % PREFIX_SIZE_FIXED == 0,
+ NS_ERROR_FAILURE);
+
+ uint32_t numPrefixes = prefixes->Length() / PREFIX_SIZE_FIXED;
+
+ // Prefixes are lexicographically-sorted, so the interger array
+ // passed to nsUrlClassifierPrefixSet should also follow the same order.
+ // Reverse byte order in-place in Little-Endian platform.
+#if MOZ_LITTLE_ENDIAN()
+ char* begin = prefixes->BeginWriting();
+ char* end = prefixes->EndWriting();
+
+ while (begin != end) {
+ uint32_t* p = reinterpret_cast<uint32_t*>(begin);
+ *p = BigEndian::readUint32(begin);
+ begin += sizeof(uint32_t);
+ }
+#endif
+ const uint32_t* arrayPtr =
+ reinterpret_cast<const uint32_t*>(prefixes->BeginReading());
+
+ nsresult rv = mFixedPrefixSet->SetPrefixes(arrayPtr, numPrefixes);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ // 5~32 bytes prefixes are stored in mVLPrefixSet.
+ for (const auto& entry : aPrefixMap) {
+ // Skip 4bytes prefixes because it is already stored in mFixedPrefixSet.
+ if (entry.GetKey() == PREFIX_SIZE_FIXED) {
+ continue;
+ }
+
+ mVLPrefixSet.InsertOrUpdate(entry.GetKey(),
+ MakeUnique<nsCString>(*entry.GetData()));
+ }
+
+ return NS_OK;
+}
+
+nsresult VariableLengthPrefixSet::GetPrefixes(PrefixStringMap& aPrefixMap) {
+ MutexAutoLock lock(mLock);
+
+ // 4-bytes prefixes are handled by nsUrlClassifierPrefixSet.
+ FallibleTArray<uint32_t> array;
+ nsresult rv = mFixedPrefixSet->GetPrefixesNative(array);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ size_t count = array.Length();
+ if (count) {
+ UniquePtr<nsCString> prefixes(new nsCString());
+ if (!prefixes->SetLength(PREFIX_SIZE_FIXED * count, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Writing integer array to character array
+ uint32_t* begin = reinterpret_cast<uint32_t*>(prefixes->BeginWriting());
+ for (uint32_t i = 0; i < count; i++) {
+ begin[i] = NativeEndian::swapToBigEndian(array[i]);
+ }
+
+ aPrefixMap.InsertOrUpdate(PREFIX_SIZE_FIXED, std::move(prefixes));
+ }
+
+ // Copy variable-length prefix set
+ for (const auto& entry : mVLPrefixSet) {
+ aPrefixMap.InsertOrUpdate(entry.GetKey(),
+ MakeUnique<nsCString>(*entry.GetData()));
+ }
+
+ return NS_OK;
+}
+
+// This is used by V2 protocol which prefixes are either 4-bytes or 32-bytes.
+nsresult VariableLengthPrefixSet::GetFixedLengthPrefixes(
+ FallibleTArray<uint32_t>* aPrefixes,
+ FallibleTArray<nsCString>* aCompletes) {
+ MOZ_ASSERT(aPrefixes || aCompletes);
+ MOZ_ASSERT_IF(aPrefixes, aPrefixes->IsEmpty());
+ MOZ_ASSERT_IF(aCompletes, aCompletes->IsEmpty());
+
+ if (aPrefixes) {
+ nsresult rv = mFixedPrefixSet->GetPrefixesNative(*aPrefixes);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ if (aCompletes) {
+ nsCString* completes = mVLPrefixSet.Get(COMPLETE_SIZE);
+ if (completes) {
+ uint32_t count = completes->Length() / COMPLETE_SIZE;
+ if (!aCompletes->SetCapacity(count, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ for (uint32_t i = 0; i < count; i++) {
+ // SetCapacity was just called, these cannot fail.
+ (void)aCompletes->AppendElement(
+ Substring(*completes, i * COMPLETE_SIZE, COMPLETE_SIZE), fallible);
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult VariableLengthPrefixSet::GetFixedLengthPrefixByIndex(
+ uint32_t aIndex, uint32_t* aOutPrefix) const {
+ NS_ENSURE_ARG_POINTER(aOutPrefix);
+
+ return mFixedPrefixSet->GetPrefixByIndex(aIndex, aOutPrefix);
+}
+
+// It should never be the case that more than one hash prefixes match a given
+// full hash. However, if that happens, this method returns any one of them.
+// It does not guarantee which one of those will be returned.
+nsresult VariableLengthPrefixSet::Matches(uint32_t aPrefix,
+ const nsACString& aFullHash,
+ uint32_t* aLength) const {
+ MutexAutoLock lock(mLock);
+
+ // Only allow full-length hash to check if match any of the prefix
+ MOZ_ASSERT(aFullHash.Length() == COMPLETE_SIZE);
+ NS_ENSURE_ARG_POINTER(aLength);
+
+ *aLength = 0;
+
+ // Check if it matches 4-bytes prefixSet first
+ bool found = false;
+ nsresult rv = mFixedPrefixSet->Contains(aPrefix, &found);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (found) {
+ *aLength = PREFIX_SIZE_FIXED;
+ return NS_OK;
+ }
+
+ for (const auto& entry : mVLPrefixSet) {
+ if (BinarySearch(aFullHash, *entry.GetData(), entry.GetKey())) {
+ *aLength = entry.GetKey();
+ MOZ_ASSERT(*aLength > 4);
+ return NS_OK;
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult VariableLengthPrefixSet::IsEmpty(bool* aEmpty) const {
+ MutexAutoLock lock(mLock);
+
+ NS_ENSURE_ARG_POINTER(aEmpty);
+
+ mFixedPrefixSet->IsEmpty(aEmpty);
+ *aEmpty = *aEmpty && mVLPrefixSet.IsEmpty();
+
+ return NS_OK;
+}
+
+nsresult VariableLengthPrefixSet::LoadPrefixes(nsCOMPtr<nsIInputStream>& in) {
+ MutexAutoLock lock(mLock);
+
+ // First read prefixes from fixed-length prefix set
+ nsresult rv = mFixedPrefixSet->LoadPrefixes(in);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Then read prefixes from variable-length prefix set
+ uint32_t magic;
+ uint32_t read;
+
+ rv = in->Read(reinterpret_cast<char*>(&magic), sizeof(uint32_t), &read);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE);
+
+ if (magic != PREFIXSET_VERSION_MAGIC) {
+ LOG(("[%s] Version magic mismatch, not loading", mName.get()));
+ return NS_ERROR_FILE_CORRUPTED;
+ }
+
+ mVLPrefixSet.Clear();
+
+ uint32_t count;
+ rv = in->Read(reinterpret_cast<char*>(&count), sizeof(uint32_t), &read);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE);
+
+ uint32_t totalPrefixes = 0;
+ for (; count > 0; count--) {
+ uint8_t prefixSize;
+ rv = in->Read(reinterpret_cast<char*>(&prefixSize), sizeof(uint8_t), &read);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(read == sizeof(uint8_t), NS_ERROR_FAILURE);
+
+ if (prefixSize < PREFIX_SIZE || prefixSize > COMPLETE_SIZE) {
+ return NS_ERROR_FILE_CORRUPTED;
+ }
+
+ uint32_t stringLength;
+ rv = in->Read(reinterpret_cast<char*>(&stringLength), sizeof(uint32_t),
+ &read);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE);
+
+ NS_ENSURE_TRUE(stringLength % prefixSize == 0, NS_ERROR_FILE_CORRUPTED);
+ uint32_t prefixCount = stringLength / prefixSize;
+
+ UniquePtr<nsCString> vlPrefixes(new nsCString());
+ if (!vlPrefixes->SetLength(stringLength, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ rv = in->Read(reinterpret_cast<char*>(vlPrefixes->BeginWriting()),
+ stringLength, &read);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(read == stringLength, NS_ERROR_FAILURE);
+
+ mVLPrefixSet.InsertOrUpdate(prefixSize, std::move(vlPrefixes));
+ totalPrefixes += prefixCount;
+ LOG(("[%s] Loaded %u %u-byte prefixes", mName.get(), prefixCount,
+ prefixSize));
+ }
+
+ LOG(("[%s] Loading VLPrefixSet successful (%u total prefixes)", mName.get(),
+ totalPrefixes));
+
+ return NS_OK;
+}
+
+uint32_t VariableLengthPrefixSet::CalculatePreallocateSize() const {
+ uint32_t fileSize = 0;
+
+ // Size of fixed length prefix set.
+ fileSize += mFixedPrefixSet->CalculatePreallocateSize();
+
+ // Size of variable length prefix set.
+ // Store how many prefix string.
+ fileSize += sizeof(uint32_t);
+
+ for (const auto& data : mVLPrefixSet.Values()) {
+ // Store prefix size, prefix string length, and prefix string.
+ fileSize += sizeof(uint8_t);
+ fileSize += sizeof(uint32_t);
+ fileSize += data->Length();
+ }
+ return fileSize;
+}
+
+uint32_t VariableLengthPrefixSet::FixedLengthPrefixLength() const {
+ return mFixedPrefixSet->Length();
+}
+
+nsresult VariableLengthPrefixSet::WritePrefixes(
+ nsCOMPtr<nsIOutputStream>& out) const {
+ MutexAutoLock lock(mLock);
+
+ // First, write fixed length prefix set
+ nsresult rv = mFixedPrefixSet->WritePrefixes(out);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Then, write variable length prefix set
+ uint32_t written;
+ uint32_t writelen = sizeof(uint32_t);
+ uint32_t magic = PREFIXSET_VERSION_MAGIC;
+ rv = out->Write(reinterpret_cast<char*>(&magic), writelen, &written);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
+
+ uint32_t count = mVLPrefixSet.Count();
+ rv = out->Write(reinterpret_cast<char*>(&count), writelen, &written);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
+
+ // Store PrefixSize, Length of Prefix String and then Prefix String
+ for (const auto& entry : mVLPrefixSet) {
+ const nsCString& vlPrefixes = *entry.GetData();
+
+ uint8_t prefixSize = entry.GetKey();
+ writelen = sizeof(uint8_t);
+ rv = out->Write(reinterpret_cast<char*>(&prefixSize), writelen, &written);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
+
+ uint32_t stringLength = vlPrefixes.Length();
+ writelen = sizeof(uint32_t);
+ rv = out->Write(reinterpret_cast<char*>(&stringLength), writelen, &written);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
+
+ rv = out->Write(const_cast<char*>(vlPrefixes.BeginReading()), stringLength,
+ &written);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(stringLength == written, NS_ERROR_FAILURE);
+ }
+
+ return NS_OK;
+}
+
+bool VariableLengthPrefixSet::BinarySearch(const nsACString& aFullHash,
+ const nsACString& aPrefixes,
+ uint32_t aPrefixSize) const {
+ const char* fullhash = aFullHash.BeginReading();
+ const char* prefixes = aPrefixes.BeginReading();
+ int32_t begin = 0, end = aPrefixes.Length() / aPrefixSize;
+
+ while (end > begin) {
+ int32_t mid = (begin + end) >> 1;
+ int cmp = memcmp(fullhash, prefixes + mid * aPrefixSize, aPrefixSize);
+ if (cmp < 0) {
+ end = mid;
+ } else if (cmp > 0) {
+ begin = mid + 1;
+ } else {
+ return true;
+ }
+ }
+ return false;
+}
+
+MOZ_DEFINE_MALLOC_SIZE_OF(UrlClassifierMallocSizeOf)
+
+NS_IMETHODIMP
+VariableLengthPrefixSet::CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ size_t amount = SizeOfIncludingThis(UrlClassifierMallocSizeOf);
+
+ return aHandleReport->Callback(
+ ""_ns, mMemoryReportPath, KIND_HEAP, UNITS_BYTES, amount,
+ nsLiteralCString("Memory used by the variable-length prefix set for a "
+ "URL classifier."),
+ aData);
+}
+
+size_t VariableLengthPrefixSet::SizeOfIncludingThis(
+ mozilla::MallocSizeOf aMallocSizeOf) const {
+ MutexAutoLock lock(mLock);
+
+ size_t n = 0;
+ n += aMallocSizeOf(this);
+
+ n += mFixedPrefixSet->SizeOfIncludingThis(moz_malloc_size_of) -
+ aMallocSizeOf(mFixedPrefixSet);
+
+ n += mVLPrefixSet.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (const auto& data : mVLPrefixSet.Values()) {
+ n += data->SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+ }
+
+ return n;
+}
+
+} // namespace mozilla::safebrowsing
diff --git a/toolkit/components/url-classifier/VariableLengthPrefixSet.h b/toolkit/components/url-classifier/VariableLengthPrefixSet.h
new file mode 100644
index 0000000000..2593267163
--- /dev/null
+++ b/toolkit/components/url-classifier/VariableLengthPrefixSet.h
@@ -0,0 +1,73 @@
+//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef VariableLengthPrefixSet_h
+#define VariableLengthPrefixSet_h
+
+#include "nsISupports.h"
+#include "nsIMemoryReporter.h"
+#include "Entries.h"
+#include "nsTArray.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Mutex.h"
+
+class nsUrlClassifierPrefixSet;
+
+namespace mozilla {
+namespace safebrowsing {
+
+class VariableLengthPrefixSet final : public nsIMemoryReporter {
+ public:
+ VariableLengthPrefixSet();
+
+ nsresult Init(const nsACString& aName);
+ nsresult SetPrefixes(mozilla::safebrowsing::PrefixStringMap& aPrefixMap);
+ nsresult SetPrefixes(AddPrefixArray& aAddPrefixes,
+ AddCompleteArray& aAddCompletes);
+ nsresult GetPrefixes(mozilla::safebrowsing::PrefixStringMap& aPrefixMap);
+ nsresult GetFixedLengthPrefixes(FallibleTArray<uint32_t>* aPrefixes,
+ FallibleTArray<nsCString>* aCompletes);
+ nsresult GetFixedLengthPrefixByIndex(uint32_t aIndex,
+ uint32_t* aOutPrefix) const;
+ nsresult Matches(uint32_t aPrefix, const nsACString& aFullHash,
+ uint32_t* aLength) const;
+ nsresult IsEmpty(bool* aEmpty) const;
+
+ nsresult WritePrefixes(nsCOMPtr<nsIOutputStream>& out) const;
+ nsresult LoadPrefixes(nsCOMPtr<nsIInputStream>& in);
+ uint32_t CalculatePreallocateSize() const;
+ uint32_t FixedLengthPrefixLength() const;
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIMEMORYREPORTER
+
+ private:
+ virtual ~VariableLengthPrefixSet();
+
+ static const uint32_t PREFIXSET_VERSION_MAGIC = 1;
+
+ bool BinarySearch(const nsACString& aFullHash, const nsACString& aPrefixes,
+ uint32_t aPrefixSize) const;
+
+ // Lock to prevent races between the url-classifier thread (which does most
+ // of the operations) and the main thread (which does memory reporting).
+ // It should be held for all operations between Init() and destruction that
+ // touch this class's data members.
+ mutable mozilla::Mutex mLock MOZ_UNANNOTATED;
+
+ const RefPtr<nsUrlClassifierPrefixSet> mFixedPrefixSet;
+ mozilla::safebrowsing::PrefixStringMap mVLPrefixSet;
+
+ nsCString mName;
+ nsCString mMemoryReportPath;
+};
+
+} // namespace safebrowsing
+} // namespace mozilla
+
+#endif
diff --git a/toolkit/components/url-classifier/chromium/README.txt b/toolkit/components/url-classifier/chromium/README.txt
new file mode 100644
index 0000000000..e4d4285804
--- /dev/null
+++ b/toolkit/components/url-classifier/chromium/README.txt
@@ -0,0 +1,41 @@
+# Overview
+
+'safebrowsing.proto' is modified from [1] with the following changes:
+
+- Added "package mozilla.safebrowsing;"
+- Added more threatHit information
+
+##################################
+ // Client-reported identification.
+ optional ClientInfo client_info = 5;
+
+ // Details about the user that encountered the threat.
+ message UserInfo {
+ // The UN M.49 region code associated with the user's location.
+ optional string region_code = 1;
+
+ // Unique ID stable over a week or two
+ optional bytes user_id = 2;
+ }
+
+ // Details about the user that encountered the threat.
+ optional UserInfo user_info = 6;
+####################################
+
+to avoid naming pollution. We use this source file along with protobuf compiler (protoc) to generate safebrowsing.pb.h/cc for safebrowsing v4 update and hash completion. The current generated files are compiled by protoc 2.6.1 since the protobuf library in gecko is not upgraded to 3.0 yet.
+
+# Update
+
+If you want to update to the latest upstream version,
+
+1. Checkout the latest one in [2]
+2. Use protoc to generate safebrowsing.pb.h and safebrowsing.pb.cc. For example,
+
+$ protoc -I=. --cpp_out="../protobuf/" safebrowsing.proto
+
+(Note that we should use protoc v2.6.1 [3] to compile. You can find the compiled protoc in [4] if you don't have one.)
+
+[1] https://chromium.googlesource.com/chromium/src.git/+/9c4485f1ce7cac7ae82f7a4ae36ccc663afe806c/components/safe_browsing_db/safebrowsing.proto
+[2] https://chromium.googlesource.com/chromium/src.git/+/master/components/safe_browsing_db/safebrowsing.proto
+[3] https://github.com/google/protobuf/releases/tag/v2.6.1
+[4] https://repo1.maven.org/maven2/com/google/protobuf/protoc
diff --git a/toolkit/components/url-classifier/chromium/safebrowsing.pb.cc b/toolkit/components/url-classifier/chromium/safebrowsing.pb.cc
new file mode 100644
index 0000000000..ec0994bfd6
--- /dev/null
+++ b/toolkit/components/url-classifier/chromium/safebrowsing.pb.cc
@@ -0,0 +1,8925 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: safebrowsing.proto
+
+#include "safebrowsing.pb.h"
+
+#include <algorithm>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/extension_set.h>
+#include <google/protobuf/wire_format_lite.h>
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+
+PROTOBUF_PRAGMA_INIT_SEG
+
+namespace _pb = ::PROTOBUF_NAMESPACE_ID;
+namespace _pbi = _pb::internal;
+
+namespace mozilla {
+namespace safebrowsing {
+PROTOBUF_CONSTEXPR ThreatInfo::ThreatInfo(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_.threat_types_)*/{}
+ , /*decltype(_impl_.platform_types_)*/{}
+ , /*decltype(_impl_.threat_entries_)*/{}
+ , /*decltype(_impl_.threat_entry_types_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}} {}
+struct ThreatInfoDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR ThreatInfoDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~ThreatInfoDefaultTypeInternal() {}
+ union {
+ ThreatInfo _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ThreatInfoDefaultTypeInternal _ThreatInfo_default_instance_;
+PROTOBUF_CONSTEXPR ThreatMatch::ThreatMatch(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.threat_)*/nullptr
+ , /*decltype(_impl_.threat_entry_metadata_)*/nullptr
+ , /*decltype(_impl_.cache_duration_)*/nullptr
+ , /*decltype(_impl_.threat_type_)*/0
+ , /*decltype(_impl_.platform_type_)*/0
+ , /*decltype(_impl_.threat_entry_type_)*/0} {}
+struct ThreatMatchDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR ThreatMatchDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~ThreatMatchDefaultTypeInternal() {}
+ union {
+ ThreatMatch _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ThreatMatchDefaultTypeInternal _ThreatMatch_default_instance_;
+PROTOBUF_CONSTEXPR FindThreatMatchesRequest::FindThreatMatchesRequest(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.client_)*/nullptr
+ , /*decltype(_impl_.threat_info_)*/nullptr} {}
+struct FindThreatMatchesRequestDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR FindThreatMatchesRequestDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~FindThreatMatchesRequestDefaultTypeInternal() {}
+ union {
+ FindThreatMatchesRequest _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 FindThreatMatchesRequestDefaultTypeInternal _FindThreatMatchesRequest_default_instance_;
+PROTOBUF_CONSTEXPR FindThreatMatchesResponse::FindThreatMatchesResponse(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_.matches_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}} {}
+struct FindThreatMatchesResponseDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR FindThreatMatchesResponseDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~FindThreatMatchesResponseDefaultTypeInternal() {}
+ union {
+ FindThreatMatchesResponse _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 FindThreatMatchesResponseDefaultTypeInternal _FindThreatMatchesResponse_default_instance_;
+PROTOBUF_CONSTEXPR FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.supported_compressions_)*/{}
+ , /*decltype(_impl_.region_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+ , /*decltype(_impl_.max_update_entries_)*/0
+ , /*decltype(_impl_.max_database_entries_)*/0} {}
+struct FetchThreatListUpdatesRequest_ListUpdateRequest_ConstraintsDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR FetchThreatListUpdatesRequest_ListUpdateRequest_ConstraintsDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~FetchThreatListUpdatesRequest_ListUpdateRequest_ConstraintsDefaultTypeInternal() {}
+ union {
+ FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 FetchThreatListUpdatesRequest_ListUpdateRequest_ConstraintsDefaultTypeInternal _FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints_default_instance_;
+PROTOBUF_CONSTEXPR FetchThreatListUpdatesRequest_ListUpdateRequest::FetchThreatListUpdatesRequest_ListUpdateRequest(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.state_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+ , /*decltype(_impl_.constraints_)*/nullptr
+ , /*decltype(_impl_.threat_type_)*/0
+ , /*decltype(_impl_.platform_type_)*/0
+ , /*decltype(_impl_.threat_entry_type_)*/0} {}
+struct FetchThreatListUpdatesRequest_ListUpdateRequestDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR FetchThreatListUpdatesRequest_ListUpdateRequestDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~FetchThreatListUpdatesRequest_ListUpdateRequestDefaultTypeInternal() {}
+ union {
+ FetchThreatListUpdatesRequest_ListUpdateRequest _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 FetchThreatListUpdatesRequest_ListUpdateRequestDefaultTypeInternal _FetchThreatListUpdatesRequest_ListUpdateRequest_default_instance_;
+PROTOBUF_CONSTEXPR FetchThreatListUpdatesRequest::FetchThreatListUpdatesRequest(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.list_update_requests_)*/{}
+ , /*decltype(_impl_.client_)*/nullptr
+ , /*decltype(_impl_.chrome_client_info_)*/nullptr} {}
+struct FetchThreatListUpdatesRequestDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR FetchThreatListUpdatesRequestDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~FetchThreatListUpdatesRequestDefaultTypeInternal() {}
+ union {
+ FetchThreatListUpdatesRequest _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 FetchThreatListUpdatesRequestDefaultTypeInternal _FetchThreatListUpdatesRequest_default_instance_;
+PROTOBUF_CONSTEXPR FetchThreatListUpdatesResponse_ListUpdateResponse::FetchThreatListUpdatesResponse_ListUpdateResponse(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.additions_)*/{}
+ , /*decltype(_impl_.removals_)*/{}
+ , /*decltype(_impl_.new_client_state_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+ , /*decltype(_impl_.checksum_)*/nullptr
+ , /*decltype(_impl_.threat_type_)*/0
+ , /*decltype(_impl_.threat_entry_type_)*/0
+ , /*decltype(_impl_.platform_type_)*/0
+ , /*decltype(_impl_.response_type_)*/0} {}
+struct FetchThreatListUpdatesResponse_ListUpdateResponseDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR FetchThreatListUpdatesResponse_ListUpdateResponseDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~FetchThreatListUpdatesResponse_ListUpdateResponseDefaultTypeInternal() {}
+ union {
+ FetchThreatListUpdatesResponse_ListUpdateResponse _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 FetchThreatListUpdatesResponse_ListUpdateResponseDefaultTypeInternal _FetchThreatListUpdatesResponse_ListUpdateResponse_default_instance_;
+PROTOBUF_CONSTEXPR FetchThreatListUpdatesResponse::FetchThreatListUpdatesResponse(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.list_update_responses_)*/{}
+ , /*decltype(_impl_.minimum_wait_duration_)*/nullptr} {}
+struct FetchThreatListUpdatesResponseDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR FetchThreatListUpdatesResponseDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~FetchThreatListUpdatesResponseDefaultTypeInternal() {}
+ union {
+ FetchThreatListUpdatesResponse _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 FetchThreatListUpdatesResponseDefaultTypeInternal _FetchThreatListUpdatesResponse_default_instance_;
+PROTOBUF_CONSTEXPR FindFullHashesRequest::FindFullHashesRequest(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.client_states_)*/{}
+ , /*decltype(_impl_.client_)*/nullptr
+ , /*decltype(_impl_.threat_info_)*/nullptr} {}
+struct FindFullHashesRequestDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR FindFullHashesRequestDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~FindFullHashesRequestDefaultTypeInternal() {}
+ union {
+ FindFullHashesRequest _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 FindFullHashesRequestDefaultTypeInternal _FindFullHashesRequest_default_instance_;
+PROTOBUF_CONSTEXPR FindFullHashesResponse::FindFullHashesResponse(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.matches_)*/{}
+ , /*decltype(_impl_.minimum_wait_duration_)*/nullptr
+ , /*decltype(_impl_.negative_cache_duration_)*/nullptr} {}
+struct FindFullHashesResponseDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR FindFullHashesResponseDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~FindFullHashesResponseDefaultTypeInternal() {}
+ union {
+ FindFullHashesResponse _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 FindFullHashesResponseDefaultTypeInternal _FindFullHashesResponse_default_instance_;
+PROTOBUF_CONSTEXPR ThreatHit_ThreatSource::ThreatHit_ThreatSource(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.url_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+ , /*decltype(_impl_.remote_ip_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+ , /*decltype(_impl_.referrer_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+ , /*decltype(_impl_.type_)*/0} {}
+struct ThreatHit_ThreatSourceDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR ThreatHit_ThreatSourceDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~ThreatHit_ThreatSourceDefaultTypeInternal() {}
+ union {
+ ThreatHit_ThreatSource _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ThreatHit_ThreatSourceDefaultTypeInternal _ThreatHit_ThreatSource_default_instance_;
+PROTOBUF_CONSTEXPR ThreatHit_UserInfo::ThreatHit_UserInfo(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.region_code_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+ , /*decltype(_impl_.user_id_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}} {}
+struct ThreatHit_UserInfoDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR ThreatHit_UserInfoDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~ThreatHit_UserInfoDefaultTypeInternal() {}
+ union {
+ ThreatHit_UserInfo _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ThreatHit_UserInfoDefaultTypeInternal _ThreatHit_UserInfo_default_instance_;
+PROTOBUF_CONSTEXPR ThreatHit::ThreatHit(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.resources_)*/{}
+ , /*decltype(_impl_.entry_)*/nullptr
+ , /*decltype(_impl_.client_info_)*/nullptr
+ , /*decltype(_impl_.user_info_)*/nullptr
+ , /*decltype(_impl_.threat_type_)*/0
+ , /*decltype(_impl_.platform_type_)*/0} {}
+struct ThreatHitDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR ThreatHitDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~ThreatHitDefaultTypeInternal() {}
+ union {
+ ThreatHit _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ThreatHitDefaultTypeInternal _ThreatHit_default_instance_;
+PROTOBUF_CONSTEXPR ClientInfo::ClientInfo(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.client_id_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+ , /*decltype(_impl_.client_version_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}} {}
+struct ClientInfoDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR ClientInfoDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~ClientInfoDefaultTypeInternal() {}
+ union {
+ ClientInfo _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ClientInfoDefaultTypeInternal _ClientInfo_default_instance_;
+PROTOBUF_CONSTEXPR ChromeClientInfo::ChromeClientInfo(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.reporting_population_)*/0} {}
+struct ChromeClientInfoDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR ChromeClientInfoDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~ChromeClientInfoDefaultTypeInternal() {}
+ union {
+ ChromeClientInfo _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ChromeClientInfoDefaultTypeInternal _ChromeClientInfo_default_instance_;
+PROTOBUF_CONSTEXPR Checksum::Checksum(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.sha256_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}} {}
+struct ChecksumDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR ChecksumDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~ChecksumDefaultTypeInternal() {}
+ union {
+ Checksum _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ChecksumDefaultTypeInternal _Checksum_default_instance_;
+PROTOBUF_CONSTEXPR ThreatEntry::ThreatEntry(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.hash_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+ , /*decltype(_impl_.url_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}} {}
+struct ThreatEntryDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR ThreatEntryDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~ThreatEntryDefaultTypeInternal() {}
+ union {
+ ThreatEntry _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ThreatEntryDefaultTypeInternal _ThreatEntry_default_instance_;
+PROTOBUF_CONSTEXPR ThreatEntrySet::ThreatEntrySet(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.raw_hashes_)*/nullptr
+ , /*decltype(_impl_.raw_indices_)*/nullptr
+ , /*decltype(_impl_.rice_hashes_)*/nullptr
+ , /*decltype(_impl_.rice_indices_)*/nullptr
+ , /*decltype(_impl_.compression_type_)*/0} {}
+struct ThreatEntrySetDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR ThreatEntrySetDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~ThreatEntrySetDefaultTypeInternal() {}
+ union {
+ ThreatEntrySet _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ThreatEntrySetDefaultTypeInternal _ThreatEntrySet_default_instance_;
+PROTOBUF_CONSTEXPR RawIndices::RawIndices(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_.indices_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}} {}
+struct RawIndicesDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR RawIndicesDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~RawIndicesDefaultTypeInternal() {}
+ union {
+ RawIndices _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 RawIndicesDefaultTypeInternal _RawIndices_default_instance_;
+PROTOBUF_CONSTEXPR RawHashes::RawHashes(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.raw_hashes_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+ , /*decltype(_impl_.prefix_size_)*/0} {}
+struct RawHashesDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR RawHashesDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~RawHashesDefaultTypeInternal() {}
+ union {
+ RawHashes _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 RawHashesDefaultTypeInternal _RawHashes_default_instance_;
+PROTOBUF_CONSTEXPR RiceDeltaEncoding::RiceDeltaEncoding(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.encoded_data_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+ , /*decltype(_impl_.first_value_)*/int64_t{0}
+ , /*decltype(_impl_.rice_parameter_)*/0
+ , /*decltype(_impl_.num_entries_)*/0} {}
+struct RiceDeltaEncodingDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR RiceDeltaEncodingDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~RiceDeltaEncodingDefaultTypeInternal() {}
+ union {
+ RiceDeltaEncoding _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 RiceDeltaEncodingDefaultTypeInternal _RiceDeltaEncoding_default_instance_;
+PROTOBUF_CONSTEXPR ThreatEntryMetadata_MetadataEntry::ThreatEntryMetadata_MetadataEntry(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.key_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}
+ , /*decltype(_impl_.value_)*/{&::_pbi::fixed_address_empty_string, ::_pbi::ConstantInitialized{}}} {}
+struct ThreatEntryMetadata_MetadataEntryDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR ThreatEntryMetadata_MetadataEntryDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~ThreatEntryMetadata_MetadataEntryDefaultTypeInternal() {}
+ union {
+ ThreatEntryMetadata_MetadataEntry _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ThreatEntryMetadata_MetadataEntryDefaultTypeInternal _ThreatEntryMetadata_MetadataEntry_default_instance_;
+PROTOBUF_CONSTEXPR ThreatEntryMetadata::ThreatEntryMetadata(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_.entries_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}} {}
+struct ThreatEntryMetadataDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR ThreatEntryMetadataDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~ThreatEntryMetadataDefaultTypeInternal() {}
+ union {
+ ThreatEntryMetadata _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ThreatEntryMetadataDefaultTypeInternal _ThreatEntryMetadata_default_instance_;
+PROTOBUF_CONSTEXPR ThreatListDescriptor::ThreatListDescriptor(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.threat_type_)*/0
+ , /*decltype(_impl_.platform_type_)*/0
+ , /*decltype(_impl_.threat_entry_type_)*/0} {}
+struct ThreatListDescriptorDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR ThreatListDescriptorDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~ThreatListDescriptorDefaultTypeInternal() {}
+ union {
+ ThreatListDescriptor _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ThreatListDescriptorDefaultTypeInternal _ThreatListDescriptor_default_instance_;
+PROTOBUF_CONSTEXPR ListThreatListsResponse::ListThreatListsResponse(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_.threat_lists_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}} {}
+struct ListThreatListsResponseDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR ListThreatListsResponseDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~ListThreatListsResponseDefaultTypeInternal() {}
+ union {
+ ListThreatListsResponse _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 ListThreatListsResponseDefaultTypeInternal _ListThreatListsResponse_default_instance_;
+PROTOBUF_CONSTEXPR Duration::Duration(
+ ::_pbi::ConstantInitialized): _impl_{
+ /*decltype(_impl_._has_bits_)*/{}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , /*decltype(_impl_.seconds_)*/int64_t{0}
+ , /*decltype(_impl_.nanos_)*/0} {}
+struct DurationDefaultTypeInternal {
+ PROTOBUF_CONSTEXPR DurationDefaultTypeInternal()
+ : _instance(::_pbi::ConstantInitialized{}) {}
+ ~DurationDefaultTypeInternal() {}
+ union {
+ Duration _instance;
+ };
+};
+PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 DurationDefaultTypeInternal _Duration_default_instance_;
+} // namespace safebrowsing
+} // namespace mozilla
+namespace mozilla {
+namespace safebrowsing {
+bool FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_IsValid(int value) {
+ switch (value) {
+ case 0:
+ case 1:
+ case 2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<std::string> FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_strings[3] = {};
+
+static const char FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_names[] =
+ "FULL_UPDATE"
+ "PARTIAL_UPDATE"
+ "RESPONSE_TYPE_UNSPECIFIED";
+
+static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_entries[] = {
+ { {FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_names + 0, 11}, 2 },
+ { {FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_names + 11, 14}, 1 },
+ { {FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_names + 25, 25}, 0 },
+};
+
+static const int FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_entries_by_number[] = {
+ 2, // 0 -> RESPONSE_TYPE_UNSPECIFIED
+ 1, // 1 -> PARTIAL_UPDATE
+ 0, // 2 -> FULL_UPDATE
+};
+
+const std::string& FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_Name(
+ FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType value) {
+ static const bool dummy =
+ ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings(
+ FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_entries,
+ FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_entries_by_number,
+ 3, FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_strings);
+ (void) dummy;
+ int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName(
+ FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_entries,
+ FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_entries_by_number,
+ 3, value);
+ return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() :
+ FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_strings[idx].get();
+}
+bool FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_Parse(
+ ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType* value) {
+ int int_value;
+ bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue(
+ FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_entries, 3, name, &int_value);
+ if (success) {
+ *value = static_cast<FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType>(int_value);
+ }
+ return success;
+}
+#if (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+constexpr FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType FetchThreatListUpdatesResponse_ListUpdateResponse::RESPONSE_TYPE_UNSPECIFIED;
+constexpr FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType FetchThreatListUpdatesResponse_ListUpdateResponse::PARTIAL_UPDATE;
+constexpr FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType FetchThreatListUpdatesResponse_ListUpdateResponse::FULL_UPDATE;
+constexpr FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType FetchThreatListUpdatesResponse_ListUpdateResponse::ResponseType_MIN;
+constexpr FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType FetchThreatListUpdatesResponse_ListUpdateResponse::ResponseType_MAX;
+constexpr int FetchThreatListUpdatesResponse_ListUpdateResponse::ResponseType_ARRAYSIZE;
+#endif // (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+bool ThreatHit_ThreatSourceType_IsValid(int value) {
+ switch (value) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<std::string> ThreatHit_ThreatSourceType_strings[5] = {};
+
+static const char ThreatHit_ThreatSourceType_names[] =
+ "MATCHING_URL"
+ "TAB_REDIRECT"
+ "TAB_RESOURCE"
+ "TAB_URL"
+ "THREAT_SOURCE_TYPE_UNSPECIFIED";
+
+static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry ThreatHit_ThreatSourceType_entries[] = {
+ { {ThreatHit_ThreatSourceType_names + 0, 12}, 1 },
+ { {ThreatHit_ThreatSourceType_names + 12, 12}, 3 },
+ { {ThreatHit_ThreatSourceType_names + 24, 12}, 4 },
+ { {ThreatHit_ThreatSourceType_names + 36, 7}, 2 },
+ { {ThreatHit_ThreatSourceType_names + 43, 30}, 0 },
+};
+
+static const int ThreatHit_ThreatSourceType_entries_by_number[] = {
+ 4, // 0 -> THREAT_SOURCE_TYPE_UNSPECIFIED
+ 0, // 1 -> MATCHING_URL
+ 3, // 2 -> TAB_URL
+ 1, // 3 -> TAB_REDIRECT
+ 2, // 4 -> TAB_RESOURCE
+};
+
+const std::string& ThreatHit_ThreatSourceType_Name(
+ ThreatHit_ThreatSourceType value) {
+ static const bool dummy =
+ ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings(
+ ThreatHit_ThreatSourceType_entries,
+ ThreatHit_ThreatSourceType_entries_by_number,
+ 5, ThreatHit_ThreatSourceType_strings);
+ (void) dummy;
+ int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName(
+ ThreatHit_ThreatSourceType_entries,
+ ThreatHit_ThreatSourceType_entries_by_number,
+ 5, value);
+ return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() :
+ ThreatHit_ThreatSourceType_strings[idx].get();
+}
+bool ThreatHit_ThreatSourceType_Parse(
+ ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ThreatHit_ThreatSourceType* value) {
+ int int_value;
+ bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue(
+ ThreatHit_ThreatSourceType_entries, 5, name, &int_value);
+ if (success) {
+ *value = static_cast<ThreatHit_ThreatSourceType>(int_value);
+ }
+ return success;
+}
+#if (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+constexpr ThreatHit_ThreatSourceType ThreatHit::THREAT_SOURCE_TYPE_UNSPECIFIED;
+constexpr ThreatHit_ThreatSourceType ThreatHit::MATCHING_URL;
+constexpr ThreatHit_ThreatSourceType ThreatHit::TAB_URL;
+constexpr ThreatHit_ThreatSourceType ThreatHit::TAB_REDIRECT;
+constexpr ThreatHit_ThreatSourceType ThreatHit::TAB_RESOURCE;
+constexpr ThreatHit_ThreatSourceType ThreatHit::ThreatSourceType_MIN;
+constexpr ThreatHit_ThreatSourceType ThreatHit::ThreatSourceType_MAX;
+constexpr int ThreatHit::ThreatSourceType_ARRAYSIZE;
+#endif // (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+bool ChromeClientInfo_SafeBrowsingReportingPopulation_IsValid(int value) {
+ switch (value) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<std::string> ChromeClientInfo_SafeBrowsingReportingPopulation_strings[4] = {};
+
+static const char ChromeClientInfo_SafeBrowsingReportingPopulation_names[] =
+ "EXTENDED"
+ "OPT_OUT"
+ "SCOUT"
+ "UNSPECIFIED";
+
+static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry ChromeClientInfo_SafeBrowsingReportingPopulation_entries[] = {
+ { {ChromeClientInfo_SafeBrowsingReportingPopulation_names + 0, 8}, 2 },
+ { {ChromeClientInfo_SafeBrowsingReportingPopulation_names + 8, 7}, 1 },
+ { {ChromeClientInfo_SafeBrowsingReportingPopulation_names + 15, 5}, 3 },
+ { {ChromeClientInfo_SafeBrowsingReportingPopulation_names + 20, 11}, 0 },
+};
+
+static const int ChromeClientInfo_SafeBrowsingReportingPopulation_entries_by_number[] = {
+ 3, // 0 -> UNSPECIFIED
+ 1, // 1 -> OPT_OUT
+ 0, // 2 -> EXTENDED
+ 2, // 3 -> SCOUT
+};
+
+const std::string& ChromeClientInfo_SafeBrowsingReportingPopulation_Name(
+ ChromeClientInfo_SafeBrowsingReportingPopulation value) {
+ static const bool dummy =
+ ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings(
+ ChromeClientInfo_SafeBrowsingReportingPopulation_entries,
+ ChromeClientInfo_SafeBrowsingReportingPopulation_entries_by_number,
+ 4, ChromeClientInfo_SafeBrowsingReportingPopulation_strings);
+ (void) dummy;
+ int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName(
+ ChromeClientInfo_SafeBrowsingReportingPopulation_entries,
+ ChromeClientInfo_SafeBrowsingReportingPopulation_entries_by_number,
+ 4, value);
+ return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() :
+ ChromeClientInfo_SafeBrowsingReportingPopulation_strings[idx].get();
+}
+bool ChromeClientInfo_SafeBrowsingReportingPopulation_Parse(
+ ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ChromeClientInfo_SafeBrowsingReportingPopulation* value) {
+ int int_value;
+ bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue(
+ ChromeClientInfo_SafeBrowsingReportingPopulation_entries, 4, name, &int_value);
+ if (success) {
+ *value = static_cast<ChromeClientInfo_SafeBrowsingReportingPopulation>(int_value);
+ }
+ return success;
+}
+#if (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+constexpr ChromeClientInfo_SafeBrowsingReportingPopulation ChromeClientInfo::UNSPECIFIED;
+constexpr ChromeClientInfo_SafeBrowsingReportingPopulation ChromeClientInfo::OPT_OUT;
+constexpr ChromeClientInfo_SafeBrowsingReportingPopulation ChromeClientInfo::EXTENDED;
+constexpr ChromeClientInfo_SafeBrowsingReportingPopulation ChromeClientInfo::SCOUT;
+constexpr ChromeClientInfo_SafeBrowsingReportingPopulation ChromeClientInfo::SafeBrowsingReportingPopulation_MIN;
+constexpr ChromeClientInfo_SafeBrowsingReportingPopulation ChromeClientInfo::SafeBrowsingReportingPopulation_MAX;
+constexpr int ChromeClientInfo::SafeBrowsingReportingPopulation_ARRAYSIZE;
+#endif // (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912))
+bool ThreatType_IsValid(int value) {
+ switch (value) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 13:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<std::string> ThreatType_strings[12] = {};
+
+static const char ThreatType_names[] =
+ "API_ABUSE"
+ "CLIENT_INCIDENT"
+ "CSD_DOWNLOAD_WHITELIST"
+ "CSD_WHITELIST"
+ "MALICIOUS_BINARY"
+ "MALWARE_THREAT"
+ "POTENTIALLY_HARMFUL_APPLICATION"
+ "SOCIAL_ENGINEERING"
+ "SOCIAL_ENGINEERING_PUBLIC"
+ "SUBRESOURCE_FILTER"
+ "THREAT_TYPE_UNSPECIFIED"
+ "UNWANTED_SOFTWARE";
+
+static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry ThreatType_entries[] = {
+ { {ThreatType_names + 0, 9}, 6 },
+ { {ThreatType_names + 9, 15}, 10 },
+ { {ThreatType_names + 24, 22}, 9 },
+ { {ThreatType_names + 46, 13}, 8 },
+ { {ThreatType_names + 59, 16}, 7 },
+ { {ThreatType_names + 75, 14}, 1 },
+ { {ThreatType_names + 89, 31}, 4 },
+ { {ThreatType_names + 120, 18}, 5 },
+ { {ThreatType_names + 138, 25}, 2 },
+ { {ThreatType_names + 163, 18}, 13 },
+ { {ThreatType_names + 181, 23}, 0 },
+ { {ThreatType_names + 204, 17}, 3 },
+};
+
+static const int ThreatType_entries_by_number[] = {
+ 10, // 0 -> THREAT_TYPE_UNSPECIFIED
+ 5, // 1 -> MALWARE_THREAT
+ 8, // 2 -> SOCIAL_ENGINEERING_PUBLIC
+ 11, // 3 -> UNWANTED_SOFTWARE
+ 6, // 4 -> POTENTIALLY_HARMFUL_APPLICATION
+ 7, // 5 -> SOCIAL_ENGINEERING
+ 0, // 6 -> API_ABUSE
+ 4, // 7 -> MALICIOUS_BINARY
+ 3, // 8 -> CSD_WHITELIST
+ 2, // 9 -> CSD_DOWNLOAD_WHITELIST
+ 1, // 10 -> CLIENT_INCIDENT
+ 9, // 13 -> SUBRESOURCE_FILTER
+};
+
+const std::string& ThreatType_Name(
+ ThreatType value) {
+ static const bool dummy =
+ ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings(
+ ThreatType_entries,
+ ThreatType_entries_by_number,
+ 12, ThreatType_strings);
+ (void) dummy;
+ int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName(
+ ThreatType_entries,
+ ThreatType_entries_by_number,
+ 12, value);
+ return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() :
+ ThreatType_strings[idx].get();
+}
+bool ThreatType_Parse(
+ ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ThreatType* value) {
+ int int_value;
+ bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue(
+ ThreatType_entries, 12, name, &int_value);
+ if (success) {
+ *value = static_cast<ThreatType>(int_value);
+ }
+ return success;
+}
+bool PlatformType_IsValid(int value) {
+ switch (value) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<std::string> PlatformType_strings[9] = {};
+
+static const char PlatformType_names[] =
+ "ALL_PLATFORMS"
+ "ANDROID_PLATFORM"
+ "ANY_PLATFORM"
+ "CHROME_PLATFORM"
+ "IOS_PLATFORM"
+ "LINUX_PLATFORM"
+ "OSX_PLATFORM"
+ "PLATFORM_TYPE_UNSPECIFIED"
+ "WINDOWS_PLATFORM";
+
+static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry PlatformType_entries[] = {
+ { {PlatformType_names + 0, 13}, 7 },
+ { {PlatformType_names + 13, 16}, 3 },
+ { {PlatformType_names + 29, 12}, 6 },
+ { {PlatformType_names + 41, 15}, 8 },
+ { {PlatformType_names + 56, 12}, 5 },
+ { {PlatformType_names + 68, 14}, 2 },
+ { {PlatformType_names + 82, 12}, 4 },
+ { {PlatformType_names + 94, 25}, 0 },
+ { {PlatformType_names + 119, 16}, 1 },
+};
+
+static const int PlatformType_entries_by_number[] = {
+ 7, // 0 -> PLATFORM_TYPE_UNSPECIFIED
+ 8, // 1 -> WINDOWS_PLATFORM
+ 5, // 2 -> LINUX_PLATFORM
+ 1, // 3 -> ANDROID_PLATFORM
+ 6, // 4 -> OSX_PLATFORM
+ 4, // 5 -> IOS_PLATFORM
+ 2, // 6 -> ANY_PLATFORM
+ 0, // 7 -> ALL_PLATFORMS
+ 3, // 8 -> CHROME_PLATFORM
+};
+
+const std::string& PlatformType_Name(
+ PlatformType value) {
+ static const bool dummy =
+ ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings(
+ PlatformType_entries,
+ PlatformType_entries_by_number,
+ 9, PlatformType_strings);
+ (void) dummy;
+ int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName(
+ PlatformType_entries,
+ PlatformType_entries_by_number,
+ 9, value);
+ return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() :
+ PlatformType_strings[idx].get();
+}
+bool PlatformType_Parse(
+ ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, PlatformType* value) {
+ int int_value;
+ bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue(
+ PlatformType_entries, 9, name, &int_value);
+ if (success) {
+ *value = static_cast<PlatformType>(int_value);
+ }
+ return success;
+}
+bool CompressionType_IsValid(int value) {
+ switch (value) {
+ case 0:
+ case 1:
+ case 2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<std::string> CompressionType_strings[3] = {};
+
+static const char CompressionType_names[] =
+ "COMPRESSION_TYPE_UNSPECIFIED"
+ "RAW"
+ "RICE";
+
+static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry CompressionType_entries[] = {
+ { {CompressionType_names + 0, 28}, 0 },
+ { {CompressionType_names + 28, 3}, 1 },
+ { {CompressionType_names + 31, 4}, 2 },
+};
+
+static const int CompressionType_entries_by_number[] = {
+ 0, // 0 -> COMPRESSION_TYPE_UNSPECIFIED
+ 1, // 1 -> RAW
+ 2, // 2 -> RICE
+};
+
+const std::string& CompressionType_Name(
+ CompressionType value) {
+ static const bool dummy =
+ ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings(
+ CompressionType_entries,
+ CompressionType_entries_by_number,
+ 3, CompressionType_strings);
+ (void) dummy;
+ int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName(
+ CompressionType_entries,
+ CompressionType_entries_by_number,
+ 3, value);
+ return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() :
+ CompressionType_strings[idx].get();
+}
+bool CompressionType_Parse(
+ ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, CompressionType* value) {
+ int int_value;
+ bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue(
+ CompressionType_entries, 3, name, &int_value);
+ if (success) {
+ *value = static_cast<CompressionType>(int_value);
+ }
+ return success;
+}
+bool ThreatEntryType_IsValid(int value) {
+ switch (value) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed<std::string> ThreatEntryType_strings[7] = {};
+
+static const char ThreatEntryType_names[] =
+ "CERT"
+ "CHROME_EXTENSION"
+ "EXECUTABLE"
+ "FILENAME"
+ "IP_RANGE"
+ "THREAT_ENTRY_TYPE_UNSPECIFIED"
+ "URL";
+
+static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry ThreatEntryType_entries[] = {
+ { {ThreatEntryType_names + 0, 4}, 6 },
+ { {ThreatEntryType_names + 4, 16}, 4 },
+ { {ThreatEntryType_names + 20, 10}, 2 },
+ { {ThreatEntryType_names + 30, 8}, 5 },
+ { {ThreatEntryType_names + 38, 8}, 3 },
+ { {ThreatEntryType_names + 46, 29}, 0 },
+ { {ThreatEntryType_names + 75, 3}, 1 },
+};
+
+static const int ThreatEntryType_entries_by_number[] = {
+ 5, // 0 -> THREAT_ENTRY_TYPE_UNSPECIFIED
+ 6, // 1 -> URL
+ 2, // 2 -> EXECUTABLE
+ 4, // 3 -> IP_RANGE
+ 1, // 4 -> CHROME_EXTENSION
+ 3, // 5 -> FILENAME
+ 0, // 6 -> CERT
+};
+
+const std::string& ThreatEntryType_Name(
+ ThreatEntryType value) {
+ static const bool dummy =
+ ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings(
+ ThreatEntryType_entries,
+ ThreatEntryType_entries_by_number,
+ 7, ThreatEntryType_strings);
+ (void) dummy;
+ int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName(
+ ThreatEntryType_entries,
+ ThreatEntryType_entries_by_number,
+ 7, value);
+ return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() :
+ ThreatEntryType_strings[idx].get();
+}
+bool ThreatEntryType_Parse(
+ ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ThreatEntryType* value) {
+ int int_value;
+ bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue(
+ ThreatEntryType_entries, 7, name, &int_value);
+ if (success) {
+ *value = static_cast<ThreatEntryType>(int_value);
+ }
+ return success;
+}
+
+// ===================================================================
+
+class ThreatInfo::_Internal {
+ public:
+};
+
+ThreatInfo::ThreatInfo(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.ThreatInfo)
+}
+ThreatInfo::ThreatInfo(const ThreatInfo& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ ThreatInfo* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_.threat_types_){from._impl_.threat_types_}
+ , decltype(_impl_.platform_types_){from._impl_.platform_types_}
+ , decltype(_impl_.threat_entries_){from._impl_.threat_entries_}
+ , decltype(_impl_.threat_entry_types_){from._impl_.threat_entry_types_}
+ , /*decltype(_impl_._cached_size_)*/{}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.ThreatInfo)
+}
+
+inline void ThreatInfo::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_.threat_types_){arena}
+ , decltype(_impl_.platform_types_){arena}
+ , decltype(_impl_.threat_entries_){arena}
+ , decltype(_impl_.threat_entry_types_){arena}
+ , /*decltype(_impl_._cached_size_)*/{}
+ };
+}
+
+ThreatInfo::~ThreatInfo() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.ThreatInfo)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void ThreatInfo::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.threat_types_.~RepeatedField();
+ _impl_.platform_types_.~RepeatedField();
+ _impl_.threat_entries_.~RepeatedPtrField();
+ _impl_.threat_entry_types_.~RepeatedField();
+}
+
+void ThreatInfo::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void ThreatInfo::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.ThreatInfo)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ _impl_.threat_types_.Clear();
+ _impl_.platform_types_.Clear();
+ _impl_.threat_entries_.Clear();
+ _impl_.threat_entry_types_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* ThreatInfo::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // repeated .mozilla.safebrowsing.ThreatType threat_types = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+ ptr -= 1;
+ do {
+ ptr += 1;
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::ThreatType_IsValid(val))) {
+ _internal_add_threat_types(static_cast<::mozilla::safebrowsing::ThreatType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(1, val, mutable_unknown_fields());
+ }
+ if (!ctx->DataAvailable(ptr)) break;
+ } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<8>(ptr));
+ } else if (static_cast<uint8_t>(tag) == 10) {
+ ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedEnumParser<std::string>(_internal_mutable_threat_types(), ptr, ctx, ::mozilla::safebrowsing::ThreatType_IsValid, &_internal_metadata_, 1);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // repeated .mozilla.safebrowsing.PlatformType platform_types = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+ ptr -= 1;
+ do {
+ ptr += 1;
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::PlatformType_IsValid(val))) {
+ _internal_add_platform_types(static_cast<::mozilla::safebrowsing::PlatformType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(2, val, mutable_unknown_fields());
+ }
+ if (!ctx->DataAvailable(ptr)) break;
+ } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<16>(ptr));
+ } else if (static_cast<uint8_t>(tag) == 18) {
+ ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedEnumParser<std::string>(_internal_mutable_platform_types(), ptr, ctx, ::mozilla::safebrowsing::PlatformType_IsValid, &_internal_metadata_, 2);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // repeated .mozilla.safebrowsing.ThreatEntry threat_entries = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+ ptr -= 1;
+ do {
+ ptr += 1;
+ ptr = ctx->ParseMessage(_internal_add_threat_entries(), ptr);
+ CHK_(ptr);
+ if (!ctx->DataAvailable(ptr)) break;
+ } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<26>(ptr));
+ } else
+ goto handle_unusual;
+ continue;
+ // repeated .mozilla.safebrowsing.ThreatEntryType threat_entry_types = 4;
+ case 4:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 32)) {
+ ptr -= 1;
+ do {
+ ptr += 1;
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::ThreatEntryType_IsValid(val))) {
+ _internal_add_threat_entry_types(static_cast<::mozilla::safebrowsing::ThreatEntryType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(4, val, mutable_unknown_fields());
+ }
+ if (!ctx->DataAvailable(ptr)) break;
+ } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<32>(ptr));
+ } else if (static_cast<uint8_t>(tag) == 34) {
+ ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedEnumParser<std::string>(_internal_mutable_threat_entry_types(), ptr, ctx, ::mozilla::safebrowsing::ThreatEntryType_IsValid, &_internal_metadata_, 4);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* ThreatInfo::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.ThreatInfo)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ // repeated .mozilla.safebrowsing.ThreatType threat_types = 1;
+ for (int i = 0, n = this->_internal_threat_types_size(); i < n; i++) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 1, this->_internal_threat_types(i), target);
+ }
+
+ // repeated .mozilla.safebrowsing.PlatformType platform_types = 2;
+ for (int i = 0, n = this->_internal_platform_types_size(); i < n; i++) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 2, this->_internal_platform_types(i), target);
+ }
+
+ // repeated .mozilla.safebrowsing.ThreatEntry threat_entries = 3;
+ for (unsigned i = 0,
+ n = static_cast<unsigned>(this->_internal_threat_entries_size()); i < n; i++) {
+ const auto& repfield = this->_internal_threat_entries(i);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(3, repfield, repfield.GetCachedSize(), target, stream);
+ }
+
+ // repeated .mozilla.safebrowsing.ThreatEntryType threat_entry_types = 4;
+ for (int i = 0, n = this->_internal_threat_entry_types_size(); i < n; i++) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 4, this->_internal_threat_entry_types(i), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.ThreatInfo)
+ return target;
+}
+
+size_t ThreatInfo::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.ThreatInfo)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ // repeated .mozilla.safebrowsing.ThreatType threat_types = 1;
+ {
+ size_t data_size = 0;
+ unsigned int count = static_cast<unsigned int>(this->_internal_threat_types_size());for (unsigned int i = 0; i < count; i++) {
+ data_size += ::_pbi::WireFormatLite::EnumSize(
+ this->_internal_threat_types(static_cast<int>(i)));
+ }
+ total_size += (1UL * count) + data_size;
+ }
+
+ // repeated .mozilla.safebrowsing.PlatformType platform_types = 2;
+ {
+ size_t data_size = 0;
+ unsigned int count = static_cast<unsigned int>(this->_internal_platform_types_size());for (unsigned int i = 0; i < count; i++) {
+ data_size += ::_pbi::WireFormatLite::EnumSize(
+ this->_internal_platform_types(static_cast<int>(i)));
+ }
+ total_size += (1UL * count) + data_size;
+ }
+
+ // repeated .mozilla.safebrowsing.ThreatEntry threat_entries = 3;
+ total_size += 1UL * this->_internal_threat_entries_size();
+ for (const auto& msg : this->_impl_.threat_entries_) {
+ total_size +=
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+ }
+
+ // repeated .mozilla.safebrowsing.ThreatEntryType threat_entry_types = 4;
+ {
+ size_t data_size = 0;
+ unsigned int count = static_cast<unsigned int>(this->_internal_threat_entry_types_size());for (unsigned int i = 0; i < count; i++) {
+ data_size += ::_pbi::WireFormatLite::EnumSize(
+ this->_internal_threat_entry_types(static_cast<int>(i)));
+ }
+ total_size += (1UL * count) + data_size;
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void ThreatInfo::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const ThreatInfo*>(
+ &from));
+}
+
+void ThreatInfo::MergeFrom(const ThreatInfo& from) {
+ ThreatInfo* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.ThreatInfo)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ _this->_impl_.threat_types_.MergeFrom(from._impl_.threat_types_);
+ _this->_impl_.platform_types_.MergeFrom(from._impl_.platform_types_);
+ _this->_impl_.threat_entries_.MergeFrom(from._impl_.threat_entries_);
+ _this->_impl_.threat_entry_types_.MergeFrom(from._impl_.threat_entry_types_);
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void ThreatInfo::CopyFrom(const ThreatInfo& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.ThreatInfo)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool ThreatInfo::IsInitialized() const {
+ return true;
+}
+
+void ThreatInfo::InternalSwap(ThreatInfo* other) {
+ using std::swap;
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ _impl_.threat_types_.InternalSwap(&other->_impl_.threat_types_);
+ _impl_.platform_types_.InternalSwap(&other->_impl_.platform_types_);
+ _impl_.threat_entries_.InternalSwap(&other->_impl_.threat_entries_);
+ _impl_.threat_entry_types_.InternalSwap(&other->_impl_.threat_entry_types_);
+}
+
+std::string ThreatInfo::GetTypeName() const {
+ return "mozilla.safebrowsing.ThreatInfo";
+}
+
+
+// ===================================================================
+
+class ThreatMatch::_Internal {
+ public:
+ using HasBits = decltype(std::declval<ThreatMatch>()._impl_._has_bits_);
+ static void set_has_threat_type(HasBits* has_bits) {
+ (*has_bits)[0] |= 8u;
+ }
+ static void set_has_platform_type(HasBits* has_bits) {
+ (*has_bits)[0] |= 16u;
+ }
+ static void set_has_threat_entry_type(HasBits* has_bits) {
+ (*has_bits)[0] |= 32u;
+ }
+ static const ::mozilla::safebrowsing::ThreatEntry& threat(const ThreatMatch* msg);
+ static void set_has_threat(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static const ::mozilla::safebrowsing::ThreatEntryMetadata& threat_entry_metadata(const ThreatMatch* msg);
+ static void set_has_threat_entry_metadata(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+ static const ::mozilla::safebrowsing::Duration& cache_duration(const ThreatMatch* msg);
+ static void set_has_cache_duration(HasBits* has_bits) {
+ (*has_bits)[0] |= 4u;
+ }
+};
+
+const ::mozilla::safebrowsing::ThreatEntry&
+ThreatMatch::_Internal::threat(const ThreatMatch* msg) {
+ return *msg->_impl_.threat_;
+}
+const ::mozilla::safebrowsing::ThreatEntryMetadata&
+ThreatMatch::_Internal::threat_entry_metadata(const ThreatMatch* msg) {
+ return *msg->_impl_.threat_entry_metadata_;
+}
+const ::mozilla::safebrowsing::Duration&
+ThreatMatch::_Internal::cache_duration(const ThreatMatch* msg) {
+ return *msg->_impl_.cache_duration_;
+}
+ThreatMatch::ThreatMatch(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.ThreatMatch)
+}
+ThreatMatch::ThreatMatch(const ThreatMatch& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ ThreatMatch* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.threat_){nullptr}
+ , decltype(_impl_.threat_entry_metadata_){nullptr}
+ , decltype(_impl_.cache_duration_){nullptr}
+ , decltype(_impl_.threat_type_){}
+ , decltype(_impl_.platform_type_){}
+ , decltype(_impl_.threat_entry_type_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ if (from._internal_has_threat()) {
+ _this->_impl_.threat_ = new ::mozilla::safebrowsing::ThreatEntry(*from._impl_.threat_);
+ }
+ if (from._internal_has_threat_entry_metadata()) {
+ _this->_impl_.threat_entry_metadata_ = new ::mozilla::safebrowsing::ThreatEntryMetadata(*from._impl_.threat_entry_metadata_);
+ }
+ if (from._internal_has_cache_duration()) {
+ _this->_impl_.cache_duration_ = new ::mozilla::safebrowsing::Duration(*from._impl_.cache_duration_);
+ }
+ ::memcpy(&_impl_.threat_type_, &from._impl_.threat_type_,
+ static_cast<size_t>(reinterpret_cast<char*>(&_impl_.threat_entry_type_) -
+ reinterpret_cast<char*>(&_impl_.threat_type_)) + sizeof(_impl_.threat_entry_type_));
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.ThreatMatch)
+}
+
+inline void ThreatMatch::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.threat_){nullptr}
+ , decltype(_impl_.threat_entry_metadata_){nullptr}
+ , decltype(_impl_.cache_duration_){nullptr}
+ , decltype(_impl_.threat_type_){0}
+ , decltype(_impl_.platform_type_){0}
+ , decltype(_impl_.threat_entry_type_){0}
+ };
+}
+
+ThreatMatch::~ThreatMatch() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.ThreatMatch)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void ThreatMatch::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ if (this != internal_default_instance()) delete _impl_.threat_;
+ if (this != internal_default_instance()) delete _impl_.threat_entry_metadata_;
+ if (this != internal_default_instance()) delete _impl_.cache_duration_;
+}
+
+void ThreatMatch::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void ThreatMatch::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.ThreatMatch)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000007u) {
+ if (cached_has_bits & 0x00000001u) {
+ GOOGLE_DCHECK(_impl_.threat_ != nullptr);
+ _impl_.threat_->Clear();
+ }
+ if (cached_has_bits & 0x00000002u) {
+ GOOGLE_DCHECK(_impl_.threat_entry_metadata_ != nullptr);
+ _impl_.threat_entry_metadata_->Clear();
+ }
+ if (cached_has_bits & 0x00000004u) {
+ GOOGLE_DCHECK(_impl_.cache_duration_ != nullptr);
+ _impl_.cache_duration_->Clear();
+ }
+ }
+ if (cached_has_bits & 0x00000038u) {
+ ::memset(&_impl_.threat_type_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&_impl_.threat_entry_type_) -
+ reinterpret_cast<char*>(&_impl_.threat_type_)) + sizeof(_impl_.threat_entry_type_));
+ }
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* ThreatMatch::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::ThreatType_IsValid(val))) {
+ _internal_set_threat_type(static_cast<::mozilla::safebrowsing::ThreatType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(1, val, mutable_unknown_fields());
+ }
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::PlatformType_IsValid(val))) {
+ _internal_set_platform_type(static_cast<::mozilla::safebrowsing::PlatformType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(2, val, mutable_unknown_fields());
+ }
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.ThreatEntry threat = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+ ptr = ctx->ParseMessage(_internal_mutable_threat(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.ThreatEntryMetadata threat_entry_metadata = 4;
+ case 4:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 34)) {
+ ptr = ctx->ParseMessage(_internal_mutable_threat_entry_metadata(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.Duration cache_duration = 5;
+ case 5:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 42)) {
+ ptr = ctx->ParseMessage(_internal_mutable_cache_duration(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 6;
+ case 6:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 48)) {
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::ThreatEntryType_IsValid(val))) {
+ _internal_set_threat_entry_type(static_cast<::mozilla::safebrowsing::ThreatEntryType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(6, val, mutable_unknown_fields());
+ }
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* ThreatMatch::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.ThreatMatch)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ if (cached_has_bits & 0x00000008u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 1, this->_internal_threat_type(), target);
+ }
+
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+ if (cached_has_bits & 0x00000010u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 2, this->_internal_platform_type(), target);
+ }
+
+ // optional .mozilla.safebrowsing.ThreatEntry threat = 3;
+ if (cached_has_bits & 0x00000001u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(3, _Internal::threat(this),
+ _Internal::threat(this).GetCachedSize(), target, stream);
+ }
+
+ // optional .mozilla.safebrowsing.ThreatEntryMetadata threat_entry_metadata = 4;
+ if (cached_has_bits & 0x00000002u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(4, _Internal::threat_entry_metadata(this),
+ _Internal::threat_entry_metadata(this).GetCachedSize(), target, stream);
+ }
+
+ // optional .mozilla.safebrowsing.Duration cache_duration = 5;
+ if (cached_has_bits & 0x00000004u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(5, _Internal::cache_duration(this),
+ _Internal::cache_duration(this).GetCachedSize(), target, stream);
+ }
+
+ // optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 6;
+ if (cached_has_bits & 0x00000020u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 6, this->_internal_threat_entry_type(), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.ThreatMatch)
+ return target;
+}
+
+size_t ThreatMatch::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.ThreatMatch)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x0000003fu) {
+ // optional .mozilla.safebrowsing.ThreatEntry threat = 3;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.threat_);
+ }
+
+ // optional .mozilla.safebrowsing.ThreatEntryMetadata threat_entry_metadata = 4;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.threat_entry_metadata_);
+ }
+
+ // optional .mozilla.safebrowsing.Duration cache_duration = 5;
+ if (cached_has_bits & 0x00000004u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.cache_duration_);
+ }
+
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ if (cached_has_bits & 0x00000008u) {
+ total_size += 1 +
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_threat_type());
+ }
+
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+ if (cached_has_bits & 0x00000010u) {
+ total_size += 1 +
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_platform_type());
+ }
+
+ // optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 6;
+ if (cached_has_bits & 0x00000020u) {
+ total_size += 1 +
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_threat_entry_type());
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void ThreatMatch::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const ThreatMatch*>(
+ &from));
+}
+
+void ThreatMatch::MergeFrom(const ThreatMatch& from) {
+ ThreatMatch* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.ThreatMatch)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x0000003fu) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_internal_mutable_threat()->::mozilla::safebrowsing::ThreatEntry::MergeFrom(
+ from._internal_threat());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_internal_mutable_threat_entry_metadata()->::mozilla::safebrowsing::ThreatEntryMetadata::MergeFrom(
+ from._internal_threat_entry_metadata());
+ }
+ if (cached_has_bits & 0x00000004u) {
+ _this->_internal_mutable_cache_duration()->::mozilla::safebrowsing::Duration::MergeFrom(
+ from._internal_cache_duration());
+ }
+ if (cached_has_bits & 0x00000008u) {
+ _this->_impl_.threat_type_ = from._impl_.threat_type_;
+ }
+ if (cached_has_bits & 0x00000010u) {
+ _this->_impl_.platform_type_ = from._impl_.platform_type_;
+ }
+ if (cached_has_bits & 0x00000020u) {
+ _this->_impl_.threat_entry_type_ = from._impl_.threat_entry_type_;
+ }
+ _this->_impl_._has_bits_[0] |= cached_has_bits;
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void ThreatMatch::CopyFrom(const ThreatMatch& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.ThreatMatch)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool ThreatMatch::IsInitialized() const {
+ return true;
+}
+
+void ThreatMatch::InternalSwap(ThreatMatch* other) {
+ using std::swap;
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+ PROTOBUF_FIELD_OFFSET(ThreatMatch, _impl_.threat_entry_type_)
+ + sizeof(ThreatMatch::_impl_.threat_entry_type_)
+ - PROTOBUF_FIELD_OFFSET(ThreatMatch, _impl_.threat_)>(
+ reinterpret_cast<char*>(&_impl_.threat_),
+ reinterpret_cast<char*>(&other->_impl_.threat_));
+}
+
+std::string ThreatMatch::GetTypeName() const {
+ return "mozilla.safebrowsing.ThreatMatch";
+}
+
+
+// ===================================================================
+
+class FindThreatMatchesRequest::_Internal {
+ public:
+ using HasBits = decltype(std::declval<FindThreatMatchesRequest>()._impl_._has_bits_);
+ static const ::mozilla::safebrowsing::ClientInfo& client(const FindThreatMatchesRequest* msg);
+ static void set_has_client(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static const ::mozilla::safebrowsing::ThreatInfo& threat_info(const FindThreatMatchesRequest* msg);
+ static void set_has_threat_info(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+};
+
+const ::mozilla::safebrowsing::ClientInfo&
+FindThreatMatchesRequest::_Internal::client(const FindThreatMatchesRequest* msg) {
+ return *msg->_impl_.client_;
+}
+const ::mozilla::safebrowsing::ThreatInfo&
+FindThreatMatchesRequest::_Internal::threat_info(const FindThreatMatchesRequest* msg) {
+ return *msg->_impl_.threat_info_;
+}
+FindThreatMatchesRequest::FindThreatMatchesRequest(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.FindThreatMatchesRequest)
+}
+FindThreatMatchesRequest::FindThreatMatchesRequest(const FindThreatMatchesRequest& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ FindThreatMatchesRequest* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.client_){nullptr}
+ , decltype(_impl_.threat_info_){nullptr}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ if (from._internal_has_client()) {
+ _this->_impl_.client_ = new ::mozilla::safebrowsing::ClientInfo(*from._impl_.client_);
+ }
+ if (from._internal_has_threat_info()) {
+ _this->_impl_.threat_info_ = new ::mozilla::safebrowsing::ThreatInfo(*from._impl_.threat_info_);
+ }
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.FindThreatMatchesRequest)
+}
+
+inline void FindThreatMatchesRequest::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.client_){nullptr}
+ , decltype(_impl_.threat_info_){nullptr}
+ };
+}
+
+FindThreatMatchesRequest::~FindThreatMatchesRequest() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.FindThreatMatchesRequest)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void FindThreatMatchesRequest::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ if (this != internal_default_instance()) delete _impl_.client_;
+ if (this != internal_default_instance()) delete _impl_.threat_info_;
+}
+
+void FindThreatMatchesRequest::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void FindThreatMatchesRequest::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.FindThreatMatchesRequest)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ GOOGLE_DCHECK(_impl_.client_ != nullptr);
+ _impl_.client_->Clear();
+ }
+ if (cached_has_bits & 0x00000002u) {
+ GOOGLE_DCHECK(_impl_.threat_info_ != nullptr);
+ _impl_.threat_info_->Clear();
+ }
+ }
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* FindThreatMatchesRequest::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional .mozilla.safebrowsing.ClientInfo client = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+ ptr = ctx->ParseMessage(_internal_mutable_client(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.ThreatInfo threat_info = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+ ptr = ctx->ParseMessage(_internal_mutable_threat_info(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* FindThreatMatchesRequest::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.FindThreatMatchesRequest)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional .mozilla.safebrowsing.ClientInfo client = 1;
+ if (cached_has_bits & 0x00000001u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(1, _Internal::client(this),
+ _Internal::client(this).GetCachedSize(), target, stream);
+ }
+
+ // optional .mozilla.safebrowsing.ThreatInfo threat_info = 2;
+ if (cached_has_bits & 0x00000002u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(2, _Internal::threat_info(this),
+ _Internal::threat_info(this).GetCachedSize(), target, stream);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.FindThreatMatchesRequest)
+ return target;
+}
+
+size_t FindThreatMatchesRequest::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.FindThreatMatchesRequest)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ // optional .mozilla.safebrowsing.ClientInfo client = 1;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.client_);
+ }
+
+ // optional .mozilla.safebrowsing.ThreatInfo threat_info = 2;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.threat_info_);
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void FindThreatMatchesRequest::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const FindThreatMatchesRequest*>(
+ &from));
+}
+
+void FindThreatMatchesRequest::MergeFrom(const FindThreatMatchesRequest& from) {
+ FindThreatMatchesRequest* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.FindThreatMatchesRequest)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_internal_mutable_client()->::mozilla::safebrowsing::ClientInfo::MergeFrom(
+ from._internal_client());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_internal_mutable_threat_info()->::mozilla::safebrowsing::ThreatInfo::MergeFrom(
+ from._internal_threat_info());
+ }
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void FindThreatMatchesRequest::CopyFrom(const FindThreatMatchesRequest& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.FindThreatMatchesRequest)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool FindThreatMatchesRequest::IsInitialized() const {
+ return true;
+}
+
+void FindThreatMatchesRequest::InternalSwap(FindThreatMatchesRequest* other) {
+ using std::swap;
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+ PROTOBUF_FIELD_OFFSET(FindThreatMatchesRequest, _impl_.threat_info_)
+ + sizeof(FindThreatMatchesRequest::_impl_.threat_info_)
+ - PROTOBUF_FIELD_OFFSET(FindThreatMatchesRequest, _impl_.client_)>(
+ reinterpret_cast<char*>(&_impl_.client_),
+ reinterpret_cast<char*>(&other->_impl_.client_));
+}
+
+std::string FindThreatMatchesRequest::GetTypeName() const {
+ return "mozilla.safebrowsing.FindThreatMatchesRequest";
+}
+
+
+// ===================================================================
+
+class FindThreatMatchesResponse::_Internal {
+ public:
+};
+
+FindThreatMatchesResponse::FindThreatMatchesResponse(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.FindThreatMatchesResponse)
+}
+FindThreatMatchesResponse::FindThreatMatchesResponse(const FindThreatMatchesResponse& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ FindThreatMatchesResponse* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_.matches_){from._impl_.matches_}
+ , /*decltype(_impl_._cached_size_)*/{}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.FindThreatMatchesResponse)
+}
+
+inline void FindThreatMatchesResponse::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_.matches_){arena}
+ , /*decltype(_impl_._cached_size_)*/{}
+ };
+}
+
+FindThreatMatchesResponse::~FindThreatMatchesResponse() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.FindThreatMatchesResponse)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void FindThreatMatchesResponse::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.matches_.~RepeatedPtrField();
+}
+
+void FindThreatMatchesResponse::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void FindThreatMatchesResponse::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.FindThreatMatchesResponse)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ _impl_.matches_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* FindThreatMatchesResponse::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // repeated .mozilla.safebrowsing.ThreatMatch matches = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+ ptr -= 1;
+ do {
+ ptr += 1;
+ ptr = ctx->ParseMessage(_internal_add_matches(), ptr);
+ CHK_(ptr);
+ if (!ctx->DataAvailable(ptr)) break;
+ } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<10>(ptr));
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* FindThreatMatchesResponse::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.FindThreatMatchesResponse)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ // repeated .mozilla.safebrowsing.ThreatMatch matches = 1;
+ for (unsigned i = 0,
+ n = static_cast<unsigned>(this->_internal_matches_size()); i < n; i++) {
+ const auto& repfield = this->_internal_matches(i);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(1, repfield, repfield.GetCachedSize(), target, stream);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.FindThreatMatchesResponse)
+ return target;
+}
+
+size_t FindThreatMatchesResponse::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.FindThreatMatchesResponse)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ // repeated .mozilla.safebrowsing.ThreatMatch matches = 1;
+ total_size += 1UL * this->_internal_matches_size();
+ for (const auto& msg : this->_impl_.matches_) {
+ total_size +=
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void FindThreatMatchesResponse::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const FindThreatMatchesResponse*>(
+ &from));
+}
+
+void FindThreatMatchesResponse::MergeFrom(const FindThreatMatchesResponse& from) {
+ FindThreatMatchesResponse* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.FindThreatMatchesResponse)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ _this->_impl_.matches_.MergeFrom(from._impl_.matches_);
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void FindThreatMatchesResponse::CopyFrom(const FindThreatMatchesResponse& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.FindThreatMatchesResponse)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool FindThreatMatchesResponse::IsInitialized() const {
+ return true;
+}
+
+void FindThreatMatchesResponse::InternalSwap(FindThreatMatchesResponse* other) {
+ using std::swap;
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ _impl_.matches_.InternalSwap(&other->_impl_.matches_);
+}
+
+std::string FindThreatMatchesResponse::GetTypeName() const {
+ return "mozilla.safebrowsing.FindThreatMatchesResponse";
+}
+
+
+// ===================================================================
+
+class FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::_Internal {
+ public:
+ using HasBits = decltype(std::declval<FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints>()._impl_._has_bits_);
+ static void set_has_max_update_entries(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+ static void set_has_max_database_entries(HasBits* has_bits) {
+ (*has_bits)[0] |= 4u;
+ }
+ static void set_has_region(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+};
+
+FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints)
+}
+FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints(const FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.supported_compressions_){from._impl_.supported_compressions_}
+ , decltype(_impl_.region_){}
+ , decltype(_impl_.max_update_entries_){}
+ , decltype(_impl_.max_database_entries_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ _impl_.region_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.region_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (from._internal_has_region()) {
+ _this->_impl_.region_.Set(from._internal_region(),
+ _this->GetArenaForAllocation());
+ }
+ ::memcpy(&_impl_.max_update_entries_, &from._impl_.max_update_entries_,
+ static_cast<size_t>(reinterpret_cast<char*>(&_impl_.max_database_entries_) -
+ reinterpret_cast<char*>(&_impl_.max_update_entries_)) + sizeof(_impl_.max_database_entries_));
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints)
+}
+
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.supported_compressions_){arena}
+ , decltype(_impl_.region_){}
+ , decltype(_impl_.max_update_entries_){0}
+ , decltype(_impl_.max_database_entries_){0}
+ };
+ _impl_.region_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.region_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::~FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.supported_compressions_.~RepeatedField();
+ _impl_.region_.Destroy();
+}
+
+void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ _impl_.supported_compressions_.Clear();
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000001u) {
+ _impl_.region_.ClearNonDefaultToEmpty();
+ }
+ if (cached_has_bits & 0x00000006u) {
+ ::memset(&_impl_.max_update_entries_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&_impl_.max_database_entries_) -
+ reinterpret_cast<char*>(&_impl_.max_update_entries_)) + sizeof(_impl_.max_database_entries_));
+ }
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional int32 max_update_entries = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+ _Internal::set_has_max_update_entries(&has_bits);
+ _impl_.max_update_entries_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional int32 max_database_entries = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+ _Internal::set_has_max_database_entries(&has_bits);
+ _impl_.max_database_entries_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional string region = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+ auto str = _internal_mutable_region();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // repeated .mozilla.safebrowsing.CompressionType supported_compressions = 4;
+ case 4:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 32)) {
+ ptr -= 1;
+ do {
+ ptr += 1;
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::CompressionType_IsValid(val))) {
+ _internal_add_supported_compressions(static_cast<::mozilla::safebrowsing::CompressionType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(4, val, mutable_unknown_fields());
+ }
+ if (!ctx->DataAvailable(ptr)) break;
+ } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<32>(ptr));
+ } else if (static_cast<uint8_t>(tag) == 34) {
+ ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedEnumParser<std::string>(_internal_mutable_supported_compressions(), ptr, ctx, ::mozilla::safebrowsing::CompressionType_IsValid, &_internal_metadata_, 4);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional int32 max_update_entries = 1;
+ if (cached_has_bits & 0x00000002u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteInt32ToArray(1, this->_internal_max_update_entries(), target);
+ }
+
+ // optional int32 max_database_entries = 2;
+ if (cached_has_bits & 0x00000004u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteInt32ToArray(2, this->_internal_max_database_entries(), target);
+ }
+
+ // optional string region = 3;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->WriteStringMaybeAliased(
+ 3, this->_internal_region(), target);
+ }
+
+ // repeated .mozilla.safebrowsing.CompressionType supported_compressions = 4;
+ for (int i = 0, n = this->_internal_supported_compressions_size(); i < n; i++) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 4, this->_internal_supported_compressions(i), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints)
+ return target;
+}
+
+size_t FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ // repeated .mozilla.safebrowsing.CompressionType supported_compressions = 4;
+ {
+ size_t data_size = 0;
+ unsigned int count = static_cast<unsigned int>(this->_internal_supported_compressions_size());for (unsigned int i = 0; i < count; i++) {
+ data_size += ::_pbi::WireFormatLite::EnumSize(
+ this->_internal_supported_compressions(static_cast<int>(i)));
+ }
+ total_size += (1UL * count) + data_size;
+ }
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000007u) {
+ // optional string region = 3;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+ this->_internal_region());
+ }
+
+ // optional int32 max_update_entries = 1;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_max_update_entries());
+ }
+
+ // optional int32 max_database_entries = 2;
+ if (cached_has_bits & 0x00000004u) {
+ total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_max_database_entries());
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints*>(
+ &from));
+}
+
+void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::MergeFrom(const FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints& from) {
+ FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ _this->_impl_.supported_compressions_.MergeFrom(from._impl_.supported_compressions_);
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000007u) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_internal_set_region(from._internal_region());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_impl_.max_update_entries_ = from._impl_.max_update_entries_;
+ }
+ if (cached_has_bits & 0x00000004u) {
+ _this->_impl_.max_database_entries_ = from._impl_.max_database_entries_;
+ }
+ _this->_impl_._has_bits_[0] |= cached_has_bits;
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::CopyFrom(const FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::IsInitialized() const {
+ return true;
+}
+
+void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::InternalSwap(FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* other) {
+ using std::swap;
+ auto* lhs_arena = GetArenaForAllocation();
+ auto* rhs_arena = other->GetArenaForAllocation();
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ _impl_.supported_compressions_.InternalSwap(&other->_impl_.supported_compressions_);
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+ &_impl_.region_, lhs_arena,
+ &other->_impl_.region_, rhs_arena
+ );
+ ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+ PROTOBUF_FIELD_OFFSET(FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints, _impl_.max_database_entries_)
+ + sizeof(FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::_impl_.max_database_entries_)
+ - PROTOBUF_FIELD_OFFSET(FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints, _impl_.max_update_entries_)>(
+ reinterpret_cast<char*>(&_impl_.max_update_entries_),
+ reinterpret_cast<char*>(&other->_impl_.max_update_entries_));
+}
+
+std::string FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::GetTypeName() const {
+ return "mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints";
+}
+
+
+// ===================================================================
+
+class FetchThreatListUpdatesRequest_ListUpdateRequest::_Internal {
+ public:
+ using HasBits = decltype(std::declval<FetchThreatListUpdatesRequest_ListUpdateRequest>()._impl_._has_bits_);
+ static void set_has_threat_type(HasBits* has_bits) {
+ (*has_bits)[0] |= 4u;
+ }
+ static void set_has_platform_type(HasBits* has_bits) {
+ (*has_bits)[0] |= 8u;
+ }
+ static void set_has_threat_entry_type(HasBits* has_bits) {
+ (*has_bits)[0] |= 16u;
+ }
+ static void set_has_state(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static const ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints& constraints(const FetchThreatListUpdatesRequest_ListUpdateRequest* msg);
+ static void set_has_constraints(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+};
+
+const ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints&
+FetchThreatListUpdatesRequest_ListUpdateRequest::_Internal::constraints(const FetchThreatListUpdatesRequest_ListUpdateRequest* msg) {
+ return *msg->_impl_.constraints_;
+}
+FetchThreatListUpdatesRequest_ListUpdateRequest::FetchThreatListUpdatesRequest_ListUpdateRequest(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest)
+}
+FetchThreatListUpdatesRequest_ListUpdateRequest::FetchThreatListUpdatesRequest_ListUpdateRequest(const FetchThreatListUpdatesRequest_ListUpdateRequest& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ FetchThreatListUpdatesRequest_ListUpdateRequest* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.state_){}
+ , decltype(_impl_.constraints_){nullptr}
+ , decltype(_impl_.threat_type_){}
+ , decltype(_impl_.platform_type_){}
+ , decltype(_impl_.threat_entry_type_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ _impl_.state_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.state_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (from._internal_has_state()) {
+ _this->_impl_.state_.Set(from._internal_state(),
+ _this->GetArenaForAllocation());
+ }
+ if (from._internal_has_constraints()) {
+ _this->_impl_.constraints_ = new ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints(*from._impl_.constraints_);
+ }
+ ::memcpy(&_impl_.threat_type_, &from._impl_.threat_type_,
+ static_cast<size_t>(reinterpret_cast<char*>(&_impl_.threat_entry_type_) -
+ reinterpret_cast<char*>(&_impl_.threat_type_)) + sizeof(_impl_.threat_entry_type_));
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest)
+}
+
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.state_){}
+ , decltype(_impl_.constraints_){nullptr}
+ , decltype(_impl_.threat_type_){0}
+ , decltype(_impl_.platform_type_){0}
+ , decltype(_impl_.threat_entry_type_){0}
+ };
+ _impl_.state_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.state_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+FetchThreatListUpdatesRequest_ListUpdateRequest::~FetchThreatListUpdatesRequest_ListUpdateRequest() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.state_.Destroy();
+ if (this != internal_default_instance()) delete _impl_.constraints_;
+}
+
+void FetchThreatListUpdatesRequest_ListUpdateRequest::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void FetchThreatListUpdatesRequest_ListUpdateRequest::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ _impl_.state_.ClearNonDefaultToEmpty();
+ }
+ if (cached_has_bits & 0x00000002u) {
+ GOOGLE_DCHECK(_impl_.constraints_ != nullptr);
+ _impl_.constraints_->Clear();
+ }
+ }
+ if (cached_has_bits & 0x0000001cu) {
+ ::memset(&_impl_.threat_type_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&_impl_.threat_entry_type_) -
+ reinterpret_cast<char*>(&_impl_.threat_type_)) + sizeof(_impl_.threat_entry_type_));
+ }
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* FetchThreatListUpdatesRequest_ListUpdateRequest::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::ThreatType_IsValid(val))) {
+ _internal_set_threat_type(static_cast<::mozilla::safebrowsing::ThreatType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(1, val, mutable_unknown_fields());
+ }
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::PlatformType_IsValid(val))) {
+ _internal_set_platform_type(static_cast<::mozilla::safebrowsing::PlatformType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(2, val, mutable_unknown_fields());
+ }
+ } else
+ goto handle_unusual;
+ continue;
+ // optional bytes state = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+ auto str = _internal_mutable_state();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints constraints = 4;
+ case 4:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 34)) {
+ ptr = ctx->ParseMessage(_internal_mutable_constraints(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 5;
+ case 5:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 40)) {
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::ThreatEntryType_IsValid(val))) {
+ _internal_set_threat_entry_type(static_cast<::mozilla::safebrowsing::ThreatEntryType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(5, val, mutable_unknown_fields());
+ }
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* FetchThreatListUpdatesRequest_ListUpdateRequest::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ if (cached_has_bits & 0x00000004u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 1, this->_internal_threat_type(), target);
+ }
+
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+ if (cached_has_bits & 0x00000008u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 2, this->_internal_platform_type(), target);
+ }
+
+ // optional bytes state = 3;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->WriteBytesMaybeAliased(
+ 3, this->_internal_state(), target);
+ }
+
+ // optional .mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints constraints = 4;
+ if (cached_has_bits & 0x00000002u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(4, _Internal::constraints(this),
+ _Internal::constraints(this).GetCachedSize(), target, stream);
+ }
+
+ // optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 5;
+ if (cached_has_bits & 0x00000010u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 5, this->_internal_threat_entry_type(), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest)
+ return target;
+}
+
+size_t FetchThreatListUpdatesRequest_ListUpdateRequest::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x0000001fu) {
+ // optional bytes state = 3;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+ this->_internal_state());
+ }
+
+ // optional .mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints constraints = 4;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.constraints_);
+ }
+
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ if (cached_has_bits & 0x00000004u) {
+ total_size += 1 +
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_threat_type());
+ }
+
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+ if (cached_has_bits & 0x00000008u) {
+ total_size += 1 +
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_platform_type());
+ }
+
+ // optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 5;
+ if (cached_has_bits & 0x00000010u) {
+ total_size += 1 +
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_threat_entry_type());
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void FetchThreatListUpdatesRequest_ListUpdateRequest::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const FetchThreatListUpdatesRequest_ListUpdateRequest*>(
+ &from));
+}
+
+void FetchThreatListUpdatesRequest_ListUpdateRequest::MergeFrom(const FetchThreatListUpdatesRequest_ListUpdateRequest& from) {
+ FetchThreatListUpdatesRequest_ListUpdateRequest* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x0000001fu) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_internal_set_state(from._internal_state());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_internal_mutable_constraints()->::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::MergeFrom(
+ from._internal_constraints());
+ }
+ if (cached_has_bits & 0x00000004u) {
+ _this->_impl_.threat_type_ = from._impl_.threat_type_;
+ }
+ if (cached_has_bits & 0x00000008u) {
+ _this->_impl_.platform_type_ = from._impl_.platform_type_;
+ }
+ if (cached_has_bits & 0x00000010u) {
+ _this->_impl_.threat_entry_type_ = from._impl_.threat_entry_type_;
+ }
+ _this->_impl_._has_bits_[0] |= cached_has_bits;
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void FetchThreatListUpdatesRequest_ListUpdateRequest::CopyFrom(const FetchThreatListUpdatesRequest_ListUpdateRequest& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool FetchThreatListUpdatesRequest_ListUpdateRequest::IsInitialized() const {
+ return true;
+}
+
+void FetchThreatListUpdatesRequest_ListUpdateRequest::InternalSwap(FetchThreatListUpdatesRequest_ListUpdateRequest* other) {
+ using std::swap;
+ auto* lhs_arena = GetArenaForAllocation();
+ auto* rhs_arena = other->GetArenaForAllocation();
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+ &_impl_.state_, lhs_arena,
+ &other->_impl_.state_, rhs_arena
+ );
+ ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+ PROTOBUF_FIELD_OFFSET(FetchThreatListUpdatesRequest_ListUpdateRequest, _impl_.threat_entry_type_)
+ + sizeof(FetchThreatListUpdatesRequest_ListUpdateRequest::_impl_.threat_entry_type_)
+ - PROTOBUF_FIELD_OFFSET(FetchThreatListUpdatesRequest_ListUpdateRequest, _impl_.constraints_)>(
+ reinterpret_cast<char*>(&_impl_.constraints_),
+ reinterpret_cast<char*>(&other->_impl_.constraints_));
+}
+
+std::string FetchThreatListUpdatesRequest_ListUpdateRequest::GetTypeName() const {
+ return "mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest";
+}
+
+
+// ===================================================================
+
+class FetchThreatListUpdatesRequest::_Internal {
+ public:
+ using HasBits = decltype(std::declval<FetchThreatListUpdatesRequest>()._impl_._has_bits_);
+ static const ::mozilla::safebrowsing::ClientInfo& client(const FetchThreatListUpdatesRequest* msg);
+ static void set_has_client(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static const ::mozilla::safebrowsing::ChromeClientInfo& chrome_client_info(const FetchThreatListUpdatesRequest* msg);
+ static void set_has_chrome_client_info(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+};
+
+const ::mozilla::safebrowsing::ClientInfo&
+FetchThreatListUpdatesRequest::_Internal::client(const FetchThreatListUpdatesRequest* msg) {
+ return *msg->_impl_.client_;
+}
+const ::mozilla::safebrowsing::ChromeClientInfo&
+FetchThreatListUpdatesRequest::_Internal::chrome_client_info(const FetchThreatListUpdatesRequest* msg) {
+ return *msg->_impl_.chrome_client_info_;
+}
+FetchThreatListUpdatesRequest::FetchThreatListUpdatesRequest(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.FetchThreatListUpdatesRequest)
+}
+FetchThreatListUpdatesRequest::FetchThreatListUpdatesRequest(const FetchThreatListUpdatesRequest& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ FetchThreatListUpdatesRequest* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.list_update_requests_){from._impl_.list_update_requests_}
+ , decltype(_impl_.client_){nullptr}
+ , decltype(_impl_.chrome_client_info_){nullptr}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ if (from._internal_has_client()) {
+ _this->_impl_.client_ = new ::mozilla::safebrowsing::ClientInfo(*from._impl_.client_);
+ }
+ if (from._internal_has_chrome_client_info()) {
+ _this->_impl_.chrome_client_info_ = new ::mozilla::safebrowsing::ChromeClientInfo(*from._impl_.chrome_client_info_);
+ }
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.FetchThreatListUpdatesRequest)
+}
+
+inline void FetchThreatListUpdatesRequest::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.list_update_requests_){arena}
+ , decltype(_impl_.client_){nullptr}
+ , decltype(_impl_.chrome_client_info_){nullptr}
+ };
+}
+
+FetchThreatListUpdatesRequest::~FetchThreatListUpdatesRequest() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.FetchThreatListUpdatesRequest)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void FetchThreatListUpdatesRequest::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.list_update_requests_.~RepeatedPtrField();
+ if (this != internal_default_instance()) delete _impl_.client_;
+ if (this != internal_default_instance()) delete _impl_.chrome_client_info_;
+}
+
+void FetchThreatListUpdatesRequest::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void FetchThreatListUpdatesRequest::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.FetchThreatListUpdatesRequest)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ _impl_.list_update_requests_.Clear();
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ GOOGLE_DCHECK(_impl_.client_ != nullptr);
+ _impl_.client_->Clear();
+ }
+ if (cached_has_bits & 0x00000002u) {
+ GOOGLE_DCHECK(_impl_.chrome_client_info_ != nullptr);
+ _impl_.chrome_client_info_->Clear();
+ }
+ }
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* FetchThreatListUpdatesRequest::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional .mozilla.safebrowsing.ClientInfo client = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+ ptr = ctx->ParseMessage(_internal_mutable_client(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // repeated .mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest list_update_requests = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+ ptr -= 1;
+ do {
+ ptr += 1;
+ ptr = ctx->ParseMessage(_internal_add_list_update_requests(), ptr);
+ CHK_(ptr);
+ if (!ctx->DataAvailable(ptr)) break;
+ } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<26>(ptr));
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.ChromeClientInfo chrome_client_info = 4;
+ case 4:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 34)) {
+ ptr = ctx->ParseMessage(_internal_mutable_chrome_client_info(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* FetchThreatListUpdatesRequest::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.FetchThreatListUpdatesRequest)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional .mozilla.safebrowsing.ClientInfo client = 1;
+ if (cached_has_bits & 0x00000001u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(1, _Internal::client(this),
+ _Internal::client(this).GetCachedSize(), target, stream);
+ }
+
+ // repeated .mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest list_update_requests = 3;
+ for (unsigned i = 0,
+ n = static_cast<unsigned>(this->_internal_list_update_requests_size()); i < n; i++) {
+ const auto& repfield = this->_internal_list_update_requests(i);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(3, repfield, repfield.GetCachedSize(), target, stream);
+ }
+
+ // optional .mozilla.safebrowsing.ChromeClientInfo chrome_client_info = 4;
+ if (cached_has_bits & 0x00000002u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(4, _Internal::chrome_client_info(this),
+ _Internal::chrome_client_info(this).GetCachedSize(), target, stream);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.FetchThreatListUpdatesRequest)
+ return target;
+}
+
+size_t FetchThreatListUpdatesRequest::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.FetchThreatListUpdatesRequest)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ // repeated .mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest list_update_requests = 3;
+ total_size += 1UL * this->_internal_list_update_requests_size();
+ for (const auto& msg : this->_impl_.list_update_requests_) {
+ total_size +=
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+ }
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ // optional .mozilla.safebrowsing.ClientInfo client = 1;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.client_);
+ }
+
+ // optional .mozilla.safebrowsing.ChromeClientInfo chrome_client_info = 4;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.chrome_client_info_);
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void FetchThreatListUpdatesRequest::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const FetchThreatListUpdatesRequest*>(
+ &from));
+}
+
+void FetchThreatListUpdatesRequest::MergeFrom(const FetchThreatListUpdatesRequest& from) {
+ FetchThreatListUpdatesRequest* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.FetchThreatListUpdatesRequest)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ _this->_impl_.list_update_requests_.MergeFrom(from._impl_.list_update_requests_);
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_internal_mutable_client()->::mozilla::safebrowsing::ClientInfo::MergeFrom(
+ from._internal_client());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_internal_mutable_chrome_client_info()->::mozilla::safebrowsing::ChromeClientInfo::MergeFrom(
+ from._internal_chrome_client_info());
+ }
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void FetchThreatListUpdatesRequest::CopyFrom(const FetchThreatListUpdatesRequest& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.FetchThreatListUpdatesRequest)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool FetchThreatListUpdatesRequest::IsInitialized() const {
+ return true;
+}
+
+void FetchThreatListUpdatesRequest::InternalSwap(FetchThreatListUpdatesRequest* other) {
+ using std::swap;
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ _impl_.list_update_requests_.InternalSwap(&other->_impl_.list_update_requests_);
+ ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+ PROTOBUF_FIELD_OFFSET(FetchThreatListUpdatesRequest, _impl_.chrome_client_info_)
+ + sizeof(FetchThreatListUpdatesRequest::_impl_.chrome_client_info_)
+ - PROTOBUF_FIELD_OFFSET(FetchThreatListUpdatesRequest, _impl_.client_)>(
+ reinterpret_cast<char*>(&_impl_.client_),
+ reinterpret_cast<char*>(&other->_impl_.client_));
+}
+
+std::string FetchThreatListUpdatesRequest::GetTypeName() const {
+ return "mozilla.safebrowsing.FetchThreatListUpdatesRequest";
+}
+
+
+// ===================================================================
+
+class FetchThreatListUpdatesResponse_ListUpdateResponse::_Internal {
+ public:
+ using HasBits = decltype(std::declval<FetchThreatListUpdatesResponse_ListUpdateResponse>()._impl_._has_bits_);
+ static void set_has_threat_type(HasBits* has_bits) {
+ (*has_bits)[0] |= 4u;
+ }
+ static void set_has_threat_entry_type(HasBits* has_bits) {
+ (*has_bits)[0] |= 8u;
+ }
+ static void set_has_platform_type(HasBits* has_bits) {
+ (*has_bits)[0] |= 16u;
+ }
+ static void set_has_response_type(HasBits* has_bits) {
+ (*has_bits)[0] |= 32u;
+ }
+ static void set_has_new_client_state(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static const ::mozilla::safebrowsing::Checksum& checksum(const FetchThreatListUpdatesResponse_ListUpdateResponse* msg);
+ static void set_has_checksum(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+};
+
+const ::mozilla::safebrowsing::Checksum&
+FetchThreatListUpdatesResponse_ListUpdateResponse::_Internal::checksum(const FetchThreatListUpdatesResponse_ListUpdateResponse* msg) {
+ return *msg->_impl_.checksum_;
+}
+FetchThreatListUpdatesResponse_ListUpdateResponse::FetchThreatListUpdatesResponse_ListUpdateResponse(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse)
+}
+FetchThreatListUpdatesResponse_ListUpdateResponse::FetchThreatListUpdatesResponse_ListUpdateResponse(const FetchThreatListUpdatesResponse_ListUpdateResponse& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ FetchThreatListUpdatesResponse_ListUpdateResponse* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.additions_){from._impl_.additions_}
+ , decltype(_impl_.removals_){from._impl_.removals_}
+ , decltype(_impl_.new_client_state_){}
+ , decltype(_impl_.checksum_){nullptr}
+ , decltype(_impl_.threat_type_){}
+ , decltype(_impl_.threat_entry_type_){}
+ , decltype(_impl_.platform_type_){}
+ , decltype(_impl_.response_type_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ _impl_.new_client_state_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.new_client_state_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (from._internal_has_new_client_state()) {
+ _this->_impl_.new_client_state_.Set(from._internal_new_client_state(),
+ _this->GetArenaForAllocation());
+ }
+ if (from._internal_has_checksum()) {
+ _this->_impl_.checksum_ = new ::mozilla::safebrowsing::Checksum(*from._impl_.checksum_);
+ }
+ ::memcpy(&_impl_.threat_type_, &from._impl_.threat_type_,
+ static_cast<size_t>(reinterpret_cast<char*>(&_impl_.response_type_) -
+ reinterpret_cast<char*>(&_impl_.threat_type_)) + sizeof(_impl_.response_type_));
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse)
+}
+
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.additions_){arena}
+ , decltype(_impl_.removals_){arena}
+ , decltype(_impl_.new_client_state_){}
+ , decltype(_impl_.checksum_){nullptr}
+ , decltype(_impl_.threat_type_){0}
+ , decltype(_impl_.threat_entry_type_){0}
+ , decltype(_impl_.platform_type_){0}
+ , decltype(_impl_.response_type_){0}
+ };
+ _impl_.new_client_state_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.new_client_state_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+FetchThreatListUpdatesResponse_ListUpdateResponse::~FetchThreatListUpdatesResponse_ListUpdateResponse() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.additions_.~RepeatedPtrField();
+ _impl_.removals_.~RepeatedPtrField();
+ _impl_.new_client_state_.Destroy();
+ if (this != internal_default_instance()) delete _impl_.checksum_;
+}
+
+void FetchThreatListUpdatesResponse_ListUpdateResponse::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void FetchThreatListUpdatesResponse_ListUpdateResponse::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ _impl_.additions_.Clear();
+ _impl_.removals_.Clear();
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ _impl_.new_client_state_.ClearNonDefaultToEmpty();
+ }
+ if (cached_has_bits & 0x00000002u) {
+ GOOGLE_DCHECK(_impl_.checksum_ != nullptr);
+ _impl_.checksum_->Clear();
+ }
+ }
+ if (cached_has_bits & 0x0000003cu) {
+ ::memset(&_impl_.threat_type_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&_impl_.response_type_) -
+ reinterpret_cast<char*>(&_impl_.threat_type_)) + sizeof(_impl_.response_type_));
+ }
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* FetchThreatListUpdatesResponse_ListUpdateResponse::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::ThreatType_IsValid(val))) {
+ _internal_set_threat_type(static_cast<::mozilla::safebrowsing::ThreatType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(1, val, mutable_unknown_fields());
+ }
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::ThreatEntryType_IsValid(val))) {
+ _internal_set_threat_entry_type(static_cast<::mozilla::safebrowsing::ThreatEntryType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(2, val, mutable_unknown_fields());
+ }
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 24)) {
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::PlatformType_IsValid(val))) {
+ _internal_set_platform_type(static_cast<::mozilla::safebrowsing::PlatformType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(3, val, mutable_unknown_fields());
+ }
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.ResponseType response_type = 4;
+ case 4:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 32)) {
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_IsValid(val))) {
+ _internal_set_response_type(static_cast<::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(4, val, mutable_unknown_fields());
+ }
+ } else
+ goto handle_unusual;
+ continue;
+ // repeated .mozilla.safebrowsing.ThreatEntrySet additions = 5;
+ case 5:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 42)) {
+ ptr -= 1;
+ do {
+ ptr += 1;
+ ptr = ctx->ParseMessage(_internal_add_additions(), ptr);
+ CHK_(ptr);
+ if (!ctx->DataAvailable(ptr)) break;
+ } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<42>(ptr));
+ } else
+ goto handle_unusual;
+ continue;
+ // repeated .mozilla.safebrowsing.ThreatEntrySet removals = 6;
+ case 6:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 50)) {
+ ptr -= 1;
+ do {
+ ptr += 1;
+ ptr = ctx->ParseMessage(_internal_add_removals(), ptr);
+ CHK_(ptr);
+ if (!ctx->DataAvailable(ptr)) break;
+ } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<50>(ptr));
+ } else
+ goto handle_unusual;
+ continue;
+ // optional bytes new_client_state = 7;
+ case 7:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 58)) {
+ auto str = _internal_mutable_new_client_state();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.Checksum checksum = 8;
+ case 8:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 66)) {
+ ptr = ctx->ParseMessage(_internal_mutable_checksum(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* FetchThreatListUpdatesResponse_ListUpdateResponse::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ if (cached_has_bits & 0x00000004u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 1, this->_internal_threat_type(), target);
+ }
+
+ // optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 2;
+ if (cached_has_bits & 0x00000008u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 2, this->_internal_threat_entry_type(), target);
+ }
+
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 3;
+ if (cached_has_bits & 0x00000010u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 3, this->_internal_platform_type(), target);
+ }
+
+ // optional .mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.ResponseType response_type = 4;
+ if (cached_has_bits & 0x00000020u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 4, this->_internal_response_type(), target);
+ }
+
+ // repeated .mozilla.safebrowsing.ThreatEntrySet additions = 5;
+ for (unsigned i = 0,
+ n = static_cast<unsigned>(this->_internal_additions_size()); i < n; i++) {
+ const auto& repfield = this->_internal_additions(i);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(5, repfield, repfield.GetCachedSize(), target, stream);
+ }
+
+ // repeated .mozilla.safebrowsing.ThreatEntrySet removals = 6;
+ for (unsigned i = 0,
+ n = static_cast<unsigned>(this->_internal_removals_size()); i < n; i++) {
+ const auto& repfield = this->_internal_removals(i);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(6, repfield, repfield.GetCachedSize(), target, stream);
+ }
+
+ // optional bytes new_client_state = 7;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->WriteBytesMaybeAliased(
+ 7, this->_internal_new_client_state(), target);
+ }
+
+ // optional .mozilla.safebrowsing.Checksum checksum = 8;
+ if (cached_has_bits & 0x00000002u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(8, _Internal::checksum(this),
+ _Internal::checksum(this).GetCachedSize(), target, stream);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse)
+ return target;
+}
+
+size_t FetchThreatListUpdatesResponse_ListUpdateResponse::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ // repeated .mozilla.safebrowsing.ThreatEntrySet additions = 5;
+ total_size += 1UL * this->_internal_additions_size();
+ for (const auto& msg : this->_impl_.additions_) {
+ total_size +=
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+ }
+
+ // repeated .mozilla.safebrowsing.ThreatEntrySet removals = 6;
+ total_size += 1UL * this->_internal_removals_size();
+ for (const auto& msg : this->_impl_.removals_) {
+ total_size +=
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+ }
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x0000003fu) {
+ // optional bytes new_client_state = 7;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+ this->_internal_new_client_state());
+ }
+
+ // optional .mozilla.safebrowsing.Checksum checksum = 8;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.checksum_);
+ }
+
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ if (cached_has_bits & 0x00000004u) {
+ total_size += 1 +
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_threat_type());
+ }
+
+ // optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 2;
+ if (cached_has_bits & 0x00000008u) {
+ total_size += 1 +
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_threat_entry_type());
+ }
+
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 3;
+ if (cached_has_bits & 0x00000010u) {
+ total_size += 1 +
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_platform_type());
+ }
+
+ // optional .mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.ResponseType response_type = 4;
+ if (cached_has_bits & 0x00000020u) {
+ total_size += 1 +
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_response_type());
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void FetchThreatListUpdatesResponse_ListUpdateResponse::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const FetchThreatListUpdatesResponse_ListUpdateResponse*>(
+ &from));
+}
+
+void FetchThreatListUpdatesResponse_ListUpdateResponse::MergeFrom(const FetchThreatListUpdatesResponse_ListUpdateResponse& from) {
+ FetchThreatListUpdatesResponse_ListUpdateResponse* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ _this->_impl_.additions_.MergeFrom(from._impl_.additions_);
+ _this->_impl_.removals_.MergeFrom(from._impl_.removals_);
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x0000003fu) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_internal_set_new_client_state(from._internal_new_client_state());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_internal_mutable_checksum()->::mozilla::safebrowsing::Checksum::MergeFrom(
+ from._internal_checksum());
+ }
+ if (cached_has_bits & 0x00000004u) {
+ _this->_impl_.threat_type_ = from._impl_.threat_type_;
+ }
+ if (cached_has_bits & 0x00000008u) {
+ _this->_impl_.threat_entry_type_ = from._impl_.threat_entry_type_;
+ }
+ if (cached_has_bits & 0x00000010u) {
+ _this->_impl_.platform_type_ = from._impl_.platform_type_;
+ }
+ if (cached_has_bits & 0x00000020u) {
+ _this->_impl_.response_type_ = from._impl_.response_type_;
+ }
+ _this->_impl_._has_bits_[0] |= cached_has_bits;
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void FetchThreatListUpdatesResponse_ListUpdateResponse::CopyFrom(const FetchThreatListUpdatesResponse_ListUpdateResponse& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool FetchThreatListUpdatesResponse_ListUpdateResponse::IsInitialized() const {
+ return true;
+}
+
+void FetchThreatListUpdatesResponse_ListUpdateResponse::InternalSwap(FetchThreatListUpdatesResponse_ListUpdateResponse* other) {
+ using std::swap;
+ auto* lhs_arena = GetArenaForAllocation();
+ auto* rhs_arena = other->GetArenaForAllocation();
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ _impl_.additions_.InternalSwap(&other->_impl_.additions_);
+ _impl_.removals_.InternalSwap(&other->_impl_.removals_);
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+ &_impl_.new_client_state_, lhs_arena,
+ &other->_impl_.new_client_state_, rhs_arena
+ );
+ ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+ PROTOBUF_FIELD_OFFSET(FetchThreatListUpdatesResponse_ListUpdateResponse, _impl_.response_type_)
+ + sizeof(FetchThreatListUpdatesResponse_ListUpdateResponse::_impl_.response_type_)
+ - PROTOBUF_FIELD_OFFSET(FetchThreatListUpdatesResponse_ListUpdateResponse, _impl_.checksum_)>(
+ reinterpret_cast<char*>(&_impl_.checksum_),
+ reinterpret_cast<char*>(&other->_impl_.checksum_));
+}
+
+std::string FetchThreatListUpdatesResponse_ListUpdateResponse::GetTypeName() const {
+ return "mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse";
+}
+
+
+// ===================================================================
+
+class FetchThreatListUpdatesResponse::_Internal {
+ public:
+ using HasBits = decltype(std::declval<FetchThreatListUpdatesResponse>()._impl_._has_bits_);
+ static const ::mozilla::safebrowsing::Duration& minimum_wait_duration(const FetchThreatListUpdatesResponse* msg);
+ static void set_has_minimum_wait_duration(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+};
+
+const ::mozilla::safebrowsing::Duration&
+FetchThreatListUpdatesResponse::_Internal::minimum_wait_duration(const FetchThreatListUpdatesResponse* msg) {
+ return *msg->_impl_.minimum_wait_duration_;
+}
+FetchThreatListUpdatesResponse::FetchThreatListUpdatesResponse(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.FetchThreatListUpdatesResponse)
+}
+FetchThreatListUpdatesResponse::FetchThreatListUpdatesResponse(const FetchThreatListUpdatesResponse& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ FetchThreatListUpdatesResponse* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.list_update_responses_){from._impl_.list_update_responses_}
+ , decltype(_impl_.minimum_wait_duration_){nullptr}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ if (from._internal_has_minimum_wait_duration()) {
+ _this->_impl_.minimum_wait_duration_ = new ::mozilla::safebrowsing::Duration(*from._impl_.minimum_wait_duration_);
+ }
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.FetchThreatListUpdatesResponse)
+}
+
+inline void FetchThreatListUpdatesResponse::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.list_update_responses_){arena}
+ , decltype(_impl_.minimum_wait_duration_){nullptr}
+ };
+}
+
+FetchThreatListUpdatesResponse::~FetchThreatListUpdatesResponse() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.FetchThreatListUpdatesResponse)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void FetchThreatListUpdatesResponse::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.list_update_responses_.~RepeatedPtrField();
+ if (this != internal_default_instance()) delete _impl_.minimum_wait_duration_;
+}
+
+void FetchThreatListUpdatesResponse::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void FetchThreatListUpdatesResponse::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.FetchThreatListUpdatesResponse)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ _impl_.list_update_responses_.Clear();
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000001u) {
+ GOOGLE_DCHECK(_impl_.minimum_wait_duration_ != nullptr);
+ _impl_.minimum_wait_duration_->Clear();
+ }
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* FetchThreatListUpdatesResponse::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // repeated .mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse list_update_responses = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+ ptr -= 1;
+ do {
+ ptr += 1;
+ ptr = ctx->ParseMessage(_internal_add_list_update_responses(), ptr);
+ CHK_(ptr);
+ if (!ctx->DataAvailable(ptr)) break;
+ } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<10>(ptr));
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.Duration minimum_wait_duration = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+ ptr = ctx->ParseMessage(_internal_mutable_minimum_wait_duration(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* FetchThreatListUpdatesResponse::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.FetchThreatListUpdatesResponse)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ // repeated .mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse list_update_responses = 1;
+ for (unsigned i = 0,
+ n = static_cast<unsigned>(this->_internal_list_update_responses_size()); i < n; i++) {
+ const auto& repfield = this->_internal_list_update_responses(i);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(1, repfield, repfield.GetCachedSize(), target, stream);
+ }
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional .mozilla.safebrowsing.Duration minimum_wait_duration = 2;
+ if (cached_has_bits & 0x00000001u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(2, _Internal::minimum_wait_duration(this),
+ _Internal::minimum_wait_duration(this).GetCachedSize(), target, stream);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.FetchThreatListUpdatesResponse)
+ return target;
+}
+
+size_t FetchThreatListUpdatesResponse::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.FetchThreatListUpdatesResponse)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ // repeated .mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse list_update_responses = 1;
+ total_size += 1UL * this->_internal_list_update_responses_size();
+ for (const auto& msg : this->_impl_.list_update_responses_) {
+ total_size +=
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+ }
+
+ // optional .mozilla.safebrowsing.Duration minimum_wait_duration = 2;
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.minimum_wait_duration_);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void FetchThreatListUpdatesResponse::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const FetchThreatListUpdatesResponse*>(
+ &from));
+}
+
+void FetchThreatListUpdatesResponse::MergeFrom(const FetchThreatListUpdatesResponse& from) {
+ FetchThreatListUpdatesResponse* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.FetchThreatListUpdatesResponse)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ _this->_impl_.list_update_responses_.MergeFrom(from._impl_.list_update_responses_);
+ if (from._internal_has_minimum_wait_duration()) {
+ _this->_internal_mutable_minimum_wait_duration()->::mozilla::safebrowsing::Duration::MergeFrom(
+ from._internal_minimum_wait_duration());
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void FetchThreatListUpdatesResponse::CopyFrom(const FetchThreatListUpdatesResponse& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.FetchThreatListUpdatesResponse)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool FetchThreatListUpdatesResponse::IsInitialized() const {
+ return true;
+}
+
+void FetchThreatListUpdatesResponse::InternalSwap(FetchThreatListUpdatesResponse* other) {
+ using std::swap;
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ _impl_.list_update_responses_.InternalSwap(&other->_impl_.list_update_responses_);
+ swap(_impl_.minimum_wait_duration_, other->_impl_.minimum_wait_duration_);
+}
+
+std::string FetchThreatListUpdatesResponse::GetTypeName() const {
+ return "mozilla.safebrowsing.FetchThreatListUpdatesResponse";
+}
+
+
+// ===================================================================
+
+class FindFullHashesRequest::_Internal {
+ public:
+ using HasBits = decltype(std::declval<FindFullHashesRequest>()._impl_._has_bits_);
+ static const ::mozilla::safebrowsing::ClientInfo& client(const FindFullHashesRequest* msg);
+ static void set_has_client(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static const ::mozilla::safebrowsing::ThreatInfo& threat_info(const FindFullHashesRequest* msg);
+ static void set_has_threat_info(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+};
+
+const ::mozilla::safebrowsing::ClientInfo&
+FindFullHashesRequest::_Internal::client(const FindFullHashesRequest* msg) {
+ return *msg->_impl_.client_;
+}
+const ::mozilla::safebrowsing::ThreatInfo&
+FindFullHashesRequest::_Internal::threat_info(const FindFullHashesRequest* msg) {
+ return *msg->_impl_.threat_info_;
+}
+FindFullHashesRequest::FindFullHashesRequest(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.FindFullHashesRequest)
+}
+FindFullHashesRequest::FindFullHashesRequest(const FindFullHashesRequest& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ FindFullHashesRequest* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.client_states_){from._impl_.client_states_}
+ , decltype(_impl_.client_){nullptr}
+ , decltype(_impl_.threat_info_){nullptr}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ if (from._internal_has_client()) {
+ _this->_impl_.client_ = new ::mozilla::safebrowsing::ClientInfo(*from._impl_.client_);
+ }
+ if (from._internal_has_threat_info()) {
+ _this->_impl_.threat_info_ = new ::mozilla::safebrowsing::ThreatInfo(*from._impl_.threat_info_);
+ }
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.FindFullHashesRequest)
+}
+
+inline void FindFullHashesRequest::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.client_states_){arena}
+ , decltype(_impl_.client_){nullptr}
+ , decltype(_impl_.threat_info_){nullptr}
+ };
+}
+
+FindFullHashesRequest::~FindFullHashesRequest() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.FindFullHashesRequest)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void FindFullHashesRequest::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.client_states_.~RepeatedPtrField();
+ if (this != internal_default_instance()) delete _impl_.client_;
+ if (this != internal_default_instance()) delete _impl_.threat_info_;
+}
+
+void FindFullHashesRequest::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void FindFullHashesRequest::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.FindFullHashesRequest)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ _impl_.client_states_.Clear();
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ GOOGLE_DCHECK(_impl_.client_ != nullptr);
+ _impl_.client_->Clear();
+ }
+ if (cached_has_bits & 0x00000002u) {
+ GOOGLE_DCHECK(_impl_.threat_info_ != nullptr);
+ _impl_.threat_info_->Clear();
+ }
+ }
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* FindFullHashesRequest::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional .mozilla.safebrowsing.ClientInfo client = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+ ptr = ctx->ParseMessage(_internal_mutable_client(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // repeated bytes client_states = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+ ptr -= 1;
+ do {
+ ptr += 1;
+ auto str = _internal_add_client_states();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ if (!ctx->DataAvailable(ptr)) break;
+ } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<18>(ptr));
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.ThreatInfo threat_info = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+ ptr = ctx->ParseMessage(_internal_mutable_threat_info(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* FindFullHashesRequest::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.FindFullHashesRequest)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional .mozilla.safebrowsing.ClientInfo client = 1;
+ if (cached_has_bits & 0x00000001u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(1, _Internal::client(this),
+ _Internal::client(this).GetCachedSize(), target, stream);
+ }
+
+ // repeated bytes client_states = 2;
+ for (int i = 0, n = this->_internal_client_states_size(); i < n; i++) {
+ const auto& s = this->_internal_client_states(i);
+ target = stream->WriteBytes(2, s, target);
+ }
+
+ // optional .mozilla.safebrowsing.ThreatInfo threat_info = 3;
+ if (cached_has_bits & 0x00000002u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(3, _Internal::threat_info(this),
+ _Internal::threat_info(this).GetCachedSize(), target, stream);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.FindFullHashesRequest)
+ return target;
+}
+
+size_t FindFullHashesRequest::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.FindFullHashesRequest)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ // repeated bytes client_states = 2;
+ total_size += 1 *
+ ::PROTOBUF_NAMESPACE_ID::internal::FromIntSize(_impl_.client_states_.size());
+ for (int i = 0, n = _impl_.client_states_.size(); i < n; i++) {
+ total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+ _impl_.client_states_.Get(i));
+ }
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ // optional .mozilla.safebrowsing.ClientInfo client = 1;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.client_);
+ }
+
+ // optional .mozilla.safebrowsing.ThreatInfo threat_info = 3;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.threat_info_);
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void FindFullHashesRequest::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const FindFullHashesRequest*>(
+ &from));
+}
+
+void FindFullHashesRequest::MergeFrom(const FindFullHashesRequest& from) {
+ FindFullHashesRequest* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.FindFullHashesRequest)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ _this->_impl_.client_states_.MergeFrom(from._impl_.client_states_);
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_internal_mutable_client()->::mozilla::safebrowsing::ClientInfo::MergeFrom(
+ from._internal_client());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_internal_mutable_threat_info()->::mozilla::safebrowsing::ThreatInfo::MergeFrom(
+ from._internal_threat_info());
+ }
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void FindFullHashesRequest::CopyFrom(const FindFullHashesRequest& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.FindFullHashesRequest)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool FindFullHashesRequest::IsInitialized() const {
+ return true;
+}
+
+void FindFullHashesRequest::InternalSwap(FindFullHashesRequest* other) {
+ using std::swap;
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ _impl_.client_states_.InternalSwap(&other->_impl_.client_states_);
+ ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+ PROTOBUF_FIELD_OFFSET(FindFullHashesRequest, _impl_.threat_info_)
+ + sizeof(FindFullHashesRequest::_impl_.threat_info_)
+ - PROTOBUF_FIELD_OFFSET(FindFullHashesRequest, _impl_.client_)>(
+ reinterpret_cast<char*>(&_impl_.client_),
+ reinterpret_cast<char*>(&other->_impl_.client_));
+}
+
+std::string FindFullHashesRequest::GetTypeName() const {
+ return "mozilla.safebrowsing.FindFullHashesRequest";
+}
+
+
+// ===================================================================
+
+class FindFullHashesResponse::_Internal {
+ public:
+ using HasBits = decltype(std::declval<FindFullHashesResponse>()._impl_._has_bits_);
+ static const ::mozilla::safebrowsing::Duration& minimum_wait_duration(const FindFullHashesResponse* msg);
+ static void set_has_minimum_wait_duration(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static const ::mozilla::safebrowsing::Duration& negative_cache_duration(const FindFullHashesResponse* msg);
+ static void set_has_negative_cache_duration(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+};
+
+const ::mozilla::safebrowsing::Duration&
+FindFullHashesResponse::_Internal::minimum_wait_duration(const FindFullHashesResponse* msg) {
+ return *msg->_impl_.minimum_wait_duration_;
+}
+const ::mozilla::safebrowsing::Duration&
+FindFullHashesResponse::_Internal::negative_cache_duration(const FindFullHashesResponse* msg) {
+ return *msg->_impl_.negative_cache_duration_;
+}
+FindFullHashesResponse::FindFullHashesResponse(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.FindFullHashesResponse)
+}
+FindFullHashesResponse::FindFullHashesResponse(const FindFullHashesResponse& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ FindFullHashesResponse* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.matches_){from._impl_.matches_}
+ , decltype(_impl_.minimum_wait_duration_){nullptr}
+ , decltype(_impl_.negative_cache_duration_){nullptr}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ if (from._internal_has_minimum_wait_duration()) {
+ _this->_impl_.minimum_wait_duration_ = new ::mozilla::safebrowsing::Duration(*from._impl_.minimum_wait_duration_);
+ }
+ if (from._internal_has_negative_cache_duration()) {
+ _this->_impl_.negative_cache_duration_ = new ::mozilla::safebrowsing::Duration(*from._impl_.negative_cache_duration_);
+ }
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.FindFullHashesResponse)
+}
+
+inline void FindFullHashesResponse::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.matches_){arena}
+ , decltype(_impl_.minimum_wait_duration_){nullptr}
+ , decltype(_impl_.negative_cache_duration_){nullptr}
+ };
+}
+
+FindFullHashesResponse::~FindFullHashesResponse() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.FindFullHashesResponse)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void FindFullHashesResponse::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.matches_.~RepeatedPtrField();
+ if (this != internal_default_instance()) delete _impl_.minimum_wait_duration_;
+ if (this != internal_default_instance()) delete _impl_.negative_cache_duration_;
+}
+
+void FindFullHashesResponse::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void FindFullHashesResponse::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.FindFullHashesResponse)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ _impl_.matches_.Clear();
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ GOOGLE_DCHECK(_impl_.minimum_wait_duration_ != nullptr);
+ _impl_.minimum_wait_duration_->Clear();
+ }
+ if (cached_has_bits & 0x00000002u) {
+ GOOGLE_DCHECK(_impl_.negative_cache_duration_ != nullptr);
+ _impl_.negative_cache_duration_->Clear();
+ }
+ }
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* FindFullHashesResponse::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // repeated .mozilla.safebrowsing.ThreatMatch matches = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+ ptr -= 1;
+ do {
+ ptr += 1;
+ ptr = ctx->ParseMessage(_internal_add_matches(), ptr);
+ CHK_(ptr);
+ if (!ctx->DataAvailable(ptr)) break;
+ } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<10>(ptr));
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.Duration minimum_wait_duration = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+ ptr = ctx->ParseMessage(_internal_mutable_minimum_wait_duration(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.Duration negative_cache_duration = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+ ptr = ctx->ParseMessage(_internal_mutable_negative_cache_duration(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* FindFullHashesResponse::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.FindFullHashesResponse)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ // repeated .mozilla.safebrowsing.ThreatMatch matches = 1;
+ for (unsigned i = 0,
+ n = static_cast<unsigned>(this->_internal_matches_size()); i < n; i++) {
+ const auto& repfield = this->_internal_matches(i);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(1, repfield, repfield.GetCachedSize(), target, stream);
+ }
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional .mozilla.safebrowsing.Duration minimum_wait_duration = 2;
+ if (cached_has_bits & 0x00000001u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(2, _Internal::minimum_wait_duration(this),
+ _Internal::minimum_wait_duration(this).GetCachedSize(), target, stream);
+ }
+
+ // optional .mozilla.safebrowsing.Duration negative_cache_duration = 3;
+ if (cached_has_bits & 0x00000002u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(3, _Internal::negative_cache_duration(this),
+ _Internal::negative_cache_duration(this).GetCachedSize(), target, stream);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.FindFullHashesResponse)
+ return target;
+}
+
+size_t FindFullHashesResponse::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.FindFullHashesResponse)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ // repeated .mozilla.safebrowsing.ThreatMatch matches = 1;
+ total_size += 1UL * this->_internal_matches_size();
+ for (const auto& msg : this->_impl_.matches_) {
+ total_size +=
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+ }
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ // optional .mozilla.safebrowsing.Duration minimum_wait_duration = 2;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.minimum_wait_duration_);
+ }
+
+ // optional .mozilla.safebrowsing.Duration negative_cache_duration = 3;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.negative_cache_duration_);
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void FindFullHashesResponse::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const FindFullHashesResponse*>(
+ &from));
+}
+
+void FindFullHashesResponse::MergeFrom(const FindFullHashesResponse& from) {
+ FindFullHashesResponse* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.FindFullHashesResponse)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ _this->_impl_.matches_.MergeFrom(from._impl_.matches_);
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_internal_mutable_minimum_wait_duration()->::mozilla::safebrowsing::Duration::MergeFrom(
+ from._internal_minimum_wait_duration());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_internal_mutable_negative_cache_duration()->::mozilla::safebrowsing::Duration::MergeFrom(
+ from._internal_negative_cache_duration());
+ }
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void FindFullHashesResponse::CopyFrom(const FindFullHashesResponse& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.FindFullHashesResponse)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool FindFullHashesResponse::IsInitialized() const {
+ return true;
+}
+
+void FindFullHashesResponse::InternalSwap(FindFullHashesResponse* other) {
+ using std::swap;
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ _impl_.matches_.InternalSwap(&other->_impl_.matches_);
+ ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+ PROTOBUF_FIELD_OFFSET(FindFullHashesResponse, _impl_.negative_cache_duration_)
+ + sizeof(FindFullHashesResponse::_impl_.negative_cache_duration_)
+ - PROTOBUF_FIELD_OFFSET(FindFullHashesResponse, _impl_.minimum_wait_duration_)>(
+ reinterpret_cast<char*>(&_impl_.minimum_wait_duration_),
+ reinterpret_cast<char*>(&other->_impl_.minimum_wait_duration_));
+}
+
+std::string FindFullHashesResponse::GetTypeName() const {
+ return "mozilla.safebrowsing.FindFullHashesResponse";
+}
+
+
+// ===================================================================
+
+class ThreatHit_ThreatSource::_Internal {
+ public:
+ using HasBits = decltype(std::declval<ThreatHit_ThreatSource>()._impl_._has_bits_);
+ static void set_has_url(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static void set_has_type(HasBits* has_bits) {
+ (*has_bits)[0] |= 8u;
+ }
+ static void set_has_remote_ip(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+ static void set_has_referrer(HasBits* has_bits) {
+ (*has_bits)[0] |= 4u;
+ }
+};
+
+ThreatHit_ThreatSource::ThreatHit_ThreatSource(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.ThreatHit.ThreatSource)
+}
+ThreatHit_ThreatSource::ThreatHit_ThreatSource(const ThreatHit_ThreatSource& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ ThreatHit_ThreatSource* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.url_){}
+ , decltype(_impl_.remote_ip_){}
+ , decltype(_impl_.referrer_){}
+ , decltype(_impl_.type_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ _impl_.url_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.url_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (from._internal_has_url()) {
+ _this->_impl_.url_.Set(from._internal_url(),
+ _this->GetArenaForAllocation());
+ }
+ _impl_.remote_ip_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.remote_ip_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (from._internal_has_remote_ip()) {
+ _this->_impl_.remote_ip_.Set(from._internal_remote_ip(),
+ _this->GetArenaForAllocation());
+ }
+ _impl_.referrer_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.referrer_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (from._internal_has_referrer()) {
+ _this->_impl_.referrer_.Set(from._internal_referrer(),
+ _this->GetArenaForAllocation());
+ }
+ _this->_impl_.type_ = from._impl_.type_;
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.ThreatHit.ThreatSource)
+}
+
+inline void ThreatHit_ThreatSource::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.url_){}
+ , decltype(_impl_.remote_ip_){}
+ , decltype(_impl_.referrer_){}
+ , decltype(_impl_.type_){0}
+ };
+ _impl_.url_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.url_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.remote_ip_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.remote_ip_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.referrer_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.referrer_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+ThreatHit_ThreatSource::~ThreatHit_ThreatSource() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.ThreatHit.ThreatSource)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void ThreatHit_ThreatSource::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.url_.Destroy();
+ _impl_.remote_ip_.Destroy();
+ _impl_.referrer_.Destroy();
+}
+
+void ThreatHit_ThreatSource::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void ThreatHit_ThreatSource::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.ThreatHit.ThreatSource)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000007u) {
+ if (cached_has_bits & 0x00000001u) {
+ _impl_.url_.ClearNonDefaultToEmpty();
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _impl_.remote_ip_.ClearNonDefaultToEmpty();
+ }
+ if (cached_has_bits & 0x00000004u) {
+ _impl_.referrer_.ClearNonDefaultToEmpty();
+ }
+ }
+ _impl_.type_ = 0;
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* ThreatHit_ThreatSource::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional string url = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+ auto str = _internal_mutable_url();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.ThreatHit.ThreatSourceType type = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::ThreatHit_ThreatSourceType_IsValid(val))) {
+ _internal_set_type(static_cast<::mozilla::safebrowsing::ThreatHit_ThreatSourceType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(2, val, mutable_unknown_fields());
+ }
+ } else
+ goto handle_unusual;
+ continue;
+ // optional string remote_ip = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+ auto str = _internal_mutable_remote_ip();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional string referrer = 4;
+ case 4:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 34)) {
+ auto str = _internal_mutable_referrer();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* ThreatHit_ThreatSource::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.ThreatHit.ThreatSource)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional string url = 1;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->WriteStringMaybeAliased(
+ 1, this->_internal_url(), target);
+ }
+
+ // optional .mozilla.safebrowsing.ThreatHit.ThreatSourceType type = 2;
+ if (cached_has_bits & 0x00000008u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 2, this->_internal_type(), target);
+ }
+
+ // optional string remote_ip = 3;
+ if (cached_has_bits & 0x00000002u) {
+ target = stream->WriteStringMaybeAliased(
+ 3, this->_internal_remote_ip(), target);
+ }
+
+ // optional string referrer = 4;
+ if (cached_has_bits & 0x00000004u) {
+ target = stream->WriteStringMaybeAliased(
+ 4, this->_internal_referrer(), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.ThreatHit.ThreatSource)
+ return target;
+}
+
+size_t ThreatHit_ThreatSource::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.ThreatHit.ThreatSource)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x0000000fu) {
+ // optional string url = 1;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+ this->_internal_url());
+ }
+
+ // optional string remote_ip = 3;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+ this->_internal_remote_ip());
+ }
+
+ // optional string referrer = 4;
+ if (cached_has_bits & 0x00000004u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+ this->_internal_referrer());
+ }
+
+ // optional .mozilla.safebrowsing.ThreatHit.ThreatSourceType type = 2;
+ if (cached_has_bits & 0x00000008u) {
+ total_size += 1 +
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_type());
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void ThreatHit_ThreatSource::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const ThreatHit_ThreatSource*>(
+ &from));
+}
+
+void ThreatHit_ThreatSource::MergeFrom(const ThreatHit_ThreatSource& from) {
+ ThreatHit_ThreatSource* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.ThreatHit.ThreatSource)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x0000000fu) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_internal_set_url(from._internal_url());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_internal_set_remote_ip(from._internal_remote_ip());
+ }
+ if (cached_has_bits & 0x00000004u) {
+ _this->_internal_set_referrer(from._internal_referrer());
+ }
+ if (cached_has_bits & 0x00000008u) {
+ _this->_impl_.type_ = from._impl_.type_;
+ }
+ _this->_impl_._has_bits_[0] |= cached_has_bits;
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void ThreatHit_ThreatSource::CopyFrom(const ThreatHit_ThreatSource& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.ThreatHit.ThreatSource)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool ThreatHit_ThreatSource::IsInitialized() const {
+ return true;
+}
+
+void ThreatHit_ThreatSource::InternalSwap(ThreatHit_ThreatSource* other) {
+ using std::swap;
+ auto* lhs_arena = GetArenaForAllocation();
+ auto* rhs_arena = other->GetArenaForAllocation();
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+ &_impl_.url_, lhs_arena,
+ &other->_impl_.url_, rhs_arena
+ );
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+ &_impl_.remote_ip_, lhs_arena,
+ &other->_impl_.remote_ip_, rhs_arena
+ );
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+ &_impl_.referrer_, lhs_arena,
+ &other->_impl_.referrer_, rhs_arena
+ );
+ swap(_impl_.type_, other->_impl_.type_);
+}
+
+std::string ThreatHit_ThreatSource::GetTypeName() const {
+ return "mozilla.safebrowsing.ThreatHit.ThreatSource";
+}
+
+
+// ===================================================================
+
+class ThreatHit_UserInfo::_Internal {
+ public:
+ using HasBits = decltype(std::declval<ThreatHit_UserInfo>()._impl_._has_bits_);
+ static void set_has_region_code(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static void set_has_user_id(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+};
+
+ThreatHit_UserInfo::ThreatHit_UserInfo(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.ThreatHit.UserInfo)
+}
+ThreatHit_UserInfo::ThreatHit_UserInfo(const ThreatHit_UserInfo& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ ThreatHit_UserInfo* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.region_code_){}
+ , decltype(_impl_.user_id_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ _impl_.region_code_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.region_code_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (from._internal_has_region_code()) {
+ _this->_impl_.region_code_.Set(from._internal_region_code(),
+ _this->GetArenaForAllocation());
+ }
+ _impl_.user_id_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.user_id_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (from._internal_has_user_id()) {
+ _this->_impl_.user_id_.Set(from._internal_user_id(),
+ _this->GetArenaForAllocation());
+ }
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.ThreatHit.UserInfo)
+}
+
+inline void ThreatHit_UserInfo::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.region_code_){}
+ , decltype(_impl_.user_id_){}
+ };
+ _impl_.region_code_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.region_code_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.user_id_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.user_id_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+ThreatHit_UserInfo::~ThreatHit_UserInfo() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.ThreatHit.UserInfo)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void ThreatHit_UserInfo::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.region_code_.Destroy();
+ _impl_.user_id_.Destroy();
+}
+
+void ThreatHit_UserInfo::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void ThreatHit_UserInfo::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.ThreatHit.UserInfo)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ _impl_.region_code_.ClearNonDefaultToEmpty();
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _impl_.user_id_.ClearNonDefaultToEmpty();
+ }
+ }
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* ThreatHit_UserInfo::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional string region_code = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+ auto str = _internal_mutable_region_code();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional bytes user_id = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+ auto str = _internal_mutable_user_id();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* ThreatHit_UserInfo::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.ThreatHit.UserInfo)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional string region_code = 1;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->WriteStringMaybeAliased(
+ 1, this->_internal_region_code(), target);
+ }
+
+ // optional bytes user_id = 2;
+ if (cached_has_bits & 0x00000002u) {
+ target = stream->WriteBytesMaybeAliased(
+ 2, this->_internal_user_id(), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.ThreatHit.UserInfo)
+ return target;
+}
+
+size_t ThreatHit_UserInfo::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.ThreatHit.UserInfo)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ // optional string region_code = 1;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+ this->_internal_region_code());
+ }
+
+ // optional bytes user_id = 2;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+ this->_internal_user_id());
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void ThreatHit_UserInfo::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const ThreatHit_UserInfo*>(
+ &from));
+}
+
+void ThreatHit_UserInfo::MergeFrom(const ThreatHit_UserInfo& from) {
+ ThreatHit_UserInfo* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.ThreatHit.UserInfo)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_internal_set_region_code(from._internal_region_code());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_internal_set_user_id(from._internal_user_id());
+ }
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void ThreatHit_UserInfo::CopyFrom(const ThreatHit_UserInfo& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.ThreatHit.UserInfo)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool ThreatHit_UserInfo::IsInitialized() const {
+ return true;
+}
+
+void ThreatHit_UserInfo::InternalSwap(ThreatHit_UserInfo* other) {
+ using std::swap;
+ auto* lhs_arena = GetArenaForAllocation();
+ auto* rhs_arena = other->GetArenaForAllocation();
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+ &_impl_.region_code_, lhs_arena,
+ &other->_impl_.region_code_, rhs_arena
+ );
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+ &_impl_.user_id_, lhs_arena,
+ &other->_impl_.user_id_, rhs_arena
+ );
+}
+
+std::string ThreatHit_UserInfo::GetTypeName() const {
+ return "mozilla.safebrowsing.ThreatHit.UserInfo";
+}
+
+
+// ===================================================================
+
+class ThreatHit::_Internal {
+ public:
+ using HasBits = decltype(std::declval<ThreatHit>()._impl_._has_bits_);
+ static void set_has_threat_type(HasBits* has_bits) {
+ (*has_bits)[0] |= 8u;
+ }
+ static void set_has_platform_type(HasBits* has_bits) {
+ (*has_bits)[0] |= 16u;
+ }
+ static const ::mozilla::safebrowsing::ThreatEntry& entry(const ThreatHit* msg);
+ static void set_has_entry(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static const ::mozilla::safebrowsing::ClientInfo& client_info(const ThreatHit* msg);
+ static void set_has_client_info(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+ static const ::mozilla::safebrowsing::ThreatHit_UserInfo& user_info(const ThreatHit* msg);
+ static void set_has_user_info(HasBits* has_bits) {
+ (*has_bits)[0] |= 4u;
+ }
+};
+
+const ::mozilla::safebrowsing::ThreatEntry&
+ThreatHit::_Internal::entry(const ThreatHit* msg) {
+ return *msg->_impl_.entry_;
+}
+const ::mozilla::safebrowsing::ClientInfo&
+ThreatHit::_Internal::client_info(const ThreatHit* msg) {
+ return *msg->_impl_.client_info_;
+}
+const ::mozilla::safebrowsing::ThreatHit_UserInfo&
+ThreatHit::_Internal::user_info(const ThreatHit* msg) {
+ return *msg->_impl_.user_info_;
+}
+ThreatHit::ThreatHit(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.ThreatHit)
+}
+ThreatHit::ThreatHit(const ThreatHit& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ ThreatHit* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.resources_){from._impl_.resources_}
+ , decltype(_impl_.entry_){nullptr}
+ , decltype(_impl_.client_info_){nullptr}
+ , decltype(_impl_.user_info_){nullptr}
+ , decltype(_impl_.threat_type_){}
+ , decltype(_impl_.platform_type_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ if (from._internal_has_entry()) {
+ _this->_impl_.entry_ = new ::mozilla::safebrowsing::ThreatEntry(*from._impl_.entry_);
+ }
+ if (from._internal_has_client_info()) {
+ _this->_impl_.client_info_ = new ::mozilla::safebrowsing::ClientInfo(*from._impl_.client_info_);
+ }
+ if (from._internal_has_user_info()) {
+ _this->_impl_.user_info_ = new ::mozilla::safebrowsing::ThreatHit_UserInfo(*from._impl_.user_info_);
+ }
+ ::memcpy(&_impl_.threat_type_, &from._impl_.threat_type_,
+ static_cast<size_t>(reinterpret_cast<char*>(&_impl_.platform_type_) -
+ reinterpret_cast<char*>(&_impl_.threat_type_)) + sizeof(_impl_.platform_type_));
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.ThreatHit)
+}
+
+inline void ThreatHit::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.resources_){arena}
+ , decltype(_impl_.entry_){nullptr}
+ , decltype(_impl_.client_info_){nullptr}
+ , decltype(_impl_.user_info_){nullptr}
+ , decltype(_impl_.threat_type_){0}
+ , decltype(_impl_.platform_type_){0}
+ };
+}
+
+ThreatHit::~ThreatHit() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.ThreatHit)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void ThreatHit::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.resources_.~RepeatedPtrField();
+ if (this != internal_default_instance()) delete _impl_.entry_;
+ if (this != internal_default_instance()) delete _impl_.client_info_;
+ if (this != internal_default_instance()) delete _impl_.user_info_;
+}
+
+void ThreatHit::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void ThreatHit::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.ThreatHit)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ _impl_.resources_.Clear();
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000007u) {
+ if (cached_has_bits & 0x00000001u) {
+ GOOGLE_DCHECK(_impl_.entry_ != nullptr);
+ _impl_.entry_->Clear();
+ }
+ if (cached_has_bits & 0x00000002u) {
+ GOOGLE_DCHECK(_impl_.client_info_ != nullptr);
+ _impl_.client_info_->Clear();
+ }
+ if (cached_has_bits & 0x00000004u) {
+ GOOGLE_DCHECK(_impl_.user_info_ != nullptr);
+ _impl_.user_info_->Clear();
+ }
+ }
+ if (cached_has_bits & 0x00000018u) {
+ ::memset(&_impl_.threat_type_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&_impl_.platform_type_) -
+ reinterpret_cast<char*>(&_impl_.threat_type_)) + sizeof(_impl_.platform_type_));
+ }
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* ThreatHit::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::ThreatType_IsValid(val))) {
+ _internal_set_threat_type(static_cast<::mozilla::safebrowsing::ThreatType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(1, val, mutable_unknown_fields());
+ }
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::PlatformType_IsValid(val))) {
+ _internal_set_platform_type(static_cast<::mozilla::safebrowsing::PlatformType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(2, val, mutable_unknown_fields());
+ }
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.ThreatEntry entry = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+ ptr = ctx->ParseMessage(_internal_mutable_entry(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // repeated .mozilla.safebrowsing.ThreatHit.ThreatSource resources = 4;
+ case 4:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 34)) {
+ ptr -= 1;
+ do {
+ ptr += 1;
+ ptr = ctx->ParseMessage(_internal_add_resources(), ptr);
+ CHK_(ptr);
+ if (!ctx->DataAvailable(ptr)) break;
+ } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<34>(ptr));
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.ClientInfo client_info = 5;
+ case 5:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 42)) {
+ ptr = ctx->ParseMessage(_internal_mutable_client_info(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.ThreatHit.UserInfo user_info = 6;
+ case 6:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 50)) {
+ ptr = ctx->ParseMessage(_internal_mutable_user_info(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* ThreatHit::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.ThreatHit)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ if (cached_has_bits & 0x00000008u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 1, this->_internal_threat_type(), target);
+ }
+
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+ if (cached_has_bits & 0x00000010u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 2, this->_internal_platform_type(), target);
+ }
+
+ // optional .mozilla.safebrowsing.ThreatEntry entry = 3;
+ if (cached_has_bits & 0x00000001u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(3, _Internal::entry(this),
+ _Internal::entry(this).GetCachedSize(), target, stream);
+ }
+
+ // repeated .mozilla.safebrowsing.ThreatHit.ThreatSource resources = 4;
+ for (unsigned i = 0,
+ n = static_cast<unsigned>(this->_internal_resources_size()); i < n; i++) {
+ const auto& repfield = this->_internal_resources(i);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(4, repfield, repfield.GetCachedSize(), target, stream);
+ }
+
+ // optional .mozilla.safebrowsing.ClientInfo client_info = 5;
+ if (cached_has_bits & 0x00000002u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(5, _Internal::client_info(this),
+ _Internal::client_info(this).GetCachedSize(), target, stream);
+ }
+
+ // optional .mozilla.safebrowsing.ThreatHit.UserInfo user_info = 6;
+ if (cached_has_bits & 0x00000004u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(6, _Internal::user_info(this),
+ _Internal::user_info(this).GetCachedSize(), target, stream);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.ThreatHit)
+ return target;
+}
+
+size_t ThreatHit::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.ThreatHit)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ // repeated .mozilla.safebrowsing.ThreatHit.ThreatSource resources = 4;
+ total_size += 1UL * this->_internal_resources_size();
+ for (const auto& msg : this->_impl_.resources_) {
+ total_size +=
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+ }
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x0000001fu) {
+ // optional .mozilla.safebrowsing.ThreatEntry entry = 3;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.entry_);
+ }
+
+ // optional .mozilla.safebrowsing.ClientInfo client_info = 5;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.client_info_);
+ }
+
+ // optional .mozilla.safebrowsing.ThreatHit.UserInfo user_info = 6;
+ if (cached_has_bits & 0x00000004u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.user_info_);
+ }
+
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ if (cached_has_bits & 0x00000008u) {
+ total_size += 1 +
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_threat_type());
+ }
+
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+ if (cached_has_bits & 0x00000010u) {
+ total_size += 1 +
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_platform_type());
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void ThreatHit::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const ThreatHit*>(
+ &from));
+}
+
+void ThreatHit::MergeFrom(const ThreatHit& from) {
+ ThreatHit* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.ThreatHit)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ _this->_impl_.resources_.MergeFrom(from._impl_.resources_);
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x0000001fu) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_internal_mutable_entry()->::mozilla::safebrowsing::ThreatEntry::MergeFrom(
+ from._internal_entry());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_internal_mutable_client_info()->::mozilla::safebrowsing::ClientInfo::MergeFrom(
+ from._internal_client_info());
+ }
+ if (cached_has_bits & 0x00000004u) {
+ _this->_internal_mutable_user_info()->::mozilla::safebrowsing::ThreatHit_UserInfo::MergeFrom(
+ from._internal_user_info());
+ }
+ if (cached_has_bits & 0x00000008u) {
+ _this->_impl_.threat_type_ = from._impl_.threat_type_;
+ }
+ if (cached_has_bits & 0x00000010u) {
+ _this->_impl_.platform_type_ = from._impl_.platform_type_;
+ }
+ _this->_impl_._has_bits_[0] |= cached_has_bits;
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void ThreatHit::CopyFrom(const ThreatHit& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.ThreatHit)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool ThreatHit::IsInitialized() const {
+ return true;
+}
+
+void ThreatHit::InternalSwap(ThreatHit* other) {
+ using std::swap;
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ _impl_.resources_.InternalSwap(&other->_impl_.resources_);
+ ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+ PROTOBUF_FIELD_OFFSET(ThreatHit, _impl_.platform_type_)
+ + sizeof(ThreatHit::_impl_.platform_type_)
+ - PROTOBUF_FIELD_OFFSET(ThreatHit, _impl_.entry_)>(
+ reinterpret_cast<char*>(&_impl_.entry_),
+ reinterpret_cast<char*>(&other->_impl_.entry_));
+}
+
+std::string ThreatHit::GetTypeName() const {
+ return "mozilla.safebrowsing.ThreatHit";
+}
+
+
+// ===================================================================
+
+class ClientInfo::_Internal {
+ public:
+ using HasBits = decltype(std::declval<ClientInfo>()._impl_._has_bits_);
+ static void set_has_client_id(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static void set_has_client_version(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+};
+
+ClientInfo::ClientInfo(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.ClientInfo)
+}
+ClientInfo::ClientInfo(const ClientInfo& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ ClientInfo* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.client_id_){}
+ , decltype(_impl_.client_version_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ _impl_.client_id_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.client_id_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (from._internal_has_client_id()) {
+ _this->_impl_.client_id_.Set(from._internal_client_id(),
+ _this->GetArenaForAllocation());
+ }
+ _impl_.client_version_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.client_version_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (from._internal_has_client_version()) {
+ _this->_impl_.client_version_.Set(from._internal_client_version(),
+ _this->GetArenaForAllocation());
+ }
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.ClientInfo)
+}
+
+inline void ClientInfo::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.client_id_){}
+ , decltype(_impl_.client_version_){}
+ };
+ _impl_.client_id_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.client_id_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.client_version_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.client_version_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+ClientInfo::~ClientInfo() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.ClientInfo)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void ClientInfo::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.client_id_.Destroy();
+ _impl_.client_version_.Destroy();
+}
+
+void ClientInfo::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void ClientInfo::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.ClientInfo)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ _impl_.client_id_.ClearNonDefaultToEmpty();
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _impl_.client_version_.ClearNonDefaultToEmpty();
+ }
+ }
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* ClientInfo::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional string client_id = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+ auto str = _internal_mutable_client_id();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional string client_version = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+ auto str = _internal_mutable_client_version();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* ClientInfo::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.ClientInfo)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional string client_id = 1;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->WriteStringMaybeAliased(
+ 1, this->_internal_client_id(), target);
+ }
+
+ // optional string client_version = 2;
+ if (cached_has_bits & 0x00000002u) {
+ target = stream->WriteStringMaybeAliased(
+ 2, this->_internal_client_version(), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.ClientInfo)
+ return target;
+}
+
+size_t ClientInfo::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.ClientInfo)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ // optional string client_id = 1;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+ this->_internal_client_id());
+ }
+
+ // optional string client_version = 2;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+ this->_internal_client_version());
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void ClientInfo::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const ClientInfo*>(
+ &from));
+}
+
+void ClientInfo::MergeFrom(const ClientInfo& from) {
+ ClientInfo* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.ClientInfo)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_internal_set_client_id(from._internal_client_id());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_internal_set_client_version(from._internal_client_version());
+ }
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void ClientInfo::CopyFrom(const ClientInfo& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.ClientInfo)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool ClientInfo::IsInitialized() const {
+ return true;
+}
+
+void ClientInfo::InternalSwap(ClientInfo* other) {
+ using std::swap;
+ auto* lhs_arena = GetArenaForAllocation();
+ auto* rhs_arena = other->GetArenaForAllocation();
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+ &_impl_.client_id_, lhs_arena,
+ &other->_impl_.client_id_, rhs_arena
+ );
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+ &_impl_.client_version_, lhs_arena,
+ &other->_impl_.client_version_, rhs_arena
+ );
+}
+
+std::string ClientInfo::GetTypeName() const {
+ return "mozilla.safebrowsing.ClientInfo";
+}
+
+
+// ===================================================================
+
+class ChromeClientInfo::_Internal {
+ public:
+ using HasBits = decltype(std::declval<ChromeClientInfo>()._impl_._has_bits_);
+ static void set_has_reporting_population(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+};
+
+ChromeClientInfo::ChromeClientInfo(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.ChromeClientInfo)
+}
+ChromeClientInfo::ChromeClientInfo(const ChromeClientInfo& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ ChromeClientInfo* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.reporting_population_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ _this->_impl_.reporting_population_ = from._impl_.reporting_population_;
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.ChromeClientInfo)
+}
+
+inline void ChromeClientInfo::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.reporting_population_){0}
+ };
+}
+
+ChromeClientInfo::~ChromeClientInfo() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.ChromeClientInfo)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void ChromeClientInfo::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+}
+
+void ChromeClientInfo::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void ChromeClientInfo::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.ChromeClientInfo)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ _impl_.reporting_population_ = 0;
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* ChromeClientInfo::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional .mozilla.safebrowsing.ChromeClientInfo.SafeBrowsingReportingPopulation reporting_population = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::ChromeClientInfo_SafeBrowsingReportingPopulation_IsValid(val))) {
+ _internal_set_reporting_population(static_cast<::mozilla::safebrowsing::ChromeClientInfo_SafeBrowsingReportingPopulation>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(1, val, mutable_unknown_fields());
+ }
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* ChromeClientInfo::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.ChromeClientInfo)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional .mozilla.safebrowsing.ChromeClientInfo.SafeBrowsingReportingPopulation reporting_population = 1;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 1, this->_internal_reporting_population(), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.ChromeClientInfo)
+ return target;
+}
+
+size_t ChromeClientInfo::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.ChromeClientInfo)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ // optional .mozilla.safebrowsing.ChromeClientInfo.SafeBrowsingReportingPopulation reporting_population = 1;
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_reporting_population());
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void ChromeClientInfo::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const ChromeClientInfo*>(
+ &from));
+}
+
+void ChromeClientInfo::MergeFrom(const ChromeClientInfo& from) {
+ ChromeClientInfo* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.ChromeClientInfo)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ if (from._internal_has_reporting_population()) {
+ _this->_internal_set_reporting_population(from._internal_reporting_population());
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void ChromeClientInfo::CopyFrom(const ChromeClientInfo& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.ChromeClientInfo)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool ChromeClientInfo::IsInitialized() const {
+ return true;
+}
+
+void ChromeClientInfo::InternalSwap(ChromeClientInfo* other) {
+ using std::swap;
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ swap(_impl_.reporting_population_, other->_impl_.reporting_population_);
+}
+
+std::string ChromeClientInfo::GetTypeName() const {
+ return "mozilla.safebrowsing.ChromeClientInfo";
+}
+
+
+// ===================================================================
+
+class Checksum::_Internal {
+ public:
+ using HasBits = decltype(std::declval<Checksum>()._impl_._has_bits_);
+ static void set_has_sha256(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+};
+
+Checksum::Checksum(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.Checksum)
+}
+Checksum::Checksum(const Checksum& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ Checksum* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.sha256_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ _impl_.sha256_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.sha256_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (from._internal_has_sha256()) {
+ _this->_impl_.sha256_.Set(from._internal_sha256(),
+ _this->GetArenaForAllocation());
+ }
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.Checksum)
+}
+
+inline void Checksum::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.sha256_){}
+ };
+ _impl_.sha256_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.sha256_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+Checksum::~Checksum() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.Checksum)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void Checksum::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.sha256_.Destroy();
+}
+
+void Checksum::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void Checksum::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.Checksum)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000001u) {
+ _impl_.sha256_.ClearNonDefaultToEmpty();
+ }
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* Checksum::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional bytes sha256 = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+ auto str = _internal_mutable_sha256();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* Checksum::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.Checksum)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional bytes sha256 = 1;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->WriteBytesMaybeAliased(
+ 1, this->_internal_sha256(), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.Checksum)
+ return target;
+}
+
+size_t Checksum::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.Checksum)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ // optional bytes sha256 = 1;
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+ this->_internal_sha256());
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void Checksum::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const Checksum*>(
+ &from));
+}
+
+void Checksum::MergeFrom(const Checksum& from) {
+ Checksum* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.Checksum)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ if (from._internal_has_sha256()) {
+ _this->_internal_set_sha256(from._internal_sha256());
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void Checksum::CopyFrom(const Checksum& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.Checksum)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool Checksum::IsInitialized() const {
+ return true;
+}
+
+void Checksum::InternalSwap(Checksum* other) {
+ using std::swap;
+ auto* lhs_arena = GetArenaForAllocation();
+ auto* rhs_arena = other->GetArenaForAllocation();
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+ &_impl_.sha256_, lhs_arena,
+ &other->_impl_.sha256_, rhs_arena
+ );
+}
+
+std::string Checksum::GetTypeName() const {
+ return "mozilla.safebrowsing.Checksum";
+}
+
+
+// ===================================================================
+
+class ThreatEntry::_Internal {
+ public:
+ using HasBits = decltype(std::declval<ThreatEntry>()._impl_._has_bits_);
+ static void set_has_hash(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static void set_has_url(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+};
+
+ThreatEntry::ThreatEntry(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.ThreatEntry)
+}
+ThreatEntry::ThreatEntry(const ThreatEntry& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ ThreatEntry* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.hash_){}
+ , decltype(_impl_.url_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ _impl_.hash_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.hash_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (from._internal_has_hash()) {
+ _this->_impl_.hash_.Set(from._internal_hash(),
+ _this->GetArenaForAllocation());
+ }
+ _impl_.url_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.url_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (from._internal_has_url()) {
+ _this->_impl_.url_.Set(from._internal_url(),
+ _this->GetArenaForAllocation());
+ }
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.ThreatEntry)
+}
+
+inline void ThreatEntry::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.hash_){}
+ , decltype(_impl_.url_){}
+ };
+ _impl_.hash_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.hash_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.url_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.url_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+ThreatEntry::~ThreatEntry() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.ThreatEntry)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void ThreatEntry::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.hash_.Destroy();
+ _impl_.url_.Destroy();
+}
+
+void ThreatEntry::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void ThreatEntry::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.ThreatEntry)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ _impl_.hash_.ClearNonDefaultToEmpty();
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _impl_.url_.ClearNonDefaultToEmpty();
+ }
+ }
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* ThreatEntry::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional bytes hash = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+ auto str = _internal_mutable_hash();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional string url = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+ auto str = _internal_mutable_url();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* ThreatEntry::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.ThreatEntry)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional bytes hash = 1;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->WriteBytesMaybeAliased(
+ 1, this->_internal_hash(), target);
+ }
+
+ // optional string url = 2;
+ if (cached_has_bits & 0x00000002u) {
+ target = stream->WriteStringMaybeAliased(
+ 2, this->_internal_url(), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.ThreatEntry)
+ return target;
+}
+
+size_t ThreatEntry::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.ThreatEntry)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ // optional bytes hash = 1;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+ this->_internal_hash());
+ }
+
+ // optional string url = 2;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize(
+ this->_internal_url());
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void ThreatEntry::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const ThreatEntry*>(
+ &from));
+}
+
+void ThreatEntry::MergeFrom(const ThreatEntry& from) {
+ ThreatEntry* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.ThreatEntry)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_internal_set_hash(from._internal_hash());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_internal_set_url(from._internal_url());
+ }
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void ThreatEntry::CopyFrom(const ThreatEntry& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.ThreatEntry)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool ThreatEntry::IsInitialized() const {
+ return true;
+}
+
+void ThreatEntry::InternalSwap(ThreatEntry* other) {
+ using std::swap;
+ auto* lhs_arena = GetArenaForAllocation();
+ auto* rhs_arena = other->GetArenaForAllocation();
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+ &_impl_.hash_, lhs_arena,
+ &other->_impl_.hash_, rhs_arena
+ );
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+ &_impl_.url_, lhs_arena,
+ &other->_impl_.url_, rhs_arena
+ );
+}
+
+std::string ThreatEntry::GetTypeName() const {
+ return "mozilla.safebrowsing.ThreatEntry";
+}
+
+
+// ===================================================================
+
+class ThreatEntrySet::_Internal {
+ public:
+ using HasBits = decltype(std::declval<ThreatEntrySet>()._impl_._has_bits_);
+ static void set_has_compression_type(HasBits* has_bits) {
+ (*has_bits)[0] |= 16u;
+ }
+ static const ::mozilla::safebrowsing::RawHashes& raw_hashes(const ThreatEntrySet* msg);
+ static void set_has_raw_hashes(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static const ::mozilla::safebrowsing::RawIndices& raw_indices(const ThreatEntrySet* msg);
+ static void set_has_raw_indices(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+ static const ::mozilla::safebrowsing::RiceDeltaEncoding& rice_hashes(const ThreatEntrySet* msg);
+ static void set_has_rice_hashes(HasBits* has_bits) {
+ (*has_bits)[0] |= 4u;
+ }
+ static const ::mozilla::safebrowsing::RiceDeltaEncoding& rice_indices(const ThreatEntrySet* msg);
+ static void set_has_rice_indices(HasBits* has_bits) {
+ (*has_bits)[0] |= 8u;
+ }
+};
+
+const ::mozilla::safebrowsing::RawHashes&
+ThreatEntrySet::_Internal::raw_hashes(const ThreatEntrySet* msg) {
+ return *msg->_impl_.raw_hashes_;
+}
+const ::mozilla::safebrowsing::RawIndices&
+ThreatEntrySet::_Internal::raw_indices(const ThreatEntrySet* msg) {
+ return *msg->_impl_.raw_indices_;
+}
+const ::mozilla::safebrowsing::RiceDeltaEncoding&
+ThreatEntrySet::_Internal::rice_hashes(const ThreatEntrySet* msg) {
+ return *msg->_impl_.rice_hashes_;
+}
+const ::mozilla::safebrowsing::RiceDeltaEncoding&
+ThreatEntrySet::_Internal::rice_indices(const ThreatEntrySet* msg) {
+ return *msg->_impl_.rice_indices_;
+}
+ThreatEntrySet::ThreatEntrySet(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.ThreatEntrySet)
+}
+ThreatEntrySet::ThreatEntrySet(const ThreatEntrySet& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ ThreatEntrySet* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.raw_hashes_){nullptr}
+ , decltype(_impl_.raw_indices_){nullptr}
+ , decltype(_impl_.rice_hashes_){nullptr}
+ , decltype(_impl_.rice_indices_){nullptr}
+ , decltype(_impl_.compression_type_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ if (from._internal_has_raw_hashes()) {
+ _this->_impl_.raw_hashes_ = new ::mozilla::safebrowsing::RawHashes(*from._impl_.raw_hashes_);
+ }
+ if (from._internal_has_raw_indices()) {
+ _this->_impl_.raw_indices_ = new ::mozilla::safebrowsing::RawIndices(*from._impl_.raw_indices_);
+ }
+ if (from._internal_has_rice_hashes()) {
+ _this->_impl_.rice_hashes_ = new ::mozilla::safebrowsing::RiceDeltaEncoding(*from._impl_.rice_hashes_);
+ }
+ if (from._internal_has_rice_indices()) {
+ _this->_impl_.rice_indices_ = new ::mozilla::safebrowsing::RiceDeltaEncoding(*from._impl_.rice_indices_);
+ }
+ _this->_impl_.compression_type_ = from._impl_.compression_type_;
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.ThreatEntrySet)
+}
+
+inline void ThreatEntrySet::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.raw_hashes_){nullptr}
+ , decltype(_impl_.raw_indices_){nullptr}
+ , decltype(_impl_.rice_hashes_){nullptr}
+ , decltype(_impl_.rice_indices_){nullptr}
+ , decltype(_impl_.compression_type_){0}
+ };
+}
+
+ThreatEntrySet::~ThreatEntrySet() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.ThreatEntrySet)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void ThreatEntrySet::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ if (this != internal_default_instance()) delete _impl_.raw_hashes_;
+ if (this != internal_default_instance()) delete _impl_.raw_indices_;
+ if (this != internal_default_instance()) delete _impl_.rice_hashes_;
+ if (this != internal_default_instance()) delete _impl_.rice_indices_;
+}
+
+void ThreatEntrySet::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void ThreatEntrySet::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.ThreatEntrySet)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x0000000fu) {
+ if (cached_has_bits & 0x00000001u) {
+ GOOGLE_DCHECK(_impl_.raw_hashes_ != nullptr);
+ _impl_.raw_hashes_->Clear();
+ }
+ if (cached_has_bits & 0x00000002u) {
+ GOOGLE_DCHECK(_impl_.raw_indices_ != nullptr);
+ _impl_.raw_indices_->Clear();
+ }
+ if (cached_has_bits & 0x00000004u) {
+ GOOGLE_DCHECK(_impl_.rice_hashes_ != nullptr);
+ _impl_.rice_hashes_->Clear();
+ }
+ if (cached_has_bits & 0x00000008u) {
+ GOOGLE_DCHECK(_impl_.rice_indices_ != nullptr);
+ _impl_.rice_indices_->Clear();
+ }
+ }
+ _impl_.compression_type_ = 0;
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* ThreatEntrySet::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional .mozilla.safebrowsing.CompressionType compression_type = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::CompressionType_IsValid(val))) {
+ _internal_set_compression_type(static_cast<::mozilla::safebrowsing::CompressionType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(1, val, mutable_unknown_fields());
+ }
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.RawHashes raw_hashes = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+ ptr = ctx->ParseMessage(_internal_mutable_raw_hashes(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.RawIndices raw_indices = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 26)) {
+ ptr = ctx->ParseMessage(_internal_mutable_raw_indices(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.RiceDeltaEncoding rice_hashes = 4;
+ case 4:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 34)) {
+ ptr = ctx->ParseMessage(_internal_mutable_rice_hashes(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.RiceDeltaEncoding rice_indices = 5;
+ case 5:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 42)) {
+ ptr = ctx->ParseMessage(_internal_mutable_rice_indices(), ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* ThreatEntrySet::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.ThreatEntrySet)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional .mozilla.safebrowsing.CompressionType compression_type = 1;
+ if (cached_has_bits & 0x00000010u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 1, this->_internal_compression_type(), target);
+ }
+
+ // optional .mozilla.safebrowsing.RawHashes raw_hashes = 2;
+ if (cached_has_bits & 0x00000001u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(2, _Internal::raw_hashes(this),
+ _Internal::raw_hashes(this).GetCachedSize(), target, stream);
+ }
+
+ // optional .mozilla.safebrowsing.RawIndices raw_indices = 3;
+ if (cached_has_bits & 0x00000002u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(3, _Internal::raw_indices(this),
+ _Internal::raw_indices(this).GetCachedSize(), target, stream);
+ }
+
+ // optional .mozilla.safebrowsing.RiceDeltaEncoding rice_hashes = 4;
+ if (cached_has_bits & 0x00000004u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(4, _Internal::rice_hashes(this),
+ _Internal::rice_hashes(this).GetCachedSize(), target, stream);
+ }
+
+ // optional .mozilla.safebrowsing.RiceDeltaEncoding rice_indices = 5;
+ if (cached_has_bits & 0x00000008u) {
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(5, _Internal::rice_indices(this),
+ _Internal::rice_indices(this).GetCachedSize(), target, stream);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.ThreatEntrySet)
+ return target;
+}
+
+size_t ThreatEntrySet::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.ThreatEntrySet)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x0000001fu) {
+ // optional .mozilla.safebrowsing.RawHashes raw_hashes = 2;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.raw_hashes_);
+ }
+
+ // optional .mozilla.safebrowsing.RawIndices raw_indices = 3;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.raw_indices_);
+ }
+
+ // optional .mozilla.safebrowsing.RiceDeltaEncoding rice_hashes = 4;
+ if (cached_has_bits & 0x00000004u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.rice_hashes_);
+ }
+
+ // optional .mozilla.safebrowsing.RiceDeltaEncoding rice_indices = 5;
+ if (cached_has_bits & 0x00000008u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(
+ *_impl_.rice_indices_);
+ }
+
+ // optional .mozilla.safebrowsing.CompressionType compression_type = 1;
+ if (cached_has_bits & 0x00000010u) {
+ total_size += 1 +
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_compression_type());
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void ThreatEntrySet::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const ThreatEntrySet*>(
+ &from));
+}
+
+void ThreatEntrySet::MergeFrom(const ThreatEntrySet& from) {
+ ThreatEntrySet* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.ThreatEntrySet)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x0000001fu) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_internal_mutable_raw_hashes()->::mozilla::safebrowsing::RawHashes::MergeFrom(
+ from._internal_raw_hashes());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_internal_mutable_raw_indices()->::mozilla::safebrowsing::RawIndices::MergeFrom(
+ from._internal_raw_indices());
+ }
+ if (cached_has_bits & 0x00000004u) {
+ _this->_internal_mutable_rice_hashes()->::mozilla::safebrowsing::RiceDeltaEncoding::MergeFrom(
+ from._internal_rice_hashes());
+ }
+ if (cached_has_bits & 0x00000008u) {
+ _this->_internal_mutable_rice_indices()->::mozilla::safebrowsing::RiceDeltaEncoding::MergeFrom(
+ from._internal_rice_indices());
+ }
+ if (cached_has_bits & 0x00000010u) {
+ _this->_impl_.compression_type_ = from._impl_.compression_type_;
+ }
+ _this->_impl_._has_bits_[0] |= cached_has_bits;
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void ThreatEntrySet::CopyFrom(const ThreatEntrySet& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.ThreatEntrySet)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool ThreatEntrySet::IsInitialized() const {
+ return true;
+}
+
+void ThreatEntrySet::InternalSwap(ThreatEntrySet* other) {
+ using std::swap;
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+ PROTOBUF_FIELD_OFFSET(ThreatEntrySet, _impl_.compression_type_)
+ + sizeof(ThreatEntrySet::_impl_.compression_type_)
+ - PROTOBUF_FIELD_OFFSET(ThreatEntrySet, _impl_.raw_hashes_)>(
+ reinterpret_cast<char*>(&_impl_.raw_hashes_),
+ reinterpret_cast<char*>(&other->_impl_.raw_hashes_));
+}
+
+std::string ThreatEntrySet::GetTypeName() const {
+ return "mozilla.safebrowsing.ThreatEntrySet";
+}
+
+
+// ===================================================================
+
+class RawIndices::_Internal {
+ public:
+};
+
+RawIndices::RawIndices(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.RawIndices)
+}
+RawIndices::RawIndices(const RawIndices& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ RawIndices* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_.indices_){from._impl_.indices_}
+ , /*decltype(_impl_._cached_size_)*/{}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.RawIndices)
+}
+
+inline void RawIndices::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_.indices_){arena}
+ , /*decltype(_impl_._cached_size_)*/{}
+ };
+}
+
+RawIndices::~RawIndices() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.RawIndices)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void RawIndices::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.indices_.~RepeatedField();
+}
+
+void RawIndices::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void RawIndices::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.RawIndices)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ _impl_.indices_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* RawIndices::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // repeated int32 indices = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+ ptr -= 1;
+ do {
+ ptr += 1;
+ _internal_add_indices(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr));
+ CHK_(ptr);
+ if (!ctx->DataAvailable(ptr)) break;
+ } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<8>(ptr));
+ } else if (static_cast<uint8_t>(tag) == 10) {
+ ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt32Parser(_internal_mutable_indices(), ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* RawIndices::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.RawIndices)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ // repeated int32 indices = 1;
+ for (int i = 0, n = this->_internal_indices_size(); i < n; i++) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteInt32ToArray(1, this->_internal_indices(i), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.RawIndices)
+ return target;
+}
+
+size_t RawIndices::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.RawIndices)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ // repeated int32 indices = 1;
+ {
+ size_t data_size = ::_pbi::WireFormatLite::
+ Int32Size(this->_impl_.indices_);
+ total_size += 1 *
+ ::_pbi::FromIntSize(this->_internal_indices_size());
+ total_size += data_size;
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void RawIndices::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const RawIndices*>(
+ &from));
+}
+
+void RawIndices::MergeFrom(const RawIndices& from) {
+ RawIndices* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.RawIndices)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ _this->_impl_.indices_.MergeFrom(from._impl_.indices_);
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void RawIndices::CopyFrom(const RawIndices& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.RawIndices)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool RawIndices::IsInitialized() const {
+ return true;
+}
+
+void RawIndices::InternalSwap(RawIndices* other) {
+ using std::swap;
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ _impl_.indices_.InternalSwap(&other->_impl_.indices_);
+}
+
+std::string RawIndices::GetTypeName() const {
+ return "mozilla.safebrowsing.RawIndices";
+}
+
+
+// ===================================================================
+
+class RawHashes::_Internal {
+ public:
+ using HasBits = decltype(std::declval<RawHashes>()._impl_._has_bits_);
+ static void set_has_prefix_size(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+ static void set_has_raw_hashes(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+};
+
+RawHashes::RawHashes(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.RawHashes)
+}
+RawHashes::RawHashes(const RawHashes& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ RawHashes* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.raw_hashes_){}
+ , decltype(_impl_.prefix_size_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ _impl_.raw_hashes_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.raw_hashes_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (from._internal_has_raw_hashes()) {
+ _this->_impl_.raw_hashes_.Set(from._internal_raw_hashes(),
+ _this->GetArenaForAllocation());
+ }
+ _this->_impl_.prefix_size_ = from._impl_.prefix_size_;
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.RawHashes)
+}
+
+inline void RawHashes::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.raw_hashes_){}
+ , decltype(_impl_.prefix_size_){0}
+ };
+ _impl_.raw_hashes_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.raw_hashes_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+RawHashes::~RawHashes() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.RawHashes)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void RawHashes::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.raw_hashes_.Destroy();
+}
+
+void RawHashes::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void RawHashes::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.RawHashes)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000001u) {
+ _impl_.raw_hashes_.ClearNonDefaultToEmpty();
+ }
+ _impl_.prefix_size_ = 0;
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* RawHashes::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional int32 prefix_size = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+ _Internal::set_has_prefix_size(&has_bits);
+ _impl_.prefix_size_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional bytes raw_hashes = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+ auto str = _internal_mutable_raw_hashes();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* RawHashes::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.RawHashes)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional int32 prefix_size = 1;
+ if (cached_has_bits & 0x00000002u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteInt32ToArray(1, this->_internal_prefix_size(), target);
+ }
+
+ // optional bytes raw_hashes = 2;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->WriteBytesMaybeAliased(
+ 2, this->_internal_raw_hashes(), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.RawHashes)
+ return target;
+}
+
+size_t RawHashes::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.RawHashes)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ // optional bytes raw_hashes = 2;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+ this->_internal_raw_hashes());
+ }
+
+ // optional int32 prefix_size = 1;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_prefix_size());
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void RawHashes::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const RawHashes*>(
+ &from));
+}
+
+void RawHashes::MergeFrom(const RawHashes& from) {
+ RawHashes* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.RawHashes)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_internal_set_raw_hashes(from._internal_raw_hashes());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_impl_.prefix_size_ = from._impl_.prefix_size_;
+ }
+ _this->_impl_._has_bits_[0] |= cached_has_bits;
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void RawHashes::CopyFrom(const RawHashes& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.RawHashes)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool RawHashes::IsInitialized() const {
+ return true;
+}
+
+void RawHashes::InternalSwap(RawHashes* other) {
+ using std::swap;
+ auto* lhs_arena = GetArenaForAllocation();
+ auto* rhs_arena = other->GetArenaForAllocation();
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+ &_impl_.raw_hashes_, lhs_arena,
+ &other->_impl_.raw_hashes_, rhs_arena
+ );
+ swap(_impl_.prefix_size_, other->_impl_.prefix_size_);
+}
+
+std::string RawHashes::GetTypeName() const {
+ return "mozilla.safebrowsing.RawHashes";
+}
+
+
+// ===================================================================
+
+class RiceDeltaEncoding::_Internal {
+ public:
+ using HasBits = decltype(std::declval<RiceDeltaEncoding>()._impl_._has_bits_);
+ static void set_has_first_value(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+ static void set_has_rice_parameter(HasBits* has_bits) {
+ (*has_bits)[0] |= 4u;
+ }
+ static void set_has_num_entries(HasBits* has_bits) {
+ (*has_bits)[0] |= 8u;
+ }
+ static void set_has_encoded_data(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+};
+
+RiceDeltaEncoding::RiceDeltaEncoding(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.RiceDeltaEncoding)
+}
+RiceDeltaEncoding::RiceDeltaEncoding(const RiceDeltaEncoding& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ RiceDeltaEncoding* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.encoded_data_){}
+ , decltype(_impl_.first_value_){}
+ , decltype(_impl_.rice_parameter_){}
+ , decltype(_impl_.num_entries_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ _impl_.encoded_data_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.encoded_data_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (from._internal_has_encoded_data()) {
+ _this->_impl_.encoded_data_.Set(from._internal_encoded_data(),
+ _this->GetArenaForAllocation());
+ }
+ ::memcpy(&_impl_.first_value_, &from._impl_.first_value_,
+ static_cast<size_t>(reinterpret_cast<char*>(&_impl_.num_entries_) -
+ reinterpret_cast<char*>(&_impl_.first_value_)) + sizeof(_impl_.num_entries_));
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.RiceDeltaEncoding)
+}
+
+inline void RiceDeltaEncoding::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.encoded_data_){}
+ , decltype(_impl_.first_value_){int64_t{0}}
+ , decltype(_impl_.rice_parameter_){0}
+ , decltype(_impl_.num_entries_){0}
+ };
+ _impl_.encoded_data_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.encoded_data_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+RiceDeltaEncoding::~RiceDeltaEncoding() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.RiceDeltaEncoding)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void RiceDeltaEncoding::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.encoded_data_.Destroy();
+}
+
+void RiceDeltaEncoding::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void RiceDeltaEncoding::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.RiceDeltaEncoding)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000001u) {
+ _impl_.encoded_data_.ClearNonDefaultToEmpty();
+ }
+ if (cached_has_bits & 0x0000000eu) {
+ ::memset(&_impl_.first_value_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&_impl_.num_entries_) -
+ reinterpret_cast<char*>(&_impl_.first_value_)) + sizeof(_impl_.num_entries_));
+ }
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* RiceDeltaEncoding::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional int64 first_value = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+ _Internal::set_has_first_value(&has_bits);
+ _impl_.first_value_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional int32 rice_parameter = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+ _Internal::set_has_rice_parameter(&has_bits);
+ _impl_.rice_parameter_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional int32 num_entries = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 24)) {
+ _Internal::set_has_num_entries(&has_bits);
+ _impl_.num_entries_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional bytes encoded_data = 4;
+ case 4:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 34)) {
+ auto str = _internal_mutable_encoded_data();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* RiceDeltaEncoding::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.RiceDeltaEncoding)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional int64 first_value = 1;
+ if (cached_has_bits & 0x00000002u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteInt64ToArray(1, this->_internal_first_value(), target);
+ }
+
+ // optional int32 rice_parameter = 2;
+ if (cached_has_bits & 0x00000004u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteInt32ToArray(2, this->_internal_rice_parameter(), target);
+ }
+
+ // optional int32 num_entries = 3;
+ if (cached_has_bits & 0x00000008u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteInt32ToArray(3, this->_internal_num_entries(), target);
+ }
+
+ // optional bytes encoded_data = 4;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->WriteBytesMaybeAliased(
+ 4, this->_internal_encoded_data(), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.RiceDeltaEncoding)
+ return target;
+}
+
+size_t RiceDeltaEncoding::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.RiceDeltaEncoding)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x0000000fu) {
+ // optional bytes encoded_data = 4;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+ this->_internal_encoded_data());
+ }
+
+ // optional int64 first_value = 1;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += ::_pbi::WireFormatLite::Int64SizePlusOne(this->_internal_first_value());
+ }
+
+ // optional int32 rice_parameter = 2;
+ if (cached_has_bits & 0x00000004u) {
+ total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_rice_parameter());
+ }
+
+ // optional int32 num_entries = 3;
+ if (cached_has_bits & 0x00000008u) {
+ total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_num_entries());
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void RiceDeltaEncoding::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const RiceDeltaEncoding*>(
+ &from));
+}
+
+void RiceDeltaEncoding::MergeFrom(const RiceDeltaEncoding& from) {
+ RiceDeltaEncoding* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.RiceDeltaEncoding)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x0000000fu) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_internal_set_encoded_data(from._internal_encoded_data());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_impl_.first_value_ = from._impl_.first_value_;
+ }
+ if (cached_has_bits & 0x00000004u) {
+ _this->_impl_.rice_parameter_ = from._impl_.rice_parameter_;
+ }
+ if (cached_has_bits & 0x00000008u) {
+ _this->_impl_.num_entries_ = from._impl_.num_entries_;
+ }
+ _this->_impl_._has_bits_[0] |= cached_has_bits;
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void RiceDeltaEncoding::CopyFrom(const RiceDeltaEncoding& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.RiceDeltaEncoding)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool RiceDeltaEncoding::IsInitialized() const {
+ return true;
+}
+
+void RiceDeltaEncoding::InternalSwap(RiceDeltaEncoding* other) {
+ using std::swap;
+ auto* lhs_arena = GetArenaForAllocation();
+ auto* rhs_arena = other->GetArenaForAllocation();
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+ &_impl_.encoded_data_, lhs_arena,
+ &other->_impl_.encoded_data_, rhs_arena
+ );
+ ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+ PROTOBUF_FIELD_OFFSET(RiceDeltaEncoding, _impl_.num_entries_)
+ + sizeof(RiceDeltaEncoding::_impl_.num_entries_)
+ - PROTOBUF_FIELD_OFFSET(RiceDeltaEncoding, _impl_.first_value_)>(
+ reinterpret_cast<char*>(&_impl_.first_value_),
+ reinterpret_cast<char*>(&other->_impl_.first_value_));
+}
+
+std::string RiceDeltaEncoding::GetTypeName() const {
+ return "mozilla.safebrowsing.RiceDeltaEncoding";
+}
+
+
+// ===================================================================
+
+class ThreatEntryMetadata_MetadataEntry::_Internal {
+ public:
+ using HasBits = decltype(std::declval<ThreatEntryMetadata_MetadataEntry>()._impl_._has_bits_);
+ static void set_has_key(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static void set_has_value(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+};
+
+ThreatEntryMetadata_MetadataEntry::ThreatEntryMetadata_MetadataEntry(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry)
+}
+ThreatEntryMetadata_MetadataEntry::ThreatEntryMetadata_MetadataEntry(const ThreatEntryMetadata_MetadataEntry& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ ThreatEntryMetadata_MetadataEntry* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.key_){}
+ , decltype(_impl_.value_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ _impl_.key_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.key_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (from._internal_has_key()) {
+ _this->_impl_.key_.Set(from._internal_key(),
+ _this->GetArenaForAllocation());
+ }
+ _impl_.value_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.value_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (from._internal_has_value()) {
+ _this->_impl_.value_.Set(from._internal_value(),
+ _this->GetArenaForAllocation());
+ }
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry)
+}
+
+inline void ThreatEntryMetadata_MetadataEntry::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.key_){}
+ , decltype(_impl_.value_){}
+ };
+ _impl_.key_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.key_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.value_.InitDefault();
+ #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ _impl_.value_.Set("", GetArenaForAllocation());
+ #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+}
+
+ThreatEntryMetadata_MetadataEntry::~ThreatEntryMetadata_MetadataEntry() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void ThreatEntryMetadata_MetadataEntry::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.key_.Destroy();
+ _impl_.value_.Destroy();
+}
+
+void ThreatEntryMetadata_MetadataEntry::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void ThreatEntryMetadata_MetadataEntry::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ _impl_.key_.ClearNonDefaultToEmpty();
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _impl_.value_.ClearNonDefaultToEmpty();
+ }
+ }
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* ThreatEntryMetadata_MetadataEntry::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional bytes key = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+ auto str = _internal_mutable_key();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional bytes value = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 18)) {
+ auto str = _internal_mutable_value();
+ ptr = ::_pbi::InlineGreedyStringParser(str, ptr, ctx);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* ThreatEntryMetadata_MetadataEntry::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional bytes key = 1;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->WriteBytesMaybeAliased(
+ 1, this->_internal_key(), target);
+ }
+
+ // optional bytes value = 2;
+ if (cached_has_bits & 0x00000002u) {
+ target = stream->WriteBytesMaybeAliased(
+ 2, this->_internal_value(), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry)
+ return target;
+}
+
+size_t ThreatEntryMetadata_MetadataEntry::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ // optional bytes key = 1;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+ this->_internal_key());
+ }
+
+ // optional bytes value = 2;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 +
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize(
+ this->_internal_value());
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void ThreatEntryMetadata_MetadataEntry::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const ThreatEntryMetadata_MetadataEntry*>(
+ &from));
+}
+
+void ThreatEntryMetadata_MetadataEntry::MergeFrom(const ThreatEntryMetadata_MetadataEntry& from) {
+ ThreatEntryMetadata_MetadataEntry* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_internal_set_key(from._internal_key());
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_internal_set_value(from._internal_value());
+ }
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void ThreatEntryMetadata_MetadataEntry::CopyFrom(const ThreatEntryMetadata_MetadataEntry& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool ThreatEntryMetadata_MetadataEntry::IsInitialized() const {
+ return true;
+}
+
+void ThreatEntryMetadata_MetadataEntry::InternalSwap(ThreatEntryMetadata_MetadataEntry* other) {
+ using std::swap;
+ auto* lhs_arena = GetArenaForAllocation();
+ auto* rhs_arena = other->GetArenaForAllocation();
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+ &_impl_.key_, lhs_arena,
+ &other->_impl_.key_, rhs_arena
+ );
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap(
+ &_impl_.value_, lhs_arena,
+ &other->_impl_.value_, rhs_arena
+ );
+}
+
+std::string ThreatEntryMetadata_MetadataEntry::GetTypeName() const {
+ return "mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry";
+}
+
+
+// ===================================================================
+
+class ThreatEntryMetadata::_Internal {
+ public:
+};
+
+ThreatEntryMetadata::ThreatEntryMetadata(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.ThreatEntryMetadata)
+}
+ThreatEntryMetadata::ThreatEntryMetadata(const ThreatEntryMetadata& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ ThreatEntryMetadata* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_.entries_){from._impl_.entries_}
+ , /*decltype(_impl_._cached_size_)*/{}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.ThreatEntryMetadata)
+}
+
+inline void ThreatEntryMetadata::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_.entries_){arena}
+ , /*decltype(_impl_._cached_size_)*/{}
+ };
+}
+
+ThreatEntryMetadata::~ThreatEntryMetadata() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.ThreatEntryMetadata)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void ThreatEntryMetadata::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.entries_.~RepeatedPtrField();
+}
+
+void ThreatEntryMetadata::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void ThreatEntryMetadata::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.ThreatEntryMetadata)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ _impl_.entries_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* ThreatEntryMetadata::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // repeated .mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry entries = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+ ptr -= 1;
+ do {
+ ptr += 1;
+ ptr = ctx->ParseMessage(_internal_add_entries(), ptr);
+ CHK_(ptr);
+ if (!ctx->DataAvailable(ptr)) break;
+ } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<10>(ptr));
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* ThreatEntryMetadata::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.ThreatEntryMetadata)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ // repeated .mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry entries = 1;
+ for (unsigned i = 0,
+ n = static_cast<unsigned>(this->_internal_entries_size()); i < n; i++) {
+ const auto& repfield = this->_internal_entries(i);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(1, repfield, repfield.GetCachedSize(), target, stream);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.ThreatEntryMetadata)
+ return target;
+}
+
+size_t ThreatEntryMetadata::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.ThreatEntryMetadata)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ // repeated .mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry entries = 1;
+ total_size += 1UL * this->_internal_entries_size();
+ for (const auto& msg : this->_impl_.entries_) {
+ total_size +=
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void ThreatEntryMetadata::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const ThreatEntryMetadata*>(
+ &from));
+}
+
+void ThreatEntryMetadata::MergeFrom(const ThreatEntryMetadata& from) {
+ ThreatEntryMetadata* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.ThreatEntryMetadata)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ _this->_impl_.entries_.MergeFrom(from._impl_.entries_);
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void ThreatEntryMetadata::CopyFrom(const ThreatEntryMetadata& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.ThreatEntryMetadata)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool ThreatEntryMetadata::IsInitialized() const {
+ return true;
+}
+
+void ThreatEntryMetadata::InternalSwap(ThreatEntryMetadata* other) {
+ using std::swap;
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ _impl_.entries_.InternalSwap(&other->_impl_.entries_);
+}
+
+std::string ThreatEntryMetadata::GetTypeName() const {
+ return "mozilla.safebrowsing.ThreatEntryMetadata";
+}
+
+
+// ===================================================================
+
+class ThreatListDescriptor::_Internal {
+ public:
+ using HasBits = decltype(std::declval<ThreatListDescriptor>()._impl_._has_bits_);
+ static void set_has_threat_type(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static void set_has_platform_type(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+ static void set_has_threat_entry_type(HasBits* has_bits) {
+ (*has_bits)[0] |= 4u;
+ }
+};
+
+ThreatListDescriptor::ThreatListDescriptor(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.ThreatListDescriptor)
+}
+ThreatListDescriptor::ThreatListDescriptor(const ThreatListDescriptor& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ ThreatListDescriptor* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.threat_type_){}
+ , decltype(_impl_.platform_type_){}
+ , decltype(_impl_.threat_entry_type_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ ::memcpy(&_impl_.threat_type_, &from._impl_.threat_type_,
+ static_cast<size_t>(reinterpret_cast<char*>(&_impl_.threat_entry_type_) -
+ reinterpret_cast<char*>(&_impl_.threat_type_)) + sizeof(_impl_.threat_entry_type_));
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.ThreatListDescriptor)
+}
+
+inline void ThreatListDescriptor::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.threat_type_){0}
+ , decltype(_impl_.platform_type_){0}
+ , decltype(_impl_.threat_entry_type_){0}
+ };
+}
+
+ThreatListDescriptor::~ThreatListDescriptor() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.ThreatListDescriptor)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void ThreatListDescriptor::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+}
+
+void ThreatListDescriptor::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void ThreatListDescriptor::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.ThreatListDescriptor)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000007u) {
+ ::memset(&_impl_.threat_type_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&_impl_.threat_entry_type_) -
+ reinterpret_cast<char*>(&_impl_.threat_type_)) + sizeof(_impl_.threat_entry_type_));
+ }
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* ThreatListDescriptor::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::ThreatType_IsValid(val))) {
+ _internal_set_threat_type(static_cast<::mozilla::safebrowsing::ThreatType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(1, val, mutable_unknown_fields());
+ }
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::PlatformType_IsValid(val))) {
+ _internal_set_platform_type(static_cast<::mozilla::safebrowsing::PlatformType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(2, val, mutable_unknown_fields());
+ }
+ } else
+ goto handle_unusual;
+ continue;
+ // optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 3;
+ case 3:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 24)) {
+ uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ if (PROTOBUF_PREDICT_TRUE(::mozilla::safebrowsing::ThreatEntryType_IsValid(val))) {
+ _internal_set_threat_entry_type(static_cast<::mozilla::safebrowsing::ThreatEntryType>(val));
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(3, val, mutable_unknown_fields());
+ }
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* ThreatListDescriptor::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.ThreatListDescriptor)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 1, this->_internal_threat_type(), target);
+ }
+
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+ if (cached_has_bits & 0x00000002u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 2, this->_internal_platform_type(), target);
+ }
+
+ // optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 3;
+ if (cached_has_bits & 0x00000004u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteEnumToArray(
+ 3, this->_internal_threat_entry_type(), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.ThreatListDescriptor)
+ return target;
+}
+
+size_t ThreatListDescriptor::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.ThreatListDescriptor)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000007u) {
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += 1 +
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_threat_type());
+ }
+
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += 1 +
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_platform_type());
+ }
+
+ // optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 3;
+ if (cached_has_bits & 0x00000004u) {
+ total_size += 1 +
+ ::_pbi::WireFormatLite::EnumSize(this->_internal_threat_entry_type());
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void ThreatListDescriptor::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const ThreatListDescriptor*>(
+ &from));
+}
+
+void ThreatListDescriptor::MergeFrom(const ThreatListDescriptor& from) {
+ ThreatListDescriptor* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.ThreatListDescriptor)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000007u) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_impl_.threat_type_ = from._impl_.threat_type_;
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_impl_.platform_type_ = from._impl_.platform_type_;
+ }
+ if (cached_has_bits & 0x00000004u) {
+ _this->_impl_.threat_entry_type_ = from._impl_.threat_entry_type_;
+ }
+ _this->_impl_._has_bits_[0] |= cached_has_bits;
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void ThreatListDescriptor::CopyFrom(const ThreatListDescriptor& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.ThreatListDescriptor)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool ThreatListDescriptor::IsInitialized() const {
+ return true;
+}
+
+void ThreatListDescriptor::InternalSwap(ThreatListDescriptor* other) {
+ using std::swap;
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+ PROTOBUF_FIELD_OFFSET(ThreatListDescriptor, _impl_.threat_entry_type_)
+ + sizeof(ThreatListDescriptor::_impl_.threat_entry_type_)
+ - PROTOBUF_FIELD_OFFSET(ThreatListDescriptor, _impl_.threat_type_)>(
+ reinterpret_cast<char*>(&_impl_.threat_type_),
+ reinterpret_cast<char*>(&other->_impl_.threat_type_));
+}
+
+std::string ThreatListDescriptor::GetTypeName() const {
+ return "mozilla.safebrowsing.ThreatListDescriptor";
+}
+
+
+// ===================================================================
+
+class ListThreatListsResponse::_Internal {
+ public:
+};
+
+ListThreatListsResponse::ListThreatListsResponse(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.ListThreatListsResponse)
+}
+ListThreatListsResponse::ListThreatListsResponse(const ListThreatListsResponse& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ ListThreatListsResponse* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_.threat_lists_){from._impl_.threat_lists_}
+ , /*decltype(_impl_._cached_size_)*/{}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.ListThreatListsResponse)
+}
+
+inline void ListThreatListsResponse::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_.threat_lists_){arena}
+ , /*decltype(_impl_._cached_size_)*/{}
+ };
+}
+
+ListThreatListsResponse::~ListThreatListsResponse() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.ListThreatListsResponse)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void ListThreatListsResponse::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+ _impl_.threat_lists_.~RepeatedPtrField();
+}
+
+void ListThreatListsResponse::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void ListThreatListsResponse::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.ListThreatListsResponse)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ _impl_.threat_lists_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* ListThreatListsResponse::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // repeated .mozilla.safebrowsing.ThreatListDescriptor threat_lists = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 10)) {
+ ptr -= 1;
+ do {
+ ptr += 1;
+ ptr = ctx->ParseMessage(_internal_add_threat_lists(), ptr);
+ CHK_(ptr);
+ if (!ctx->DataAvailable(ptr)) break;
+ } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<10>(ptr));
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* ListThreatListsResponse::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.ListThreatListsResponse)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ // repeated .mozilla.safebrowsing.ThreatListDescriptor threat_lists = 1;
+ for (unsigned i = 0,
+ n = static_cast<unsigned>(this->_internal_threat_lists_size()); i < n; i++) {
+ const auto& repfield = this->_internal_threat_lists(i);
+ target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::
+ InternalWriteMessage(1, repfield, repfield.GetCachedSize(), target, stream);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.ListThreatListsResponse)
+ return target;
+}
+
+size_t ListThreatListsResponse::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.ListThreatListsResponse)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ // repeated .mozilla.safebrowsing.ThreatListDescriptor threat_lists = 1;
+ total_size += 1UL * this->_internal_threat_lists_size();
+ for (const auto& msg : this->_impl_.threat_lists_) {
+ total_size +=
+ ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void ListThreatListsResponse::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const ListThreatListsResponse*>(
+ &from));
+}
+
+void ListThreatListsResponse::MergeFrom(const ListThreatListsResponse& from) {
+ ListThreatListsResponse* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.ListThreatListsResponse)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ _this->_impl_.threat_lists_.MergeFrom(from._impl_.threat_lists_);
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void ListThreatListsResponse::CopyFrom(const ListThreatListsResponse& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.ListThreatListsResponse)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool ListThreatListsResponse::IsInitialized() const {
+ return true;
+}
+
+void ListThreatListsResponse::InternalSwap(ListThreatListsResponse* other) {
+ using std::swap;
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ _impl_.threat_lists_.InternalSwap(&other->_impl_.threat_lists_);
+}
+
+std::string ListThreatListsResponse::GetTypeName() const {
+ return "mozilla.safebrowsing.ListThreatListsResponse";
+}
+
+
+// ===================================================================
+
+class Duration::_Internal {
+ public:
+ using HasBits = decltype(std::declval<Duration>()._impl_._has_bits_);
+ static void set_has_seconds(HasBits* has_bits) {
+ (*has_bits)[0] |= 1u;
+ }
+ static void set_has_nanos(HasBits* has_bits) {
+ (*has_bits)[0] |= 2u;
+ }
+};
+
+Duration::Duration(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) {
+ SharedCtor(arena, is_message_owned);
+ // @@protoc_insertion_point(arena_constructor:mozilla.safebrowsing.Duration)
+}
+Duration::Duration(const Duration& from)
+ : ::PROTOBUF_NAMESPACE_ID::MessageLite() {
+ Duration* const _this = this; (void)_this;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){from._impl_._has_bits_}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.seconds_){}
+ , decltype(_impl_.nanos_){}};
+
+ _internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+ ::memcpy(&_impl_.seconds_, &from._impl_.seconds_,
+ static_cast<size_t>(reinterpret_cast<char*>(&_impl_.nanos_) -
+ reinterpret_cast<char*>(&_impl_.seconds_)) + sizeof(_impl_.nanos_));
+ // @@protoc_insertion_point(copy_constructor:mozilla.safebrowsing.Duration)
+}
+
+inline void Duration::SharedCtor(
+ ::_pb::Arena* arena, bool is_message_owned) {
+ (void)arena;
+ (void)is_message_owned;
+ new (&_impl_) Impl_{
+ decltype(_impl_._has_bits_){}
+ , /*decltype(_impl_._cached_size_)*/{}
+ , decltype(_impl_.seconds_){int64_t{0}}
+ , decltype(_impl_.nanos_){0}
+ };
+}
+
+Duration::~Duration() {
+ // @@protoc_insertion_point(destructor:mozilla.safebrowsing.Duration)
+ if (auto *arena = _internal_metadata_.DeleteReturnArena<std::string>()) {
+ (void)arena;
+ return;
+ }
+ SharedDtor();
+}
+
+inline void Duration::SharedDtor() {
+ GOOGLE_DCHECK(GetArenaForAllocation() == nullptr);
+}
+
+void Duration::SetCachedSize(int size) const {
+ _impl_._cached_size_.Set(size);
+}
+
+void Duration::Clear() {
+// @@protoc_insertion_point(message_clear_start:mozilla.safebrowsing.Duration)
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ ::memset(&_impl_.seconds_, 0, static_cast<size_t>(
+ reinterpret_cast<char*>(&_impl_.nanos_) -
+ reinterpret_cast<char*>(&_impl_.seconds_)) + sizeof(_impl_.nanos_));
+ }
+ _impl_._has_bits_.Clear();
+ _internal_metadata_.Clear<std::string>();
+}
+
+const char* Duration::_InternalParse(const char* ptr, ::_pbi::ParseContext* ctx) {
+#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure
+ _Internal::HasBits has_bits{};
+ while (!ctx->Done(&ptr)) {
+ uint32_t tag;
+ ptr = ::_pbi::ReadTag(ptr, &tag);
+ switch (tag >> 3) {
+ // optional int64 seconds = 1;
+ case 1:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 8)) {
+ _Internal::set_has_seconds(&has_bits);
+ _impl_.seconds_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ // optional int32 nanos = 2;
+ case 2:
+ if (PROTOBUF_PREDICT_TRUE(static_cast<uint8_t>(tag) == 16)) {
+ _Internal::set_has_nanos(&has_bits);
+ _impl_.nanos_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr);
+ CHK_(ptr);
+ } else
+ goto handle_unusual;
+ continue;
+ default:
+ goto handle_unusual;
+ } // switch
+ handle_unusual:
+ if ((tag == 0) || ((tag & 7) == 4)) {
+ CHK_(ptr);
+ ctx->SetLastTag(tag);
+ goto message_done;
+ }
+ ptr = UnknownFieldParse(
+ tag,
+ _internal_metadata_.mutable_unknown_fields<std::string>(),
+ ptr, ctx);
+ CHK_(ptr != nullptr);
+ } // while
+message_done:
+ _impl_._has_bits_.Or(has_bits);
+ return ptr;
+failure:
+ ptr = nullptr;
+ goto message_done;
+#undef CHK_
+}
+
+uint8_t* Duration::_InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const {
+ // @@protoc_insertion_point(serialize_to_array_start:mozilla.safebrowsing.Duration)
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ // optional int64 seconds = 1;
+ if (cached_has_bits & 0x00000001u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteInt64ToArray(1, this->_internal_seconds(), target);
+ }
+
+ // optional int32 nanos = 2;
+ if (cached_has_bits & 0x00000002u) {
+ target = stream->EnsureSpace(target);
+ target = ::_pbi::WireFormatLite::WriteInt32ToArray(2, this->_internal_nanos(), target);
+ }
+
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ target = stream->WriteRaw(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(),
+ static_cast<int>(_internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target);
+ }
+ // @@protoc_insertion_point(serialize_to_array_end:mozilla.safebrowsing.Duration)
+ return target;
+}
+
+size_t Duration::ByteSizeLong() const {
+// @@protoc_insertion_point(message_byte_size_start:mozilla.safebrowsing.Duration)
+ size_t total_size = 0;
+
+ uint32_t cached_has_bits = 0;
+ // Prevent compiler warnings about cached_has_bits being unused
+ (void) cached_has_bits;
+
+ cached_has_bits = _impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ // optional int64 seconds = 1;
+ if (cached_has_bits & 0x00000001u) {
+ total_size += ::_pbi::WireFormatLite::Int64SizePlusOne(this->_internal_seconds());
+ }
+
+ // optional int32 nanos = 2;
+ if (cached_has_bits & 0x00000002u) {
+ total_size += ::_pbi::WireFormatLite::Int32SizePlusOne(this->_internal_nanos());
+ }
+
+ }
+ if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) {
+ total_size += _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size();
+ }
+ int cached_size = ::_pbi::ToCachedSize(total_size);
+ SetCachedSize(cached_size);
+ return total_size;
+}
+
+void Duration::CheckTypeAndMergeFrom(
+ const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) {
+ MergeFrom(*::_pbi::DownCast<const Duration*>(
+ &from));
+}
+
+void Duration::MergeFrom(const Duration& from) {
+ Duration* const _this = this;
+ // @@protoc_insertion_point(class_specific_merge_from_start:mozilla.safebrowsing.Duration)
+ GOOGLE_DCHECK_NE(&from, _this);
+ uint32_t cached_has_bits = 0;
+ (void) cached_has_bits;
+
+ cached_has_bits = from._impl_._has_bits_[0];
+ if (cached_has_bits & 0x00000003u) {
+ if (cached_has_bits & 0x00000001u) {
+ _this->_impl_.seconds_ = from._impl_.seconds_;
+ }
+ if (cached_has_bits & 0x00000002u) {
+ _this->_impl_.nanos_ = from._impl_.nanos_;
+ }
+ _this->_impl_._has_bits_[0] |= cached_has_bits;
+ }
+ _this->_internal_metadata_.MergeFrom<std::string>(from._internal_metadata_);
+}
+
+void Duration::CopyFrom(const Duration& from) {
+// @@protoc_insertion_point(class_specific_copy_from_start:mozilla.safebrowsing.Duration)
+ if (&from == this) return;
+ Clear();
+ MergeFrom(from);
+}
+
+bool Duration::IsInitialized() const {
+ return true;
+}
+
+void Duration::InternalSwap(Duration* other) {
+ using std::swap;
+ _internal_metadata_.InternalSwap(&other->_internal_metadata_);
+ swap(_impl_._has_bits_[0], other->_impl_._has_bits_[0]);
+ ::PROTOBUF_NAMESPACE_ID::internal::memswap<
+ PROTOBUF_FIELD_OFFSET(Duration, _impl_.nanos_)
+ + sizeof(Duration::_impl_.nanos_)
+ - PROTOBUF_FIELD_OFFSET(Duration, _impl_.seconds_)>(
+ reinterpret_cast<char*>(&_impl_.seconds_),
+ reinterpret_cast<char*>(&other->_impl_.seconds_));
+}
+
+std::string Duration::GetTypeName() const {
+ return "mozilla.safebrowsing.Duration";
+}
+
+
+// @@protoc_insertion_point(namespace_scope)
+} // namespace safebrowsing
+} // namespace mozilla
+PROTOBUF_NAMESPACE_OPEN
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::ThreatInfo*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::ThreatInfo >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::ThreatInfo >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::ThreatMatch*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::ThreatMatch >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::ThreatMatch >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::FindThreatMatchesRequest*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::FindThreatMatchesRequest >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::FindThreatMatchesRequest >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::FindThreatMatchesResponse*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::FindThreatMatchesResponse >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::FindThreatMatchesResponse >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::FetchThreatListUpdatesRequest*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::FetchThreatListUpdatesRequest >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::FetchThreatListUpdatesRequest >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::FetchThreatListUpdatesResponse*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::FetchThreatListUpdatesResponse >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::FetchThreatListUpdatesResponse >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::FindFullHashesRequest*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::FindFullHashesRequest >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::FindFullHashesRequest >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::FindFullHashesResponse*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::FindFullHashesResponse >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::FindFullHashesResponse >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::ThreatHit_ThreatSource*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::ThreatHit_ThreatSource >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::ThreatHit_ThreatSource >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::ThreatHit_UserInfo*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::ThreatHit_UserInfo >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::ThreatHit_UserInfo >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::ThreatHit*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::ThreatHit >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::ThreatHit >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::ClientInfo*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::ClientInfo >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::ClientInfo >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::ChromeClientInfo*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::ChromeClientInfo >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::ChromeClientInfo >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::Checksum*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::Checksum >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::Checksum >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::ThreatEntry*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::ThreatEntry >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::ThreatEntry >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::ThreatEntrySet*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::ThreatEntrySet >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::ThreatEntrySet >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::RawIndices*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::RawIndices >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::RawIndices >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::RawHashes*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::RawHashes >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::RawHashes >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::RiceDeltaEncoding*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::RiceDeltaEncoding >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::RiceDeltaEncoding >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::ThreatEntryMetadata*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::ThreatEntryMetadata >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::ThreatEntryMetadata >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::ThreatListDescriptor*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::ThreatListDescriptor >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::ThreatListDescriptor >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::ListThreatListsResponse*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::ListThreatListsResponse >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::ListThreatListsResponse >(arena);
+}
+template<> PROTOBUF_NOINLINE ::mozilla::safebrowsing::Duration*
+Arena::CreateMaybeMessage< ::mozilla::safebrowsing::Duration >(Arena* arena) {
+ return Arena::CreateMessageInternal< ::mozilla::safebrowsing::Duration >(arena);
+}
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+#include <google/protobuf/port_undef.inc>
diff --git a/toolkit/components/url-classifier/chromium/safebrowsing.pb.h b/toolkit/components/url-classifier/chromium/safebrowsing.pb.h
new file mode 100644
index 0000000000..194b2c410f
--- /dev/null
+++ b/toolkit/components/url-classifier/chromium/safebrowsing.pb.h
@@ -0,0 +1,10010 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: safebrowsing.proto
+
+#ifndef GOOGLE_PROTOBUF_INCLUDED_safebrowsing_2eproto
+#define GOOGLE_PROTOBUF_INCLUDED_safebrowsing_2eproto
+
+#include <limits>
+#include <string>
+
+#include <google/protobuf/port_def.inc>
+#if PROTOBUF_VERSION < 3021000
+#error This file was generated by a newer version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please update
+#error your headers.
+#endif
+#if 3021006 < PROTOBUF_MIN_PROTOC_VERSION
+#error This file was generated by an older version of protoc which is
+#error incompatible with your Protocol Buffer headers. Please
+#error regenerate this file with a newer version of protoc.
+#endif
+
+#include <google/protobuf/port_undef.inc>
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/arena.h>
+#include <google/protobuf/arenastring.h>
+#include <google/protobuf/generated_message_util.h>
+#include <google/protobuf/metadata_lite.h>
+#include <google/protobuf/message_lite.h>
+#include <google/protobuf/repeated_field.h> // IWYU pragma: export
+#include <google/protobuf/extension_set.h> // IWYU pragma: export
+#include <google/protobuf/generated_enum_util.h>
+// @@protoc_insertion_point(includes)
+#include <google/protobuf/port_def.inc>
+#define PROTOBUF_INTERNAL_EXPORT_safebrowsing_2eproto
+PROTOBUF_NAMESPACE_OPEN
+namespace internal {
+class AnyMetadata;
+} // namespace internal
+PROTOBUF_NAMESPACE_CLOSE
+
+// Internal implementation detail -- do not use these members.
+struct TableStruct_safebrowsing_2eproto {
+ static const uint32_t offsets[];
+};
+namespace mozilla {
+namespace safebrowsing {
+class Checksum;
+struct ChecksumDefaultTypeInternal;
+extern ChecksumDefaultTypeInternal _Checksum_default_instance_;
+class ChromeClientInfo;
+struct ChromeClientInfoDefaultTypeInternal;
+extern ChromeClientInfoDefaultTypeInternal _ChromeClientInfo_default_instance_;
+class ClientInfo;
+struct ClientInfoDefaultTypeInternal;
+extern ClientInfoDefaultTypeInternal _ClientInfo_default_instance_;
+class Duration;
+struct DurationDefaultTypeInternal;
+extern DurationDefaultTypeInternal _Duration_default_instance_;
+class FetchThreatListUpdatesRequest;
+struct FetchThreatListUpdatesRequestDefaultTypeInternal;
+extern FetchThreatListUpdatesRequestDefaultTypeInternal _FetchThreatListUpdatesRequest_default_instance_;
+class FetchThreatListUpdatesRequest_ListUpdateRequest;
+struct FetchThreatListUpdatesRequest_ListUpdateRequestDefaultTypeInternal;
+extern FetchThreatListUpdatesRequest_ListUpdateRequestDefaultTypeInternal _FetchThreatListUpdatesRequest_ListUpdateRequest_default_instance_;
+class FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints;
+struct FetchThreatListUpdatesRequest_ListUpdateRequest_ConstraintsDefaultTypeInternal;
+extern FetchThreatListUpdatesRequest_ListUpdateRequest_ConstraintsDefaultTypeInternal _FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints_default_instance_;
+class FetchThreatListUpdatesResponse;
+struct FetchThreatListUpdatesResponseDefaultTypeInternal;
+extern FetchThreatListUpdatesResponseDefaultTypeInternal _FetchThreatListUpdatesResponse_default_instance_;
+class FetchThreatListUpdatesResponse_ListUpdateResponse;
+struct FetchThreatListUpdatesResponse_ListUpdateResponseDefaultTypeInternal;
+extern FetchThreatListUpdatesResponse_ListUpdateResponseDefaultTypeInternal _FetchThreatListUpdatesResponse_ListUpdateResponse_default_instance_;
+class FindFullHashesRequest;
+struct FindFullHashesRequestDefaultTypeInternal;
+extern FindFullHashesRequestDefaultTypeInternal _FindFullHashesRequest_default_instance_;
+class FindFullHashesResponse;
+struct FindFullHashesResponseDefaultTypeInternal;
+extern FindFullHashesResponseDefaultTypeInternal _FindFullHashesResponse_default_instance_;
+class FindThreatMatchesRequest;
+struct FindThreatMatchesRequestDefaultTypeInternal;
+extern FindThreatMatchesRequestDefaultTypeInternal _FindThreatMatchesRequest_default_instance_;
+class FindThreatMatchesResponse;
+struct FindThreatMatchesResponseDefaultTypeInternal;
+extern FindThreatMatchesResponseDefaultTypeInternal _FindThreatMatchesResponse_default_instance_;
+class ListThreatListsResponse;
+struct ListThreatListsResponseDefaultTypeInternal;
+extern ListThreatListsResponseDefaultTypeInternal _ListThreatListsResponse_default_instance_;
+class RawHashes;
+struct RawHashesDefaultTypeInternal;
+extern RawHashesDefaultTypeInternal _RawHashes_default_instance_;
+class RawIndices;
+struct RawIndicesDefaultTypeInternal;
+extern RawIndicesDefaultTypeInternal _RawIndices_default_instance_;
+class RiceDeltaEncoding;
+struct RiceDeltaEncodingDefaultTypeInternal;
+extern RiceDeltaEncodingDefaultTypeInternal _RiceDeltaEncoding_default_instance_;
+class ThreatEntry;
+struct ThreatEntryDefaultTypeInternal;
+extern ThreatEntryDefaultTypeInternal _ThreatEntry_default_instance_;
+class ThreatEntryMetadata;
+struct ThreatEntryMetadataDefaultTypeInternal;
+extern ThreatEntryMetadataDefaultTypeInternal _ThreatEntryMetadata_default_instance_;
+class ThreatEntryMetadata_MetadataEntry;
+struct ThreatEntryMetadata_MetadataEntryDefaultTypeInternal;
+extern ThreatEntryMetadata_MetadataEntryDefaultTypeInternal _ThreatEntryMetadata_MetadataEntry_default_instance_;
+class ThreatEntrySet;
+struct ThreatEntrySetDefaultTypeInternal;
+extern ThreatEntrySetDefaultTypeInternal _ThreatEntrySet_default_instance_;
+class ThreatHit;
+struct ThreatHitDefaultTypeInternal;
+extern ThreatHitDefaultTypeInternal _ThreatHit_default_instance_;
+class ThreatHit_ThreatSource;
+struct ThreatHit_ThreatSourceDefaultTypeInternal;
+extern ThreatHit_ThreatSourceDefaultTypeInternal _ThreatHit_ThreatSource_default_instance_;
+class ThreatHit_UserInfo;
+struct ThreatHit_UserInfoDefaultTypeInternal;
+extern ThreatHit_UserInfoDefaultTypeInternal _ThreatHit_UserInfo_default_instance_;
+class ThreatInfo;
+struct ThreatInfoDefaultTypeInternal;
+extern ThreatInfoDefaultTypeInternal _ThreatInfo_default_instance_;
+class ThreatListDescriptor;
+struct ThreatListDescriptorDefaultTypeInternal;
+extern ThreatListDescriptorDefaultTypeInternal _ThreatListDescriptor_default_instance_;
+class ThreatMatch;
+struct ThreatMatchDefaultTypeInternal;
+extern ThreatMatchDefaultTypeInternal _ThreatMatch_default_instance_;
+} // namespace safebrowsing
+} // namespace mozilla
+PROTOBUF_NAMESPACE_OPEN
+template<> ::mozilla::safebrowsing::Checksum* Arena::CreateMaybeMessage<::mozilla::safebrowsing::Checksum>(Arena*);
+template<> ::mozilla::safebrowsing::ChromeClientInfo* Arena::CreateMaybeMessage<::mozilla::safebrowsing::ChromeClientInfo>(Arena*);
+template<> ::mozilla::safebrowsing::ClientInfo* Arena::CreateMaybeMessage<::mozilla::safebrowsing::ClientInfo>(Arena*);
+template<> ::mozilla::safebrowsing::Duration* Arena::CreateMaybeMessage<::mozilla::safebrowsing::Duration>(Arena*);
+template<> ::mozilla::safebrowsing::FetchThreatListUpdatesRequest* Arena::CreateMaybeMessage<::mozilla::safebrowsing::FetchThreatListUpdatesRequest>(Arena*);
+template<> ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest* Arena::CreateMaybeMessage<::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest>(Arena*);
+template<> ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* Arena::CreateMaybeMessage<::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints>(Arena*);
+template<> ::mozilla::safebrowsing::FetchThreatListUpdatesResponse* Arena::CreateMaybeMessage<::mozilla::safebrowsing::FetchThreatListUpdatesResponse>(Arena*);
+template<> ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse* Arena::CreateMaybeMessage<::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse>(Arena*);
+template<> ::mozilla::safebrowsing::FindFullHashesRequest* Arena::CreateMaybeMessage<::mozilla::safebrowsing::FindFullHashesRequest>(Arena*);
+template<> ::mozilla::safebrowsing::FindFullHashesResponse* Arena::CreateMaybeMessage<::mozilla::safebrowsing::FindFullHashesResponse>(Arena*);
+template<> ::mozilla::safebrowsing::FindThreatMatchesRequest* Arena::CreateMaybeMessage<::mozilla::safebrowsing::FindThreatMatchesRequest>(Arena*);
+template<> ::mozilla::safebrowsing::FindThreatMatchesResponse* Arena::CreateMaybeMessage<::mozilla::safebrowsing::FindThreatMatchesResponse>(Arena*);
+template<> ::mozilla::safebrowsing::ListThreatListsResponse* Arena::CreateMaybeMessage<::mozilla::safebrowsing::ListThreatListsResponse>(Arena*);
+template<> ::mozilla::safebrowsing::RawHashes* Arena::CreateMaybeMessage<::mozilla::safebrowsing::RawHashes>(Arena*);
+template<> ::mozilla::safebrowsing::RawIndices* Arena::CreateMaybeMessage<::mozilla::safebrowsing::RawIndices>(Arena*);
+template<> ::mozilla::safebrowsing::RiceDeltaEncoding* Arena::CreateMaybeMessage<::mozilla::safebrowsing::RiceDeltaEncoding>(Arena*);
+template<> ::mozilla::safebrowsing::ThreatEntry* Arena::CreateMaybeMessage<::mozilla::safebrowsing::ThreatEntry>(Arena*);
+template<> ::mozilla::safebrowsing::ThreatEntryMetadata* Arena::CreateMaybeMessage<::mozilla::safebrowsing::ThreatEntryMetadata>(Arena*);
+template<> ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry* Arena::CreateMaybeMessage<::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry>(Arena*);
+template<> ::mozilla::safebrowsing::ThreatEntrySet* Arena::CreateMaybeMessage<::mozilla::safebrowsing::ThreatEntrySet>(Arena*);
+template<> ::mozilla::safebrowsing::ThreatHit* Arena::CreateMaybeMessage<::mozilla::safebrowsing::ThreatHit>(Arena*);
+template<> ::mozilla::safebrowsing::ThreatHit_ThreatSource* Arena::CreateMaybeMessage<::mozilla::safebrowsing::ThreatHit_ThreatSource>(Arena*);
+template<> ::mozilla::safebrowsing::ThreatHit_UserInfo* Arena::CreateMaybeMessage<::mozilla::safebrowsing::ThreatHit_UserInfo>(Arena*);
+template<> ::mozilla::safebrowsing::ThreatInfo* Arena::CreateMaybeMessage<::mozilla::safebrowsing::ThreatInfo>(Arena*);
+template<> ::mozilla::safebrowsing::ThreatListDescriptor* Arena::CreateMaybeMessage<::mozilla::safebrowsing::ThreatListDescriptor>(Arena*);
+template<> ::mozilla::safebrowsing::ThreatMatch* Arena::CreateMaybeMessage<::mozilla::safebrowsing::ThreatMatch>(Arena*);
+PROTOBUF_NAMESPACE_CLOSE
+namespace mozilla {
+namespace safebrowsing {
+
+enum FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType : int {
+ FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_RESPONSE_TYPE_UNSPECIFIED = 0,
+ FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_PARTIAL_UPDATE = 1,
+ FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_FULL_UPDATE = 2
+};
+bool FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_IsValid(int value);
+constexpr FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_ResponseType_MIN = FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_RESPONSE_TYPE_UNSPECIFIED;
+constexpr FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_ResponseType_MAX = FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_FULL_UPDATE;
+constexpr int FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_ResponseType_ARRAYSIZE = FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_ResponseType_MAX + 1;
+
+const std::string& FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_Name(FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType value);
+template<typename T>
+inline const std::string& FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_Name(T enum_t_value) {
+ static_assert(::std::is_same<T, FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType>::value ||
+ ::std::is_integral<T>::value,
+ "Incorrect type passed to function FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_Name.");
+ return FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_Name(static_cast<FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType>(enum_t_value));
+}
+bool FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_Parse(
+ ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType* value);
+enum ThreatHit_ThreatSourceType : int {
+ ThreatHit_ThreatSourceType_THREAT_SOURCE_TYPE_UNSPECIFIED = 0,
+ ThreatHit_ThreatSourceType_MATCHING_URL = 1,
+ ThreatHit_ThreatSourceType_TAB_URL = 2,
+ ThreatHit_ThreatSourceType_TAB_REDIRECT = 3,
+ ThreatHit_ThreatSourceType_TAB_RESOURCE = 4
+};
+bool ThreatHit_ThreatSourceType_IsValid(int value);
+constexpr ThreatHit_ThreatSourceType ThreatHit_ThreatSourceType_ThreatSourceType_MIN = ThreatHit_ThreatSourceType_THREAT_SOURCE_TYPE_UNSPECIFIED;
+constexpr ThreatHit_ThreatSourceType ThreatHit_ThreatSourceType_ThreatSourceType_MAX = ThreatHit_ThreatSourceType_TAB_RESOURCE;
+constexpr int ThreatHit_ThreatSourceType_ThreatSourceType_ARRAYSIZE = ThreatHit_ThreatSourceType_ThreatSourceType_MAX + 1;
+
+const std::string& ThreatHit_ThreatSourceType_Name(ThreatHit_ThreatSourceType value);
+template<typename T>
+inline const std::string& ThreatHit_ThreatSourceType_Name(T enum_t_value) {
+ static_assert(::std::is_same<T, ThreatHit_ThreatSourceType>::value ||
+ ::std::is_integral<T>::value,
+ "Incorrect type passed to function ThreatHit_ThreatSourceType_Name.");
+ return ThreatHit_ThreatSourceType_Name(static_cast<ThreatHit_ThreatSourceType>(enum_t_value));
+}
+bool ThreatHit_ThreatSourceType_Parse(
+ ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ThreatHit_ThreatSourceType* value);
+enum ChromeClientInfo_SafeBrowsingReportingPopulation : int {
+ ChromeClientInfo_SafeBrowsingReportingPopulation_UNSPECIFIED = 0,
+ ChromeClientInfo_SafeBrowsingReportingPopulation_OPT_OUT = 1,
+ ChromeClientInfo_SafeBrowsingReportingPopulation_EXTENDED = 2,
+ ChromeClientInfo_SafeBrowsingReportingPopulation_SCOUT = 3
+};
+bool ChromeClientInfo_SafeBrowsingReportingPopulation_IsValid(int value);
+constexpr ChromeClientInfo_SafeBrowsingReportingPopulation ChromeClientInfo_SafeBrowsingReportingPopulation_SafeBrowsingReportingPopulation_MIN = ChromeClientInfo_SafeBrowsingReportingPopulation_UNSPECIFIED;
+constexpr ChromeClientInfo_SafeBrowsingReportingPopulation ChromeClientInfo_SafeBrowsingReportingPopulation_SafeBrowsingReportingPopulation_MAX = ChromeClientInfo_SafeBrowsingReportingPopulation_SCOUT;
+constexpr int ChromeClientInfo_SafeBrowsingReportingPopulation_SafeBrowsingReportingPopulation_ARRAYSIZE = ChromeClientInfo_SafeBrowsingReportingPopulation_SafeBrowsingReportingPopulation_MAX + 1;
+
+const std::string& ChromeClientInfo_SafeBrowsingReportingPopulation_Name(ChromeClientInfo_SafeBrowsingReportingPopulation value);
+template<typename T>
+inline const std::string& ChromeClientInfo_SafeBrowsingReportingPopulation_Name(T enum_t_value) {
+ static_assert(::std::is_same<T, ChromeClientInfo_SafeBrowsingReportingPopulation>::value ||
+ ::std::is_integral<T>::value,
+ "Incorrect type passed to function ChromeClientInfo_SafeBrowsingReportingPopulation_Name.");
+ return ChromeClientInfo_SafeBrowsingReportingPopulation_Name(static_cast<ChromeClientInfo_SafeBrowsingReportingPopulation>(enum_t_value));
+}
+bool ChromeClientInfo_SafeBrowsingReportingPopulation_Parse(
+ ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ChromeClientInfo_SafeBrowsingReportingPopulation* value);
+enum ThreatType : int {
+ THREAT_TYPE_UNSPECIFIED = 0,
+ MALWARE_THREAT = 1,
+ SOCIAL_ENGINEERING_PUBLIC = 2,
+ UNWANTED_SOFTWARE = 3,
+ POTENTIALLY_HARMFUL_APPLICATION = 4,
+ SOCIAL_ENGINEERING = 5,
+ API_ABUSE = 6,
+ MALICIOUS_BINARY = 7,
+ CSD_WHITELIST = 8,
+ CSD_DOWNLOAD_WHITELIST = 9,
+ CLIENT_INCIDENT = 10,
+ SUBRESOURCE_FILTER = 13
+};
+bool ThreatType_IsValid(int value);
+constexpr ThreatType ThreatType_MIN = THREAT_TYPE_UNSPECIFIED;
+constexpr ThreatType ThreatType_MAX = SUBRESOURCE_FILTER;
+constexpr int ThreatType_ARRAYSIZE = ThreatType_MAX + 1;
+
+const std::string& ThreatType_Name(ThreatType value);
+template<typename T>
+inline const std::string& ThreatType_Name(T enum_t_value) {
+ static_assert(::std::is_same<T, ThreatType>::value ||
+ ::std::is_integral<T>::value,
+ "Incorrect type passed to function ThreatType_Name.");
+ return ThreatType_Name(static_cast<ThreatType>(enum_t_value));
+}
+bool ThreatType_Parse(
+ ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ThreatType* value);
+enum PlatformType : int {
+ PLATFORM_TYPE_UNSPECIFIED = 0,
+ WINDOWS_PLATFORM = 1,
+ LINUX_PLATFORM = 2,
+ ANDROID_PLATFORM = 3,
+ OSX_PLATFORM = 4,
+ IOS_PLATFORM = 5,
+ ANY_PLATFORM = 6,
+ ALL_PLATFORMS = 7,
+ CHROME_PLATFORM = 8
+};
+bool PlatformType_IsValid(int value);
+constexpr PlatformType PlatformType_MIN = PLATFORM_TYPE_UNSPECIFIED;
+constexpr PlatformType PlatformType_MAX = CHROME_PLATFORM;
+constexpr int PlatformType_ARRAYSIZE = PlatformType_MAX + 1;
+
+const std::string& PlatformType_Name(PlatformType value);
+template<typename T>
+inline const std::string& PlatformType_Name(T enum_t_value) {
+ static_assert(::std::is_same<T, PlatformType>::value ||
+ ::std::is_integral<T>::value,
+ "Incorrect type passed to function PlatformType_Name.");
+ return PlatformType_Name(static_cast<PlatformType>(enum_t_value));
+}
+bool PlatformType_Parse(
+ ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, PlatformType* value);
+enum CompressionType : int {
+ COMPRESSION_TYPE_UNSPECIFIED = 0,
+ RAW = 1,
+ RICE = 2
+};
+bool CompressionType_IsValid(int value);
+constexpr CompressionType CompressionType_MIN = COMPRESSION_TYPE_UNSPECIFIED;
+constexpr CompressionType CompressionType_MAX = RICE;
+constexpr int CompressionType_ARRAYSIZE = CompressionType_MAX + 1;
+
+const std::string& CompressionType_Name(CompressionType value);
+template<typename T>
+inline const std::string& CompressionType_Name(T enum_t_value) {
+ static_assert(::std::is_same<T, CompressionType>::value ||
+ ::std::is_integral<T>::value,
+ "Incorrect type passed to function CompressionType_Name.");
+ return CompressionType_Name(static_cast<CompressionType>(enum_t_value));
+}
+bool CompressionType_Parse(
+ ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, CompressionType* value);
+enum ThreatEntryType : int {
+ THREAT_ENTRY_TYPE_UNSPECIFIED = 0,
+ URL = 1,
+ EXECUTABLE = 2,
+ IP_RANGE = 3,
+ CHROME_EXTENSION = 4,
+ FILENAME = 5,
+ CERT = 6
+};
+bool ThreatEntryType_IsValid(int value);
+constexpr ThreatEntryType ThreatEntryType_MIN = THREAT_ENTRY_TYPE_UNSPECIFIED;
+constexpr ThreatEntryType ThreatEntryType_MAX = CERT;
+constexpr int ThreatEntryType_ARRAYSIZE = ThreatEntryType_MAX + 1;
+
+const std::string& ThreatEntryType_Name(ThreatEntryType value);
+template<typename T>
+inline const std::string& ThreatEntryType_Name(T enum_t_value) {
+ static_assert(::std::is_same<T, ThreatEntryType>::value ||
+ ::std::is_integral<T>::value,
+ "Incorrect type passed to function ThreatEntryType_Name.");
+ return ThreatEntryType_Name(static_cast<ThreatEntryType>(enum_t_value));
+}
+bool ThreatEntryType_Parse(
+ ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ThreatEntryType* value);
+// ===================================================================
+
+class ThreatInfo final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.ThreatInfo) */ {
+ public:
+ inline ThreatInfo() : ThreatInfo(nullptr) {}
+ ~ThreatInfo() override;
+ explicit PROTOBUF_CONSTEXPR ThreatInfo(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ ThreatInfo(const ThreatInfo& from);
+ ThreatInfo(ThreatInfo&& from) noexcept
+ : ThreatInfo() {
+ *this = ::std::move(from);
+ }
+
+ inline ThreatInfo& operator=(const ThreatInfo& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline ThreatInfo& operator=(ThreatInfo&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const ThreatInfo& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const ThreatInfo* internal_default_instance() {
+ return reinterpret_cast<const ThreatInfo*>(
+ &_ThreatInfo_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 0;
+
+ friend void swap(ThreatInfo& a, ThreatInfo& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(ThreatInfo* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(ThreatInfo* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ ThreatInfo* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<ThreatInfo>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const ThreatInfo& from);
+ void MergeFrom(const ThreatInfo& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(ThreatInfo* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.ThreatInfo";
+ }
+ protected:
+ explicit ThreatInfo(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kThreatTypesFieldNumber = 1,
+ kPlatformTypesFieldNumber = 2,
+ kThreatEntriesFieldNumber = 3,
+ kThreatEntryTypesFieldNumber = 4,
+ };
+ // repeated .mozilla.safebrowsing.ThreatType threat_types = 1;
+ int threat_types_size() const;
+ private:
+ int _internal_threat_types_size() const;
+ public:
+ void clear_threat_types();
+ private:
+ ::mozilla::safebrowsing::ThreatType _internal_threat_types(int index) const;
+ void _internal_add_threat_types(::mozilla::safebrowsing::ThreatType value);
+ ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>* _internal_mutable_threat_types();
+ public:
+ ::mozilla::safebrowsing::ThreatType threat_types(int index) const;
+ void set_threat_types(int index, ::mozilla::safebrowsing::ThreatType value);
+ void add_threat_types(::mozilla::safebrowsing::ThreatType value);
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>& threat_types() const;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>* mutable_threat_types();
+
+ // repeated .mozilla.safebrowsing.PlatformType platform_types = 2;
+ int platform_types_size() const;
+ private:
+ int _internal_platform_types_size() const;
+ public:
+ void clear_platform_types();
+ private:
+ ::mozilla::safebrowsing::PlatformType _internal_platform_types(int index) const;
+ void _internal_add_platform_types(::mozilla::safebrowsing::PlatformType value);
+ ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>* _internal_mutable_platform_types();
+ public:
+ ::mozilla::safebrowsing::PlatformType platform_types(int index) const;
+ void set_platform_types(int index, ::mozilla::safebrowsing::PlatformType value);
+ void add_platform_types(::mozilla::safebrowsing::PlatformType value);
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>& platform_types() const;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>* mutable_platform_types();
+
+ // repeated .mozilla.safebrowsing.ThreatEntry threat_entries = 3;
+ int threat_entries_size() const;
+ private:
+ int _internal_threat_entries_size() const;
+ public:
+ void clear_threat_entries();
+ ::mozilla::safebrowsing::ThreatEntry* mutable_threat_entries(int index);
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntry >*
+ mutable_threat_entries();
+ private:
+ const ::mozilla::safebrowsing::ThreatEntry& _internal_threat_entries(int index) const;
+ ::mozilla::safebrowsing::ThreatEntry* _internal_add_threat_entries();
+ public:
+ const ::mozilla::safebrowsing::ThreatEntry& threat_entries(int index) const;
+ ::mozilla::safebrowsing::ThreatEntry* add_threat_entries();
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntry >&
+ threat_entries() const;
+
+ // repeated .mozilla.safebrowsing.ThreatEntryType threat_entry_types = 4;
+ int threat_entry_types_size() const;
+ private:
+ int _internal_threat_entry_types_size() const;
+ public:
+ void clear_threat_entry_types();
+ private:
+ ::mozilla::safebrowsing::ThreatEntryType _internal_threat_entry_types(int index) const;
+ void _internal_add_threat_entry_types(::mozilla::safebrowsing::ThreatEntryType value);
+ ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>* _internal_mutable_threat_entry_types();
+ public:
+ ::mozilla::safebrowsing::ThreatEntryType threat_entry_types(int index) const;
+ void set_threat_entry_types(int index, ::mozilla::safebrowsing::ThreatEntryType value);
+ void add_threat_entry_types(::mozilla::safebrowsing::ThreatEntryType value);
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>& threat_entry_types() const;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>* mutable_threat_entry_types();
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.ThreatInfo)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::RepeatedField<int> threat_types_;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedField<int> platform_types_;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntry > threat_entries_;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedField<int> threat_entry_types_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class ThreatMatch final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.ThreatMatch) */ {
+ public:
+ inline ThreatMatch() : ThreatMatch(nullptr) {}
+ ~ThreatMatch() override;
+ explicit PROTOBUF_CONSTEXPR ThreatMatch(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ ThreatMatch(const ThreatMatch& from);
+ ThreatMatch(ThreatMatch&& from) noexcept
+ : ThreatMatch() {
+ *this = ::std::move(from);
+ }
+
+ inline ThreatMatch& operator=(const ThreatMatch& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline ThreatMatch& operator=(ThreatMatch&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const ThreatMatch& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const ThreatMatch* internal_default_instance() {
+ return reinterpret_cast<const ThreatMatch*>(
+ &_ThreatMatch_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 1;
+
+ friend void swap(ThreatMatch& a, ThreatMatch& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(ThreatMatch* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(ThreatMatch* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ ThreatMatch* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<ThreatMatch>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const ThreatMatch& from);
+ void MergeFrom(const ThreatMatch& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(ThreatMatch* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.ThreatMatch";
+ }
+ protected:
+ explicit ThreatMatch(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kThreatFieldNumber = 3,
+ kThreatEntryMetadataFieldNumber = 4,
+ kCacheDurationFieldNumber = 5,
+ kThreatTypeFieldNumber = 1,
+ kPlatformTypeFieldNumber = 2,
+ kThreatEntryTypeFieldNumber = 6,
+ };
+ // optional .mozilla.safebrowsing.ThreatEntry threat = 3;
+ bool has_threat() const;
+ private:
+ bool _internal_has_threat() const;
+ public:
+ void clear_threat();
+ const ::mozilla::safebrowsing::ThreatEntry& threat() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::ThreatEntry* release_threat();
+ ::mozilla::safebrowsing::ThreatEntry* mutable_threat();
+ void set_allocated_threat(::mozilla::safebrowsing::ThreatEntry* threat);
+ private:
+ const ::mozilla::safebrowsing::ThreatEntry& _internal_threat() const;
+ ::mozilla::safebrowsing::ThreatEntry* _internal_mutable_threat();
+ public:
+ void unsafe_arena_set_allocated_threat(
+ ::mozilla::safebrowsing::ThreatEntry* threat);
+ ::mozilla::safebrowsing::ThreatEntry* unsafe_arena_release_threat();
+
+ // optional .mozilla.safebrowsing.ThreatEntryMetadata threat_entry_metadata = 4;
+ bool has_threat_entry_metadata() const;
+ private:
+ bool _internal_has_threat_entry_metadata() const;
+ public:
+ void clear_threat_entry_metadata();
+ const ::mozilla::safebrowsing::ThreatEntryMetadata& threat_entry_metadata() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::ThreatEntryMetadata* release_threat_entry_metadata();
+ ::mozilla::safebrowsing::ThreatEntryMetadata* mutable_threat_entry_metadata();
+ void set_allocated_threat_entry_metadata(::mozilla::safebrowsing::ThreatEntryMetadata* threat_entry_metadata);
+ private:
+ const ::mozilla::safebrowsing::ThreatEntryMetadata& _internal_threat_entry_metadata() const;
+ ::mozilla::safebrowsing::ThreatEntryMetadata* _internal_mutable_threat_entry_metadata();
+ public:
+ void unsafe_arena_set_allocated_threat_entry_metadata(
+ ::mozilla::safebrowsing::ThreatEntryMetadata* threat_entry_metadata);
+ ::mozilla::safebrowsing::ThreatEntryMetadata* unsafe_arena_release_threat_entry_metadata();
+
+ // optional .mozilla.safebrowsing.Duration cache_duration = 5;
+ bool has_cache_duration() const;
+ private:
+ bool _internal_has_cache_duration() const;
+ public:
+ void clear_cache_duration();
+ const ::mozilla::safebrowsing::Duration& cache_duration() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::Duration* release_cache_duration();
+ ::mozilla::safebrowsing::Duration* mutable_cache_duration();
+ void set_allocated_cache_duration(::mozilla::safebrowsing::Duration* cache_duration);
+ private:
+ const ::mozilla::safebrowsing::Duration& _internal_cache_duration() const;
+ ::mozilla::safebrowsing::Duration* _internal_mutable_cache_duration();
+ public:
+ void unsafe_arena_set_allocated_cache_duration(
+ ::mozilla::safebrowsing::Duration* cache_duration);
+ ::mozilla::safebrowsing::Duration* unsafe_arena_release_cache_duration();
+
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ bool has_threat_type() const;
+ private:
+ bool _internal_has_threat_type() const;
+ public:
+ void clear_threat_type();
+ ::mozilla::safebrowsing::ThreatType threat_type() const;
+ void set_threat_type(::mozilla::safebrowsing::ThreatType value);
+ private:
+ ::mozilla::safebrowsing::ThreatType _internal_threat_type() const;
+ void _internal_set_threat_type(::mozilla::safebrowsing::ThreatType value);
+ public:
+
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+ bool has_platform_type() const;
+ private:
+ bool _internal_has_platform_type() const;
+ public:
+ void clear_platform_type();
+ ::mozilla::safebrowsing::PlatformType platform_type() const;
+ void set_platform_type(::mozilla::safebrowsing::PlatformType value);
+ private:
+ ::mozilla::safebrowsing::PlatformType _internal_platform_type() const;
+ void _internal_set_platform_type(::mozilla::safebrowsing::PlatformType value);
+ public:
+
+ // optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 6;
+ bool has_threat_entry_type() const;
+ private:
+ bool _internal_has_threat_entry_type() const;
+ public:
+ void clear_threat_entry_type();
+ ::mozilla::safebrowsing::ThreatEntryType threat_entry_type() const;
+ void set_threat_entry_type(::mozilla::safebrowsing::ThreatEntryType value);
+ private:
+ ::mozilla::safebrowsing::ThreatEntryType _internal_threat_entry_type() const;
+ void _internal_set_threat_entry_type(::mozilla::safebrowsing::ThreatEntryType value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.ThreatMatch)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::mozilla::safebrowsing::ThreatEntry* threat_;
+ ::mozilla::safebrowsing::ThreatEntryMetadata* threat_entry_metadata_;
+ ::mozilla::safebrowsing::Duration* cache_duration_;
+ int threat_type_;
+ int platform_type_;
+ int threat_entry_type_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class FindThreatMatchesRequest final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.FindThreatMatchesRequest) */ {
+ public:
+ inline FindThreatMatchesRequest() : FindThreatMatchesRequest(nullptr) {}
+ ~FindThreatMatchesRequest() override;
+ explicit PROTOBUF_CONSTEXPR FindThreatMatchesRequest(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ FindThreatMatchesRequest(const FindThreatMatchesRequest& from);
+ FindThreatMatchesRequest(FindThreatMatchesRequest&& from) noexcept
+ : FindThreatMatchesRequest() {
+ *this = ::std::move(from);
+ }
+
+ inline FindThreatMatchesRequest& operator=(const FindThreatMatchesRequest& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline FindThreatMatchesRequest& operator=(FindThreatMatchesRequest&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const FindThreatMatchesRequest& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const FindThreatMatchesRequest* internal_default_instance() {
+ return reinterpret_cast<const FindThreatMatchesRequest*>(
+ &_FindThreatMatchesRequest_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 2;
+
+ friend void swap(FindThreatMatchesRequest& a, FindThreatMatchesRequest& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(FindThreatMatchesRequest* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(FindThreatMatchesRequest* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ FindThreatMatchesRequest* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<FindThreatMatchesRequest>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const FindThreatMatchesRequest& from);
+ void MergeFrom(const FindThreatMatchesRequest& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(FindThreatMatchesRequest* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.FindThreatMatchesRequest";
+ }
+ protected:
+ explicit FindThreatMatchesRequest(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kClientFieldNumber = 1,
+ kThreatInfoFieldNumber = 2,
+ };
+ // optional .mozilla.safebrowsing.ClientInfo client = 1;
+ bool has_client() const;
+ private:
+ bool _internal_has_client() const;
+ public:
+ void clear_client();
+ const ::mozilla::safebrowsing::ClientInfo& client() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::ClientInfo* release_client();
+ ::mozilla::safebrowsing::ClientInfo* mutable_client();
+ void set_allocated_client(::mozilla::safebrowsing::ClientInfo* client);
+ private:
+ const ::mozilla::safebrowsing::ClientInfo& _internal_client() const;
+ ::mozilla::safebrowsing::ClientInfo* _internal_mutable_client();
+ public:
+ void unsafe_arena_set_allocated_client(
+ ::mozilla::safebrowsing::ClientInfo* client);
+ ::mozilla::safebrowsing::ClientInfo* unsafe_arena_release_client();
+
+ // optional .mozilla.safebrowsing.ThreatInfo threat_info = 2;
+ bool has_threat_info() const;
+ private:
+ bool _internal_has_threat_info() const;
+ public:
+ void clear_threat_info();
+ const ::mozilla::safebrowsing::ThreatInfo& threat_info() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::ThreatInfo* release_threat_info();
+ ::mozilla::safebrowsing::ThreatInfo* mutable_threat_info();
+ void set_allocated_threat_info(::mozilla::safebrowsing::ThreatInfo* threat_info);
+ private:
+ const ::mozilla::safebrowsing::ThreatInfo& _internal_threat_info() const;
+ ::mozilla::safebrowsing::ThreatInfo* _internal_mutable_threat_info();
+ public:
+ void unsafe_arena_set_allocated_threat_info(
+ ::mozilla::safebrowsing::ThreatInfo* threat_info);
+ ::mozilla::safebrowsing::ThreatInfo* unsafe_arena_release_threat_info();
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.FindThreatMatchesRequest)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::mozilla::safebrowsing::ClientInfo* client_;
+ ::mozilla::safebrowsing::ThreatInfo* threat_info_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class FindThreatMatchesResponse final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.FindThreatMatchesResponse) */ {
+ public:
+ inline FindThreatMatchesResponse() : FindThreatMatchesResponse(nullptr) {}
+ ~FindThreatMatchesResponse() override;
+ explicit PROTOBUF_CONSTEXPR FindThreatMatchesResponse(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ FindThreatMatchesResponse(const FindThreatMatchesResponse& from);
+ FindThreatMatchesResponse(FindThreatMatchesResponse&& from) noexcept
+ : FindThreatMatchesResponse() {
+ *this = ::std::move(from);
+ }
+
+ inline FindThreatMatchesResponse& operator=(const FindThreatMatchesResponse& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline FindThreatMatchesResponse& operator=(FindThreatMatchesResponse&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const FindThreatMatchesResponse& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const FindThreatMatchesResponse* internal_default_instance() {
+ return reinterpret_cast<const FindThreatMatchesResponse*>(
+ &_FindThreatMatchesResponse_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 3;
+
+ friend void swap(FindThreatMatchesResponse& a, FindThreatMatchesResponse& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(FindThreatMatchesResponse* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(FindThreatMatchesResponse* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ FindThreatMatchesResponse* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<FindThreatMatchesResponse>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const FindThreatMatchesResponse& from);
+ void MergeFrom(const FindThreatMatchesResponse& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(FindThreatMatchesResponse* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.FindThreatMatchesResponse";
+ }
+ protected:
+ explicit FindThreatMatchesResponse(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kMatchesFieldNumber = 1,
+ };
+ // repeated .mozilla.safebrowsing.ThreatMatch matches = 1;
+ int matches_size() const;
+ private:
+ int _internal_matches_size() const;
+ public:
+ void clear_matches();
+ ::mozilla::safebrowsing::ThreatMatch* mutable_matches(int index);
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatMatch >*
+ mutable_matches();
+ private:
+ const ::mozilla::safebrowsing::ThreatMatch& _internal_matches(int index) const;
+ ::mozilla::safebrowsing::ThreatMatch* _internal_add_matches();
+ public:
+ const ::mozilla::safebrowsing::ThreatMatch& matches(int index) const;
+ ::mozilla::safebrowsing::ThreatMatch* add_matches();
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatMatch >&
+ matches() const;
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.FindThreatMatchesResponse)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatMatch > matches_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints) */ {
+ public:
+ inline FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints() : FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints(nullptr) {}
+ ~FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints() override;
+ explicit PROTOBUF_CONSTEXPR FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints(const FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints& from);
+ FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints(FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints&& from) noexcept
+ : FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints() {
+ *this = ::std::move(from);
+ }
+
+ inline FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints& operator=(const FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints& operator=(FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* internal_default_instance() {
+ return reinterpret_cast<const FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints*>(
+ &_FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 4;
+
+ friend void swap(FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints& a, FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints& from);
+ void MergeFrom(const FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints";
+ }
+ protected:
+ explicit FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kSupportedCompressionsFieldNumber = 4,
+ kRegionFieldNumber = 3,
+ kMaxUpdateEntriesFieldNumber = 1,
+ kMaxDatabaseEntriesFieldNumber = 2,
+ };
+ // repeated .mozilla.safebrowsing.CompressionType supported_compressions = 4;
+ int supported_compressions_size() const;
+ private:
+ int _internal_supported_compressions_size() const;
+ public:
+ void clear_supported_compressions();
+ private:
+ ::mozilla::safebrowsing::CompressionType _internal_supported_compressions(int index) const;
+ void _internal_add_supported_compressions(::mozilla::safebrowsing::CompressionType value);
+ ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>* _internal_mutable_supported_compressions();
+ public:
+ ::mozilla::safebrowsing::CompressionType supported_compressions(int index) const;
+ void set_supported_compressions(int index, ::mozilla::safebrowsing::CompressionType value);
+ void add_supported_compressions(::mozilla::safebrowsing::CompressionType value);
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>& supported_compressions() const;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>* mutable_supported_compressions();
+
+ // optional string region = 3;
+ bool has_region() const;
+ private:
+ bool _internal_has_region() const;
+ public:
+ void clear_region();
+ const std::string& region() const;
+ template <typename ArgT0 = const std::string&, typename... ArgT>
+ void set_region(ArgT0&& arg0, ArgT... args);
+ std::string* mutable_region();
+ PROTOBUF_NODISCARD std::string* release_region();
+ void set_allocated_region(std::string* region);
+ private:
+ const std::string& _internal_region() const;
+ inline PROTOBUF_ALWAYS_INLINE void _internal_set_region(const std::string& value);
+ std::string* _internal_mutable_region();
+ public:
+
+ // optional int32 max_update_entries = 1;
+ bool has_max_update_entries() const;
+ private:
+ bool _internal_has_max_update_entries() const;
+ public:
+ void clear_max_update_entries();
+ int32_t max_update_entries() const;
+ void set_max_update_entries(int32_t value);
+ private:
+ int32_t _internal_max_update_entries() const;
+ void _internal_set_max_update_entries(int32_t value);
+ public:
+
+ // optional int32 max_database_entries = 2;
+ bool has_max_database_entries() const;
+ private:
+ bool _internal_has_max_database_entries() const;
+ public:
+ void clear_max_database_entries();
+ int32_t max_database_entries() const;
+ void set_max_database_entries(int32_t value);
+ private:
+ int32_t _internal_max_database_entries() const;
+ void _internal_set_max_database_entries(int32_t value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedField<int> supported_compressions_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr region_;
+ int32_t max_update_entries_;
+ int32_t max_database_entries_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class FetchThreatListUpdatesRequest_ListUpdateRequest final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest) */ {
+ public:
+ inline FetchThreatListUpdatesRequest_ListUpdateRequest() : FetchThreatListUpdatesRequest_ListUpdateRequest(nullptr) {}
+ ~FetchThreatListUpdatesRequest_ListUpdateRequest() override;
+ explicit PROTOBUF_CONSTEXPR FetchThreatListUpdatesRequest_ListUpdateRequest(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ FetchThreatListUpdatesRequest_ListUpdateRequest(const FetchThreatListUpdatesRequest_ListUpdateRequest& from);
+ FetchThreatListUpdatesRequest_ListUpdateRequest(FetchThreatListUpdatesRequest_ListUpdateRequest&& from) noexcept
+ : FetchThreatListUpdatesRequest_ListUpdateRequest() {
+ *this = ::std::move(from);
+ }
+
+ inline FetchThreatListUpdatesRequest_ListUpdateRequest& operator=(const FetchThreatListUpdatesRequest_ListUpdateRequest& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline FetchThreatListUpdatesRequest_ListUpdateRequest& operator=(FetchThreatListUpdatesRequest_ListUpdateRequest&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const FetchThreatListUpdatesRequest_ListUpdateRequest& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const FetchThreatListUpdatesRequest_ListUpdateRequest* internal_default_instance() {
+ return reinterpret_cast<const FetchThreatListUpdatesRequest_ListUpdateRequest*>(
+ &_FetchThreatListUpdatesRequest_ListUpdateRequest_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 5;
+
+ friend void swap(FetchThreatListUpdatesRequest_ListUpdateRequest& a, FetchThreatListUpdatesRequest_ListUpdateRequest& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(FetchThreatListUpdatesRequest_ListUpdateRequest* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(FetchThreatListUpdatesRequest_ListUpdateRequest* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ FetchThreatListUpdatesRequest_ListUpdateRequest* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<FetchThreatListUpdatesRequest_ListUpdateRequest>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const FetchThreatListUpdatesRequest_ListUpdateRequest& from);
+ void MergeFrom(const FetchThreatListUpdatesRequest_ListUpdateRequest& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(FetchThreatListUpdatesRequest_ListUpdateRequest* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest";
+ }
+ protected:
+ explicit FetchThreatListUpdatesRequest_ListUpdateRequest(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ typedef FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints Constraints;
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kStateFieldNumber = 3,
+ kConstraintsFieldNumber = 4,
+ kThreatTypeFieldNumber = 1,
+ kPlatformTypeFieldNumber = 2,
+ kThreatEntryTypeFieldNumber = 5,
+ };
+ // optional bytes state = 3;
+ bool has_state() const;
+ private:
+ bool _internal_has_state() const;
+ public:
+ void clear_state();
+ const std::string& state() const;
+ template <typename ArgT0 = const std::string&, typename... ArgT>
+ void set_state(ArgT0&& arg0, ArgT... args);
+ std::string* mutable_state();
+ PROTOBUF_NODISCARD std::string* release_state();
+ void set_allocated_state(std::string* state);
+ private:
+ const std::string& _internal_state() const;
+ inline PROTOBUF_ALWAYS_INLINE void _internal_set_state(const std::string& value);
+ std::string* _internal_mutable_state();
+ public:
+
+ // optional .mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints constraints = 4;
+ bool has_constraints() const;
+ private:
+ bool _internal_has_constraints() const;
+ public:
+ void clear_constraints();
+ const ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints& constraints() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* release_constraints();
+ ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* mutable_constraints();
+ void set_allocated_constraints(::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* constraints);
+ private:
+ const ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints& _internal_constraints() const;
+ ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* _internal_mutable_constraints();
+ public:
+ void unsafe_arena_set_allocated_constraints(
+ ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* constraints);
+ ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* unsafe_arena_release_constraints();
+
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ bool has_threat_type() const;
+ private:
+ bool _internal_has_threat_type() const;
+ public:
+ void clear_threat_type();
+ ::mozilla::safebrowsing::ThreatType threat_type() const;
+ void set_threat_type(::mozilla::safebrowsing::ThreatType value);
+ private:
+ ::mozilla::safebrowsing::ThreatType _internal_threat_type() const;
+ void _internal_set_threat_type(::mozilla::safebrowsing::ThreatType value);
+ public:
+
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+ bool has_platform_type() const;
+ private:
+ bool _internal_has_platform_type() const;
+ public:
+ void clear_platform_type();
+ ::mozilla::safebrowsing::PlatformType platform_type() const;
+ void set_platform_type(::mozilla::safebrowsing::PlatformType value);
+ private:
+ ::mozilla::safebrowsing::PlatformType _internal_platform_type() const;
+ void _internal_set_platform_type(::mozilla::safebrowsing::PlatformType value);
+ public:
+
+ // optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 5;
+ bool has_threat_entry_type() const;
+ private:
+ bool _internal_has_threat_entry_type() const;
+ public:
+ void clear_threat_entry_type();
+ ::mozilla::safebrowsing::ThreatEntryType threat_entry_type() const;
+ void set_threat_entry_type(::mozilla::safebrowsing::ThreatEntryType value);
+ private:
+ ::mozilla::safebrowsing::ThreatEntryType _internal_threat_entry_type() const;
+ void _internal_set_threat_entry_type(::mozilla::safebrowsing::ThreatEntryType value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr state_;
+ ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* constraints_;
+ int threat_type_;
+ int platform_type_;
+ int threat_entry_type_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class FetchThreatListUpdatesRequest final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.FetchThreatListUpdatesRequest) */ {
+ public:
+ inline FetchThreatListUpdatesRequest() : FetchThreatListUpdatesRequest(nullptr) {}
+ ~FetchThreatListUpdatesRequest() override;
+ explicit PROTOBUF_CONSTEXPR FetchThreatListUpdatesRequest(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ FetchThreatListUpdatesRequest(const FetchThreatListUpdatesRequest& from);
+ FetchThreatListUpdatesRequest(FetchThreatListUpdatesRequest&& from) noexcept
+ : FetchThreatListUpdatesRequest() {
+ *this = ::std::move(from);
+ }
+
+ inline FetchThreatListUpdatesRequest& operator=(const FetchThreatListUpdatesRequest& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline FetchThreatListUpdatesRequest& operator=(FetchThreatListUpdatesRequest&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const FetchThreatListUpdatesRequest& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const FetchThreatListUpdatesRequest* internal_default_instance() {
+ return reinterpret_cast<const FetchThreatListUpdatesRequest*>(
+ &_FetchThreatListUpdatesRequest_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 6;
+
+ friend void swap(FetchThreatListUpdatesRequest& a, FetchThreatListUpdatesRequest& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(FetchThreatListUpdatesRequest* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(FetchThreatListUpdatesRequest* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ FetchThreatListUpdatesRequest* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<FetchThreatListUpdatesRequest>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const FetchThreatListUpdatesRequest& from);
+ void MergeFrom(const FetchThreatListUpdatesRequest& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(FetchThreatListUpdatesRequest* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.FetchThreatListUpdatesRequest";
+ }
+ protected:
+ explicit FetchThreatListUpdatesRequest(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ typedef FetchThreatListUpdatesRequest_ListUpdateRequest ListUpdateRequest;
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kListUpdateRequestsFieldNumber = 3,
+ kClientFieldNumber = 1,
+ kChromeClientInfoFieldNumber = 4,
+ };
+ // repeated .mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest list_update_requests = 3;
+ int list_update_requests_size() const;
+ private:
+ int _internal_list_update_requests_size() const;
+ public:
+ void clear_list_update_requests();
+ ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest* mutable_list_update_requests(int index);
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest >*
+ mutable_list_update_requests();
+ private:
+ const ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest& _internal_list_update_requests(int index) const;
+ ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest* _internal_add_list_update_requests();
+ public:
+ const ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest& list_update_requests(int index) const;
+ ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest* add_list_update_requests();
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest >&
+ list_update_requests() const;
+
+ // optional .mozilla.safebrowsing.ClientInfo client = 1;
+ bool has_client() const;
+ private:
+ bool _internal_has_client() const;
+ public:
+ void clear_client();
+ const ::mozilla::safebrowsing::ClientInfo& client() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::ClientInfo* release_client();
+ ::mozilla::safebrowsing::ClientInfo* mutable_client();
+ void set_allocated_client(::mozilla::safebrowsing::ClientInfo* client);
+ private:
+ const ::mozilla::safebrowsing::ClientInfo& _internal_client() const;
+ ::mozilla::safebrowsing::ClientInfo* _internal_mutable_client();
+ public:
+ void unsafe_arena_set_allocated_client(
+ ::mozilla::safebrowsing::ClientInfo* client);
+ ::mozilla::safebrowsing::ClientInfo* unsafe_arena_release_client();
+
+ // optional .mozilla.safebrowsing.ChromeClientInfo chrome_client_info = 4;
+ bool has_chrome_client_info() const;
+ private:
+ bool _internal_has_chrome_client_info() const;
+ public:
+ void clear_chrome_client_info();
+ const ::mozilla::safebrowsing::ChromeClientInfo& chrome_client_info() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::ChromeClientInfo* release_chrome_client_info();
+ ::mozilla::safebrowsing::ChromeClientInfo* mutable_chrome_client_info();
+ void set_allocated_chrome_client_info(::mozilla::safebrowsing::ChromeClientInfo* chrome_client_info);
+ private:
+ const ::mozilla::safebrowsing::ChromeClientInfo& _internal_chrome_client_info() const;
+ ::mozilla::safebrowsing::ChromeClientInfo* _internal_mutable_chrome_client_info();
+ public:
+ void unsafe_arena_set_allocated_chrome_client_info(
+ ::mozilla::safebrowsing::ChromeClientInfo* chrome_client_info);
+ ::mozilla::safebrowsing::ChromeClientInfo* unsafe_arena_release_chrome_client_info();
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.FetchThreatListUpdatesRequest)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest > list_update_requests_;
+ ::mozilla::safebrowsing::ClientInfo* client_;
+ ::mozilla::safebrowsing::ChromeClientInfo* chrome_client_info_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class FetchThreatListUpdatesResponse_ListUpdateResponse final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse) */ {
+ public:
+ inline FetchThreatListUpdatesResponse_ListUpdateResponse() : FetchThreatListUpdatesResponse_ListUpdateResponse(nullptr) {}
+ ~FetchThreatListUpdatesResponse_ListUpdateResponse() override;
+ explicit PROTOBUF_CONSTEXPR FetchThreatListUpdatesResponse_ListUpdateResponse(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ FetchThreatListUpdatesResponse_ListUpdateResponse(const FetchThreatListUpdatesResponse_ListUpdateResponse& from);
+ FetchThreatListUpdatesResponse_ListUpdateResponse(FetchThreatListUpdatesResponse_ListUpdateResponse&& from) noexcept
+ : FetchThreatListUpdatesResponse_ListUpdateResponse() {
+ *this = ::std::move(from);
+ }
+
+ inline FetchThreatListUpdatesResponse_ListUpdateResponse& operator=(const FetchThreatListUpdatesResponse_ListUpdateResponse& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline FetchThreatListUpdatesResponse_ListUpdateResponse& operator=(FetchThreatListUpdatesResponse_ListUpdateResponse&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const FetchThreatListUpdatesResponse_ListUpdateResponse& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const FetchThreatListUpdatesResponse_ListUpdateResponse* internal_default_instance() {
+ return reinterpret_cast<const FetchThreatListUpdatesResponse_ListUpdateResponse*>(
+ &_FetchThreatListUpdatesResponse_ListUpdateResponse_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 7;
+
+ friend void swap(FetchThreatListUpdatesResponse_ListUpdateResponse& a, FetchThreatListUpdatesResponse_ListUpdateResponse& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(FetchThreatListUpdatesResponse_ListUpdateResponse* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(FetchThreatListUpdatesResponse_ListUpdateResponse* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ FetchThreatListUpdatesResponse_ListUpdateResponse* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<FetchThreatListUpdatesResponse_ListUpdateResponse>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const FetchThreatListUpdatesResponse_ListUpdateResponse& from);
+ void MergeFrom(const FetchThreatListUpdatesResponse_ListUpdateResponse& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(FetchThreatListUpdatesResponse_ListUpdateResponse* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse";
+ }
+ protected:
+ explicit FetchThreatListUpdatesResponse_ListUpdateResponse(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ typedef FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType ResponseType;
+ static constexpr ResponseType RESPONSE_TYPE_UNSPECIFIED =
+ FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_RESPONSE_TYPE_UNSPECIFIED;
+ static constexpr ResponseType PARTIAL_UPDATE =
+ FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_PARTIAL_UPDATE;
+ static constexpr ResponseType FULL_UPDATE =
+ FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_FULL_UPDATE;
+ static inline bool ResponseType_IsValid(int value) {
+ return FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_IsValid(value);
+ }
+ static constexpr ResponseType ResponseType_MIN =
+ FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_ResponseType_MIN;
+ static constexpr ResponseType ResponseType_MAX =
+ FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_ResponseType_MAX;
+ static constexpr int ResponseType_ARRAYSIZE =
+ FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_ResponseType_ARRAYSIZE;
+ template<typename T>
+ static inline const std::string& ResponseType_Name(T enum_t_value) {
+ static_assert(::std::is_same<T, ResponseType>::value ||
+ ::std::is_integral<T>::value,
+ "Incorrect type passed to function ResponseType_Name.");
+ return FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_Name(enum_t_value);
+ }
+ static inline bool ResponseType_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name,
+ ResponseType* value) {
+ return FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_Parse(name, value);
+ }
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kAdditionsFieldNumber = 5,
+ kRemovalsFieldNumber = 6,
+ kNewClientStateFieldNumber = 7,
+ kChecksumFieldNumber = 8,
+ kThreatTypeFieldNumber = 1,
+ kThreatEntryTypeFieldNumber = 2,
+ kPlatformTypeFieldNumber = 3,
+ kResponseTypeFieldNumber = 4,
+ };
+ // repeated .mozilla.safebrowsing.ThreatEntrySet additions = 5;
+ int additions_size() const;
+ private:
+ int _internal_additions_size() const;
+ public:
+ void clear_additions();
+ ::mozilla::safebrowsing::ThreatEntrySet* mutable_additions(int index);
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntrySet >*
+ mutable_additions();
+ private:
+ const ::mozilla::safebrowsing::ThreatEntrySet& _internal_additions(int index) const;
+ ::mozilla::safebrowsing::ThreatEntrySet* _internal_add_additions();
+ public:
+ const ::mozilla::safebrowsing::ThreatEntrySet& additions(int index) const;
+ ::mozilla::safebrowsing::ThreatEntrySet* add_additions();
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntrySet >&
+ additions() const;
+
+ // repeated .mozilla.safebrowsing.ThreatEntrySet removals = 6;
+ int removals_size() const;
+ private:
+ int _internal_removals_size() const;
+ public:
+ void clear_removals();
+ ::mozilla::safebrowsing::ThreatEntrySet* mutable_removals(int index);
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntrySet >*
+ mutable_removals();
+ private:
+ const ::mozilla::safebrowsing::ThreatEntrySet& _internal_removals(int index) const;
+ ::mozilla::safebrowsing::ThreatEntrySet* _internal_add_removals();
+ public:
+ const ::mozilla::safebrowsing::ThreatEntrySet& removals(int index) const;
+ ::mozilla::safebrowsing::ThreatEntrySet* add_removals();
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntrySet >&
+ removals() const;
+
+ // optional bytes new_client_state = 7;
+ bool has_new_client_state() const;
+ private:
+ bool _internal_has_new_client_state() const;
+ public:
+ void clear_new_client_state();
+ const std::string& new_client_state() const;
+ template <typename ArgT0 = const std::string&, typename... ArgT>
+ void set_new_client_state(ArgT0&& arg0, ArgT... args);
+ std::string* mutable_new_client_state();
+ PROTOBUF_NODISCARD std::string* release_new_client_state();
+ void set_allocated_new_client_state(std::string* new_client_state);
+ private:
+ const std::string& _internal_new_client_state() const;
+ inline PROTOBUF_ALWAYS_INLINE void _internal_set_new_client_state(const std::string& value);
+ std::string* _internal_mutable_new_client_state();
+ public:
+
+ // optional .mozilla.safebrowsing.Checksum checksum = 8;
+ bool has_checksum() const;
+ private:
+ bool _internal_has_checksum() const;
+ public:
+ void clear_checksum();
+ const ::mozilla::safebrowsing::Checksum& checksum() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::Checksum* release_checksum();
+ ::mozilla::safebrowsing::Checksum* mutable_checksum();
+ void set_allocated_checksum(::mozilla::safebrowsing::Checksum* checksum);
+ private:
+ const ::mozilla::safebrowsing::Checksum& _internal_checksum() const;
+ ::mozilla::safebrowsing::Checksum* _internal_mutable_checksum();
+ public:
+ void unsafe_arena_set_allocated_checksum(
+ ::mozilla::safebrowsing::Checksum* checksum);
+ ::mozilla::safebrowsing::Checksum* unsafe_arena_release_checksum();
+
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ bool has_threat_type() const;
+ private:
+ bool _internal_has_threat_type() const;
+ public:
+ void clear_threat_type();
+ ::mozilla::safebrowsing::ThreatType threat_type() const;
+ void set_threat_type(::mozilla::safebrowsing::ThreatType value);
+ private:
+ ::mozilla::safebrowsing::ThreatType _internal_threat_type() const;
+ void _internal_set_threat_type(::mozilla::safebrowsing::ThreatType value);
+ public:
+
+ // optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 2;
+ bool has_threat_entry_type() const;
+ private:
+ bool _internal_has_threat_entry_type() const;
+ public:
+ void clear_threat_entry_type();
+ ::mozilla::safebrowsing::ThreatEntryType threat_entry_type() const;
+ void set_threat_entry_type(::mozilla::safebrowsing::ThreatEntryType value);
+ private:
+ ::mozilla::safebrowsing::ThreatEntryType _internal_threat_entry_type() const;
+ void _internal_set_threat_entry_type(::mozilla::safebrowsing::ThreatEntryType value);
+ public:
+
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 3;
+ bool has_platform_type() const;
+ private:
+ bool _internal_has_platform_type() const;
+ public:
+ void clear_platform_type();
+ ::mozilla::safebrowsing::PlatformType platform_type() const;
+ void set_platform_type(::mozilla::safebrowsing::PlatformType value);
+ private:
+ ::mozilla::safebrowsing::PlatformType _internal_platform_type() const;
+ void _internal_set_platform_type(::mozilla::safebrowsing::PlatformType value);
+ public:
+
+ // optional .mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.ResponseType response_type = 4;
+ bool has_response_type() const;
+ private:
+ bool _internal_has_response_type() const;
+ public:
+ void clear_response_type();
+ ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType response_type() const;
+ void set_response_type(::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType value);
+ private:
+ ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType _internal_response_type() const;
+ void _internal_set_response_type(::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntrySet > additions_;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntrySet > removals_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr new_client_state_;
+ ::mozilla::safebrowsing::Checksum* checksum_;
+ int threat_type_;
+ int threat_entry_type_;
+ int platform_type_;
+ int response_type_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class FetchThreatListUpdatesResponse final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.FetchThreatListUpdatesResponse) */ {
+ public:
+ inline FetchThreatListUpdatesResponse() : FetchThreatListUpdatesResponse(nullptr) {}
+ ~FetchThreatListUpdatesResponse() override;
+ explicit PROTOBUF_CONSTEXPR FetchThreatListUpdatesResponse(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ FetchThreatListUpdatesResponse(const FetchThreatListUpdatesResponse& from);
+ FetchThreatListUpdatesResponse(FetchThreatListUpdatesResponse&& from) noexcept
+ : FetchThreatListUpdatesResponse() {
+ *this = ::std::move(from);
+ }
+
+ inline FetchThreatListUpdatesResponse& operator=(const FetchThreatListUpdatesResponse& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline FetchThreatListUpdatesResponse& operator=(FetchThreatListUpdatesResponse&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const FetchThreatListUpdatesResponse& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const FetchThreatListUpdatesResponse* internal_default_instance() {
+ return reinterpret_cast<const FetchThreatListUpdatesResponse*>(
+ &_FetchThreatListUpdatesResponse_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 8;
+
+ friend void swap(FetchThreatListUpdatesResponse& a, FetchThreatListUpdatesResponse& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(FetchThreatListUpdatesResponse* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(FetchThreatListUpdatesResponse* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ FetchThreatListUpdatesResponse* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<FetchThreatListUpdatesResponse>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const FetchThreatListUpdatesResponse& from);
+ void MergeFrom(const FetchThreatListUpdatesResponse& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(FetchThreatListUpdatesResponse* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.FetchThreatListUpdatesResponse";
+ }
+ protected:
+ explicit FetchThreatListUpdatesResponse(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ typedef FetchThreatListUpdatesResponse_ListUpdateResponse ListUpdateResponse;
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kListUpdateResponsesFieldNumber = 1,
+ kMinimumWaitDurationFieldNumber = 2,
+ };
+ // repeated .mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse list_update_responses = 1;
+ int list_update_responses_size() const;
+ private:
+ int _internal_list_update_responses_size() const;
+ public:
+ void clear_list_update_responses();
+ ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse* mutable_list_update_responses(int index);
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse >*
+ mutable_list_update_responses();
+ private:
+ const ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse& _internal_list_update_responses(int index) const;
+ ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse* _internal_add_list_update_responses();
+ public:
+ const ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse& list_update_responses(int index) const;
+ ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse* add_list_update_responses();
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse >&
+ list_update_responses() const;
+
+ // optional .mozilla.safebrowsing.Duration minimum_wait_duration = 2;
+ bool has_minimum_wait_duration() const;
+ private:
+ bool _internal_has_minimum_wait_duration() const;
+ public:
+ void clear_minimum_wait_duration();
+ const ::mozilla::safebrowsing::Duration& minimum_wait_duration() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::Duration* release_minimum_wait_duration();
+ ::mozilla::safebrowsing::Duration* mutable_minimum_wait_duration();
+ void set_allocated_minimum_wait_duration(::mozilla::safebrowsing::Duration* minimum_wait_duration);
+ private:
+ const ::mozilla::safebrowsing::Duration& _internal_minimum_wait_duration() const;
+ ::mozilla::safebrowsing::Duration* _internal_mutable_minimum_wait_duration();
+ public:
+ void unsafe_arena_set_allocated_minimum_wait_duration(
+ ::mozilla::safebrowsing::Duration* minimum_wait_duration);
+ ::mozilla::safebrowsing::Duration* unsafe_arena_release_minimum_wait_duration();
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.FetchThreatListUpdatesResponse)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse > list_update_responses_;
+ ::mozilla::safebrowsing::Duration* minimum_wait_duration_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class FindFullHashesRequest final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.FindFullHashesRequest) */ {
+ public:
+ inline FindFullHashesRequest() : FindFullHashesRequest(nullptr) {}
+ ~FindFullHashesRequest() override;
+ explicit PROTOBUF_CONSTEXPR FindFullHashesRequest(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ FindFullHashesRequest(const FindFullHashesRequest& from);
+ FindFullHashesRequest(FindFullHashesRequest&& from) noexcept
+ : FindFullHashesRequest() {
+ *this = ::std::move(from);
+ }
+
+ inline FindFullHashesRequest& operator=(const FindFullHashesRequest& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline FindFullHashesRequest& operator=(FindFullHashesRequest&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const FindFullHashesRequest& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const FindFullHashesRequest* internal_default_instance() {
+ return reinterpret_cast<const FindFullHashesRequest*>(
+ &_FindFullHashesRequest_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 9;
+
+ friend void swap(FindFullHashesRequest& a, FindFullHashesRequest& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(FindFullHashesRequest* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(FindFullHashesRequest* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ FindFullHashesRequest* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<FindFullHashesRequest>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const FindFullHashesRequest& from);
+ void MergeFrom(const FindFullHashesRequest& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(FindFullHashesRequest* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.FindFullHashesRequest";
+ }
+ protected:
+ explicit FindFullHashesRequest(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kClientStatesFieldNumber = 2,
+ kClientFieldNumber = 1,
+ kThreatInfoFieldNumber = 3,
+ };
+ // repeated bytes client_states = 2;
+ int client_states_size() const;
+ private:
+ int _internal_client_states_size() const;
+ public:
+ void clear_client_states();
+ const std::string& client_states(int index) const;
+ std::string* mutable_client_states(int index);
+ void set_client_states(int index, const std::string& value);
+ void set_client_states(int index, std::string&& value);
+ void set_client_states(int index, const char* value);
+ void set_client_states(int index, const void* value, size_t size);
+ std::string* add_client_states();
+ void add_client_states(const std::string& value);
+ void add_client_states(std::string&& value);
+ void add_client_states(const char* value);
+ void add_client_states(const void* value, size_t size);
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>& client_states() const;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>* mutable_client_states();
+ private:
+ const std::string& _internal_client_states(int index) const;
+ std::string* _internal_add_client_states();
+ public:
+
+ // optional .mozilla.safebrowsing.ClientInfo client = 1;
+ bool has_client() const;
+ private:
+ bool _internal_has_client() const;
+ public:
+ void clear_client();
+ const ::mozilla::safebrowsing::ClientInfo& client() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::ClientInfo* release_client();
+ ::mozilla::safebrowsing::ClientInfo* mutable_client();
+ void set_allocated_client(::mozilla::safebrowsing::ClientInfo* client);
+ private:
+ const ::mozilla::safebrowsing::ClientInfo& _internal_client() const;
+ ::mozilla::safebrowsing::ClientInfo* _internal_mutable_client();
+ public:
+ void unsafe_arena_set_allocated_client(
+ ::mozilla::safebrowsing::ClientInfo* client);
+ ::mozilla::safebrowsing::ClientInfo* unsafe_arena_release_client();
+
+ // optional .mozilla.safebrowsing.ThreatInfo threat_info = 3;
+ bool has_threat_info() const;
+ private:
+ bool _internal_has_threat_info() const;
+ public:
+ void clear_threat_info();
+ const ::mozilla::safebrowsing::ThreatInfo& threat_info() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::ThreatInfo* release_threat_info();
+ ::mozilla::safebrowsing::ThreatInfo* mutable_threat_info();
+ void set_allocated_threat_info(::mozilla::safebrowsing::ThreatInfo* threat_info);
+ private:
+ const ::mozilla::safebrowsing::ThreatInfo& _internal_threat_info() const;
+ ::mozilla::safebrowsing::ThreatInfo* _internal_mutable_threat_info();
+ public:
+ void unsafe_arena_set_allocated_threat_info(
+ ::mozilla::safebrowsing::ThreatInfo* threat_info);
+ ::mozilla::safebrowsing::ThreatInfo* unsafe_arena_release_threat_info();
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.FindFullHashesRequest)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string> client_states_;
+ ::mozilla::safebrowsing::ClientInfo* client_;
+ ::mozilla::safebrowsing::ThreatInfo* threat_info_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class FindFullHashesResponse final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.FindFullHashesResponse) */ {
+ public:
+ inline FindFullHashesResponse() : FindFullHashesResponse(nullptr) {}
+ ~FindFullHashesResponse() override;
+ explicit PROTOBUF_CONSTEXPR FindFullHashesResponse(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ FindFullHashesResponse(const FindFullHashesResponse& from);
+ FindFullHashesResponse(FindFullHashesResponse&& from) noexcept
+ : FindFullHashesResponse() {
+ *this = ::std::move(from);
+ }
+
+ inline FindFullHashesResponse& operator=(const FindFullHashesResponse& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline FindFullHashesResponse& operator=(FindFullHashesResponse&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const FindFullHashesResponse& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const FindFullHashesResponse* internal_default_instance() {
+ return reinterpret_cast<const FindFullHashesResponse*>(
+ &_FindFullHashesResponse_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 10;
+
+ friend void swap(FindFullHashesResponse& a, FindFullHashesResponse& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(FindFullHashesResponse* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(FindFullHashesResponse* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ FindFullHashesResponse* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<FindFullHashesResponse>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const FindFullHashesResponse& from);
+ void MergeFrom(const FindFullHashesResponse& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(FindFullHashesResponse* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.FindFullHashesResponse";
+ }
+ protected:
+ explicit FindFullHashesResponse(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kMatchesFieldNumber = 1,
+ kMinimumWaitDurationFieldNumber = 2,
+ kNegativeCacheDurationFieldNumber = 3,
+ };
+ // repeated .mozilla.safebrowsing.ThreatMatch matches = 1;
+ int matches_size() const;
+ private:
+ int _internal_matches_size() const;
+ public:
+ void clear_matches();
+ ::mozilla::safebrowsing::ThreatMatch* mutable_matches(int index);
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatMatch >*
+ mutable_matches();
+ private:
+ const ::mozilla::safebrowsing::ThreatMatch& _internal_matches(int index) const;
+ ::mozilla::safebrowsing::ThreatMatch* _internal_add_matches();
+ public:
+ const ::mozilla::safebrowsing::ThreatMatch& matches(int index) const;
+ ::mozilla::safebrowsing::ThreatMatch* add_matches();
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatMatch >&
+ matches() const;
+
+ // optional .mozilla.safebrowsing.Duration minimum_wait_duration = 2;
+ bool has_minimum_wait_duration() const;
+ private:
+ bool _internal_has_minimum_wait_duration() const;
+ public:
+ void clear_minimum_wait_duration();
+ const ::mozilla::safebrowsing::Duration& minimum_wait_duration() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::Duration* release_minimum_wait_duration();
+ ::mozilla::safebrowsing::Duration* mutable_minimum_wait_duration();
+ void set_allocated_minimum_wait_duration(::mozilla::safebrowsing::Duration* minimum_wait_duration);
+ private:
+ const ::mozilla::safebrowsing::Duration& _internal_minimum_wait_duration() const;
+ ::mozilla::safebrowsing::Duration* _internal_mutable_minimum_wait_duration();
+ public:
+ void unsafe_arena_set_allocated_minimum_wait_duration(
+ ::mozilla::safebrowsing::Duration* minimum_wait_duration);
+ ::mozilla::safebrowsing::Duration* unsafe_arena_release_minimum_wait_duration();
+
+ // optional .mozilla.safebrowsing.Duration negative_cache_duration = 3;
+ bool has_negative_cache_duration() const;
+ private:
+ bool _internal_has_negative_cache_duration() const;
+ public:
+ void clear_negative_cache_duration();
+ const ::mozilla::safebrowsing::Duration& negative_cache_duration() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::Duration* release_negative_cache_duration();
+ ::mozilla::safebrowsing::Duration* mutable_negative_cache_duration();
+ void set_allocated_negative_cache_duration(::mozilla::safebrowsing::Duration* negative_cache_duration);
+ private:
+ const ::mozilla::safebrowsing::Duration& _internal_negative_cache_duration() const;
+ ::mozilla::safebrowsing::Duration* _internal_mutable_negative_cache_duration();
+ public:
+ void unsafe_arena_set_allocated_negative_cache_duration(
+ ::mozilla::safebrowsing::Duration* negative_cache_duration);
+ ::mozilla::safebrowsing::Duration* unsafe_arena_release_negative_cache_duration();
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.FindFullHashesResponse)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatMatch > matches_;
+ ::mozilla::safebrowsing::Duration* minimum_wait_duration_;
+ ::mozilla::safebrowsing::Duration* negative_cache_duration_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class ThreatHit_ThreatSource final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.ThreatHit.ThreatSource) */ {
+ public:
+ inline ThreatHit_ThreatSource() : ThreatHit_ThreatSource(nullptr) {}
+ ~ThreatHit_ThreatSource() override;
+ explicit PROTOBUF_CONSTEXPR ThreatHit_ThreatSource(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ ThreatHit_ThreatSource(const ThreatHit_ThreatSource& from);
+ ThreatHit_ThreatSource(ThreatHit_ThreatSource&& from) noexcept
+ : ThreatHit_ThreatSource() {
+ *this = ::std::move(from);
+ }
+
+ inline ThreatHit_ThreatSource& operator=(const ThreatHit_ThreatSource& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline ThreatHit_ThreatSource& operator=(ThreatHit_ThreatSource&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const ThreatHit_ThreatSource& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const ThreatHit_ThreatSource* internal_default_instance() {
+ return reinterpret_cast<const ThreatHit_ThreatSource*>(
+ &_ThreatHit_ThreatSource_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 11;
+
+ friend void swap(ThreatHit_ThreatSource& a, ThreatHit_ThreatSource& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(ThreatHit_ThreatSource* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(ThreatHit_ThreatSource* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ ThreatHit_ThreatSource* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<ThreatHit_ThreatSource>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const ThreatHit_ThreatSource& from);
+ void MergeFrom(const ThreatHit_ThreatSource& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(ThreatHit_ThreatSource* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.ThreatHit.ThreatSource";
+ }
+ protected:
+ explicit ThreatHit_ThreatSource(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kUrlFieldNumber = 1,
+ kRemoteIpFieldNumber = 3,
+ kReferrerFieldNumber = 4,
+ kTypeFieldNumber = 2,
+ };
+ // optional string url = 1;
+ bool has_url() const;
+ private:
+ bool _internal_has_url() const;
+ public:
+ void clear_url();
+ const std::string& url() const;
+ template <typename ArgT0 = const std::string&, typename... ArgT>
+ void set_url(ArgT0&& arg0, ArgT... args);
+ std::string* mutable_url();
+ PROTOBUF_NODISCARD std::string* release_url();
+ void set_allocated_url(std::string* url);
+ private:
+ const std::string& _internal_url() const;
+ inline PROTOBUF_ALWAYS_INLINE void _internal_set_url(const std::string& value);
+ std::string* _internal_mutable_url();
+ public:
+
+ // optional string remote_ip = 3;
+ bool has_remote_ip() const;
+ private:
+ bool _internal_has_remote_ip() const;
+ public:
+ void clear_remote_ip();
+ const std::string& remote_ip() const;
+ template <typename ArgT0 = const std::string&, typename... ArgT>
+ void set_remote_ip(ArgT0&& arg0, ArgT... args);
+ std::string* mutable_remote_ip();
+ PROTOBUF_NODISCARD std::string* release_remote_ip();
+ void set_allocated_remote_ip(std::string* remote_ip);
+ private:
+ const std::string& _internal_remote_ip() const;
+ inline PROTOBUF_ALWAYS_INLINE void _internal_set_remote_ip(const std::string& value);
+ std::string* _internal_mutable_remote_ip();
+ public:
+
+ // optional string referrer = 4;
+ bool has_referrer() const;
+ private:
+ bool _internal_has_referrer() const;
+ public:
+ void clear_referrer();
+ const std::string& referrer() const;
+ template <typename ArgT0 = const std::string&, typename... ArgT>
+ void set_referrer(ArgT0&& arg0, ArgT... args);
+ std::string* mutable_referrer();
+ PROTOBUF_NODISCARD std::string* release_referrer();
+ void set_allocated_referrer(std::string* referrer);
+ private:
+ const std::string& _internal_referrer() const;
+ inline PROTOBUF_ALWAYS_INLINE void _internal_set_referrer(const std::string& value);
+ std::string* _internal_mutable_referrer();
+ public:
+
+ // optional .mozilla.safebrowsing.ThreatHit.ThreatSourceType type = 2;
+ bool has_type() const;
+ private:
+ bool _internal_has_type() const;
+ public:
+ void clear_type();
+ ::mozilla::safebrowsing::ThreatHit_ThreatSourceType type() const;
+ void set_type(::mozilla::safebrowsing::ThreatHit_ThreatSourceType value);
+ private:
+ ::mozilla::safebrowsing::ThreatHit_ThreatSourceType _internal_type() const;
+ void _internal_set_type(::mozilla::safebrowsing::ThreatHit_ThreatSourceType value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.ThreatHit.ThreatSource)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr url_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr remote_ip_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr referrer_;
+ int type_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class ThreatHit_UserInfo final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.ThreatHit.UserInfo) */ {
+ public:
+ inline ThreatHit_UserInfo() : ThreatHit_UserInfo(nullptr) {}
+ ~ThreatHit_UserInfo() override;
+ explicit PROTOBUF_CONSTEXPR ThreatHit_UserInfo(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ ThreatHit_UserInfo(const ThreatHit_UserInfo& from);
+ ThreatHit_UserInfo(ThreatHit_UserInfo&& from) noexcept
+ : ThreatHit_UserInfo() {
+ *this = ::std::move(from);
+ }
+
+ inline ThreatHit_UserInfo& operator=(const ThreatHit_UserInfo& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline ThreatHit_UserInfo& operator=(ThreatHit_UserInfo&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const ThreatHit_UserInfo& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const ThreatHit_UserInfo* internal_default_instance() {
+ return reinterpret_cast<const ThreatHit_UserInfo*>(
+ &_ThreatHit_UserInfo_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 12;
+
+ friend void swap(ThreatHit_UserInfo& a, ThreatHit_UserInfo& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(ThreatHit_UserInfo* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(ThreatHit_UserInfo* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ ThreatHit_UserInfo* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<ThreatHit_UserInfo>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const ThreatHit_UserInfo& from);
+ void MergeFrom(const ThreatHit_UserInfo& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(ThreatHit_UserInfo* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.ThreatHit.UserInfo";
+ }
+ protected:
+ explicit ThreatHit_UserInfo(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kRegionCodeFieldNumber = 1,
+ kUserIdFieldNumber = 2,
+ };
+ // optional string region_code = 1;
+ bool has_region_code() const;
+ private:
+ bool _internal_has_region_code() const;
+ public:
+ void clear_region_code();
+ const std::string& region_code() const;
+ template <typename ArgT0 = const std::string&, typename... ArgT>
+ void set_region_code(ArgT0&& arg0, ArgT... args);
+ std::string* mutable_region_code();
+ PROTOBUF_NODISCARD std::string* release_region_code();
+ void set_allocated_region_code(std::string* region_code);
+ private:
+ const std::string& _internal_region_code() const;
+ inline PROTOBUF_ALWAYS_INLINE void _internal_set_region_code(const std::string& value);
+ std::string* _internal_mutable_region_code();
+ public:
+
+ // optional bytes user_id = 2;
+ bool has_user_id() const;
+ private:
+ bool _internal_has_user_id() const;
+ public:
+ void clear_user_id();
+ const std::string& user_id() const;
+ template <typename ArgT0 = const std::string&, typename... ArgT>
+ void set_user_id(ArgT0&& arg0, ArgT... args);
+ std::string* mutable_user_id();
+ PROTOBUF_NODISCARD std::string* release_user_id();
+ void set_allocated_user_id(std::string* user_id);
+ private:
+ const std::string& _internal_user_id() const;
+ inline PROTOBUF_ALWAYS_INLINE void _internal_set_user_id(const std::string& value);
+ std::string* _internal_mutable_user_id();
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.ThreatHit.UserInfo)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr region_code_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr user_id_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class ThreatHit final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.ThreatHit) */ {
+ public:
+ inline ThreatHit() : ThreatHit(nullptr) {}
+ ~ThreatHit() override;
+ explicit PROTOBUF_CONSTEXPR ThreatHit(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ ThreatHit(const ThreatHit& from);
+ ThreatHit(ThreatHit&& from) noexcept
+ : ThreatHit() {
+ *this = ::std::move(from);
+ }
+
+ inline ThreatHit& operator=(const ThreatHit& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline ThreatHit& operator=(ThreatHit&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const ThreatHit& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const ThreatHit* internal_default_instance() {
+ return reinterpret_cast<const ThreatHit*>(
+ &_ThreatHit_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 13;
+
+ friend void swap(ThreatHit& a, ThreatHit& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(ThreatHit* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(ThreatHit* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ ThreatHit* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<ThreatHit>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const ThreatHit& from);
+ void MergeFrom(const ThreatHit& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(ThreatHit* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.ThreatHit";
+ }
+ protected:
+ explicit ThreatHit(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ typedef ThreatHit_ThreatSource ThreatSource;
+ typedef ThreatHit_UserInfo UserInfo;
+
+ typedef ThreatHit_ThreatSourceType ThreatSourceType;
+ static constexpr ThreatSourceType THREAT_SOURCE_TYPE_UNSPECIFIED =
+ ThreatHit_ThreatSourceType_THREAT_SOURCE_TYPE_UNSPECIFIED;
+ static constexpr ThreatSourceType MATCHING_URL =
+ ThreatHit_ThreatSourceType_MATCHING_URL;
+ static constexpr ThreatSourceType TAB_URL =
+ ThreatHit_ThreatSourceType_TAB_URL;
+ static constexpr ThreatSourceType TAB_REDIRECT =
+ ThreatHit_ThreatSourceType_TAB_REDIRECT;
+ static constexpr ThreatSourceType TAB_RESOURCE =
+ ThreatHit_ThreatSourceType_TAB_RESOURCE;
+ static inline bool ThreatSourceType_IsValid(int value) {
+ return ThreatHit_ThreatSourceType_IsValid(value);
+ }
+ static constexpr ThreatSourceType ThreatSourceType_MIN =
+ ThreatHit_ThreatSourceType_ThreatSourceType_MIN;
+ static constexpr ThreatSourceType ThreatSourceType_MAX =
+ ThreatHit_ThreatSourceType_ThreatSourceType_MAX;
+ static constexpr int ThreatSourceType_ARRAYSIZE =
+ ThreatHit_ThreatSourceType_ThreatSourceType_ARRAYSIZE;
+ template<typename T>
+ static inline const std::string& ThreatSourceType_Name(T enum_t_value) {
+ static_assert(::std::is_same<T, ThreatSourceType>::value ||
+ ::std::is_integral<T>::value,
+ "Incorrect type passed to function ThreatSourceType_Name.");
+ return ThreatHit_ThreatSourceType_Name(enum_t_value);
+ }
+ static inline bool ThreatSourceType_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name,
+ ThreatSourceType* value) {
+ return ThreatHit_ThreatSourceType_Parse(name, value);
+ }
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kResourcesFieldNumber = 4,
+ kEntryFieldNumber = 3,
+ kClientInfoFieldNumber = 5,
+ kUserInfoFieldNumber = 6,
+ kThreatTypeFieldNumber = 1,
+ kPlatformTypeFieldNumber = 2,
+ };
+ // repeated .mozilla.safebrowsing.ThreatHit.ThreatSource resources = 4;
+ int resources_size() const;
+ private:
+ int _internal_resources_size() const;
+ public:
+ void clear_resources();
+ ::mozilla::safebrowsing::ThreatHit_ThreatSource* mutable_resources(int index);
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatHit_ThreatSource >*
+ mutable_resources();
+ private:
+ const ::mozilla::safebrowsing::ThreatHit_ThreatSource& _internal_resources(int index) const;
+ ::mozilla::safebrowsing::ThreatHit_ThreatSource* _internal_add_resources();
+ public:
+ const ::mozilla::safebrowsing::ThreatHit_ThreatSource& resources(int index) const;
+ ::mozilla::safebrowsing::ThreatHit_ThreatSource* add_resources();
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatHit_ThreatSource >&
+ resources() const;
+
+ // optional .mozilla.safebrowsing.ThreatEntry entry = 3;
+ bool has_entry() const;
+ private:
+ bool _internal_has_entry() const;
+ public:
+ void clear_entry();
+ const ::mozilla::safebrowsing::ThreatEntry& entry() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::ThreatEntry* release_entry();
+ ::mozilla::safebrowsing::ThreatEntry* mutable_entry();
+ void set_allocated_entry(::mozilla::safebrowsing::ThreatEntry* entry);
+ private:
+ const ::mozilla::safebrowsing::ThreatEntry& _internal_entry() const;
+ ::mozilla::safebrowsing::ThreatEntry* _internal_mutable_entry();
+ public:
+ void unsafe_arena_set_allocated_entry(
+ ::mozilla::safebrowsing::ThreatEntry* entry);
+ ::mozilla::safebrowsing::ThreatEntry* unsafe_arena_release_entry();
+
+ // optional .mozilla.safebrowsing.ClientInfo client_info = 5;
+ bool has_client_info() const;
+ private:
+ bool _internal_has_client_info() const;
+ public:
+ void clear_client_info();
+ const ::mozilla::safebrowsing::ClientInfo& client_info() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::ClientInfo* release_client_info();
+ ::mozilla::safebrowsing::ClientInfo* mutable_client_info();
+ void set_allocated_client_info(::mozilla::safebrowsing::ClientInfo* client_info);
+ private:
+ const ::mozilla::safebrowsing::ClientInfo& _internal_client_info() const;
+ ::mozilla::safebrowsing::ClientInfo* _internal_mutable_client_info();
+ public:
+ void unsafe_arena_set_allocated_client_info(
+ ::mozilla::safebrowsing::ClientInfo* client_info);
+ ::mozilla::safebrowsing::ClientInfo* unsafe_arena_release_client_info();
+
+ // optional .mozilla.safebrowsing.ThreatHit.UserInfo user_info = 6;
+ bool has_user_info() const;
+ private:
+ bool _internal_has_user_info() const;
+ public:
+ void clear_user_info();
+ const ::mozilla::safebrowsing::ThreatHit_UserInfo& user_info() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::ThreatHit_UserInfo* release_user_info();
+ ::mozilla::safebrowsing::ThreatHit_UserInfo* mutable_user_info();
+ void set_allocated_user_info(::mozilla::safebrowsing::ThreatHit_UserInfo* user_info);
+ private:
+ const ::mozilla::safebrowsing::ThreatHit_UserInfo& _internal_user_info() const;
+ ::mozilla::safebrowsing::ThreatHit_UserInfo* _internal_mutable_user_info();
+ public:
+ void unsafe_arena_set_allocated_user_info(
+ ::mozilla::safebrowsing::ThreatHit_UserInfo* user_info);
+ ::mozilla::safebrowsing::ThreatHit_UserInfo* unsafe_arena_release_user_info();
+
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ bool has_threat_type() const;
+ private:
+ bool _internal_has_threat_type() const;
+ public:
+ void clear_threat_type();
+ ::mozilla::safebrowsing::ThreatType threat_type() const;
+ void set_threat_type(::mozilla::safebrowsing::ThreatType value);
+ private:
+ ::mozilla::safebrowsing::ThreatType _internal_threat_type() const;
+ void _internal_set_threat_type(::mozilla::safebrowsing::ThreatType value);
+ public:
+
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+ bool has_platform_type() const;
+ private:
+ bool _internal_has_platform_type() const;
+ public:
+ void clear_platform_type();
+ ::mozilla::safebrowsing::PlatformType platform_type() const;
+ void set_platform_type(::mozilla::safebrowsing::PlatformType value);
+ private:
+ ::mozilla::safebrowsing::PlatformType _internal_platform_type() const;
+ void _internal_set_platform_type(::mozilla::safebrowsing::PlatformType value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.ThreatHit)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatHit_ThreatSource > resources_;
+ ::mozilla::safebrowsing::ThreatEntry* entry_;
+ ::mozilla::safebrowsing::ClientInfo* client_info_;
+ ::mozilla::safebrowsing::ThreatHit_UserInfo* user_info_;
+ int threat_type_;
+ int platform_type_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class ClientInfo final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.ClientInfo) */ {
+ public:
+ inline ClientInfo() : ClientInfo(nullptr) {}
+ ~ClientInfo() override;
+ explicit PROTOBUF_CONSTEXPR ClientInfo(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ ClientInfo(const ClientInfo& from);
+ ClientInfo(ClientInfo&& from) noexcept
+ : ClientInfo() {
+ *this = ::std::move(from);
+ }
+
+ inline ClientInfo& operator=(const ClientInfo& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline ClientInfo& operator=(ClientInfo&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const ClientInfo& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const ClientInfo* internal_default_instance() {
+ return reinterpret_cast<const ClientInfo*>(
+ &_ClientInfo_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 14;
+
+ friend void swap(ClientInfo& a, ClientInfo& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(ClientInfo* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(ClientInfo* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ ClientInfo* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<ClientInfo>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const ClientInfo& from);
+ void MergeFrom(const ClientInfo& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(ClientInfo* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.ClientInfo";
+ }
+ protected:
+ explicit ClientInfo(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kClientIdFieldNumber = 1,
+ kClientVersionFieldNumber = 2,
+ };
+ // optional string client_id = 1;
+ bool has_client_id() const;
+ private:
+ bool _internal_has_client_id() const;
+ public:
+ void clear_client_id();
+ const std::string& client_id() const;
+ template <typename ArgT0 = const std::string&, typename... ArgT>
+ void set_client_id(ArgT0&& arg0, ArgT... args);
+ std::string* mutable_client_id();
+ PROTOBUF_NODISCARD std::string* release_client_id();
+ void set_allocated_client_id(std::string* client_id);
+ private:
+ const std::string& _internal_client_id() const;
+ inline PROTOBUF_ALWAYS_INLINE void _internal_set_client_id(const std::string& value);
+ std::string* _internal_mutable_client_id();
+ public:
+
+ // optional string client_version = 2;
+ bool has_client_version() const;
+ private:
+ bool _internal_has_client_version() const;
+ public:
+ void clear_client_version();
+ const std::string& client_version() const;
+ template <typename ArgT0 = const std::string&, typename... ArgT>
+ void set_client_version(ArgT0&& arg0, ArgT... args);
+ std::string* mutable_client_version();
+ PROTOBUF_NODISCARD std::string* release_client_version();
+ void set_allocated_client_version(std::string* client_version);
+ private:
+ const std::string& _internal_client_version() const;
+ inline PROTOBUF_ALWAYS_INLINE void _internal_set_client_version(const std::string& value);
+ std::string* _internal_mutable_client_version();
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.ClientInfo)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr client_id_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr client_version_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class ChromeClientInfo final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.ChromeClientInfo) */ {
+ public:
+ inline ChromeClientInfo() : ChromeClientInfo(nullptr) {}
+ ~ChromeClientInfo() override;
+ explicit PROTOBUF_CONSTEXPR ChromeClientInfo(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ ChromeClientInfo(const ChromeClientInfo& from);
+ ChromeClientInfo(ChromeClientInfo&& from) noexcept
+ : ChromeClientInfo() {
+ *this = ::std::move(from);
+ }
+
+ inline ChromeClientInfo& operator=(const ChromeClientInfo& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline ChromeClientInfo& operator=(ChromeClientInfo&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const ChromeClientInfo& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const ChromeClientInfo* internal_default_instance() {
+ return reinterpret_cast<const ChromeClientInfo*>(
+ &_ChromeClientInfo_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 15;
+
+ friend void swap(ChromeClientInfo& a, ChromeClientInfo& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(ChromeClientInfo* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(ChromeClientInfo* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ ChromeClientInfo* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<ChromeClientInfo>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const ChromeClientInfo& from);
+ void MergeFrom(const ChromeClientInfo& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(ChromeClientInfo* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.ChromeClientInfo";
+ }
+ protected:
+ explicit ChromeClientInfo(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ typedef ChromeClientInfo_SafeBrowsingReportingPopulation SafeBrowsingReportingPopulation;
+ static constexpr SafeBrowsingReportingPopulation UNSPECIFIED =
+ ChromeClientInfo_SafeBrowsingReportingPopulation_UNSPECIFIED;
+ static constexpr SafeBrowsingReportingPopulation OPT_OUT =
+ ChromeClientInfo_SafeBrowsingReportingPopulation_OPT_OUT;
+ static constexpr SafeBrowsingReportingPopulation EXTENDED =
+ ChromeClientInfo_SafeBrowsingReportingPopulation_EXTENDED;
+ static constexpr SafeBrowsingReportingPopulation SCOUT =
+ ChromeClientInfo_SafeBrowsingReportingPopulation_SCOUT;
+ static inline bool SafeBrowsingReportingPopulation_IsValid(int value) {
+ return ChromeClientInfo_SafeBrowsingReportingPopulation_IsValid(value);
+ }
+ static constexpr SafeBrowsingReportingPopulation SafeBrowsingReportingPopulation_MIN =
+ ChromeClientInfo_SafeBrowsingReportingPopulation_SafeBrowsingReportingPopulation_MIN;
+ static constexpr SafeBrowsingReportingPopulation SafeBrowsingReportingPopulation_MAX =
+ ChromeClientInfo_SafeBrowsingReportingPopulation_SafeBrowsingReportingPopulation_MAX;
+ static constexpr int SafeBrowsingReportingPopulation_ARRAYSIZE =
+ ChromeClientInfo_SafeBrowsingReportingPopulation_SafeBrowsingReportingPopulation_ARRAYSIZE;
+ template<typename T>
+ static inline const std::string& SafeBrowsingReportingPopulation_Name(T enum_t_value) {
+ static_assert(::std::is_same<T, SafeBrowsingReportingPopulation>::value ||
+ ::std::is_integral<T>::value,
+ "Incorrect type passed to function SafeBrowsingReportingPopulation_Name.");
+ return ChromeClientInfo_SafeBrowsingReportingPopulation_Name(enum_t_value);
+ }
+ static inline bool SafeBrowsingReportingPopulation_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name,
+ SafeBrowsingReportingPopulation* value) {
+ return ChromeClientInfo_SafeBrowsingReportingPopulation_Parse(name, value);
+ }
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kReportingPopulationFieldNumber = 1,
+ };
+ // optional .mozilla.safebrowsing.ChromeClientInfo.SafeBrowsingReportingPopulation reporting_population = 1;
+ bool has_reporting_population() const;
+ private:
+ bool _internal_has_reporting_population() const;
+ public:
+ void clear_reporting_population();
+ ::mozilla::safebrowsing::ChromeClientInfo_SafeBrowsingReportingPopulation reporting_population() const;
+ void set_reporting_population(::mozilla::safebrowsing::ChromeClientInfo_SafeBrowsingReportingPopulation value);
+ private:
+ ::mozilla::safebrowsing::ChromeClientInfo_SafeBrowsingReportingPopulation _internal_reporting_population() const;
+ void _internal_set_reporting_population(::mozilla::safebrowsing::ChromeClientInfo_SafeBrowsingReportingPopulation value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.ChromeClientInfo)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ int reporting_population_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class Checksum final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.Checksum) */ {
+ public:
+ inline Checksum() : Checksum(nullptr) {}
+ ~Checksum() override;
+ explicit PROTOBUF_CONSTEXPR Checksum(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ Checksum(const Checksum& from);
+ Checksum(Checksum&& from) noexcept
+ : Checksum() {
+ *this = ::std::move(from);
+ }
+
+ inline Checksum& operator=(const Checksum& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline Checksum& operator=(Checksum&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const Checksum& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const Checksum* internal_default_instance() {
+ return reinterpret_cast<const Checksum*>(
+ &_Checksum_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 16;
+
+ friend void swap(Checksum& a, Checksum& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(Checksum* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(Checksum* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ Checksum* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<Checksum>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const Checksum& from);
+ void MergeFrom(const Checksum& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(Checksum* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.Checksum";
+ }
+ protected:
+ explicit Checksum(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kSha256FieldNumber = 1,
+ };
+ // optional bytes sha256 = 1;
+ bool has_sha256() const;
+ private:
+ bool _internal_has_sha256() const;
+ public:
+ void clear_sha256();
+ const std::string& sha256() const;
+ template <typename ArgT0 = const std::string&, typename... ArgT>
+ void set_sha256(ArgT0&& arg0, ArgT... args);
+ std::string* mutable_sha256();
+ PROTOBUF_NODISCARD std::string* release_sha256();
+ void set_allocated_sha256(std::string* sha256);
+ private:
+ const std::string& _internal_sha256() const;
+ inline PROTOBUF_ALWAYS_INLINE void _internal_set_sha256(const std::string& value);
+ std::string* _internal_mutable_sha256();
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.Checksum)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr sha256_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class ThreatEntry final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.ThreatEntry) */ {
+ public:
+ inline ThreatEntry() : ThreatEntry(nullptr) {}
+ ~ThreatEntry() override;
+ explicit PROTOBUF_CONSTEXPR ThreatEntry(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ ThreatEntry(const ThreatEntry& from);
+ ThreatEntry(ThreatEntry&& from) noexcept
+ : ThreatEntry() {
+ *this = ::std::move(from);
+ }
+
+ inline ThreatEntry& operator=(const ThreatEntry& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline ThreatEntry& operator=(ThreatEntry&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const ThreatEntry& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const ThreatEntry* internal_default_instance() {
+ return reinterpret_cast<const ThreatEntry*>(
+ &_ThreatEntry_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 17;
+
+ friend void swap(ThreatEntry& a, ThreatEntry& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(ThreatEntry* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(ThreatEntry* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ ThreatEntry* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<ThreatEntry>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const ThreatEntry& from);
+ void MergeFrom(const ThreatEntry& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(ThreatEntry* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.ThreatEntry";
+ }
+ protected:
+ explicit ThreatEntry(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kHashFieldNumber = 1,
+ kUrlFieldNumber = 2,
+ };
+ // optional bytes hash = 1;
+ bool has_hash() const;
+ private:
+ bool _internal_has_hash() const;
+ public:
+ void clear_hash();
+ const std::string& hash() const;
+ template <typename ArgT0 = const std::string&, typename... ArgT>
+ void set_hash(ArgT0&& arg0, ArgT... args);
+ std::string* mutable_hash();
+ PROTOBUF_NODISCARD std::string* release_hash();
+ void set_allocated_hash(std::string* hash);
+ private:
+ const std::string& _internal_hash() const;
+ inline PROTOBUF_ALWAYS_INLINE void _internal_set_hash(const std::string& value);
+ std::string* _internal_mutable_hash();
+ public:
+
+ // optional string url = 2;
+ bool has_url() const;
+ private:
+ bool _internal_has_url() const;
+ public:
+ void clear_url();
+ const std::string& url() const;
+ template <typename ArgT0 = const std::string&, typename... ArgT>
+ void set_url(ArgT0&& arg0, ArgT... args);
+ std::string* mutable_url();
+ PROTOBUF_NODISCARD std::string* release_url();
+ void set_allocated_url(std::string* url);
+ private:
+ const std::string& _internal_url() const;
+ inline PROTOBUF_ALWAYS_INLINE void _internal_set_url(const std::string& value);
+ std::string* _internal_mutable_url();
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.ThreatEntry)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr hash_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr url_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class ThreatEntrySet final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.ThreatEntrySet) */ {
+ public:
+ inline ThreatEntrySet() : ThreatEntrySet(nullptr) {}
+ ~ThreatEntrySet() override;
+ explicit PROTOBUF_CONSTEXPR ThreatEntrySet(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ ThreatEntrySet(const ThreatEntrySet& from);
+ ThreatEntrySet(ThreatEntrySet&& from) noexcept
+ : ThreatEntrySet() {
+ *this = ::std::move(from);
+ }
+
+ inline ThreatEntrySet& operator=(const ThreatEntrySet& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline ThreatEntrySet& operator=(ThreatEntrySet&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const ThreatEntrySet& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const ThreatEntrySet* internal_default_instance() {
+ return reinterpret_cast<const ThreatEntrySet*>(
+ &_ThreatEntrySet_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 18;
+
+ friend void swap(ThreatEntrySet& a, ThreatEntrySet& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(ThreatEntrySet* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(ThreatEntrySet* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ ThreatEntrySet* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<ThreatEntrySet>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const ThreatEntrySet& from);
+ void MergeFrom(const ThreatEntrySet& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(ThreatEntrySet* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.ThreatEntrySet";
+ }
+ protected:
+ explicit ThreatEntrySet(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kRawHashesFieldNumber = 2,
+ kRawIndicesFieldNumber = 3,
+ kRiceHashesFieldNumber = 4,
+ kRiceIndicesFieldNumber = 5,
+ kCompressionTypeFieldNumber = 1,
+ };
+ // optional .mozilla.safebrowsing.RawHashes raw_hashes = 2;
+ bool has_raw_hashes() const;
+ private:
+ bool _internal_has_raw_hashes() const;
+ public:
+ void clear_raw_hashes();
+ const ::mozilla::safebrowsing::RawHashes& raw_hashes() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::RawHashes* release_raw_hashes();
+ ::mozilla::safebrowsing::RawHashes* mutable_raw_hashes();
+ void set_allocated_raw_hashes(::mozilla::safebrowsing::RawHashes* raw_hashes);
+ private:
+ const ::mozilla::safebrowsing::RawHashes& _internal_raw_hashes() const;
+ ::mozilla::safebrowsing::RawHashes* _internal_mutable_raw_hashes();
+ public:
+ void unsafe_arena_set_allocated_raw_hashes(
+ ::mozilla::safebrowsing::RawHashes* raw_hashes);
+ ::mozilla::safebrowsing::RawHashes* unsafe_arena_release_raw_hashes();
+
+ // optional .mozilla.safebrowsing.RawIndices raw_indices = 3;
+ bool has_raw_indices() const;
+ private:
+ bool _internal_has_raw_indices() const;
+ public:
+ void clear_raw_indices();
+ const ::mozilla::safebrowsing::RawIndices& raw_indices() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::RawIndices* release_raw_indices();
+ ::mozilla::safebrowsing::RawIndices* mutable_raw_indices();
+ void set_allocated_raw_indices(::mozilla::safebrowsing::RawIndices* raw_indices);
+ private:
+ const ::mozilla::safebrowsing::RawIndices& _internal_raw_indices() const;
+ ::mozilla::safebrowsing::RawIndices* _internal_mutable_raw_indices();
+ public:
+ void unsafe_arena_set_allocated_raw_indices(
+ ::mozilla::safebrowsing::RawIndices* raw_indices);
+ ::mozilla::safebrowsing::RawIndices* unsafe_arena_release_raw_indices();
+
+ // optional .mozilla.safebrowsing.RiceDeltaEncoding rice_hashes = 4;
+ bool has_rice_hashes() const;
+ private:
+ bool _internal_has_rice_hashes() const;
+ public:
+ void clear_rice_hashes();
+ const ::mozilla::safebrowsing::RiceDeltaEncoding& rice_hashes() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::RiceDeltaEncoding* release_rice_hashes();
+ ::mozilla::safebrowsing::RiceDeltaEncoding* mutable_rice_hashes();
+ void set_allocated_rice_hashes(::mozilla::safebrowsing::RiceDeltaEncoding* rice_hashes);
+ private:
+ const ::mozilla::safebrowsing::RiceDeltaEncoding& _internal_rice_hashes() const;
+ ::mozilla::safebrowsing::RiceDeltaEncoding* _internal_mutable_rice_hashes();
+ public:
+ void unsafe_arena_set_allocated_rice_hashes(
+ ::mozilla::safebrowsing::RiceDeltaEncoding* rice_hashes);
+ ::mozilla::safebrowsing::RiceDeltaEncoding* unsafe_arena_release_rice_hashes();
+
+ // optional .mozilla.safebrowsing.RiceDeltaEncoding rice_indices = 5;
+ bool has_rice_indices() const;
+ private:
+ bool _internal_has_rice_indices() const;
+ public:
+ void clear_rice_indices();
+ const ::mozilla::safebrowsing::RiceDeltaEncoding& rice_indices() const;
+ PROTOBUF_NODISCARD ::mozilla::safebrowsing::RiceDeltaEncoding* release_rice_indices();
+ ::mozilla::safebrowsing::RiceDeltaEncoding* mutable_rice_indices();
+ void set_allocated_rice_indices(::mozilla::safebrowsing::RiceDeltaEncoding* rice_indices);
+ private:
+ const ::mozilla::safebrowsing::RiceDeltaEncoding& _internal_rice_indices() const;
+ ::mozilla::safebrowsing::RiceDeltaEncoding* _internal_mutable_rice_indices();
+ public:
+ void unsafe_arena_set_allocated_rice_indices(
+ ::mozilla::safebrowsing::RiceDeltaEncoding* rice_indices);
+ ::mozilla::safebrowsing::RiceDeltaEncoding* unsafe_arena_release_rice_indices();
+
+ // optional .mozilla.safebrowsing.CompressionType compression_type = 1;
+ bool has_compression_type() const;
+ private:
+ bool _internal_has_compression_type() const;
+ public:
+ void clear_compression_type();
+ ::mozilla::safebrowsing::CompressionType compression_type() const;
+ void set_compression_type(::mozilla::safebrowsing::CompressionType value);
+ private:
+ ::mozilla::safebrowsing::CompressionType _internal_compression_type() const;
+ void _internal_set_compression_type(::mozilla::safebrowsing::CompressionType value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.ThreatEntrySet)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::mozilla::safebrowsing::RawHashes* raw_hashes_;
+ ::mozilla::safebrowsing::RawIndices* raw_indices_;
+ ::mozilla::safebrowsing::RiceDeltaEncoding* rice_hashes_;
+ ::mozilla::safebrowsing::RiceDeltaEncoding* rice_indices_;
+ int compression_type_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class RawIndices final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.RawIndices) */ {
+ public:
+ inline RawIndices() : RawIndices(nullptr) {}
+ ~RawIndices() override;
+ explicit PROTOBUF_CONSTEXPR RawIndices(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ RawIndices(const RawIndices& from);
+ RawIndices(RawIndices&& from) noexcept
+ : RawIndices() {
+ *this = ::std::move(from);
+ }
+
+ inline RawIndices& operator=(const RawIndices& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline RawIndices& operator=(RawIndices&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const RawIndices& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const RawIndices* internal_default_instance() {
+ return reinterpret_cast<const RawIndices*>(
+ &_RawIndices_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 19;
+
+ friend void swap(RawIndices& a, RawIndices& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(RawIndices* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(RawIndices* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ RawIndices* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<RawIndices>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const RawIndices& from);
+ void MergeFrom(const RawIndices& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(RawIndices* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.RawIndices";
+ }
+ protected:
+ explicit RawIndices(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kIndicesFieldNumber = 1,
+ };
+ // repeated int32 indices = 1;
+ int indices_size() const;
+ private:
+ int _internal_indices_size() const;
+ public:
+ void clear_indices();
+ private:
+ int32_t _internal_indices(int index) const;
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+ _internal_indices() const;
+ void _internal_add_indices(int32_t value);
+ ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+ _internal_mutable_indices();
+ public:
+ int32_t indices(int index) const;
+ void set_indices(int index, int32_t value);
+ void add_indices(int32_t value);
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+ indices() const;
+ ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+ mutable_indices();
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.RawIndices)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t > indices_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class RawHashes final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.RawHashes) */ {
+ public:
+ inline RawHashes() : RawHashes(nullptr) {}
+ ~RawHashes() override;
+ explicit PROTOBUF_CONSTEXPR RawHashes(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ RawHashes(const RawHashes& from);
+ RawHashes(RawHashes&& from) noexcept
+ : RawHashes() {
+ *this = ::std::move(from);
+ }
+
+ inline RawHashes& operator=(const RawHashes& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline RawHashes& operator=(RawHashes&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const RawHashes& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const RawHashes* internal_default_instance() {
+ return reinterpret_cast<const RawHashes*>(
+ &_RawHashes_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 20;
+
+ friend void swap(RawHashes& a, RawHashes& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(RawHashes* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(RawHashes* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ RawHashes* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<RawHashes>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const RawHashes& from);
+ void MergeFrom(const RawHashes& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(RawHashes* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.RawHashes";
+ }
+ protected:
+ explicit RawHashes(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kRawHashesFieldNumber = 2,
+ kPrefixSizeFieldNumber = 1,
+ };
+ // optional bytes raw_hashes = 2;
+ bool has_raw_hashes() const;
+ private:
+ bool _internal_has_raw_hashes() const;
+ public:
+ void clear_raw_hashes();
+ const std::string& raw_hashes() const;
+ template <typename ArgT0 = const std::string&, typename... ArgT>
+ void set_raw_hashes(ArgT0&& arg0, ArgT... args);
+ std::string* mutable_raw_hashes();
+ PROTOBUF_NODISCARD std::string* release_raw_hashes();
+ void set_allocated_raw_hashes(std::string* raw_hashes);
+ private:
+ const std::string& _internal_raw_hashes() const;
+ inline PROTOBUF_ALWAYS_INLINE void _internal_set_raw_hashes(const std::string& value);
+ std::string* _internal_mutable_raw_hashes();
+ public:
+
+ // optional int32 prefix_size = 1;
+ bool has_prefix_size() const;
+ private:
+ bool _internal_has_prefix_size() const;
+ public:
+ void clear_prefix_size();
+ int32_t prefix_size() const;
+ void set_prefix_size(int32_t value);
+ private:
+ int32_t _internal_prefix_size() const;
+ void _internal_set_prefix_size(int32_t value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.RawHashes)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr raw_hashes_;
+ int32_t prefix_size_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class RiceDeltaEncoding final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.RiceDeltaEncoding) */ {
+ public:
+ inline RiceDeltaEncoding() : RiceDeltaEncoding(nullptr) {}
+ ~RiceDeltaEncoding() override;
+ explicit PROTOBUF_CONSTEXPR RiceDeltaEncoding(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ RiceDeltaEncoding(const RiceDeltaEncoding& from);
+ RiceDeltaEncoding(RiceDeltaEncoding&& from) noexcept
+ : RiceDeltaEncoding() {
+ *this = ::std::move(from);
+ }
+
+ inline RiceDeltaEncoding& operator=(const RiceDeltaEncoding& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline RiceDeltaEncoding& operator=(RiceDeltaEncoding&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const RiceDeltaEncoding& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const RiceDeltaEncoding* internal_default_instance() {
+ return reinterpret_cast<const RiceDeltaEncoding*>(
+ &_RiceDeltaEncoding_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 21;
+
+ friend void swap(RiceDeltaEncoding& a, RiceDeltaEncoding& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(RiceDeltaEncoding* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(RiceDeltaEncoding* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ RiceDeltaEncoding* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<RiceDeltaEncoding>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const RiceDeltaEncoding& from);
+ void MergeFrom(const RiceDeltaEncoding& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(RiceDeltaEncoding* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.RiceDeltaEncoding";
+ }
+ protected:
+ explicit RiceDeltaEncoding(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kEncodedDataFieldNumber = 4,
+ kFirstValueFieldNumber = 1,
+ kRiceParameterFieldNumber = 2,
+ kNumEntriesFieldNumber = 3,
+ };
+ // optional bytes encoded_data = 4;
+ bool has_encoded_data() const;
+ private:
+ bool _internal_has_encoded_data() const;
+ public:
+ void clear_encoded_data();
+ const std::string& encoded_data() const;
+ template <typename ArgT0 = const std::string&, typename... ArgT>
+ void set_encoded_data(ArgT0&& arg0, ArgT... args);
+ std::string* mutable_encoded_data();
+ PROTOBUF_NODISCARD std::string* release_encoded_data();
+ void set_allocated_encoded_data(std::string* encoded_data);
+ private:
+ const std::string& _internal_encoded_data() const;
+ inline PROTOBUF_ALWAYS_INLINE void _internal_set_encoded_data(const std::string& value);
+ std::string* _internal_mutable_encoded_data();
+ public:
+
+ // optional int64 first_value = 1;
+ bool has_first_value() const;
+ private:
+ bool _internal_has_first_value() const;
+ public:
+ void clear_first_value();
+ int64_t first_value() const;
+ void set_first_value(int64_t value);
+ private:
+ int64_t _internal_first_value() const;
+ void _internal_set_first_value(int64_t value);
+ public:
+
+ // optional int32 rice_parameter = 2;
+ bool has_rice_parameter() const;
+ private:
+ bool _internal_has_rice_parameter() const;
+ public:
+ void clear_rice_parameter();
+ int32_t rice_parameter() const;
+ void set_rice_parameter(int32_t value);
+ private:
+ int32_t _internal_rice_parameter() const;
+ void _internal_set_rice_parameter(int32_t value);
+ public:
+
+ // optional int32 num_entries = 3;
+ bool has_num_entries() const;
+ private:
+ bool _internal_has_num_entries() const;
+ public:
+ void clear_num_entries();
+ int32_t num_entries() const;
+ void set_num_entries(int32_t value);
+ private:
+ int32_t _internal_num_entries() const;
+ void _internal_set_num_entries(int32_t value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.RiceDeltaEncoding)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr encoded_data_;
+ int64_t first_value_;
+ int32_t rice_parameter_;
+ int32_t num_entries_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class ThreatEntryMetadata_MetadataEntry final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry) */ {
+ public:
+ inline ThreatEntryMetadata_MetadataEntry() : ThreatEntryMetadata_MetadataEntry(nullptr) {}
+ ~ThreatEntryMetadata_MetadataEntry() override;
+ explicit PROTOBUF_CONSTEXPR ThreatEntryMetadata_MetadataEntry(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ ThreatEntryMetadata_MetadataEntry(const ThreatEntryMetadata_MetadataEntry& from);
+ ThreatEntryMetadata_MetadataEntry(ThreatEntryMetadata_MetadataEntry&& from) noexcept
+ : ThreatEntryMetadata_MetadataEntry() {
+ *this = ::std::move(from);
+ }
+
+ inline ThreatEntryMetadata_MetadataEntry& operator=(const ThreatEntryMetadata_MetadataEntry& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline ThreatEntryMetadata_MetadataEntry& operator=(ThreatEntryMetadata_MetadataEntry&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const ThreatEntryMetadata_MetadataEntry& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const ThreatEntryMetadata_MetadataEntry* internal_default_instance() {
+ return reinterpret_cast<const ThreatEntryMetadata_MetadataEntry*>(
+ &_ThreatEntryMetadata_MetadataEntry_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 22;
+
+ friend void swap(ThreatEntryMetadata_MetadataEntry& a, ThreatEntryMetadata_MetadataEntry& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(ThreatEntryMetadata_MetadataEntry* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(ThreatEntryMetadata_MetadataEntry* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ ThreatEntryMetadata_MetadataEntry* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<ThreatEntryMetadata_MetadataEntry>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const ThreatEntryMetadata_MetadataEntry& from);
+ void MergeFrom(const ThreatEntryMetadata_MetadataEntry& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(ThreatEntryMetadata_MetadataEntry* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry";
+ }
+ protected:
+ explicit ThreatEntryMetadata_MetadataEntry(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kKeyFieldNumber = 1,
+ kValueFieldNumber = 2,
+ };
+ // optional bytes key = 1;
+ bool has_key() const;
+ private:
+ bool _internal_has_key() const;
+ public:
+ void clear_key();
+ const std::string& key() const;
+ template <typename ArgT0 = const std::string&, typename... ArgT>
+ void set_key(ArgT0&& arg0, ArgT... args);
+ std::string* mutable_key();
+ PROTOBUF_NODISCARD std::string* release_key();
+ void set_allocated_key(std::string* key);
+ private:
+ const std::string& _internal_key() const;
+ inline PROTOBUF_ALWAYS_INLINE void _internal_set_key(const std::string& value);
+ std::string* _internal_mutable_key();
+ public:
+
+ // optional bytes value = 2;
+ bool has_value() const;
+ private:
+ bool _internal_has_value() const;
+ public:
+ void clear_value();
+ const std::string& value() const;
+ template <typename ArgT0 = const std::string&, typename... ArgT>
+ void set_value(ArgT0&& arg0, ArgT... args);
+ std::string* mutable_value();
+ PROTOBUF_NODISCARD std::string* release_value();
+ void set_allocated_value(std::string* value);
+ private:
+ const std::string& _internal_value() const;
+ inline PROTOBUF_ALWAYS_INLINE void _internal_set_value(const std::string& value);
+ std::string* _internal_mutable_value();
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr key_;
+ ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr value_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class ThreatEntryMetadata final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.ThreatEntryMetadata) */ {
+ public:
+ inline ThreatEntryMetadata() : ThreatEntryMetadata(nullptr) {}
+ ~ThreatEntryMetadata() override;
+ explicit PROTOBUF_CONSTEXPR ThreatEntryMetadata(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ ThreatEntryMetadata(const ThreatEntryMetadata& from);
+ ThreatEntryMetadata(ThreatEntryMetadata&& from) noexcept
+ : ThreatEntryMetadata() {
+ *this = ::std::move(from);
+ }
+
+ inline ThreatEntryMetadata& operator=(const ThreatEntryMetadata& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline ThreatEntryMetadata& operator=(ThreatEntryMetadata&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const ThreatEntryMetadata& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const ThreatEntryMetadata* internal_default_instance() {
+ return reinterpret_cast<const ThreatEntryMetadata*>(
+ &_ThreatEntryMetadata_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 23;
+
+ friend void swap(ThreatEntryMetadata& a, ThreatEntryMetadata& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(ThreatEntryMetadata* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(ThreatEntryMetadata* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ ThreatEntryMetadata* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<ThreatEntryMetadata>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const ThreatEntryMetadata& from);
+ void MergeFrom(const ThreatEntryMetadata& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(ThreatEntryMetadata* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.ThreatEntryMetadata";
+ }
+ protected:
+ explicit ThreatEntryMetadata(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ typedef ThreatEntryMetadata_MetadataEntry MetadataEntry;
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kEntriesFieldNumber = 1,
+ };
+ // repeated .mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry entries = 1;
+ int entries_size() const;
+ private:
+ int _internal_entries_size() const;
+ public:
+ void clear_entries();
+ ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry* mutable_entries(int index);
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry >*
+ mutable_entries();
+ private:
+ const ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry& _internal_entries(int index) const;
+ ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry* _internal_add_entries();
+ public:
+ const ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry& entries(int index) const;
+ ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry* add_entries();
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry >&
+ entries() const;
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.ThreatEntryMetadata)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry > entries_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class ThreatListDescriptor final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.ThreatListDescriptor) */ {
+ public:
+ inline ThreatListDescriptor() : ThreatListDescriptor(nullptr) {}
+ ~ThreatListDescriptor() override;
+ explicit PROTOBUF_CONSTEXPR ThreatListDescriptor(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ ThreatListDescriptor(const ThreatListDescriptor& from);
+ ThreatListDescriptor(ThreatListDescriptor&& from) noexcept
+ : ThreatListDescriptor() {
+ *this = ::std::move(from);
+ }
+
+ inline ThreatListDescriptor& operator=(const ThreatListDescriptor& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline ThreatListDescriptor& operator=(ThreatListDescriptor&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const ThreatListDescriptor& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const ThreatListDescriptor* internal_default_instance() {
+ return reinterpret_cast<const ThreatListDescriptor*>(
+ &_ThreatListDescriptor_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 24;
+
+ friend void swap(ThreatListDescriptor& a, ThreatListDescriptor& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(ThreatListDescriptor* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(ThreatListDescriptor* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ ThreatListDescriptor* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<ThreatListDescriptor>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const ThreatListDescriptor& from);
+ void MergeFrom(const ThreatListDescriptor& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(ThreatListDescriptor* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.ThreatListDescriptor";
+ }
+ protected:
+ explicit ThreatListDescriptor(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kThreatTypeFieldNumber = 1,
+ kPlatformTypeFieldNumber = 2,
+ kThreatEntryTypeFieldNumber = 3,
+ };
+ // optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+ bool has_threat_type() const;
+ private:
+ bool _internal_has_threat_type() const;
+ public:
+ void clear_threat_type();
+ ::mozilla::safebrowsing::ThreatType threat_type() const;
+ void set_threat_type(::mozilla::safebrowsing::ThreatType value);
+ private:
+ ::mozilla::safebrowsing::ThreatType _internal_threat_type() const;
+ void _internal_set_threat_type(::mozilla::safebrowsing::ThreatType value);
+ public:
+
+ // optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+ bool has_platform_type() const;
+ private:
+ bool _internal_has_platform_type() const;
+ public:
+ void clear_platform_type();
+ ::mozilla::safebrowsing::PlatformType platform_type() const;
+ void set_platform_type(::mozilla::safebrowsing::PlatformType value);
+ private:
+ ::mozilla::safebrowsing::PlatformType _internal_platform_type() const;
+ void _internal_set_platform_type(::mozilla::safebrowsing::PlatformType value);
+ public:
+
+ // optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 3;
+ bool has_threat_entry_type() const;
+ private:
+ bool _internal_has_threat_entry_type() const;
+ public:
+ void clear_threat_entry_type();
+ ::mozilla::safebrowsing::ThreatEntryType threat_entry_type() const;
+ void set_threat_entry_type(::mozilla::safebrowsing::ThreatEntryType value);
+ private:
+ ::mozilla::safebrowsing::ThreatEntryType _internal_threat_entry_type() const;
+ void _internal_set_threat_entry_type(::mozilla::safebrowsing::ThreatEntryType value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.ThreatListDescriptor)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ int threat_type_;
+ int platform_type_;
+ int threat_entry_type_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class ListThreatListsResponse final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.ListThreatListsResponse) */ {
+ public:
+ inline ListThreatListsResponse() : ListThreatListsResponse(nullptr) {}
+ ~ListThreatListsResponse() override;
+ explicit PROTOBUF_CONSTEXPR ListThreatListsResponse(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ ListThreatListsResponse(const ListThreatListsResponse& from);
+ ListThreatListsResponse(ListThreatListsResponse&& from) noexcept
+ : ListThreatListsResponse() {
+ *this = ::std::move(from);
+ }
+
+ inline ListThreatListsResponse& operator=(const ListThreatListsResponse& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline ListThreatListsResponse& operator=(ListThreatListsResponse&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const ListThreatListsResponse& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const ListThreatListsResponse* internal_default_instance() {
+ return reinterpret_cast<const ListThreatListsResponse*>(
+ &_ListThreatListsResponse_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 25;
+
+ friend void swap(ListThreatListsResponse& a, ListThreatListsResponse& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(ListThreatListsResponse* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(ListThreatListsResponse* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ ListThreatListsResponse* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<ListThreatListsResponse>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const ListThreatListsResponse& from);
+ void MergeFrom(const ListThreatListsResponse& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(ListThreatListsResponse* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.ListThreatListsResponse";
+ }
+ protected:
+ explicit ListThreatListsResponse(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kThreatListsFieldNumber = 1,
+ };
+ // repeated .mozilla.safebrowsing.ThreatListDescriptor threat_lists = 1;
+ int threat_lists_size() const;
+ private:
+ int _internal_threat_lists_size() const;
+ public:
+ void clear_threat_lists();
+ ::mozilla::safebrowsing::ThreatListDescriptor* mutable_threat_lists(int index);
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatListDescriptor >*
+ mutable_threat_lists();
+ private:
+ const ::mozilla::safebrowsing::ThreatListDescriptor& _internal_threat_lists(int index) const;
+ ::mozilla::safebrowsing::ThreatListDescriptor* _internal_add_threat_lists();
+ public:
+ const ::mozilla::safebrowsing::ThreatListDescriptor& threat_lists(int index) const;
+ ::mozilla::safebrowsing::ThreatListDescriptor* add_threat_lists();
+ const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatListDescriptor >&
+ threat_lists() const;
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.ListThreatListsResponse)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatListDescriptor > threat_lists_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// -------------------------------------------------------------------
+
+class Duration final :
+ public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:mozilla.safebrowsing.Duration) */ {
+ public:
+ inline Duration() : Duration(nullptr) {}
+ ~Duration() override;
+ explicit PROTOBUF_CONSTEXPR Duration(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized);
+
+ Duration(const Duration& from);
+ Duration(Duration&& from) noexcept
+ : Duration() {
+ *this = ::std::move(from);
+ }
+
+ inline Duration& operator=(const Duration& from) {
+ CopyFrom(from);
+ return *this;
+ }
+ inline Duration& operator=(Duration&& from) noexcept {
+ if (this == &from) return *this;
+ if (GetOwningArena() == from.GetOwningArena()
+ #ifdef PROTOBUF_FORCE_COPY_IN_MOVE
+ && GetOwningArena() != nullptr
+ #endif // !PROTOBUF_FORCE_COPY_IN_MOVE
+ ) {
+ InternalSwap(&from);
+ } else {
+ CopyFrom(from);
+ }
+ return *this;
+ }
+
+ inline const std::string& unknown_fields() const {
+ return _internal_metadata_.unknown_fields<std::string>(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString);
+ }
+ inline std::string* mutable_unknown_fields() {
+ return _internal_metadata_.mutable_unknown_fields<std::string>();
+ }
+
+ static const Duration& default_instance() {
+ return *internal_default_instance();
+ }
+ static inline const Duration* internal_default_instance() {
+ return reinterpret_cast<const Duration*>(
+ &_Duration_default_instance_);
+ }
+ static constexpr int kIndexInFileMessages =
+ 26;
+
+ friend void swap(Duration& a, Duration& b) {
+ a.Swap(&b);
+ }
+ inline void Swap(Duration* other) {
+ if (other == this) return;
+ #ifdef PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() != nullptr &&
+ GetOwningArena() == other->GetOwningArena()) {
+ #else // PROTOBUF_FORCE_COPY_IN_SWAP
+ if (GetOwningArena() == other->GetOwningArena()) {
+ #endif // !PROTOBUF_FORCE_COPY_IN_SWAP
+ InternalSwap(other);
+ } else {
+ ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other);
+ }
+ }
+ void UnsafeArenaSwap(Duration* other) {
+ if (other == this) return;
+ GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena());
+ InternalSwap(other);
+ }
+
+ // implements Message ----------------------------------------------
+
+ Duration* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final {
+ return CreateMaybeMessage<Duration>(arena);
+ }
+ void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final;
+ void CopyFrom(const Duration& from);
+ void MergeFrom(const Duration& from);
+ PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final;
+ bool IsInitialized() const final;
+
+ size_t ByteSizeLong() const final;
+ const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final;
+ uint8_t* _InternalSerialize(
+ uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final;
+ int GetCachedSize() const final { return _impl_._cached_size_.Get(); }
+
+ private:
+ void SharedCtor(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned);
+ void SharedDtor();
+ void SetCachedSize(int size) const;
+ void InternalSwap(Duration* other);
+
+ private:
+ friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata;
+ static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() {
+ return "mozilla.safebrowsing.Duration";
+ }
+ protected:
+ explicit Duration(::PROTOBUF_NAMESPACE_ID::Arena* arena,
+ bool is_message_owned = false);
+ public:
+
+ std::string GetTypeName() const final;
+
+ // nested types ----------------------------------------------------
+
+ // accessors -------------------------------------------------------
+
+ enum : int {
+ kSecondsFieldNumber = 1,
+ kNanosFieldNumber = 2,
+ };
+ // optional int64 seconds = 1;
+ bool has_seconds() const;
+ private:
+ bool _internal_has_seconds() const;
+ public:
+ void clear_seconds();
+ int64_t seconds() const;
+ void set_seconds(int64_t value);
+ private:
+ int64_t _internal_seconds() const;
+ void _internal_set_seconds(int64_t value);
+ public:
+
+ // optional int32 nanos = 2;
+ bool has_nanos() const;
+ private:
+ bool _internal_has_nanos() const;
+ public:
+ void clear_nanos();
+ int32_t nanos() const;
+ void set_nanos(int32_t value);
+ private:
+ int32_t _internal_nanos() const;
+ void _internal_set_nanos(int32_t value);
+ public:
+
+ // @@protoc_insertion_point(class_scope:mozilla.safebrowsing.Duration)
+ private:
+ class _Internal;
+
+ template <typename T> friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper;
+ typedef void InternalArenaConstructable_;
+ typedef void DestructorSkippable_;
+ struct Impl_ {
+ ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_;
+ mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_;
+ int64_t seconds_;
+ int32_t nanos_;
+ };
+ union { Impl_ _impl_; };
+ friend struct ::TableStruct_safebrowsing_2eproto;
+};
+// ===================================================================
+
+
+// ===================================================================
+
+#ifdef __GNUC__
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#endif // __GNUC__
+// ThreatInfo
+
+// repeated .mozilla.safebrowsing.ThreatType threat_types = 1;
+inline int ThreatInfo::_internal_threat_types_size() const {
+ return _impl_.threat_types_.size();
+}
+inline int ThreatInfo::threat_types_size() const {
+ return _internal_threat_types_size();
+}
+inline void ThreatInfo::clear_threat_types() {
+ _impl_.threat_types_.Clear();
+}
+inline ::mozilla::safebrowsing::ThreatType ThreatInfo::_internal_threat_types(int index) const {
+ return static_cast< ::mozilla::safebrowsing::ThreatType >(_impl_.threat_types_.Get(index));
+}
+inline ::mozilla::safebrowsing::ThreatType ThreatInfo::threat_types(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatInfo.threat_types)
+ return _internal_threat_types(index);
+}
+inline void ThreatInfo::set_threat_types(int index, ::mozilla::safebrowsing::ThreatType value) {
+ assert(::mozilla::safebrowsing::ThreatType_IsValid(value));
+ _impl_.threat_types_.Set(index, value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatInfo.threat_types)
+}
+inline void ThreatInfo::_internal_add_threat_types(::mozilla::safebrowsing::ThreatType value) {
+ assert(::mozilla::safebrowsing::ThreatType_IsValid(value));
+ _impl_.threat_types_.Add(value);
+}
+inline void ThreatInfo::add_threat_types(::mozilla::safebrowsing::ThreatType value) {
+ _internal_add_threat_types(value);
+ // @@protoc_insertion_point(field_add:mozilla.safebrowsing.ThreatInfo.threat_types)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>&
+ThreatInfo::threat_types() const {
+ // @@protoc_insertion_point(field_list:mozilla.safebrowsing.ThreatInfo.threat_types)
+ return _impl_.threat_types_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>*
+ThreatInfo::_internal_mutable_threat_types() {
+ return &_impl_.threat_types_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>*
+ThreatInfo::mutable_threat_types() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.safebrowsing.ThreatInfo.threat_types)
+ return _internal_mutable_threat_types();
+}
+
+// repeated .mozilla.safebrowsing.PlatformType platform_types = 2;
+inline int ThreatInfo::_internal_platform_types_size() const {
+ return _impl_.platform_types_.size();
+}
+inline int ThreatInfo::platform_types_size() const {
+ return _internal_platform_types_size();
+}
+inline void ThreatInfo::clear_platform_types() {
+ _impl_.platform_types_.Clear();
+}
+inline ::mozilla::safebrowsing::PlatformType ThreatInfo::_internal_platform_types(int index) const {
+ return static_cast< ::mozilla::safebrowsing::PlatformType >(_impl_.platform_types_.Get(index));
+}
+inline ::mozilla::safebrowsing::PlatformType ThreatInfo::platform_types(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatInfo.platform_types)
+ return _internal_platform_types(index);
+}
+inline void ThreatInfo::set_platform_types(int index, ::mozilla::safebrowsing::PlatformType value) {
+ assert(::mozilla::safebrowsing::PlatformType_IsValid(value));
+ _impl_.platform_types_.Set(index, value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatInfo.platform_types)
+}
+inline void ThreatInfo::_internal_add_platform_types(::mozilla::safebrowsing::PlatformType value) {
+ assert(::mozilla::safebrowsing::PlatformType_IsValid(value));
+ _impl_.platform_types_.Add(value);
+}
+inline void ThreatInfo::add_platform_types(::mozilla::safebrowsing::PlatformType value) {
+ _internal_add_platform_types(value);
+ // @@protoc_insertion_point(field_add:mozilla.safebrowsing.ThreatInfo.platform_types)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>&
+ThreatInfo::platform_types() const {
+ // @@protoc_insertion_point(field_list:mozilla.safebrowsing.ThreatInfo.platform_types)
+ return _impl_.platform_types_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>*
+ThreatInfo::_internal_mutable_platform_types() {
+ return &_impl_.platform_types_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>*
+ThreatInfo::mutable_platform_types() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.safebrowsing.ThreatInfo.platform_types)
+ return _internal_mutable_platform_types();
+}
+
+// repeated .mozilla.safebrowsing.ThreatEntryType threat_entry_types = 4;
+inline int ThreatInfo::_internal_threat_entry_types_size() const {
+ return _impl_.threat_entry_types_.size();
+}
+inline int ThreatInfo::threat_entry_types_size() const {
+ return _internal_threat_entry_types_size();
+}
+inline void ThreatInfo::clear_threat_entry_types() {
+ _impl_.threat_entry_types_.Clear();
+}
+inline ::mozilla::safebrowsing::ThreatEntryType ThreatInfo::_internal_threat_entry_types(int index) const {
+ return static_cast< ::mozilla::safebrowsing::ThreatEntryType >(_impl_.threat_entry_types_.Get(index));
+}
+inline ::mozilla::safebrowsing::ThreatEntryType ThreatInfo::threat_entry_types(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatInfo.threat_entry_types)
+ return _internal_threat_entry_types(index);
+}
+inline void ThreatInfo::set_threat_entry_types(int index, ::mozilla::safebrowsing::ThreatEntryType value) {
+ assert(::mozilla::safebrowsing::ThreatEntryType_IsValid(value));
+ _impl_.threat_entry_types_.Set(index, value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatInfo.threat_entry_types)
+}
+inline void ThreatInfo::_internal_add_threat_entry_types(::mozilla::safebrowsing::ThreatEntryType value) {
+ assert(::mozilla::safebrowsing::ThreatEntryType_IsValid(value));
+ _impl_.threat_entry_types_.Add(value);
+}
+inline void ThreatInfo::add_threat_entry_types(::mozilla::safebrowsing::ThreatEntryType value) {
+ _internal_add_threat_entry_types(value);
+ // @@protoc_insertion_point(field_add:mozilla.safebrowsing.ThreatInfo.threat_entry_types)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>&
+ThreatInfo::threat_entry_types() const {
+ // @@protoc_insertion_point(field_list:mozilla.safebrowsing.ThreatInfo.threat_entry_types)
+ return _impl_.threat_entry_types_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>*
+ThreatInfo::_internal_mutable_threat_entry_types() {
+ return &_impl_.threat_entry_types_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>*
+ThreatInfo::mutable_threat_entry_types() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.safebrowsing.ThreatInfo.threat_entry_types)
+ return _internal_mutable_threat_entry_types();
+}
+
+// repeated .mozilla.safebrowsing.ThreatEntry threat_entries = 3;
+inline int ThreatInfo::_internal_threat_entries_size() const {
+ return _impl_.threat_entries_.size();
+}
+inline int ThreatInfo::threat_entries_size() const {
+ return _internal_threat_entries_size();
+}
+inline void ThreatInfo::clear_threat_entries() {
+ _impl_.threat_entries_.Clear();
+}
+inline ::mozilla::safebrowsing::ThreatEntry* ThreatInfo::mutable_threat_entries(int index) {
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatInfo.threat_entries)
+ return _impl_.threat_entries_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntry >*
+ThreatInfo::mutable_threat_entries() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.safebrowsing.ThreatInfo.threat_entries)
+ return &_impl_.threat_entries_;
+}
+inline const ::mozilla::safebrowsing::ThreatEntry& ThreatInfo::_internal_threat_entries(int index) const {
+ return _impl_.threat_entries_.Get(index);
+}
+inline const ::mozilla::safebrowsing::ThreatEntry& ThreatInfo::threat_entries(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatInfo.threat_entries)
+ return _internal_threat_entries(index);
+}
+inline ::mozilla::safebrowsing::ThreatEntry* ThreatInfo::_internal_add_threat_entries() {
+ return _impl_.threat_entries_.Add();
+}
+inline ::mozilla::safebrowsing::ThreatEntry* ThreatInfo::add_threat_entries() {
+ ::mozilla::safebrowsing::ThreatEntry* _add = _internal_add_threat_entries();
+ // @@protoc_insertion_point(field_add:mozilla.safebrowsing.ThreatInfo.threat_entries)
+ return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntry >&
+ThreatInfo::threat_entries() const {
+ // @@protoc_insertion_point(field_list:mozilla.safebrowsing.ThreatInfo.threat_entries)
+ return _impl_.threat_entries_;
+}
+
+// -------------------------------------------------------------------
+
+// ThreatMatch
+
+// optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+inline bool ThreatMatch::_internal_has_threat_type() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000008u) != 0;
+ return value;
+}
+inline bool ThreatMatch::has_threat_type() const {
+ return _internal_has_threat_type();
+}
+inline void ThreatMatch::clear_threat_type() {
+ _impl_.threat_type_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000008u;
+}
+inline ::mozilla::safebrowsing::ThreatType ThreatMatch::_internal_threat_type() const {
+ return static_cast< ::mozilla::safebrowsing::ThreatType >(_impl_.threat_type_);
+}
+inline ::mozilla::safebrowsing::ThreatType ThreatMatch::threat_type() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatMatch.threat_type)
+ return _internal_threat_type();
+}
+inline void ThreatMatch::_internal_set_threat_type(::mozilla::safebrowsing::ThreatType value) {
+ assert(::mozilla::safebrowsing::ThreatType_IsValid(value));
+ _impl_._has_bits_[0] |= 0x00000008u;
+ _impl_.threat_type_ = value;
+}
+inline void ThreatMatch::set_threat_type(::mozilla::safebrowsing::ThreatType value) {
+ _internal_set_threat_type(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatMatch.threat_type)
+}
+
+// optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+inline bool ThreatMatch::_internal_has_platform_type() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000010u) != 0;
+ return value;
+}
+inline bool ThreatMatch::has_platform_type() const {
+ return _internal_has_platform_type();
+}
+inline void ThreatMatch::clear_platform_type() {
+ _impl_.platform_type_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000010u;
+}
+inline ::mozilla::safebrowsing::PlatformType ThreatMatch::_internal_platform_type() const {
+ return static_cast< ::mozilla::safebrowsing::PlatformType >(_impl_.platform_type_);
+}
+inline ::mozilla::safebrowsing::PlatformType ThreatMatch::platform_type() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatMatch.platform_type)
+ return _internal_platform_type();
+}
+inline void ThreatMatch::_internal_set_platform_type(::mozilla::safebrowsing::PlatformType value) {
+ assert(::mozilla::safebrowsing::PlatformType_IsValid(value));
+ _impl_._has_bits_[0] |= 0x00000010u;
+ _impl_.platform_type_ = value;
+}
+inline void ThreatMatch::set_platform_type(::mozilla::safebrowsing::PlatformType value) {
+ _internal_set_platform_type(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatMatch.platform_type)
+}
+
+// optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 6;
+inline bool ThreatMatch::_internal_has_threat_entry_type() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000020u) != 0;
+ return value;
+}
+inline bool ThreatMatch::has_threat_entry_type() const {
+ return _internal_has_threat_entry_type();
+}
+inline void ThreatMatch::clear_threat_entry_type() {
+ _impl_.threat_entry_type_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000020u;
+}
+inline ::mozilla::safebrowsing::ThreatEntryType ThreatMatch::_internal_threat_entry_type() const {
+ return static_cast< ::mozilla::safebrowsing::ThreatEntryType >(_impl_.threat_entry_type_);
+}
+inline ::mozilla::safebrowsing::ThreatEntryType ThreatMatch::threat_entry_type() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatMatch.threat_entry_type)
+ return _internal_threat_entry_type();
+}
+inline void ThreatMatch::_internal_set_threat_entry_type(::mozilla::safebrowsing::ThreatEntryType value) {
+ assert(::mozilla::safebrowsing::ThreatEntryType_IsValid(value));
+ _impl_._has_bits_[0] |= 0x00000020u;
+ _impl_.threat_entry_type_ = value;
+}
+inline void ThreatMatch::set_threat_entry_type(::mozilla::safebrowsing::ThreatEntryType value) {
+ _internal_set_threat_entry_type(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatMatch.threat_entry_type)
+}
+
+// optional .mozilla.safebrowsing.ThreatEntry threat = 3;
+inline bool ThreatMatch::_internal_has_threat() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.threat_ != nullptr);
+ return value;
+}
+inline bool ThreatMatch::has_threat() const {
+ return _internal_has_threat();
+}
+inline void ThreatMatch::clear_threat() {
+ if (_impl_.threat_ != nullptr) _impl_.threat_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const ::mozilla::safebrowsing::ThreatEntry& ThreatMatch::_internal_threat() const {
+ const ::mozilla::safebrowsing::ThreatEntry* p = _impl_.threat_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::ThreatEntry&>(
+ ::mozilla::safebrowsing::_ThreatEntry_default_instance_);
+}
+inline const ::mozilla::safebrowsing::ThreatEntry& ThreatMatch::threat() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatMatch.threat)
+ return _internal_threat();
+}
+inline void ThreatMatch::unsafe_arena_set_allocated_threat(
+ ::mozilla::safebrowsing::ThreatEntry* threat) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.threat_);
+ }
+ _impl_.threat_ = threat;
+ if (threat) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.ThreatMatch.threat)
+}
+inline ::mozilla::safebrowsing::ThreatEntry* ThreatMatch::release_threat() {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ ::mozilla::safebrowsing::ThreatEntry* temp = _impl_.threat_;
+ _impl_.threat_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::ThreatEntry* ThreatMatch::unsafe_arena_release_threat() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ThreatMatch.threat)
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ ::mozilla::safebrowsing::ThreatEntry* temp = _impl_.threat_;
+ _impl_.threat_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::ThreatEntry* ThreatMatch::_internal_mutable_threat() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ if (_impl_.threat_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::ThreatEntry>(GetArenaForAllocation());
+ _impl_.threat_ = p;
+ }
+ return _impl_.threat_;
+}
+inline ::mozilla::safebrowsing::ThreatEntry* ThreatMatch::mutable_threat() {
+ ::mozilla::safebrowsing::ThreatEntry* _msg = _internal_mutable_threat();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatMatch.threat)
+ return _msg;
+}
+inline void ThreatMatch::set_allocated_threat(::mozilla::safebrowsing::ThreatEntry* threat) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.threat_;
+ }
+ if (threat) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(threat);
+ if (message_arena != submessage_arena) {
+ threat = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, threat, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.threat_ = threat;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ThreatMatch.threat)
+}
+
+// optional .mozilla.safebrowsing.ThreatEntryMetadata threat_entry_metadata = 4;
+inline bool ThreatMatch::_internal_has_threat_entry_metadata() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.threat_entry_metadata_ != nullptr);
+ return value;
+}
+inline bool ThreatMatch::has_threat_entry_metadata() const {
+ return _internal_has_threat_entry_metadata();
+}
+inline void ThreatMatch::clear_threat_entry_metadata() {
+ if (_impl_.threat_entry_metadata_ != nullptr) _impl_.threat_entry_metadata_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const ::mozilla::safebrowsing::ThreatEntryMetadata& ThreatMatch::_internal_threat_entry_metadata() const {
+ const ::mozilla::safebrowsing::ThreatEntryMetadata* p = _impl_.threat_entry_metadata_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::ThreatEntryMetadata&>(
+ ::mozilla::safebrowsing::_ThreatEntryMetadata_default_instance_);
+}
+inline const ::mozilla::safebrowsing::ThreatEntryMetadata& ThreatMatch::threat_entry_metadata() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatMatch.threat_entry_metadata)
+ return _internal_threat_entry_metadata();
+}
+inline void ThreatMatch::unsafe_arena_set_allocated_threat_entry_metadata(
+ ::mozilla::safebrowsing::ThreatEntryMetadata* threat_entry_metadata) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.threat_entry_metadata_);
+ }
+ _impl_.threat_entry_metadata_ = threat_entry_metadata;
+ if (threat_entry_metadata) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.ThreatMatch.threat_entry_metadata)
+}
+inline ::mozilla::safebrowsing::ThreatEntryMetadata* ThreatMatch::release_threat_entry_metadata() {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::safebrowsing::ThreatEntryMetadata* temp = _impl_.threat_entry_metadata_;
+ _impl_.threat_entry_metadata_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::ThreatEntryMetadata* ThreatMatch::unsafe_arena_release_threat_entry_metadata() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ThreatMatch.threat_entry_metadata)
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::safebrowsing::ThreatEntryMetadata* temp = _impl_.threat_entry_metadata_;
+ _impl_.threat_entry_metadata_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::ThreatEntryMetadata* ThreatMatch::_internal_mutable_threat_entry_metadata() {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ if (_impl_.threat_entry_metadata_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::ThreatEntryMetadata>(GetArenaForAllocation());
+ _impl_.threat_entry_metadata_ = p;
+ }
+ return _impl_.threat_entry_metadata_;
+}
+inline ::mozilla::safebrowsing::ThreatEntryMetadata* ThreatMatch::mutable_threat_entry_metadata() {
+ ::mozilla::safebrowsing::ThreatEntryMetadata* _msg = _internal_mutable_threat_entry_metadata();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatMatch.threat_entry_metadata)
+ return _msg;
+}
+inline void ThreatMatch::set_allocated_threat_entry_metadata(::mozilla::safebrowsing::ThreatEntryMetadata* threat_entry_metadata) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.threat_entry_metadata_;
+ }
+ if (threat_entry_metadata) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(threat_entry_metadata);
+ if (message_arena != submessage_arena) {
+ threat_entry_metadata = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, threat_entry_metadata, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ _impl_.threat_entry_metadata_ = threat_entry_metadata;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ThreatMatch.threat_entry_metadata)
+}
+
+// optional .mozilla.safebrowsing.Duration cache_duration = 5;
+inline bool ThreatMatch::_internal_has_cache_duration() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.cache_duration_ != nullptr);
+ return value;
+}
+inline bool ThreatMatch::has_cache_duration() const {
+ return _internal_has_cache_duration();
+}
+inline void ThreatMatch::clear_cache_duration() {
+ if (_impl_.cache_duration_ != nullptr) _impl_.cache_duration_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline const ::mozilla::safebrowsing::Duration& ThreatMatch::_internal_cache_duration() const {
+ const ::mozilla::safebrowsing::Duration* p = _impl_.cache_duration_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::Duration&>(
+ ::mozilla::safebrowsing::_Duration_default_instance_);
+}
+inline const ::mozilla::safebrowsing::Duration& ThreatMatch::cache_duration() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatMatch.cache_duration)
+ return _internal_cache_duration();
+}
+inline void ThreatMatch::unsafe_arena_set_allocated_cache_duration(
+ ::mozilla::safebrowsing::Duration* cache_duration) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.cache_duration_);
+ }
+ _impl_.cache_duration_ = cache_duration;
+ if (cache_duration) {
+ _impl_._has_bits_[0] |= 0x00000004u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000004u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.ThreatMatch.cache_duration)
+}
+inline ::mozilla::safebrowsing::Duration* ThreatMatch::release_cache_duration() {
+ _impl_._has_bits_[0] &= ~0x00000004u;
+ ::mozilla::safebrowsing::Duration* temp = _impl_.cache_duration_;
+ _impl_.cache_duration_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::Duration* ThreatMatch::unsafe_arena_release_cache_duration() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ThreatMatch.cache_duration)
+ _impl_._has_bits_[0] &= ~0x00000004u;
+ ::mozilla::safebrowsing::Duration* temp = _impl_.cache_duration_;
+ _impl_.cache_duration_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::Duration* ThreatMatch::_internal_mutable_cache_duration() {
+ _impl_._has_bits_[0] |= 0x00000004u;
+ if (_impl_.cache_duration_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::Duration>(GetArenaForAllocation());
+ _impl_.cache_duration_ = p;
+ }
+ return _impl_.cache_duration_;
+}
+inline ::mozilla::safebrowsing::Duration* ThreatMatch::mutable_cache_duration() {
+ ::mozilla::safebrowsing::Duration* _msg = _internal_mutable_cache_duration();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatMatch.cache_duration)
+ return _msg;
+}
+inline void ThreatMatch::set_allocated_cache_duration(::mozilla::safebrowsing::Duration* cache_duration) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.cache_duration_;
+ }
+ if (cache_duration) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(cache_duration);
+ if (message_arena != submessage_arena) {
+ cache_duration = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, cache_duration, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000004u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000004u;
+ }
+ _impl_.cache_duration_ = cache_duration;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ThreatMatch.cache_duration)
+}
+
+// -------------------------------------------------------------------
+
+// FindThreatMatchesRequest
+
+// optional .mozilla.safebrowsing.ClientInfo client = 1;
+inline bool FindThreatMatchesRequest::_internal_has_client() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.client_ != nullptr);
+ return value;
+}
+inline bool FindThreatMatchesRequest::has_client() const {
+ return _internal_has_client();
+}
+inline void FindThreatMatchesRequest::clear_client() {
+ if (_impl_.client_ != nullptr) _impl_.client_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const ::mozilla::safebrowsing::ClientInfo& FindThreatMatchesRequest::_internal_client() const {
+ const ::mozilla::safebrowsing::ClientInfo* p = _impl_.client_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::ClientInfo&>(
+ ::mozilla::safebrowsing::_ClientInfo_default_instance_);
+}
+inline const ::mozilla::safebrowsing::ClientInfo& FindThreatMatchesRequest::client() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FindThreatMatchesRequest.client)
+ return _internal_client();
+}
+inline void FindThreatMatchesRequest::unsafe_arena_set_allocated_client(
+ ::mozilla::safebrowsing::ClientInfo* client) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.client_);
+ }
+ _impl_.client_ = client;
+ if (client) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.FindThreatMatchesRequest.client)
+}
+inline ::mozilla::safebrowsing::ClientInfo* FindThreatMatchesRequest::release_client() {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ ::mozilla::safebrowsing::ClientInfo* temp = _impl_.client_;
+ _impl_.client_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::ClientInfo* FindThreatMatchesRequest::unsafe_arena_release_client() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.FindThreatMatchesRequest.client)
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ ::mozilla::safebrowsing::ClientInfo* temp = _impl_.client_;
+ _impl_.client_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::ClientInfo* FindThreatMatchesRequest::_internal_mutable_client() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ if (_impl_.client_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::ClientInfo>(GetArenaForAllocation());
+ _impl_.client_ = p;
+ }
+ return _impl_.client_;
+}
+inline ::mozilla::safebrowsing::ClientInfo* FindThreatMatchesRequest::mutable_client() {
+ ::mozilla::safebrowsing::ClientInfo* _msg = _internal_mutable_client();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FindThreatMatchesRequest.client)
+ return _msg;
+}
+inline void FindThreatMatchesRequest::set_allocated_client(::mozilla::safebrowsing::ClientInfo* client) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.client_;
+ }
+ if (client) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(client);
+ if (message_arena != submessage_arena) {
+ client = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, client, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.client_ = client;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.FindThreatMatchesRequest.client)
+}
+
+// optional .mozilla.safebrowsing.ThreatInfo threat_info = 2;
+inline bool FindThreatMatchesRequest::_internal_has_threat_info() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.threat_info_ != nullptr);
+ return value;
+}
+inline bool FindThreatMatchesRequest::has_threat_info() const {
+ return _internal_has_threat_info();
+}
+inline void FindThreatMatchesRequest::clear_threat_info() {
+ if (_impl_.threat_info_ != nullptr) _impl_.threat_info_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const ::mozilla::safebrowsing::ThreatInfo& FindThreatMatchesRequest::_internal_threat_info() const {
+ const ::mozilla::safebrowsing::ThreatInfo* p = _impl_.threat_info_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::ThreatInfo&>(
+ ::mozilla::safebrowsing::_ThreatInfo_default_instance_);
+}
+inline const ::mozilla::safebrowsing::ThreatInfo& FindThreatMatchesRequest::threat_info() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FindThreatMatchesRequest.threat_info)
+ return _internal_threat_info();
+}
+inline void FindThreatMatchesRequest::unsafe_arena_set_allocated_threat_info(
+ ::mozilla::safebrowsing::ThreatInfo* threat_info) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.threat_info_);
+ }
+ _impl_.threat_info_ = threat_info;
+ if (threat_info) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.FindThreatMatchesRequest.threat_info)
+}
+inline ::mozilla::safebrowsing::ThreatInfo* FindThreatMatchesRequest::release_threat_info() {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::safebrowsing::ThreatInfo* temp = _impl_.threat_info_;
+ _impl_.threat_info_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::ThreatInfo* FindThreatMatchesRequest::unsafe_arena_release_threat_info() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.FindThreatMatchesRequest.threat_info)
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::safebrowsing::ThreatInfo* temp = _impl_.threat_info_;
+ _impl_.threat_info_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::ThreatInfo* FindThreatMatchesRequest::_internal_mutable_threat_info() {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ if (_impl_.threat_info_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::ThreatInfo>(GetArenaForAllocation());
+ _impl_.threat_info_ = p;
+ }
+ return _impl_.threat_info_;
+}
+inline ::mozilla::safebrowsing::ThreatInfo* FindThreatMatchesRequest::mutable_threat_info() {
+ ::mozilla::safebrowsing::ThreatInfo* _msg = _internal_mutable_threat_info();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FindThreatMatchesRequest.threat_info)
+ return _msg;
+}
+inline void FindThreatMatchesRequest::set_allocated_threat_info(::mozilla::safebrowsing::ThreatInfo* threat_info) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.threat_info_;
+ }
+ if (threat_info) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(threat_info);
+ if (message_arena != submessage_arena) {
+ threat_info = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, threat_info, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ _impl_.threat_info_ = threat_info;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.FindThreatMatchesRequest.threat_info)
+}
+
+// -------------------------------------------------------------------
+
+// FindThreatMatchesResponse
+
+// repeated .mozilla.safebrowsing.ThreatMatch matches = 1;
+inline int FindThreatMatchesResponse::_internal_matches_size() const {
+ return _impl_.matches_.size();
+}
+inline int FindThreatMatchesResponse::matches_size() const {
+ return _internal_matches_size();
+}
+inline void FindThreatMatchesResponse::clear_matches() {
+ _impl_.matches_.Clear();
+}
+inline ::mozilla::safebrowsing::ThreatMatch* FindThreatMatchesResponse::mutable_matches(int index) {
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FindThreatMatchesResponse.matches)
+ return _impl_.matches_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatMatch >*
+FindThreatMatchesResponse::mutable_matches() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.safebrowsing.FindThreatMatchesResponse.matches)
+ return &_impl_.matches_;
+}
+inline const ::mozilla::safebrowsing::ThreatMatch& FindThreatMatchesResponse::_internal_matches(int index) const {
+ return _impl_.matches_.Get(index);
+}
+inline const ::mozilla::safebrowsing::ThreatMatch& FindThreatMatchesResponse::matches(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FindThreatMatchesResponse.matches)
+ return _internal_matches(index);
+}
+inline ::mozilla::safebrowsing::ThreatMatch* FindThreatMatchesResponse::_internal_add_matches() {
+ return _impl_.matches_.Add();
+}
+inline ::mozilla::safebrowsing::ThreatMatch* FindThreatMatchesResponse::add_matches() {
+ ::mozilla::safebrowsing::ThreatMatch* _add = _internal_add_matches();
+ // @@protoc_insertion_point(field_add:mozilla.safebrowsing.FindThreatMatchesResponse.matches)
+ return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatMatch >&
+FindThreatMatchesResponse::matches() const {
+ // @@protoc_insertion_point(field_list:mozilla.safebrowsing.FindThreatMatchesResponse.matches)
+ return _impl_.matches_;
+}
+
+// -------------------------------------------------------------------
+
+// FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints
+
+// optional int32 max_update_entries = 1;
+inline bool FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::_internal_has_max_update_entries() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ return value;
+}
+inline bool FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::has_max_update_entries() const {
+ return _internal_has_max_update_entries();
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::clear_max_update_entries() {
+ _impl_.max_update_entries_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline int32_t FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::_internal_max_update_entries() const {
+ return _impl_.max_update_entries_;
+}
+inline int32_t FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::max_update_entries() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints.max_update_entries)
+ return _internal_max_update_entries();
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::_internal_set_max_update_entries(int32_t value) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.max_update_entries_ = value;
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::set_max_update_entries(int32_t value) {
+ _internal_set_max_update_entries(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints.max_update_entries)
+}
+
+// optional int32 max_database_entries = 2;
+inline bool FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::_internal_has_max_database_entries() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+ return value;
+}
+inline bool FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::has_max_database_entries() const {
+ return _internal_has_max_database_entries();
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::clear_max_database_entries() {
+ _impl_.max_database_entries_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline int32_t FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::_internal_max_database_entries() const {
+ return _impl_.max_database_entries_;
+}
+inline int32_t FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::max_database_entries() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints.max_database_entries)
+ return _internal_max_database_entries();
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::_internal_set_max_database_entries(int32_t value) {
+ _impl_._has_bits_[0] |= 0x00000004u;
+ _impl_.max_database_entries_ = value;
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::set_max_database_entries(int32_t value) {
+ _internal_set_max_database_entries(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints.max_database_entries)
+}
+
+// optional string region = 3;
+inline bool FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::_internal_has_region() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::has_region() const {
+ return _internal_has_region();
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::clear_region() {
+ _impl_.region_.ClearToEmpty();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::region() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints.region)
+ return _internal_region();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::set_region(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.region_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints.region)
+}
+inline std::string* FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::mutable_region() {
+ std::string* _s = _internal_mutable_region();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints.region)
+ return _s;
+}
+inline const std::string& FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::_internal_region() const {
+ return _impl_.region_.Get();
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::_internal_set_region(const std::string& value) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.region_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::_internal_mutable_region() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ return _impl_.region_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::release_region() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints.region)
+ if (!_internal_has_region()) {
+ return nullptr;
+ }
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ auto* p = _impl_.region_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.region_.IsDefault()) {
+ _impl_.region_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ return p;
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::set_allocated_region(std::string* region) {
+ if (region != nullptr) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.region_.SetAllocated(region, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.region_.IsDefault()) {
+ _impl_.region_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints.region)
+}
+
+// repeated .mozilla.safebrowsing.CompressionType supported_compressions = 4;
+inline int FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::_internal_supported_compressions_size() const {
+ return _impl_.supported_compressions_.size();
+}
+inline int FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::supported_compressions_size() const {
+ return _internal_supported_compressions_size();
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::clear_supported_compressions() {
+ _impl_.supported_compressions_.Clear();
+}
+inline ::mozilla::safebrowsing::CompressionType FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::_internal_supported_compressions(int index) const {
+ return static_cast< ::mozilla::safebrowsing::CompressionType >(_impl_.supported_compressions_.Get(index));
+}
+inline ::mozilla::safebrowsing::CompressionType FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::supported_compressions(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints.supported_compressions)
+ return _internal_supported_compressions(index);
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::set_supported_compressions(int index, ::mozilla::safebrowsing::CompressionType value) {
+ assert(::mozilla::safebrowsing::CompressionType_IsValid(value));
+ _impl_.supported_compressions_.Set(index, value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints.supported_compressions)
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::_internal_add_supported_compressions(::mozilla::safebrowsing::CompressionType value) {
+ assert(::mozilla::safebrowsing::CompressionType_IsValid(value));
+ _impl_.supported_compressions_.Add(value);
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::add_supported_compressions(::mozilla::safebrowsing::CompressionType value) {
+ _internal_add_supported_compressions(value);
+ // @@protoc_insertion_point(field_add:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints.supported_compressions)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>&
+FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::supported_compressions() const {
+ // @@protoc_insertion_point(field_list:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints.supported_compressions)
+ return _impl_.supported_compressions_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>*
+FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::_internal_mutable_supported_compressions() {
+ return &_impl_.supported_compressions_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField<int>*
+FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints::mutable_supported_compressions() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints.supported_compressions)
+ return _internal_mutable_supported_compressions();
+}
+
+// -------------------------------------------------------------------
+
+// FetchThreatListUpdatesRequest_ListUpdateRequest
+
+// optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+inline bool FetchThreatListUpdatesRequest_ListUpdateRequest::_internal_has_threat_type() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+ return value;
+}
+inline bool FetchThreatListUpdatesRequest_ListUpdateRequest::has_threat_type() const {
+ return _internal_has_threat_type();
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest::clear_threat_type() {
+ _impl_.threat_type_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline ::mozilla::safebrowsing::ThreatType FetchThreatListUpdatesRequest_ListUpdateRequest::_internal_threat_type() const {
+ return static_cast< ::mozilla::safebrowsing::ThreatType >(_impl_.threat_type_);
+}
+inline ::mozilla::safebrowsing::ThreatType FetchThreatListUpdatesRequest_ListUpdateRequest::threat_type() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.threat_type)
+ return _internal_threat_type();
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest::_internal_set_threat_type(::mozilla::safebrowsing::ThreatType value) {
+ assert(::mozilla::safebrowsing::ThreatType_IsValid(value));
+ _impl_._has_bits_[0] |= 0x00000004u;
+ _impl_.threat_type_ = value;
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest::set_threat_type(::mozilla::safebrowsing::ThreatType value) {
+ _internal_set_threat_type(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.threat_type)
+}
+
+// optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+inline bool FetchThreatListUpdatesRequest_ListUpdateRequest::_internal_has_platform_type() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000008u) != 0;
+ return value;
+}
+inline bool FetchThreatListUpdatesRequest_ListUpdateRequest::has_platform_type() const {
+ return _internal_has_platform_type();
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest::clear_platform_type() {
+ _impl_.platform_type_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000008u;
+}
+inline ::mozilla::safebrowsing::PlatformType FetchThreatListUpdatesRequest_ListUpdateRequest::_internal_platform_type() const {
+ return static_cast< ::mozilla::safebrowsing::PlatformType >(_impl_.platform_type_);
+}
+inline ::mozilla::safebrowsing::PlatformType FetchThreatListUpdatesRequest_ListUpdateRequest::platform_type() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.platform_type)
+ return _internal_platform_type();
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest::_internal_set_platform_type(::mozilla::safebrowsing::PlatformType value) {
+ assert(::mozilla::safebrowsing::PlatformType_IsValid(value));
+ _impl_._has_bits_[0] |= 0x00000008u;
+ _impl_.platform_type_ = value;
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest::set_platform_type(::mozilla::safebrowsing::PlatformType value) {
+ _internal_set_platform_type(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.platform_type)
+}
+
+// optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 5;
+inline bool FetchThreatListUpdatesRequest_ListUpdateRequest::_internal_has_threat_entry_type() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000010u) != 0;
+ return value;
+}
+inline bool FetchThreatListUpdatesRequest_ListUpdateRequest::has_threat_entry_type() const {
+ return _internal_has_threat_entry_type();
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest::clear_threat_entry_type() {
+ _impl_.threat_entry_type_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000010u;
+}
+inline ::mozilla::safebrowsing::ThreatEntryType FetchThreatListUpdatesRequest_ListUpdateRequest::_internal_threat_entry_type() const {
+ return static_cast< ::mozilla::safebrowsing::ThreatEntryType >(_impl_.threat_entry_type_);
+}
+inline ::mozilla::safebrowsing::ThreatEntryType FetchThreatListUpdatesRequest_ListUpdateRequest::threat_entry_type() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.threat_entry_type)
+ return _internal_threat_entry_type();
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest::_internal_set_threat_entry_type(::mozilla::safebrowsing::ThreatEntryType value) {
+ assert(::mozilla::safebrowsing::ThreatEntryType_IsValid(value));
+ _impl_._has_bits_[0] |= 0x00000010u;
+ _impl_.threat_entry_type_ = value;
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest::set_threat_entry_type(::mozilla::safebrowsing::ThreatEntryType value) {
+ _internal_set_threat_entry_type(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.threat_entry_type)
+}
+
+// optional bytes state = 3;
+inline bool FetchThreatListUpdatesRequest_ListUpdateRequest::_internal_has_state() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool FetchThreatListUpdatesRequest_ListUpdateRequest::has_state() const {
+ return _internal_has_state();
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest::clear_state() {
+ _impl_.state_.ClearToEmpty();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& FetchThreatListUpdatesRequest_ListUpdateRequest::state() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.state)
+ return _internal_state();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FetchThreatListUpdatesRequest_ListUpdateRequest::set_state(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.state_.SetBytes(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.state)
+}
+inline std::string* FetchThreatListUpdatesRequest_ListUpdateRequest::mutable_state() {
+ std::string* _s = _internal_mutable_state();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.state)
+ return _s;
+}
+inline const std::string& FetchThreatListUpdatesRequest_ListUpdateRequest::_internal_state() const {
+ return _impl_.state_.Get();
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest::_internal_set_state(const std::string& value) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.state_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FetchThreatListUpdatesRequest_ListUpdateRequest::_internal_mutable_state() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ return _impl_.state_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FetchThreatListUpdatesRequest_ListUpdateRequest::release_state() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.state)
+ if (!_internal_has_state()) {
+ return nullptr;
+ }
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ auto* p = _impl_.state_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.state_.IsDefault()) {
+ _impl_.state_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ return p;
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest::set_allocated_state(std::string* state) {
+ if (state != nullptr) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.state_.SetAllocated(state, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.state_.IsDefault()) {
+ _impl_.state_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.state)
+}
+
+// optional .mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.Constraints constraints = 4;
+inline bool FetchThreatListUpdatesRequest_ListUpdateRequest::_internal_has_constraints() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.constraints_ != nullptr);
+ return value;
+}
+inline bool FetchThreatListUpdatesRequest_ListUpdateRequest::has_constraints() const {
+ return _internal_has_constraints();
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest::clear_constraints() {
+ if (_impl_.constraints_ != nullptr) _impl_.constraints_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints& FetchThreatListUpdatesRequest_ListUpdateRequest::_internal_constraints() const {
+ const ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* p = _impl_.constraints_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints&>(
+ ::mozilla::safebrowsing::_FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints_default_instance_);
+}
+inline const ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints& FetchThreatListUpdatesRequest_ListUpdateRequest::constraints() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.constraints)
+ return _internal_constraints();
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest::unsafe_arena_set_allocated_constraints(
+ ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* constraints) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.constraints_);
+ }
+ _impl_.constraints_ = constraints;
+ if (constraints) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.constraints)
+}
+inline ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* FetchThreatListUpdatesRequest_ListUpdateRequest::release_constraints() {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* temp = _impl_.constraints_;
+ _impl_.constraints_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* FetchThreatListUpdatesRequest_ListUpdateRequest::unsafe_arena_release_constraints() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.constraints)
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* temp = _impl_.constraints_;
+ _impl_.constraints_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* FetchThreatListUpdatesRequest_ListUpdateRequest::_internal_mutable_constraints() {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ if (_impl_.constraints_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints>(GetArenaForAllocation());
+ _impl_.constraints_ = p;
+ }
+ return _impl_.constraints_;
+}
+inline ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* FetchThreatListUpdatesRequest_ListUpdateRequest::mutable_constraints() {
+ ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* _msg = _internal_mutable_constraints();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.constraints)
+ return _msg;
+}
+inline void FetchThreatListUpdatesRequest_ListUpdateRequest::set_allocated_constraints(::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints* constraints) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.constraints_;
+ }
+ if (constraints) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(constraints);
+ if (message_arena != submessage_arena) {
+ constraints = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, constraints, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ _impl_.constraints_ = constraints;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest.constraints)
+}
+
+// -------------------------------------------------------------------
+
+// FetchThreatListUpdatesRequest
+
+// optional .mozilla.safebrowsing.ClientInfo client = 1;
+inline bool FetchThreatListUpdatesRequest::_internal_has_client() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.client_ != nullptr);
+ return value;
+}
+inline bool FetchThreatListUpdatesRequest::has_client() const {
+ return _internal_has_client();
+}
+inline void FetchThreatListUpdatesRequest::clear_client() {
+ if (_impl_.client_ != nullptr) _impl_.client_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const ::mozilla::safebrowsing::ClientInfo& FetchThreatListUpdatesRequest::_internal_client() const {
+ const ::mozilla::safebrowsing::ClientInfo* p = _impl_.client_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::ClientInfo&>(
+ ::mozilla::safebrowsing::_ClientInfo_default_instance_);
+}
+inline const ::mozilla::safebrowsing::ClientInfo& FetchThreatListUpdatesRequest::client() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesRequest.client)
+ return _internal_client();
+}
+inline void FetchThreatListUpdatesRequest::unsafe_arena_set_allocated_client(
+ ::mozilla::safebrowsing::ClientInfo* client) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.client_);
+ }
+ _impl_.client_ = client;
+ if (client) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.FetchThreatListUpdatesRequest.client)
+}
+inline ::mozilla::safebrowsing::ClientInfo* FetchThreatListUpdatesRequest::release_client() {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ ::mozilla::safebrowsing::ClientInfo* temp = _impl_.client_;
+ _impl_.client_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::ClientInfo* FetchThreatListUpdatesRequest::unsafe_arena_release_client() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.FetchThreatListUpdatesRequest.client)
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ ::mozilla::safebrowsing::ClientInfo* temp = _impl_.client_;
+ _impl_.client_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::ClientInfo* FetchThreatListUpdatesRequest::_internal_mutable_client() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ if (_impl_.client_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::ClientInfo>(GetArenaForAllocation());
+ _impl_.client_ = p;
+ }
+ return _impl_.client_;
+}
+inline ::mozilla::safebrowsing::ClientInfo* FetchThreatListUpdatesRequest::mutable_client() {
+ ::mozilla::safebrowsing::ClientInfo* _msg = _internal_mutable_client();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FetchThreatListUpdatesRequest.client)
+ return _msg;
+}
+inline void FetchThreatListUpdatesRequest::set_allocated_client(::mozilla::safebrowsing::ClientInfo* client) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.client_;
+ }
+ if (client) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(client);
+ if (message_arena != submessage_arena) {
+ client = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, client, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.client_ = client;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.FetchThreatListUpdatesRequest.client)
+}
+
+// repeated .mozilla.safebrowsing.FetchThreatListUpdatesRequest.ListUpdateRequest list_update_requests = 3;
+inline int FetchThreatListUpdatesRequest::_internal_list_update_requests_size() const {
+ return _impl_.list_update_requests_.size();
+}
+inline int FetchThreatListUpdatesRequest::list_update_requests_size() const {
+ return _internal_list_update_requests_size();
+}
+inline void FetchThreatListUpdatesRequest::clear_list_update_requests() {
+ _impl_.list_update_requests_.Clear();
+}
+inline ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest* FetchThreatListUpdatesRequest::mutable_list_update_requests(int index) {
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FetchThreatListUpdatesRequest.list_update_requests)
+ return _impl_.list_update_requests_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest >*
+FetchThreatListUpdatesRequest::mutable_list_update_requests() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.safebrowsing.FetchThreatListUpdatesRequest.list_update_requests)
+ return &_impl_.list_update_requests_;
+}
+inline const ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest& FetchThreatListUpdatesRequest::_internal_list_update_requests(int index) const {
+ return _impl_.list_update_requests_.Get(index);
+}
+inline const ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest& FetchThreatListUpdatesRequest::list_update_requests(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesRequest.list_update_requests)
+ return _internal_list_update_requests(index);
+}
+inline ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest* FetchThreatListUpdatesRequest::_internal_add_list_update_requests() {
+ return _impl_.list_update_requests_.Add();
+}
+inline ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest* FetchThreatListUpdatesRequest::add_list_update_requests() {
+ ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest* _add = _internal_add_list_update_requests();
+ // @@protoc_insertion_point(field_add:mozilla.safebrowsing.FetchThreatListUpdatesRequest.list_update_requests)
+ return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::FetchThreatListUpdatesRequest_ListUpdateRequest >&
+FetchThreatListUpdatesRequest::list_update_requests() const {
+ // @@protoc_insertion_point(field_list:mozilla.safebrowsing.FetchThreatListUpdatesRequest.list_update_requests)
+ return _impl_.list_update_requests_;
+}
+
+// optional .mozilla.safebrowsing.ChromeClientInfo chrome_client_info = 4;
+inline bool FetchThreatListUpdatesRequest::_internal_has_chrome_client_info() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.chrome_client_info_ != nullptr);
+ return value;
+}
+inline bool FetchThreatListUpdatesRequest::has_chrome_client_info() const {
+ return _internal_has_chrome_client_info();
+}
+inline void FetchThreatListUpdatesRequest::clear_chrome_client_info() {
+ if (_impl_.chrome_client_info_ != nullptr) _impl_.chrome_client_info_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const ::mozilla::safebrowsing::ChromeClientInfo& FetchThreatListUpdatesRequest::_internal_chrome_client_info() const {
+ const ::mozilla::safebrowsing::ChromeClientInfo* p = _impl_.chrome_client_info_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::ChromeClientInfo&>(
+ ::mozilla::safebrowsing::_ChromeClientInfo_default_instance_);
+}
+inline const ::mozilla::safebrowsing::ChromeClientInfo& FetchThreatListUpdatesRequest::chrome_client_info() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesRequest.chrome_client_info)
+ return _internal_chrome_client_info();
+}
+inline void FetchThreatListUpdatesRequest::unsafe_arena_set_allocated_chrome_client_info(
+ ::mozilla::safebrowsing::ChromeClientInfo* chrome_client_info) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.chrome_client_info_);
+ }
+ _impl_.chrome_client_info_ = chrome_client_info;
+ if (chrome_client_info) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.FetchThreatListUpdatesRequest.chrome_client_info)
+}
+inline ::mozilla::safebrowsing::ChromeClientInfo* FetchThreatListUpdatesRequest::release_chrome_client_info() {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::safebrowsing::ChromeClientInfo* temp = _impl_.chrome_client_info_;
+ _impl_.chrome_client_info_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::ChromeClientInfo* FetchThreatListUpdatesRequest::unsafe_arena_release_chrome_client_info() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.FetchThreatListUpdatesRequest.chrome_client_info)
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::safebrowsing::ChromeClientInfo* temp = _impl_.chrome_client_info_;
+ _impl_.chrome_client_info_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::ChromeClientInfo* FetchThreatListUpdatesRequest::_internal_mutable_chrome_client_info() {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ if (_impl_.chrome_client_info_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::ChromeClientInfo>(GetArenaForAllocation());
+ _impl_.chrome_client_info_ = p;
+ }
+ return _impl_.chrome_client_info_;
+}
+inline ::mozilla::safebrowsing::ChromeClientInfo* FetchThreatListUpdatesRequest::mutable_chrome_client_info() {
+ ::mozilla::safebrowsing::ChromeClientInfo* _msg = _internal_mutable_chrome_client_info();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FetchThreatListUpdatesRequest.chrome_client_info)
+ return _msg;
+}
+inline void FetchThreatListUpdatesRequest::set_allocated_chrome_client_info(::mozilla::safebrowsing::ChromeClientInfo* chrome_client_info) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.chrome_client_info_;
+ }
+ if (chrome_client_info) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(chrome_client_info);
+ if (message_arena != submessage_arena) {
+ chrome_client_info = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, chrome_client_info, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ _impl_.chrome_client_info_ = chrome_client_info;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.FetchThreatListUpdatesRequest.chrome_client_info)
+}
+
+// -------------------------------------------------------------------
+
+// FetchThreatListUpdatesResponse_ListUpdateResponse
+
+// optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+inline bool FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_has_threat_type() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+ return value;
+}
+inline bool FetchThreatListUpdatesResponse_ListUpdateResponse::has_threat_type() const {
+ return _internal_has_threat_type();
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::clear_threat_type() {
+ _impl_.threat_type_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline ::mozilla::safebrowsing::ThreatType FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_threat_type() const {
+ return static_cast< ::mozilla::safebrowsing::ThreatType >(_impl_.threat_type_);
+}
+inline ::mozilla::safebrowsing::ThreatType FetchThreatListUpdatesResponse_ListUpdateResponse::threat_type() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.threat_type)
+ return _internal_threat_type();
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_set_threat_type(::mozilla::safebrowsing::ThreatType value) {
+ assert(::mozilla::safebrowsing::ThreatType_IsValid(value));
+ _impl_._has_bits_[0] |= 0x00000004u;
+ _impl_.threat_type_ = value;
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::set_threat_type(::mozilla::safebrowsing::ThreatType value) {
+ _internal_set_threat_type(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.threat_type)
+}
+
+// optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 2;
+inline bool FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_has_threat_entry_type() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000008u) != 0;
+ return value;
+}
+inline bool FetchThreatListUpdatesResponse_ListUpdateResponse::has_threat_entry_type() const {
+ return _internal_has_threat_entry_type();
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::clear_threat_entry_type() {
+ _impl_.threat_entry_type_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000008u;
+}
+inline ::mozilla::safebrowsing::ThreatEntryType FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_threat_entry_type() const {
+ return static_cast< ::mozilla::safebrowsing::ThreatEntryType >(_impl_.threat_entry_type_);
+}
+inline ::mozilla::safebrowsing::ThreatEntryType FetchThreatListUpdatesResponse_ListUpdateResponse::threat_entry_type() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.threat_entry_type)
+ return _internal_threat_entry_type();
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_set_threat_entry_type(::mozilla::safebrowsing::ThreatEntryType value) {
+ assert(::mozilla::safebrowsing::ThreatEntryType_IsValid(value));
+ _impl_._has_bits_[0] |= 0x00000008u;
+ _impl_.threat_entry_type_ = value;
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::set_threat_entry_type(::mozilla::safebrowsing::ThreatEntryType value) {
+ _internal_set_threat_entry_type(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.threat_entry_type)
+}
+
+// optional .mozilla.safebrowsing.PlatformType platform_type = 3;
+inline bool FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_has_platform_type() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000010u) != 0;
+ return value;
+}
+inline bool FetchThreatListUpdatesResponse_ListUpdateResponse::has_platform_type() const {
+ return _internal_has_platform_type();
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::clear_platform_type() {
+ _impl_.platform_type_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000010u;
+}
+inline ::mozilla::safebrowsing::PlatformType FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_platform_type() const {
+ return static_cast< ::mozilla::safebrowsing::PlatformType >(_impl_.platform_type_);
+}
+inline ::mozilla::safebrowsing::PlatformType FetchThreatListUpdatesResponse_ListUpdateResponse::platform_type() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.platform_type)
+ return _internal_platform_type();
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_set_platform_type(::mozilla::safebrowsing::PlatformType value) {
+ assert(::mozilla::safebrowsing::PlatformType_IsValid(value));
+ _impl_._has_bits_[0] |= 0x00000010u;
+ _impl_.platform_type_ = value;
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::set_platform_type(::mozilla::safebrowsing::PlatformType value) {
+ _internal_set_platform_type(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.platform_type)
+}
+
+// optional .mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.ResponseType response_type = 4;
+inline bool FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_has_response_type() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000020u) != 0;
+ return value;
+}
+inline bool FetchThreatListUpdatesResponse_ListUpdateResponse::has_response_type() const {
+ return _internal_has_response_type();
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::clear_response_type() {
+ _impl_.response_type_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000020u;
+}
+inline ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_response_type() const {
+ return static_cast< ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType >(_impl_.response_type_);
+}
+inline ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType FetchThreatListUpdatesResponse_ListUpdateResponse::response_type() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.response_type)
+ return _internal_response_type();
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_set_response_type(::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType value) {
+ assert(::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType_IsValid(value));
+ _impl_._has_bits_[0] |= 0x00000020u;
+ _impl_.response_type_ = value;
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::set_response_type(::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType value) {
+ _internal_set_response_type(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.response_type)
+}
+
+// repeated .mozilla.safebrowsing.ThreatEntrySet additions = 5;
+inline int FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_additions_size() const {
+ return _impl_.additions_.size();
+}
+inline int FetchThreatListUpdatesResponse_ListUpdateResponse::additions_size() const {
+ return _internal_additions_size();
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::clear_additions() {
+ _impl_.additions_.Clear();
+}
+inline ::mozilla::safebrowsing::ThreatEntrySet* FetchThreatListUpdatesResponse_ListUpdateResponse::mutable_additions(int index) {
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.additions)
+ return _impl_.additions_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntrySet >*
+FetchThreatListUpdatesResponse_ListUpdateResponse::mutable_additions() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.additions)
+ return &_impl_.additions_;
+}
+inline const ::mozilla::safebrowsing::ThreatEntrySet& FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_additions(int index) const {
+ return _impl_.additions_.Get(index);
+}
+inline const ::mozilla::safebrowsing::ThreatEntrySet& FetchThreatListUpdatesResponse_ListUpdateResponse::additions(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.additions)
+ return _internal_additions(index);
+}
+inline ::mozilla::safebrowsing::ThreatEntrySet* FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_add_additions() {
+ return _impl_.additions_.Add();
+}
+inline ::mozilla::safebrowsing::ThreatEntrySet* FetchThreatListUpdatesResponse_ListUpdateResponse::add_additions() {
+ ::mozilla::safebrowsing::ThreatEntrySet* _add = _internal_add_additions();
+ // @@protoc_insertion_point(field_add:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.additions)
+ return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntrySet >&
+FetchThreatListUpdatesResponse_ListUpdateResponse::additions() const {
+ // @@protoc_insertion_point(field_list:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.additions)
+ return _impl_.additions_;
+}
+
+// repeated .mozilla.safebrowsing.ThreatEntrySet removals = 6;
+inline int FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_removals_size() const {
+ return _impl_.removals_.size();
+}
+inline int FetchThreatListUpdatesResponse_ListUpdateResponse::removals_size() const {
+ return _internal_removals_size();
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::clear_removals() {
+ _impl_.removals_.Clear();
+}
+inline ::mozilla::safebrowsing::ThreatEntrySet* FetchThreatListUpdatesResponse_ListUpdateResponse::mutable_removals(int index) {
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.removals)
+ return _impl_.removals_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntrySet >*
+FetchThreatListUpdatesResponse_ListUpdateResponse::mutable_removals() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.removals)
+ return &_impl_.removals_;
+}
+inline const ::mozilla::safebrowsing::ThreatEntrySet& FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_removals(int index) const {
+ return _impl_.removals_.Get(index);
+}
+inline const ::mozilla::safebrowsing::ThreatEntrySet& FetchThreatListUpdatesResponse_ListUpdateResponse::removals(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.removals)
+ return _internal_removals(index);
+}
+inline ::mozilla::safebrowsing::ThreatEntrySet* FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_add_removals() {
+ return _impl_.removals_.Add();
+}
+inline ::mozilla::safebrowsing::ThreatEntrySet* FetchThreatListUpdatesResponse_ListUpdateResponse::add_removals() {
+ ::mozilla::safebrowsing::ThreatEntrySet* _add = _internal_add_removals();
+ // @@protoc_insertion_point(field_add:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.removals)
+ return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntrySet >&
+FetchThreatListUpdatesResponse_ListUpdateResponse::removals() const {
+ // @@protoc_insertion_point(field_list:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.removals)
+ return _impl_.removals_;
+}
+
+// optional bytes new_client_state = 7;
+inline bool FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_has_new_client_state() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool FetchThreatListUpdatesResponse_ListUpdateResponse::has_new_client_state() const {
+ return _internal_has_new_client_state();
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::clear_new_client_state() {
+ _impl_.new_client_state_.ClearToEmpty();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& FetchThreatListUpdatesResponse_ListUpdateResponse::new_client_state() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.new_client_state)
+ return _internal_new_client_state();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void FetchThreatListUpdatesResponse_ListUpdateResponse::set_new_client_state(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.new_client_state_.SetBytes(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.new_client_state)
+}
+inline std::string* FetchThreatListUpdatesResponse_ListUpdateResponse::mutable_new_client_state() {
+ std::string* _s = _internal_mutable_new_client_state();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.new_client_state)
+ return _s;
+}
+inline const std::string& FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_new_client_state() const {
+ return _impl_.new_client_state_.Get();
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_set_new_client_state(const std::string& value) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.new_client_state_.Set(value, GetArenaForAllocation());
+}
+inline std::string* FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_mutable_new_client_state() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ return _impl_.new_client_state_.Mutable(GetArenaForAllocation());
+}
+inline std::string* FetchThreatListUpdatesResponse_ListUpdateResponse::release_new_client_state() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.new_client_state)
+ if (!_internal_has_new_client_state()) {
+ return nullptr;
+ }
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ auto* p = _impl_.new_client_state_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.new_client_state_.IsDefault()) {
+ _impl_.new_client_state_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ return p;
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::set_allocated_new_client_state(std::string* new_client_state) {
+ if (new_client_state != nullptr) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.new_client_state_.SetAllocated(new_client_state, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.new_client_state_.IsDefault()) {
+ _impl_.new_client_state_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.new_client_state)
+}
+
+// optional .mozilla.safebrowsing.Checksum checksum = 8;
+inline bool FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_has_checksum() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.checksum_ != nullptr);
+ return value;
+}
+inline bool FetchThreatListUpdatesResponse_ListUpdateResponse::has_checksum() const {
+ return _internal_has_checksum();
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::clear_checksum() {
+ if (_impl_.checksum_ != nullptr) _impl_.checksum_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const ::mozilla::safebrowsing::Checksum& FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_checksum() const {
+ const ::mozilla::safebrowsing::Checksum* p = _impl_.checksum_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::Checksum&>(
+ ::mozilla::safebrowsing::_Checksum_default_instance_);
+}
+inline const ::mozilla::safebrowsing::Checksum& FetchThreatListUpdatesResponse_ListUpdateResponse::checksum() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.checksum)
+ return _internal_checksum();
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::unsafe_arena_set_allocated_checksum(
+ ::mozilla::safebrowsing::Checksum* checksum) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.checksum_);
+ }
+ _impl_.checksum_ = checksum;
+ if (checksum) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.checksum)
+}
+inline ::mozilla::safebrowsing::Checksum* FetchThreatListUpdatesResponse_ListUpdateResponse::release_checksum() {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::safebrowsing::Checksum* temp = _impl_.checksum_;
+ _impl_.checksum_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::Checksum* FetchThreatListUpdatesResponse_ListUpdateResponse::unsafe_arena_release_checksum() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.checksum)
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::safebrowsing::Checksum* temp = _impl_.checksum_;
+ _impl_.checksum_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::Checksum* FetchThreatListUpdatesResponse_ListUpdateResponse::_internal_mutable_checksum() {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ if (_impl_.checksum_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::Checksum>(GetArenaForAllocation());
+ _impl_.checksum_ = p;
+ }
+ return _impl_.checksum_;
+}
+inline ::mozilla::safebrowsing::Checksum* FetchThreatListUpdatesResponse_ListUpdateResponse::mutable_checksum() {
+ ::mozilla::safebrowsing::Checksum* _msg = _internal_mutable_checksum();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.checksum)
+ return _msg;
+}
+inline void FetchThreatListUpdatesResponse_ListUpdateResponse::set_allocated_checksum(::mozilla::safebrowsing::Checksum* checksum) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.checksum_;
+ }
+ if (checksum) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(checksum);
+ if (message_arena != submessage_arena) {
+ checksum = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, checksum, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ _impl_.checksum_ = checksum;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse.checksum)
+}
+
+// -------------------------------------------------------------------
+
+// FetchThreatListUpdatesResponse
+
+// repeated .mozilla.safebrowsing.FetchThreatListUpdatesResponse.ListUpdateResponse list_update_responses = 1;
+inline int FetchThreatListUpdatesResponse::_internal_list_update_responses_size() const {
+ return _impl_.list_update_responses_.size();
+}
+inline int FetchThreatListUpdatesResponse::list_update_responses_size() const {
+ return _internal_list_update_responses_size();
+}
+inline void FetchThreatListUpdatesResponse::clear_list_update_responses() {
+ _impl_.list_update_responses_.Clear();
+}
+inline ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse* FetchThreatListUpdatesResponse::mutable_list_update_responses(int index) {
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FetchThreatListUpdatesResponse.list_update_responses)
+ return _impl_.list_update_responses_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse >*
+FetchThreatListUpdatesResponse::mutable_list_update_responses() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.safebrowsing.FetchThreatListUpdatesResponse.list_update_responses)
+ return &_impl_.list_update_responses_;
+}
+inline const ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse& FetchThreatListUpdatesResponse::_internal_list_update_responses(int index) const {
+ return _impl_.list_update_responses_.Get(index);
+}
+inline const ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse& FetchThreatListUpdatesResponse::list_update_responses(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesResponse.list_update_responses)
+ return _internal_list_update_responses(index);
+}
+inline ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse* FetchThreatListUpdatesResponse::_internal_add_list_update_responses() {
+ return _impl_.list_update_responses_.Add();
+}
+inline ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse* FetchThreatListUpdatesResponse::add_list_update_responses() {
+ ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse* _add = _internal_add_list_update_responses();
+ // @@protoc_insertion_point(field_add:mozilla.safebrowsing.FetchThreatListUpdatesResponse.list_update_responses)
+ return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse >&
+FetchThreatListUpdatesResponse::list_update_responses() const {
+ // @@protoc_insertion_point(field_list:mozilla.safebrowsing.FetchThreatListUpdatesResponse.list_update_responses)
+ return _impl_.list_update_responses_;
+}
+
+// optional .mozilla.safebrowsing.Duration minimum_wait_duration = 2;
+inline bool FetchThreatListUpdatesResponse::_internal_has_minimum_wait_duration() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.minimum_wait_duration_ != nullptr);
+ return value;
+}
+inline bool FetchThreatListUpdatesResponse::has_minimum_wait_duration() const {
+ return _internal_has_minimum_wait_duration();
+}
+inline void FetchThreatListUpdatesResponse::clear_minimum_wait_duration() {
+ if (_impl_.minimum_wait_duration_ != nullptr) _impl_.minimum_wait_duration_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const ::mozilla::safebrowsing::Duration& FetchThreatListUpdatesResponse::_internal_minimum_wait_duration() const {
+ const ::mozilla::safebrowsing::Duration* p = _impl_.minimum_wait_duration_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::Duration&>(
+ ::mozilla::safebrowsing::_Duration_default_instance_);
+}
+inline const ::mozilla::safebrowsing::Duration& FetchThreatListUpdatesResponse::minimum_wait_duration() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FetchThreatListUpdatesResponse.minimum_wait_duration)
+ return _internal_minimum_wait_duration();
+}
+inline void FetchThreatListUpdatesResponse::unsafe_arena_set_allocated_minimum_wait_duration(
+ ::mozilla::safebrowsing::Duration* minimum_wait_duration) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.minimum_wait_duration_);
+ }
+ _impl_.minimum_wait_duration_ = minimum_wait_duration;
+ if (minimum_wait_duration) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.FetchThreatListUpdatesResponse.minimum_wait_duration)
+}
+inline ::mozilla::safebrowsing::Duration* FetchThreatListUpdatesResponse::release_minimum_wait_duration() {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ ::mozilla::safebrowsing::Duration* temp = _impl_.minimum_wait_duration_;
+ _impl_.minimum_wait_duration_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::Duration* FetchThreatListUpdatesResponse::unsafe_arena_release_minimum_wait_duration() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.FetchThreatListUpdatesResponse.minimum_wait_duration)
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ ::mozilla::safebrowsing::Duration* temp = _impl_.minimum_wait_duration_;
+ _impl_.minimum_wait_duration_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::Duration* FetchThreatListUpdatesResponse::_internal_mutable_minimum_wait_duration() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ if (_impl_.minimum_wait_duration_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::Duration>(GetArenaForAllocation());
+ _impl_.minimum_wait_duration_ = p;
+ }
+ return _impl_.minimum_wait_duration_;
+}
+inline ::mozilla::safebrowsing::Duration* FetchThreatListUpdatesResponse::mutable_minimum_wait_duration() {
+ ::mozilla::safebrowsing::Duration* _msg = _internal_mutable_minimum_wait_duration();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FetchThreatListUpdatesResponse.minimum_wait_duration)
+ return _msg;
+}
+inline void FetchThreatListUpdatesResponse::set_allocated_minimum_wait_duration(::mozilla::safebrowsing::Duration* minimum_wait_duration) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.minimum_wait_duration_;
+ }
+ if (minimum_wait_duration) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(minimum_wait_duration);
+ if (message_arena != submessage_arena) {
+ minimum_wait_duration = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, minimum_wait_duration, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.minimum_wait_duration_ = minimum_wait_duration;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.FetchThreatListUpdatesResponse.minimum_wait_duration)
+}
+
+// -------------------------------------------------------------------
+
+// FindFullHashesRequest
+
+// optional .mozilla.safebrowsing.ClientInfo client = 1;
+inline bool FindFullHashesRequest::_internal_has_client() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.client_ != nullptr);
+ return value;
+}
+inline bool FindFullHashesRequest::has_client() const {
+ return _internal_has_client();
+}
+inline void FindFullHashesRequest::clear_client() {
+ if (_impl_.client_ != nullptr) _impl_.client_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const ::mozilla::safebrowsing::ClientInfo& FindFullHashesRequest::_internal_client() const {
+ const ::mozilla::safebrowsing::ClientInfo* p = _impl_.client_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::ClientInfo&>(
+ ::mozilla::safebrowsing::_ClientInfo_default_instance_);
+}
+inline const ::mozilla::safebrowsing::ClientInfo& FindFullHashesRequest::client() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FindFullHashesRequest.client)
+ return _internal_client();
+}
+inline void FindFullHashesRequest::unsafe_arena_set_allocated_client(
+ ::mozilla::safebrowsing::ClientInfo* client) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.client_);
+ }
+ _impl_.client_ = client;
+ if (client) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.FindFullHashesRequest.client)
+}
+inline ::mozilla::safebrowsing::ClientInfo* FindFullHashesRequest::release_client() {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ ::mozilla::safebrowsing::ClientInfo* temp = _impl_.client_;
+ _impl_.client_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::ClientInfo* FindFullHashesRequest::unsafe_arena_release_client() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.FindFullHashesRequest.client)
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ ::mozilla::safebrowsing::ClientInfo* temp = _impl_.client_;
+ _impl_.client_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::ClientInfo* FindFullHashesRequest::_internal_mutable_client() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ if (_impl_.client_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::ClientInfo>(GetArenaForAllocation());
+ _impl_.client_ = p;
+ }
+ return _impl_.client_;
+}
+inline ::mozilla::safebrowsing::ClientInfo* FindFullHashesRequest::mutable_client() {
+ ::mozilla::safebrowsing::ClientInfo* _msg = _internal_mutable_client();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FindFullHashesRequest.client)
+ return _msg;
+}
+inline void FindFullHashesRequest::set_allocated_client(::mozilla::safebrowsing::ClientInfo* client) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.client_;
+ }
+ if (client) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(client);
+ if (message_arena != submessage_arena) {
+ client = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, client, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.client_ = client;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.FindFullHashesRequest.client)
+}
+
+// repeated bytes client_states = 2;
+inline int FindFullHashesRequest::_internal_client_states_size() const {
+ return _impl_.client_states_.size();
+}
+inline int FindFullHashesRequest::client_states_size() const {
+ return _internal_client_states_size();
+}
+inline void FindFullHashesRequest::clear_client_states() {
+ _impl_.client_states_.Clear();
+}
+inline std::string* FindFullHashesRequest::add_client_states() {
+ std::string* _s = _internal_add_client_states();
+ // @@protoc_insertion_point(field_add_mutable:mozilla.safebrowsing.FindFullHashesRequest.client_states)
+ return _s;
+}
+inline const std::string& FindFullHashesRequest::_internal_client_states(int index) const {
+ return _impl_.client_states_.Get(index);
+}
+inline const std::string& FindFullHashesRequest::client_states(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FindFullHashesRequest.client_states)
+ return _internal_client_states(index);
+}
+inline std::string* FindFullHashesRequest::mutable_client_states(int index) {
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FindFullHashesRequest.client_states)
+ return _impl_.client_states_.Mutable(index);
+}
+inline void FindFullHashesRequest::set_client_states(int index, const std::string& value) {
+ _impl_.client_states_.Mutable(index)->assign(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.FindFullHashesRequest.client_states)
+}
+inline void FindFullHashesRequest::set_client_states(int index, std::string&& value) {
+ _impl_.client_states_.Mutable(index)->assign(std::move(value));
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.FindFullHashesRequest.client_states)
+}
+inline void FindFullHashesRequest::set_client_states(int index, const char* value) {
+ GOOGLE_DCHECK(value != nullptr);
+ _impl_.client_states_.Mutable(index)->assign(value);
+ // @@protoc_insertion_point(field_set_char:mozilla.safebrowsing.FindFullHashesRequest.client_states)
+}
+inline void FindFullHashesRequest::set_client_states(int index, const void* value, size_t size) {
+ _impl_.client_states_.Mutable(index)->assign(
+ reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_set_pointer:mozilla.safebrowsing.FindFullHashesRequest.client_states)
+}
+inline std::string* FindFullHashesRequest::_internal_add_client_states() {
+ return _impl_.client_states_.Add();
+}
+inline void FindFullHashesRequest::add_client_states(const std::string& value) {
+ _impl_.client_states_.Add()->assign(value);
+ // @@protoc_insertion_point(field_add:mozilla.safebrowsing.FindFullHashesRequest.client_states)
+}
+inline void FindFullHashesRequest::add_client_states(std::string&& value) {
+ _impl_.client_states_.Add(std::move(value));
+ // @@protoc_insertion_point(field_add:mozilla.safebrowsing.FindFullHashesRequest.client_states)
+}
+inline void FindFullHashesRequest::add_client_states(const char* value) {
+ GOOGLE_DCHECK(value != nullptr);
+ _impl_.client_states_.Add()->assign(value);
+ // @@protoc_insertion_point(field_add_char:mozilla.safebrowsing.FindFullHashesRequest.client_states)
+}
+inline void FindFullHashesRequest::add_client_states(const void* value, size_t size) {
+ _impl_.client_states_.Add()->assign(reinterpret_cast<const char*>(value), size);
+ // @@protoc_insertion_point(field_add_pointer:mozilla.safebrowsing.FindFullHashesRequest.client_states)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>&
+FindFullHashesRequest::client_states() const {
+ // @@protoc_insertion_point(field_list:mozilla.safebrowsing.FindFullHashesRequest.client_states)
+ return _impl_.client_states_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField<std::string>*
+FindFullHashesRequest::mutable_client_states() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.safebrowsing.FindFullHashesRequest.client_states)
+ return &_impl_.client_states_;
+}
+
+// optional .mozilla.safebrowsing.ThreatInfo threat_info = 3;
+inline bool FindFullHashesRequest::_internal_has_threat_info() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.threat_info_ != nullptr);
+ return value;
+}
+inline bool FindFullHashesRequest::has_threat_info() const {
+ return _internal_has_threat_info();
+}
+inline void FindFullHashesRequest::clear_threat_info() {
+ if (_impl_.threat_info_ != nullptr) _impl_.threat_info_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const ::mozilla::safebrowsing::ThreatInfo& FindFullHashesRequest::_internal_threat_info() const {
+ const ::mozilla::safebrowsing::ThreatInfo* p = _impl_.threat_info_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::ThreatInfo&>(
+ ::mozilla::safebrowsing::_ThreatInfo_default_instance_);
+}
+inline const ::mozilla::safebrowsing::ThreatInfo& FindFullHashesRequest::threat_info() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FindFullHashesRequest.threat_info)
+ return _internal_threat_info();
+}
+inline void FindFullHashesRequest::unsafe_arena_set_allocated_threat_info(
+ ::mozilla::safebrowsing::ThreatInfo* threat_info) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.threat_info_);
+ }
+ _impl_.threat_info_ = threat_info;
+ if (threat_info) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.FindFullHashesRequest.threat_info)
+}
+inline ::mozilla::safebrowsing::ThreatInfo* FindFullHashesRequest::release_threat_info() {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::safebrowsing::ThreatInfo* temp = _impl_.threat_info_;
+ _impl_.threat_info_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::ThreatInfo* FindFullHashesRequest::unsafe_arena_release_threat_info() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.FindFullHashesRequest.threat_info)
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::safebrowsing::ThreatInfo* temp = _impl_.threat_info_;
+ _impl_.threat_info_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::ThreatInfo* FindFullHashesRequest::_internal_mutable_threat_info() {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ if (_impl_.threat_info_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::ThreatInfo>(GetArenaForAllocation());
+ _impl_.threat_info_ = p;
+ }
+ return _impl_.threat_info_;
+}
+inline ::mozilla::safebrowsing::ThreatInfo* FindFullHashesRequest::mutable_threat_info() {
+ ::mozilla::safebrowsing::ThreatInfo* _msg = _internal_mutable_threat_info();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FindFullHashesRequest.threat_info)
+ return _msg;
+}
+inline void FindFullHashesRequest::set_allocated_threat_info(::mozilla::safebrowsing::ThreatInfo* threat_info) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.threat_info_;
+ }
+ if (threat_info) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(threat_info);
+ if (message_arena != submessage_arena) {
+ threat_info = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, threat_info, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ _impl_.threat_info_ = threat_info;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.FindFullHashesRequest.threat_info)
+}
+
+// -------------------------------------------------------------------
+
+// FindFullHashesResponse
+
+// repeated .mozilla.safebrowsing.ThreatMatch matches = 1;
+inline int FindFullHashesResponse::_internal_matches_size() const {
+ return _impl_.matches_.size();
+}
+inline int FindFullHashesResponse::matches_size() const {
+ return _internal_matches_size();
+}
+inline void FindFullHashesResponse::clear_matches() {
+ _impl_.matches_.Clear();
+}
+inline ::mozilla::safebrowsing::ThreatMatch* FindFullHashesResponse::mutable_matches(int index) {
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FindFullHashesResponse.matches)
+ return _impl_.matches_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatMatch >*
+FindFullHashesResponse::mutable_matches() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.safebrowsing.FindFullHashesResponse.matches)
+ return &_impl_.matches_;
+}
+inline const ::mozilla::safebrowsing::ThreatMatch& FindFullHashesResponse::_internal_matches(int index) const {
+ return _impl_.matches_.Get(index);
+}
+inline const ::mozilla::safebrowsing::ThreatMatch& FindFullHashesResponse::matches(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FindFullHashesResponse.matches)
+ return _internal_matches(index);
+}
+inline ::mozilla::safebrowsing::ThreatMatch* FindFullHashesResponse::_internal_add_matches() {
+ return _impl_.matches_.Add();
+}
+inline ::mozilla::safebrowsing::ThreatMatch* FindFullHashesResponse::add_matches() {
+ ::mozilla::safebrowsing::ThreatMatch* _add = _internal_add_matches();
+ // @@protoc_insertion_point(field_add:mozilla.safebrowsing.FindFullHashesResponse.matches)
+ return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatMatch >&
+FindFullHashesResponse::matches() const {
+ // @@protoc_insertion_point(field_list:mozilla.safebrowsing.FindFullHashesResponse.matches)
+ return _impl_.matches_;
+}
+
+// optional .mozilla.safebrowsing.Duration minimum_wait_duration = 2;
+inline bool FindFullHashesResponse::_internal_has_minimum_wait_duration() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.minimum_wait_duration_ != nullptr);
+ return value;
+}
+inline bool FindFullHashesResponse::has_minimum_wait_duration() const {
+ return _internal_has_minimum_wait_duration();
+}
+inline void FindFullHashesResponse::clear_minimum_wait_duration() {
+ if (_impl_.minimum_wait_duration_ != nullptr) _impl_.minimum_wait_duration_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const ::mozilla::safebrowsing::Duration& FindFullHashesResponse::_internal_minimum_wait_duration() const {
+ const ::mozilla::safebrowsing::Duration* p = _impl_.minimum_wait_duration_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::Duration&>(
+ ::mozilla::safebrowsing::_Duration_default_instance_);
+}
+inline const ::mozilla::safebrowsing::Duration& FindFullHashesResponse::minimum_wait_duration() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FindFullHashesResponse.minimum_wait_duration)
+ return _internal_minimum_wait_duration();
+}
+inline void FindFullHashesResponse::unsafe_arena_set_allocated_minimum_wait_duration(
+ ::mozilla::safebrowsing::Duration* minimum_wait_duration) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.minimum_wait_duration_);
+ }
+ _impl_.minimum_wait_duration_ = minimum_wait_duration;
+ if (minimum_wait_duration) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.FindFullHashesResponse.minimum_wait_duration)
+}
+inline ::mozilla::safebrowsing::Duration* FindFullHashesResponse::release_minimum_wait_duration() {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ ::mozilla::safebrowsing::Duration* temp = _impl_.minimum_wait_duration_;
+ _impl_.minimum_wait_duration_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::Duration* FindFullHashesResponse::unsafe_arena_release_minimum_wait_duration() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.FindFullHashesResponse.minimum_wait_duration)
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ ::mozilla::safebrowsing::Duration* temp = _impl_.minimum_wait_duration_;
+ _impl_.minimum_wait_duration_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::Duration* FindFullHashesResponse::_internal_mutable_minimum_wait_duration() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ if (_impl_.minimum_wait_duration_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::Duration>(GetArenaForAllocation());
+ _impl_.minimum_wait_duration_ = p;
+ }
+ return _impl_.minimum_wait_duration_;
+}
+inline ::mozilla::safebrowsing::Duration* FindFullHashesResponse::mutable_minimum_wait_duration() {
+ ::mozilla::safebrowsing::Duration* _msg = _internal_mutable_minimum_wait_duration();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FindFullHashesResponse.minimum_wait_duration)
+ return _msg;
+}
+inline void FindFullHashesResponse::set_allocated_minimum_wait_duration(::mozilla::safebrowsing::Duration* minimum_wait_duration) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.minimum_wait_duration_;
+ }
+ if (minimum_wait_duration) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(minimum_wait_duration);
+ if (message_arena != submessage_arena) {
+ minimum_wait_duration = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, minimum_wait_duration, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.minimum_wait_duration_ = minimum_wait_duration;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.FindFullHashesResponse.minimum_wait_duration)
+}
+
+// optional .mozilla.safebrowsing.Duration negative_cache_duration = 3;
+inline bool FindFullHashesResponse::_internal_has_negative_cache_duration() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.negative_cache_duration_ != nullptr);
+ return value;
+}
+inline bool FindFullHashesResponse::has_negative_cache_duration() const {
+ return _internal_has_negative_cache_duration();
+}
+inline void FindFullHashesResponse::clear_negative_cache_duration() {
+ if (_impl_.negative_cache_duration_ != nullptr) _impl_.negative_cache_duration_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const ::mozilla::safebrowsing::Duration& FindFullHashesResponse::_internal_negative_cache_duration() const {
+ const ::mozilla::safebrowsing::Duration* p = _impl_.negative_cache_duration_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::Duration&>(
+ ::mozilla::safebrowsing::_Duration_default_instance_);
+}
+inline const ::mozilla::safebrowsing::Duration& FindFullHashesResponse::negative_cache_duration() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.FindFullHashesResponse.negative_cache_duration)
+ return _internal_negative_cache_duration();
+}
+inline void FindFullHashesResponse::unsafe_arena_set_allocated_negative_cache_duration(
+ ::mozilla::safebrowsing::Duration* negative_cache_duration) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.negative_cache_duration_);
+ }
+ _impl_.negative_cache_duration_ = negative_cache_duration;
+ if (negative_cache_duration) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.FindFullHashesResponse.negative_cache_duration)
+}
+inline ::mozilla::safebrowsing::Duration* FindFullHashesResponse::release_negative_cache_duration() {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::safebrowsing::Duration* temp = _impl_.negative_cache_duration_;
+ _impl_.negative_cache_duration_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::Duration* FindFullHashesResponse::unsafe_arena_release_negative_cache_duration() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.FindFullHashesResponse.negative_cache_duration)
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::safebrowsing::Duration* temp = _impl_.negative_cache_duration_;
+ _impl_.negative_cache_duration_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::Duration* FindFullHashesResponse::_internal_mutable_negative_cache_duration() {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ if (_impl_.negative_cache_duration_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::Duration>(GetArenaForAllocation());
+ _impl_.negative_cache_duration_ = p;
+ }
+ return _impl_.negative_cache_duration_;
+}
+inline ::mozilla::safebrowsing::Duration* FindFullHashesResponse::mutable_negative_cache_duration() {
+ ::mozilla::safebrowsing::Duration* _msg = _internal_mutable_negative_cache_duration();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.FindFullHashesResponse.negative_cache_duration)
+ return _msg;
+}
+inline void FindFullHashesResponse::set_allocated_negative_cache_duration(::mozilla::safebrowsing::Duration* negative_cache_duration) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.negative_cache_duration_;
+ }
+ if (negative_cache_duration) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(negative_cache_duration);
+ if (message_arena != submessage_arena) {
+ negative_cache_duration = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, negative_cache_duration, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ _impl_.negative_cache_duration_ = negative_cache_duration;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.FindFullHashesResponse.negative_cache_duration)
+}
+
+// -------------------------------------------------------------------
+
+// ThreatHit_ThreatSource
+
+// optional string url = 1;
+inline bool ThreatHit_ThreatSource::_internal_has_url() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool ThreatHit_ThreatSource::has_url() const {
+ return _internal_has_url();
+}
+inline void ThreatHit_ThreatSource::clear_url() {
+ _impl_.url_.ClearToEmpty();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& ThreatHit_ThreatSource::url() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatHit.ThreatSource.url)
+ return _internal_url();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void ThreatHit_ThreatSource::set_url(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.url_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatHit.ThreatSource.url)
+}
+inline std::string* ThreatHit_ThreatSource::mutable_url() {
+ std::string* _s = _internal_mutable_url();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatHit.ThreatSource.url)
+ return _s;
+}
+inline const std::string& ThreatHit_ThreatSource::_internal_url() const {
+ return _impl_.url_.Get();
+}
+inline void ThreatHit_ThreatSource::_internal_set_url(const std::string& value) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.url_.Set(value, GetArenaForAllocation());
+}
+inline std::string* ThreatHit_ThreatSource::_internal_mutable_url() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ return _impl_.url_.Mutable(GetArenaForAllocation());
+}
+inline std::string* ThreatHit_ThreatSource::release_url() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ThreatHit.ThreatSource.url)
+ if (!_internal_has_url()) {
+ return nullptr;
+ }
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ auto* p = _impl_.url_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.url_.IsDefault()) {
+ _impl_.url_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ return p;
+}
+inline void ThreatHit_ThreatSource::set_allocated_url(std::string* url) {
+ if (url != nullptr) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.url_.SetAllocated(url, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.url_.IsDefault()) {
+ _impl_.url_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ThreatHit.ThreatSource.url)
+}
+
+// optional .mozilla.safebrowsing.ThreatHit.ThreatSourceType type = 2;
+inline bool ThreatHit_ThreatSource::_internal_has_type() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000008u) != 0;
+ return value;
+}
+inline bool ThreatHit_ThreatSource::has_type() const {
+ return _internal_has_type();
+}
+inline void ThreatHit_ThreatSource::clear_type() {
+ _impl_.type_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000008u;
+}
+inline ::mozilla::safebrowsing::ThreatHit_ThreatSourceType ThreatHit_ThreatSource::_internal_type() const {
+ return static_cast< ::mozilla::safebrowsing::ThreatHit_ThreatSourceType >(_impl_.type_);
+}
+inline ::mozilla::safebrowsing::ThreatHit_ThreatSourceType ThreatHit_ThreatSource::type() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatHit.ThreatSource.type)
+ return _internal_type();
+}
+inline void ThreatHit_ThreatSource::_internal_set_type(::mozilla::safebrowsing::ThreatHit_ThreatSourceType value) {
+ assert(::mozilla::safebrowsing::ThreatHit_ThreatSourceType_IsValid(value));
+ _impl_._has_bits_[0] |= 0x00000008u;
+ _impl_.type_ = value;
+}
+inline void ThreatHit_ThreatSource::set_type(::mozilla::safebrowsing::ThreatHit_ThreatSourceType value) {
+ _internal_set_type(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatHit.ThreatSource.type)
+}
+
+// optional string remote_ip = 3;
+inline bool ThreatHit_ThreatSource::_internal_has_remote_ip() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ return value;
+}
+inline bool ThreatHit_ThreatSource::has_remote_ip() const {
+ return _internal_has_remote_ip();
+}
+inline void ThreatHit_ThreatSource::clear_remote_ip() {
+ _impl_.remote_ip_.ClearToEmpty();
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const std::string& ThreatHit_ThreatSource::remote_ip() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatHit.ThreatSource.remote_ip)
+ return _internal_remote_ip();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void ThreatHit_ThreatSource::set_remote_ip(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.remote_ip_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatHit.ThreatSource.remote_ip)
+}
+inline std::string* ThreatHit_ThreatSource::mutable_remote_ip() {
+ std::string* _s = _internal_mutable_remote_ip();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatHit.ThreatSource.remote_ip)
+ return _s;
+}
+inline const std::string& ThreatHit_ThreatSource::_internal_remote_ip() const {
+ return _impl_.remote_ip_.Get();
+}
+inline void ThreatHit_ThreatSource::_internal_set_remote_ip(const std::string& value) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.remote_ip_.Set(value, GetArenaForAllocation());
+}
+inline std::string* ThreatHit_ThreatSource::_internal_mutable_remote_ip() {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ return _impl_.remote_ip_.Mutable(GetArenaForAllocation());
+}
+inline std::string* ThreatHit_ThreatSource::release_remote_ip() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ThreatHit.ThreatSource.remote_ip)
+ if (!_internal_has_remote_ip()) {
+ return nullptr;
+ }
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ auto* p = _impl_.remote_ip_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.remote_ip_.IsDefault()) {
+ _impl_.remote_ip_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ return p;
+}
+inline void ThreatHit_ThreatSource::set_allocated_remote_ip(std::string* remote_ip) {
+ if (remote_ip != nullptr) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ _impl_.remote_ip_.SetAllocated(remote_ip, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.remote_ip_.IsDefault()) {
+ _impl_.remote_ip_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ThreatHit.ThreatSource.remote_ip)
+}
+
+// optional string referrer = 4;
+inline bool ThreatHit_ThreatSource::_internal_has_referrer() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+ return value;
+}
+inline bool ThreatHit_ThreatSource::has_referrer() const {
+ return _internal_has_referrer();
+}
+inline void ThreatHit_ThreatSource::clear_referrer() {
+ _impl_.referrer_.ClearToEmpty();
+ _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline const std::string& ThreatHit_ThreatSource::referrer() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatHit.ThreatSource.referrer)
+ return _internal_referrer();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void ThreatHit_ThreatSource::set_referrer(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000004u;
+ _impl_.referrer_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatHit.ThreatSource.referrer)
+}
+inline std::string* ThreatHit_ThreatSource::mutable_referrer() {
+ std::string* _s = _internal_mutable_referrer();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatHit.ThreatSource.referrer)
+ return _s;
+}
+inline const std::string& ThreatHit_ThreatSource::_internal_referrer() const {
+ return _impl_.referrer_.Get();
+}
+inline void ThreatHit_ThreatSource::_internal_set_referrer(const std::string& value) {
+ _impl_._has_bits_[0] |= 0x00000004u;
+ _impl_.referrer_.Set(value, GetArenaForAllocation());
+}
+inline std::string* ThreatHit_ThreatSource::_internal_mutable_referrer() {
+ _impl_._has_bits_[0] |= 0x00000004u;
+ return _impl_.referrer_.Mutable(GetArenaForAllocation());
+}
+inline std::string* ThreatHit_ThreatSource::release_referrer() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ThreatHit.ThreatSource.referrer)
+ if (!_internal_has_referrer()) {
+ return nullptr;
+ }
+ _impl_._has_bits_[0] &= ~0x00000004u;
+ auto* p = _impl_.referrer_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.referrer_.IsDefault()) {
+ _impl_.referrer_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ return p;
+}
+inline void ThreatHit_ThreatSource::set_allocated_referrer(std::string* referrer) {
+ if (referrer != nullptr) {
+ _impl_._has_bits_[0] |= 0x00000004u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000004u;
+ }
+ _impl_.referrer_.SetAllocated(referrer, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.referrer_.IsDefault()) {
+ _impl_.referrer_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ThreatHit.ThreatSource.referrer)
+}
+
+// -------------------------------------------------------------------
+
+// ThreatHit_UserInfo
+
+// optional string region_code = 1;
+inline bool ThreatHit_UserInfo::_internal_has_region_code() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool ThreatHit_UserInfo::has_region_code() const {
+ return _internal_has_region_code();
+}
+inline void ThreatHit_UserInfo::clear_region_code() {
+ _impl_.region_code_.ClearToEmpty();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& ThreatHit_UserInfo::region_code() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatHit.UserInfo.region_code)
+ return _internal_region_code();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void ThreatHit_UserInfo::set_region_code(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.region_code_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatHit.UserInfo.region_code)
+}
+inline std::string* ThreatHit_UserInfo::mutable_region_code() {
+ std::string* _s = _internal_mutable_region_code();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatHit.UserInfo.region_code)
+ return _s;
+}
+inline const std::string& ThreatHit_UserInfo::_internal_region_code() const {
+ return _impl_.region_code_.Get();
+}
+inline void ThreatHit_UserInfo::_internal_set_region_code(const std::string& value) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.region_code_.Set(value, GetArenaForAllocation());
+}
+inline std::string* ThreatHit_UserInfo::_internal_mutable_region_code() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ return _impl_.region_code_.Mutable(GetArenaForAllocation());
+}
+inline std::string* ThreatHit_UserInfo::release_region_code() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ThreatHit.UserInfo.region_code)
+ if (!_internal_has_region_code()) {
+ return nullptr;
+ }
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ auto* p = _impl_.region_code_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.region_code_.IsDefault()) {
+ _impl_.region_code_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ return p;
+}
+inline void ThreatHit_UserInfo::set_allocated_region_code(std::string* region_code) {
+ if (region_code != nullptr) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.region_code_.SetAllocated(region_code, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.region_code_.IsDefault()) {
+ _impl_.region_code_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ThreatHit.UserInfo.region_code)
+}
+
+// optional bytes user_id = 2;
+inline bool ThreatHit_UserInfo::_internal_has_user_id() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ return value;
+}
+inline bool ThreatHit_UserInfo::has_user_id() const {
+ return _internal_has_user_id();
+}
+inline void ThreatHit_UserInfo::clear_user_id() {
+ _impl_.user_id_.ClearToEmpty();
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const std::string& ThreatHit_UserInfo::user_id() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatHit.UserInfo.user_id)
+ return _internal_user_id();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void ThreatHit_UserInfo::set_user_id(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.user_id_.SetBytes(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatHit.UserInfo.user_id)
+}
+inline std::string* ThreatHit_UserInfo::mutable_user_id() {
+ std::string* _s = _internal_mutable_user_id();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatHit.UserInfo.user_id)
+ return _s;
+}
+inline const std::string& ThreatHit_UserInfo::_internal_user_id() const {
+ return _impl_.user_id_.Get();
+}
+inline void ThreatHit_UserInfo::_internal_set_user_id(const std::string& value) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.user_id_.Set(value, GetArenaForAllocation());
+}
+inline std::string* ThreatHit_UserInfo::_internal_mutable_user_id() {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ return _impl_.user_id_.Mutable(GetArenaForAllocation());
+}
+inline std::string* ThreatHit_UserInfo::release_user_id() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ThreatHit.UserInfo.user_id)
+ if (!_internal_has_user_id()) {
+ return nullptr;
+ }
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ auto* p = _impl_.user_id_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.user_id_.IsDefault()) {
+ _impl_.user_id_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ return p;
+}
+inline void ThreatHit_UserInfo::set_allocated_user_id(std::string* user_id) {
+ if (user_id != nullptr) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ _impl_.user_id_.SetAllocated(user_id, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.user_id_.IsDefault()) {
+ _impl_.user_id_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ThreatHit.UserInfo.user_id)
+}
+
+// -------------------------------------------------------------------
+
+// ThreatHit
+
+// optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+inline bool ThreatHit::_internal_has_threat_type() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000008u) != 0;
+ return value;
+}
+inline bool ThreatHit::has_threat_type() const {
+ return _internal_has_threat_type();
+}
+inline void ThreatHit::clear_threat_type() {
+ _impl_.threat_type_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000008u;
+}
+inline ::mozilla::safebrowsing::ThreatType ThreatHit::_internal_threat_type() const {
+ return static_cast< ::mozilla::safebrowsing::ThreatType >(_impl_.threat_type_);
+}
+inline ::mozilla::safebrowsing::ThreatType ThreatHit::threat_type() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatHit.threat_type)
+ return _internal_threat_type();
+}
+inline void ThreatHit::_internal_set_threat_type(::mozilla::safebrowsing::ThreatType value) {
+ assert(::mozilla::safebrowsing::ThreatType_IsValid(value));
+ _impl_._has_bits_[0] |= 0x00000008u;
+ _impl_.threat_type_ = value;
+}
+inline void ThreatHit::set_threat_type(::mozilla::safebrowsing::ThreatType value) {
+ _internal_set_threat_type(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatHit.threat_type)
+}
+
+// optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+inline bool ThreatHit::_internal_has_platform_type() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000010u) != 0;
+ return value;
+}
+inline bool ThreatHit::has_platform_type() const {
+ return _internal_has_platform_type();
+}
+inline void ThreatHit::clear_platform_type() {
+ _impl_.platform_type_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000010u;
+}
+inline ::mozilla::safebrowsing::PlatformType ThreatHit::_internal_platform_type() const {
+ return static_cast< ::mozilla::safebrowsing::PlatformType >(_impl_.platform_type_);
+}
+inline ::mozilla::safebrowsing::PlatformType ThreatHit::platform_type() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatHit.platform_type)
+ return _internal_platform_type();
+}
+inline void ThreatHit::_internal_set_platform_type(::mozilla::safebrowsing::PlatformType value) {
+ assert(::mozilla::safebrowsing::PlatformType_IsValid(value));
+ _impl_._has_bits_[0] |= 0x00000010u;
+ _impl_.platform_type_ = value;
+}
+inline void ThreatHit::set_platform_type(::mozilla::safebrowsing::PlatformType value) {
+ _internal_set_platform_type(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatHit.platform_type)
+}
+
+// optional .mozilla.safebrowsing.ThreatEntry entry = 3;
+inline bool ThreatHit::_internal_has_entry() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.entry_ != nullptr);
+ return value;
+}
+inline bool ThreatHit::has_entry() const {
+ return _internal_has_entry();
+}
+inline void ThreatHit::clear_entry() {
+ if (_impl_.entry_ != nullptr) _impl_.entry_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const ::mozilla::safebrowsing::ThreatEntry& ThreatHit::_internal_entry() const {
+ const ::mozilla::safebrowsing::ThreatEntry* p = _impl_.entry_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::ThreatEntry&>(
+ ::mozilla::safebrowsing::_ThreatEntry_default_instance_);
+}
+inline const ::mozilla::safebrowsing::ThreatEntry& ThreatHit::entry() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatHit.entry)
+ return _internal_entry();
+}
+inline void ThreatHit::unsafe_arena_set_allocated_entry(
+ ::mozilla::safebrowsing::ThreatEntry* entry) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.entry_);
+ }
+ _impl_.entry_ = entry;
+ if (entry) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.ThreatHit.entry)
+}
+inline ::mozilla::safebrowsing::ThreatEntry* ThreatHit::release_entry() {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ ::mozilla::safebrowsing::ThreatEntry* temp = _impl_.entry_;
+ _impl_.entry_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::ThreatEntry* ThreatHit::unsafe_arena_release_entry() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ThreatHit.entry)
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ ::mozilla::safebrowsing::ThreatEntry* temp = _impl_.entry_;
+ _impl_.entry_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::ThreatEntry* ThreatHit::_internal_mutable_entry() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ if (_impl_.entry_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::ThreatEntry>(GetArenaForAllocation());
+ _impl_.entry_ = p;
+ }
+ return _impl_.entry_;
+}
+inline ::mozilla::safebrowsing::ThreatEntry* ThreatHit::mutable_entry() {
+ ::mozilla::safebrowsing::ThreatEntry* _msg = _internal_mutable_entry();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatHit.entry)
+ return _msg;
+}
+inline void ThreatHit::set_allocated_entry(::mozilla::safebrowsing::ThreatEntry* entry) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.entry_;
+ }
+ if (entry) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(entry);
+ if (message_arena != submessage_arena) {
+ entry = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, entry, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.entry_ = entry;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ThreatHit.entry)
+}
+
+// repeated .mozilla.safebrowsing.ThreatHit.ThreatSource resources = 4;
+inline int ThreatHit::_internal_resources_size() const {
+ return _impl_.resources_.size();
+}
+inline int ThreatHit::resources_size() const {
+ return _internal_resources_size();
+}
+inline void ThreatHit::clear_resources() {
+ _impl_.resources_.Clear();
+}
+inline ::mozilla::safebrowsing::ThreatHit_ThreatSource* ThreatHit::mutable_resources(int index) {
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatHit.resources)
+ return _impl_.resources_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatHit_ThreatSource >*
+ThreatHit::mutable_resources() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.safebrowsing.ThreatHit.resources)
+ return &_impl_.resources_;
+}
+inline const ::mozilla::safebrowsing::ThreatHit_ThreatSource& ThreatHit::_internal_resources(int index) const {
+ return _impl_.resources_.Get(index);
+}
+inline const ::mozilla::safebrowsing::ThreatHit_ThreatSource& ThreatHit::resources(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatHit.resources)
+ return _internal_resources(index);
+}
+inline ::mozilla::safebrowsing::ThreatHit_ThreatSource* ThreatHit::_internal_add_resources() {
+ return _impl_.resources_.Add();
+}
+inline ::mozilla::safebrowsing::ThreatHit_ThreatSource* ThreatHit::add_resources() {
+ ::mozilla::safebrowsing::ThreatHit_ThreatSource* _add = _internal_add_resources();
+ // @@protoc_insertion_point(field_add:mozilla.safebrowsing.ThreatHit.resources)
+ return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatHit_ThreatSource >&
+ThreatHit::resources() const {
+ // @@protoc_insertion_point(field_list:mozilla.safebrowsing.ThreatHit.resources)
+ return _impl_.resources_;
+}
+
+// optional .mozilla.safebrowsing.ClientInfo client_info = 5;
+inline bool ThreatHit::_internal_has_client_info() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.client_info_ != nullptr);
+ return value;
+}
+inline bool ThreatHit::has_client_info() const {
+ return _internal_has_client_info();
+}
+inline void ThreatHit::clear_client_info() {
+ if (_impl_.client_info_ != nullptr) _impl_.client_info_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const ::mozilla::safebrowsing::ClientInfo& ThreatHit::_internal_client_info() const {
+ const ::mozilla::safebrowsing::ClientInfo* p = _impl_.client_info_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::ClientInfo&>(
+ ::mozilla::safebrowsing::_ClientInfo_default_instance_);
+}
+inline const ::mozilla::safebrowsing::ClientInfo& ThreatHit::client_info() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatHit.client_info)
+ return _internal_client_info();
+}
+inline void ThreatHit::unsafe_arena_set_allocated_client_info(
+ ::mozilla::safebrowsing::ClientInfo* client_info) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.client_info_);
+ }
+ _impl_.client_info_ = client_info;
+ if (client_info) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.ThreatHit.client_info)
+}
+inline ::mozilla::safebrowsing::ClientInfo* ThreatHit::release_client_info() {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::safebrowsing::ClientInfo* temp = _impl_.client_info_;
+ _impl_.client_info_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::ClientInfo* ThreatHit::unsafe_arena_release_client_info() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ThreatHit.client_info)
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::safebrowsing::ClientInfo* temp = _impl_.client_info_;
+ _impl_.client_info_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::ClientInfo* ThreatHit::_internal_mutable_client_info() {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ if (_impl_.client_info_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::ClientInfo>(GetArenaForAllocation());
+ _impl_.client_info_ = p;
+ }
+ return _impl_.client_info_;
+}
+inline ::mozilla::safebrowsing::ClientInfo* ThreatHit::mutable_client_info() {
+ ::mozilla::safebrowsing::ClientInfo* _msg = _internal_mutable_client_info();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatHit.client_info)
+ return _msg;
+}
+inline void ThreatHit::set_allocated_client_info(::mozilla::safebrowsing::ClientInfo* client_info) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.client_info_;
+ }
+ if (client_info) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(client_info);
+ if (message_arena != submessage_arena) {
+ client_info = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, client_info, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ _impl_.client_info_ = client_info;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ThreatHit.client_info)
+}
+
+// optional .mozilla.safebrowsing.ThreatHit.UserInfo user_info = 6;
+inline bool ThreatHit::_internal_has_user_info() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.user_info_ != nullptr);
+ return value;
+}
+inline bool ThreatHit::has_user_info() const {
+ return _internal_has_user_info();
+}
+inline void ThreatHit::clear_user_info() {
+ if (_impl_.user_info_ != nullptr) _impl_.user_info_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline const ::mozilla::safebrowsing::ThreatHit_UserInfo& ThreatHit::_internal_user_info() const {
+ const ::mozilla::safebrowsing::ThreatHit_UserInfo* p = _impl_.user_info_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::ThreatHit_UserInfo&>(
+ ::mozilla::safebrowsing::_ThreatHit_UserInfo_default_instance_);
+}
+inline const ::mozilla::safebrowsing::ThreatHit_UserInfo& ThreatHit::user_info() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatHit.user_info)
+ return _internal_user_info();
+}
+inline void ThreatHit::unsafe_arena_set_allocated_user_info(
+ ::mozilla::safebrowsing::ThreatHit_UserInfo* user_info) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.user_info_);
+ }
+ _impl_.user_info_ = user_info;
+ if (user_info) {
+ _impl_._has_bits_[0] |= 0x00000004u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000004u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.ThreatHit.user_info)
+}
+inline ::mozilla::safebrowsing::ThreatHit_UserInfo* ThreatHit::release_user_info() {
+ _impl_._has_bits_[0] &= ~0x00000004u;
+ ::mozilla::safebrowsing::ThreatHit_UserInfo* temp = _impl_.user_info_;
+ _impl_.user_info_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::ThreatHit_UserInfo* ThreatHit::unsafe_arena_release_user_info() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ThreatHit.user_info)
+ _impl_._has_bits_[0] &= ~0x00000004u;
+ ::mozilla::safebrowsing::ThreatHit_UserInfo* temp = _impl_.user_info_;
+ _impl_.user_info_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::ThreatHit_UserInfo* ThreatHit::_internal_mutable_user_info() {
+ _impl_._has_bits_[0] |= 0x00000004u;
+ if (_impl_.user_info_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::ThreatHit_UserInfo>(GetArenaForAllocation());
+ _impl_.user_info_ = p;
+ }
+ return _impl_.user_info_;
+}
+inline ::mozilla::safebrowsing::ThreatHit_UserInfo* ThreatHit::mutable_user_info() {
+ ::mozilla::safebrowsing::ThreatHit_UserInfo* _msg = _internal_mutable_user_info();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatHit.user_info)
+ return _msg;
+}
+inline void ThreatHit::set_allocated_user_info(::mozilla::safebrowsing::ThreatHit_UserInfo* user_info) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.user_info_;
+ }
+ if (user_info) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(user_info);
+ if (message_arena != submessage_arena) {
+ user_info = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, user_info, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000004u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000004u;
+ }
+ _impl_.user_info_ = user_info;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ThreatHit.user_info)
+}
+
+// -------------------------------------------------------------------
+
+// ClientInfo
+
+// optional string client_id = 1;
+inline bool ClientInfo::_internal_has_client_id() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool ClientInfo::has_client_id() const {
+ return _internal_has_client_id();
+}
+inline void ClientInfo::clear_client_id() {
+ _impl_.client_id_.ClearToEmpty();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& ClientInfo::client_id() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ClientInfo.client_id)
+ return _internal_client_id();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void ClientInfo::set_client_id(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.client_id_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ClientInfo.client_id)
+}
+inline std::string* ClientInfo::mutable_client_id() {
+ std::string* _s = _internal_mutable_client_id();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ClientInfo.client_id)
+ return _s;
+}
+inline const std::string& ClientInfo::_internal_client_id() const {
+ return _impl_.client_id_.Get();
+}
+inline void ClientInfo::_internal_set_client_id(const std::string& value) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.client_id_.Set(value, GetArenaForAllocation());
+}
+inline std::string* ClientInfo::_internal_mutable_client_id() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ return _impl_.client_id_.Mutable(GetArenaForAllocation());
+}
+inline std::string* ClientInfo::release_client_id() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ClientInfo.client_id)
+ if (!_internal_has_client_id()) {
+ return nullptr;
+ }
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ auto* p = _impl_.client_id_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.client_id_.IsDefault()) {
+ _impl_.client_id_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ return p;
+}
+inline void ClientInfo::set_allocated_client_id(std::string* client_id) {
+ if (client_id != nullptr) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.client_id_.SetAllocated(client_id, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.client_id_.IsDefault()) {
+ _impl_.client_id_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ClientInfo.client_id)
+}
+
+// optional string client_version = 2;
+inline bool ClientInfo::_internal_has_client_version() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ return value;
+}
+inline bool ClientInfo::has_client_version() const {
+ return _internal_has_client_version();
+}
+inline void ClientInfo::clear_client_version() {
+ _impl_.client_version_.ClearToEmpty();
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const std::string& ClientInfo::client_version() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ClientInfo.client_version)
+ return _internal_client_version();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void ClientInfo::set_client_version(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.client_version_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ClientInfo.client_version)
+}
+inline std::string* ClientInfo::mutable_client_version() {
+ std::string* _s = _internal_mutable_client_version();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ClientInfo.client_version)
+ return _s;
+}
+inline const std::string& ClientInfo::_internal_client_version() const {
+ return _impl_.client_version_.Get();
+}
+inline void ClientInfo::_internal_set_client_version(const std::string& value) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.client_version_.Set(value, GetArenaForAllocation());
+}
+inline std::string* ClientInfo::_internal_mutable_client_version() {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ return _impl_.client_version_.Mutable(GetArenaForAllocation());
+}
+inline std::string* ClientInfo::release_client_version() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ClientInfo.client_version)
+ if (!_internal_has_client_version()) {
+ return nullptr;
+ }
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ auto* p = _impl_.client_version_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.client_version_.IsDefault()) {
+ _impl_.client_version_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ return p;
+}
+inline void ClientInfo::set_allocated_client_version(std::string* client_version) {
+ if (client_version != nullptr) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ _impl_.client_version_.SetAllocated(client_version, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.client_version_.IsDefault()) {
+ _impl_.client_version_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ClientInfo.client_version)
+}
+
+// -------------------------------------------------------------------
+
+// ChromeClientInfo
+
+// optional .mozilla.safebrowsing.ChromeClientInfo.SafeBrowsingReportingPopulation reporting_population = 1;
+inline bool ChromeClientInfo::_internal_has_reporting_population() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool ChromeClientInfo::has_reporting_population() const {
+ return _internal_has_reporting_population();
+}
+inline void ChromeClientInfo::clear_reporting_population() {
+ _impl_.reporting_population_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline ::mozilla::safebrowsing::ChromeClientInfo_SafeBrowsingReportingPopulation ChromeClientInfo::_internal_reporting_population() const {
+ return static_cast< ::mozilla::safebrowsing::ChromeClientInfo_SafeBrowsingReportingPopulation >(_impl_.reporting_population_);
+}
+inline ::mozilla::safebrowsing::ChromeClientInfo_SafeBrowsingReportingPopulation ChromeClientInfo::reporting_population() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ChromeClientInfo.reporting_population)
+ return _internal_reporting_population();
+}
+inline void ChromeClientInfo::_internal_set_reporting_population(::mozilla::safebrowsing::ChromeClientInfo_SafeBrowsingReportingPopulation value) {
+ assert(::mozilla::safebrowsing::ChromeClientInfo_SafeBrowsingReportingPopulation_IsValid(value));
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.reporting_population_ = value;
+}
+inline void ChromeClientInfo::set_reporting_population(::mozilla::safebrowsing::ChromeClientInfo_SafeBrowsingReportingPopulation value) {
+ _internal_set_reporting_population(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ChromeClientInfo.reporting_population)
+}
+
+// -------------------------------------------------------------------
+
+// Checksum
+
+// optional bytes sha256 = 1;
+inline bool Checksum::_internal_has_sha256() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool Checksum::has_sha256() const {
+ return _internal_has_sha256();
+}
+inline void Checksum::clear_sha256() {
+ _impl_.sha256_.ClearToEmpty();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& Checksum::sha256() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.Checksum.sha256)
+ return _internal_sha256();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void Checksum::set_sha256(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.sha256_.SetBytes(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.Checksum.sha256)
+}
+inline std::string* Checksum::mutable_sha256() {
+ std::string* _s = _internal_mutable_sha256();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.Checksum.sha256)
+ return _s;
+}
+inline const std::string& Checksum::_internal_sha256() const {
+ return _impl_.sha256_.Get();
+}
+inline void Checksum::_internal_set_sha256(const std::string& value) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.sha256_.Set(value, GetArenaForAllocation());
+}
+inline std::string* Checksum::_internal_mutable_sha256() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ return _impl_.sha256_.Mutable(GetArenaForAllocation());
+}
+inline std::string* Checksum::release_sha256() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.Checksum.sha256)
+ if (!_internal_has_sha256()) {
+ return nullptr;
+ }
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ auto* p = _impl_.sha256_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.sha256_.IsDefault()) {
+ _impl_.sha256_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ return p;
+}
+inline void Checksum::set_allocated_sha256(std::string* sha256) {
+ if (sha256 != nullptr) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.sha256_.SetAllocated(sha256, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.sha256_.IsDefault()) {
+ _impl_.sha256_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.Checksum.sha256)
+}
+
+// -------------------------------------------------------------------
+
+// ThreatEntry
+
+// optional bytes hash = 1;
+inline bool ThreatEntry::_internal_has_hash() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool ThreatEntry::has_hash() const {
+ return _internal_has_hash();
+}
+inline void ThreatEntry::clear_hash() {
+ _impl_.hash_.ClearToEmpty();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& ThreatEntry::hash() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatEntry.hash)
+ return _internal_hash();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void ThreatEntry::set_hash(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.hash_.SetBytes(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatEntry.hash)
+}
+inline std::string* ThreatEntry::mutable_hash() {
+ std::string* _s = _internal_mutable_hash();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatEntry.hash)
+ return _s;
+}
+inline const std::string& ThreatEntry::_internal_hash() const {
+ return _impl_.hash_.Get();
+}
+inline void ThreatEntry::_internal_set_hash(const std::string& value) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.hash_.Set(value, GetArenaForAllocation());
+}
+inline std::string* ThreatEntry::_internal_mutable_hash() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ return _impl_.hash_.Mutable(GetArenaForAllocation());
+}
+inline std::string* ThreatEntry::release_hash() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ThreatEntry.hash)
+ if (!_internal_has_hash()) {
+ return nullptr;
+ }
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ auto* p = _impl_.hash_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.hash_.IsDefault()) {
+ _impl_.hash_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ return p;
+}
+inline void ThreatEntry::set_allocated_hash(std::string* hash) {
+ if (hash != nullptr) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.hash_.SetAllocated(hash, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.hash_.IsDefault()) {
+ _impl_.hash_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ThreatEntry.hash)
+}
+
+// optional string url = 2;
+inline bool ThreatEntry::_internal_has_url() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ return value;
+}
+inline bool ThreatEntry::has_url() const {
+ return _internal_has_url();
+}
+inline void ThreatEntry::clear_url() {
+ _impl_.url_.ClearToEmpty();
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const std::string& ThreatEntry::url() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatEntry.url)
+ return _internal_url();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void ThreatEntry::set_url(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.url_.Set(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatEntry.url)
+}
+inline std::string* ThreatEntry::mutable_url() {
+ std::string* _s = _internal_mutable_url();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatEntry.url)
+ return _s;
+}
+inline const std::string& ThreatEntry::_internal_url() const {
+ return _impl_.url_.Get();
+}
+inline void ThreatEntry::_internal_set_url(const std::string& value) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.url_.Set(value, GetArenaForAllocation());
+}
+inline std::string* ThreatEntry::_internal_mutable_url() {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ return _impl_.url_.Mutable(GetArenaForAllocation());
+}
+inline std::string* ThreatEntry::release_url() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ThreatEntry.url)
+ if (!_internal_has_url()) {
+ return nullptr;
+ }
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ auto* p = _impl_.url_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.url_.IsDefault()) {
+ _impl_.url_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ return p;
+}
+inline void ThreatEntry::set_allocated_url(std::string* url) {
+ if (url != nullptr) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ _impl_.url_.SetAllocated(url, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.url_.IsDefault()) {
+ _impl_.url_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ThreatEntry.url)
+}
+
+// -------------------------------------------------------------------
+
+// ThreatEntrySet
+
+// optional .mozilla.safebrowsing.CompressionType compression_type = 1;
+inline bool ThreatEntrySet::_internal_has_compression_type() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000010u) != 0;
+ return value;
+}
+inline bool ThreatEntrySet::has_compression_type() const {
+ return _internal_has_compression_type();
+}
+inline void ThreatEntrySet::clear_compression_type() {
+ _impl_.compression_type_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000010u;
+}
+inline ::mozilla::safebrowsing::CompressionType ThreatEntrySet::_internal_compression_type() const {
+ return static_cast< ::mozilla::safebrowsing::CompressionType >(_impl_.compression_type_);
+}
+inline ::mozilla::safebrowsing::CompressionType ThreatEntrySet::compression_type() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatEntrySet.compression_type)
+ return _internal_compression_type();
+}
+inline void ThreatEntrySet::_internal_set_compression_type(::mozilla::safebrowsing::CompressionType value) {
+ assert(::mozilla::safebrowsing::CompressionType_IsValid(value));
+ _impl_._has_bits_[0] |= 0x00000010u;
+ _impl_.compression_type_ = value;
+}
+inline void ThreatEntrySet::set_compression_type(::mozilla::safebrowsing::CompressionType value) {
+ _internal_set_compression_type(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatEntrySet.compression_type)
+}
+
+// optional .mozilla.safebrowsing.RawHashes raw_hashes = 2;
+inline bool ThreatEntrySet::_internal_has_raw_hashes() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.raw_hashes_ != nullptr);
+ return value;
+}
+inline bool ThreatEntrySet::has_raw_hashes() const {
+ return _internal_has_raw_hashes();
+}
+inline void ThreatEntrySet::clear_raw_hashes() {
+ if (_impl_.raw_hashes_ != nullptr) _impl_.raw_hashes_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const ::mozilla::safebrowsing::RawHashes& ThreatEntrySet::_internal_raw_hashes() const {
+ const ::mozilla::safebrowsing::RawHashes* p = _impl_.raw_hashes_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::RawHashes&>(
+ ::mozilla::safebrowsing::_RawHashes_default_instance_);
+}
+inline const ::mozilla::safebrowsing::RawHashes& ThreatEntrySet::raw_hashes() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatEntrySet.raw_hashes)
+ return _internal_raw_hashes();
+}
+inline void ThreatEntrySet::unsafe_arena_set_allocated_raw_hashes(
+ ::mozilla::safebrowsing::RawHashes* raw_hashes) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.raw_hashes_);
+ }
+ _impl_.raw_hashes_ = raw_hashes;
+ if (raw_hashes) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.ThreatEntrySet.raw_hashes)
+}
+inline ::mozilla::safebrowsing::RawHashes* ThreatEntrySet::release_raw_hashes() {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ ::mozilla::safebrowsing::RawHashes* temp = _impl_.raw_hashes_;
+ _impl_.raw_hashes_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::RawHashes* ThreatEntrySet::unsafe_arena_release_raw_hashes() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ThreatEntrySet.raw_hashes)
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ ::mozilla::safebrowsing::RawHashes* temp = _impl_.raw_hashes_;
+ _impl_.raw_hashes_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::RawHashes* ThreatEntrySet::_internal_mutable_raw_hashes() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ if (_impl_.raw_hashes_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::RawHashes>(GetArenaForAllocation());
+ _impl_.raw_hashes_ = p;
+ }
+ return _impl_.raw_hashes_;
+}
+inline ::mozilla::safebrowsing::RawHashes* ThreatEntrySet::mutable_raw_hashes() {
+ ::mozilla::safebrowsing::RawHashes* _msg = _internal_mutable_raw_hashes();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatEntrySet.raw_hashes)
+ return _msg;
+}
+inline void ThreatEntrySet::set_allocated_raw_hashes(::mozilla::safebrowsing::RawHashes* raw_hashes) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.raw_hashes_;
+ }
+ if (raw_hashes) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(raw_hashes);
+ if (message_arena != submessage_arena) {
+ raw_hashes = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, raw_hashes, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.raw_hashes_ = raw_hashes;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ThreatEntrySet.raw_hashes)
+}
+
+// optional .mozilla.safebrowsing.RawIndices raw_indices = 3;
+inline bool ThreatEntrySet::_internal_has_raw_indices() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.raw_indices_ != nullptr);
+ return value;
+}
+inline bool ThreatEntrySet::has_raw_indices() const {
+ return _internal_has_raw_indices();
+}
+inline void ThreatEntrySet::clear_raw_indices() {
+ if (_impl_.raw_indices_ != nullptr) _impl_.raw_indices_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const ::mozilla::safebrowsing::RawIndices& ThreatEntrySet::_internal_raw_indices() const {
+ const ::mozilla::safebrowsing::RawIndices* p = _impl_.raw_indices_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::RawIndices&>(
+ ::mozilla::safebrowsing::_RawIndices_default_instance_);
+}
+inline const ::mozilla::safebrowsing::RawIndices& ThreatEntrySet::raw_indices() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatEntrySet.raw_indices)
+ return _internal_raw_indices();
+}
+inline void ThreatEntrySet::unsafe_arena_set_allocated_raw_indices(
+ ::mozilla::safebrowsing::RawIndices* raw_indices) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.raw_indices_);
+ }
+ _impl_.raw_indices_ = raw_indices;
+ if (raw_indices) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.ThreatEntrySet.raw_indices)
+}
+inline ::mozilla::safebrowsing::RawIndices* ThreatEntrySet::release_raw_indices() {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::safebrowsing::RawIndices* temp = _impl_.raw_indices_;
+ _impl_.raw_indices_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::RawIndices* ThreatEntrySet::unsafe_arena_release_raw_indices() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ThreatEntrySet.raw_indices)
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ ::mozilla::safebrowsing::RawIndices* temp = _impl_.raw_indices_;
+ _impl_.raw_indices_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::RawIndices* ThreatEntrySet::_internal_mutable_raw_indices() {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ if (_impl_.raw_indices_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::RawIndices>(GetArenaForAllocation());
+ _impl_.raw_indices_ = p;
+ }
+ return _impl_.raw_indices_;
+}
+inline ::mozilla::safebrowsing::RawIndices* ThreatEntrySet::mutable_raw_indices() {
+ ::mozilla::safebrowsing::RawIndices* _msg = _internal_mutable_raw_indices();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatEntrySet.raw_indices)
+ return _msg;
+}
+inline void ThreatEntrySet::set_allocated_raw_indices(::mozilla::safebrowsing::RawIndices* raw_indices) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.raw_indices_;
+ }
+ if (raw_indices) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(raw_indices);
+ if (message_arena != submessage_arena) {
+ raw_indices = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, raw_indices, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ _impl_.raw_indices_ = raw_indices;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ThreatEntrySet.raw_indices)
+}
+
+// optional .mozilla.safebrowsing.RiceDeltaEncoding rice_hashes = 4;
+inline bool ThreatEntrySet::_internal_has_rice_hashes() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.rice_hashes_ != nullptr);
+ return value;
+}
+inline bool ThreatEntrySet::has_rice_hashes() const {
+ return _internal_has_rice_hashes();
+}
+inline void ThreatEntrySet::clear_rice_hashes() {
+ if (_impl_.rice_hashes_ != nullptr) _impl_.rice_hashes_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline const ::mozilla::safebrowsing::RiceDeltaEncoding& ThreatEntrySet::_internal_rice_hashes() const {
+ const ::mozilla::safebrowsing::RiceDeltaEncoding* p = _impl_.rice_hashes_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::RiceDeltaEncoding&>(
+ ::mozilla::safebrowsing::_RiceDeltaEncoding_default_instance_);
+}
+inline const ::mozilla::safebrowsing::RiceDeltaEncoding& ThreatEntrySet::rice_hashes() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatEntrySet.rice_hashes)
+ return _internal_rice_hashes();
+}
+inline void ThreatEntrySet::unsafe_arena_set_allocated_rice_hashes(
+ ::mozilla::safebrowsing::RiceDeltaEncoding* rice_hashes) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.rice_hashes_);
+ }
+ _impl_.rice_hashes_ = rice_hashes;
+ if (rice_hashes) {
+ _impl_._has_bits_[0] |= 0x00000004u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000004u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.ThreatEntrySet.rice_hashes)
+}
+inline ::mozilla::safebrowsing::RiceDeltaEncoding* ThreatEntrySet::release_rice_hashes() {
+ _impl_._has_bits_[0] &= ~0x00000004u;
+ ::mozilla::safebrowsing::RiceDeltaEncoding* temp = _impl_.rice_hashes_;
+ _impl_.rice_hashes_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::RiceDeltaEncoding* ThreatEntrySet::unsafe_arena_release_rice_hashes() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ThreatEntrySet.rice_hashes)
+ _impl_._has_bits_[0] &= ~0x00000004u;
+ ::mozilla::safebrowsing::RiceDeltaEncoding* temp = _impl_.rice_hashes_;
+ _impl_.rice_hashes_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::RiceDeltaEncoding* ThreatEntrySet::_internal_mutable_rice_hashes() {
+ _impl_._has_bits_[0] |= 0x00000004u;
+ if (_impl_.rice_hashes_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::RiceDeltaEncoding>(GetArenaForAllocation());
+ _impl_.rice_hashes_ = p;
+ }
+ return _impl_.rice_hashes_;
+}
+inline ::mozilla::safebrowsing::RiceDeltaEncoding* ThreatEntrySet::mutable_rice_hashes() {
+ ::mozilla::safebrowsing::RiceDeltaEncoding* _msg = _internal_mutable_rice_hashes();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatEntrySet.rice_hashes)
+ return _msg;
+}
+inline void ThreatEntrySet::set_allocated_rice_hashes(::mozilla::safebrowsing::RiceDeltaEncoding* rice_hashes) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.rice_hashes_;
+ }
+ if (rice_hashes) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(rice_hashes);
+ if (message_arena != submessage_arena) {
+ rice_hashes = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, rice_hashes, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000004u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000004u;
+ }
+ _impl_.rice_hashes_ = rice_hashes;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ThreatEntrySet.rice_hashes)
+}
+
+// optional .mozilla.safebrowsing.RiceDeltaEncoding rice_indices = 5;
+inline bool ThreatEntrySet::_internal_has_rice_indices() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000008u) != 0;
+ PROTOBUF_ASSUME(!value || _impl_.rice_indices_ != nullptr);
+ return value;
+}
+inline bool ThreatEntrySet::has_rice_indices() const {
+ return _internal_has_rice_indices();
+}
+inline void ThreatEntrySet::clear_rice_indices() {
+ if (_impl_.rice_indices_ != nullptr) _impl_.rice_indices_->Clear();
+ _impl_._has_bits_[0] &= ~0x00000008u;
+}
+inline const ::mozilla::safebrowsing::RiceDeltaEncoding& ThreatEntrySet::_internal_rice_indices() const {
+ const ::mozilla::safebrowsing::RiceDeltaEncoding* p = _impl_.rice_indices_;
+ return p != nullptr ? *p : reinterpret_cast<const ::mozilla::safebrowsing::RiceDeltaEncoding&>(
+ ::mozilla::safebrowsing::_RiceDeltaEncoding_default_instance_);
+}
+inline const ::mozilla::safebrowsing::RiceDeltaEncoding& ThreatEntrySet::rice_indices() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatEntrySet.rice_indices)
+ return _internal_rice_indices();
+}
+inline void ThreatEntrySet::unsafe_arena_set_allocated_rice_indices(
+ ::mozilla::safebrowsing::RiceDeltaEncoding* rice_indices) {
+ if (GetArenaForAllocation() == nullptr) {
+ delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(_impl_.rice_indices_);
+ }
+ _impl_.rice_indices_ = rice_indices;
+ if (rice_indices) {
+ _impl_._has_bits_[0] |= 0x00000008u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000008u;
+ }
+ // @@protoc_insertion_point(field_unsafe_arena_set_allocated:mozilla.safebrowsing.ThreatEntrySet.rice_indices)
+}
+inline ::mozilla::safebrowsing::RiceDeltaEncoding* ThreatEntrySet::release_rice_indices() {
+ _impl_._has_bits_[0] &= ~0x00000008u;
+ ::mozilla::safebrowsing::RiceDeltaEncoding* temp = _impl_.rice_indices_;
+ _impl_.rice_indices_ = nullptr;
+#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE
+ auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp);
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ if (GetArenaForAllocation() == nullptr) { delete old; }
+#else // PROTOBUF_FORCE_COPY_IN_RELEASE
+ if (GetArenaForAllocation() != nullptr) {
+ temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp);
+ }
+#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE
+ return temp;
+}
+inline ::mozilla::safebrowsing::RiceDeltaEncoding* ThreatEntrySet::unsafe_arena_release_rice_indices() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ThreatEntrySet.rice_indices)
+ _impl_._has_bits_[0] &= ~0x00000008u;
+ ::mozilla::safebrowsing::RiceDeltaEncoding* temp = _impl_.rice_indices_;
+ _impl_.rice_indices_ = nullptr;
+ return temp;
+}
+inline ::mozilla::safebrowsing::RiceDeltaEncoding* ThreatEntrySet::_internal_mutable_rice_indices() {
+ _impl_._has_bits_[0] |= 0x00000008u;
+ if (_impl_.rice_indices_ == nullptr) {
+ auto* p = CreateMaybeMessage<::mozilla::safebrowsing::RiceDeltaEncoding>(GetArenaForAllocation());
+ _impl_.rice_indices_ = p;
+ }
+ return _impl_.rice_indices_;
+}
+inline ::mozilla::safebrowsing::RiceDeltaEncoding* ThreatEntrySet::mutable_rice_indices() {
+ ::mozilla::safebrowsing::RiceDeltaEncoding* _msg = _internal_mutable_rice_indices();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatEntrySet.rice_indices)
+ return _msg;
+}
+inline void ThreatEntrySet::set_allocated_rice_indices(::mozilla::safebrowsing::RiceDeltaEncoding* rice_indices) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation();
+ if (message_arena == nullptr) {
+ delete _impl_.rice_indices_;
+ }
+ if (rice_indices) {
+ ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena =
+ ::PROTOBUF_NAMESPACE_ID::Arena::InternalGetOwningArena(rice_indices);
+ if (message_arena != submessage_arena) {
+ rice_indices = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage(
+ message_arena, rice_indices, submessage_arena);
+ }
+ _impl_._has_bits_[0] |= 0x00000008u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000008u;
+ }
+ _impl_.rice_indices_ = rice_indices;
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ThreatEntrySet.rice_indices)
+}
+
+// -------------------------------------------------------------------
+
+// RawIndices
+
+// repeated int32 indices = 1;
+inline int RawIndices::_internal_indices_size() const {
+ return _impl_.indices_.size();
+}
+inline int RawIndices::indices_size() const {
+ return _internal_indices_size();
+}
+inline void RawIndices::clear_indices() {
+ _impl_.indices_.Clear();
+}
+inline int32_t RawIndices::_internal_indices(int index) const {
+ return _impl_.indices_.Get(index);
+}
+inline int32_t RawIndices::indices(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.RawIndices.indices)
+ return _internal_indices(index);
+}
+inline void RawIndices::set_indices(int index, int32_t value) {
+ _impl_.indices_.Set(index, value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.RawIndices.indices)
+}
+inline void RawIndices::_internal_add_indices(int32_t value) {
+ _impl_.indices_.Add(value);
+}
+inline void RawIndices::add_indices(int32_t value) {
+ _internal_add_indices(value);
+ // @@protoc_insertion_point(field_add:mozilla.safebrowsing.RawIndices.indices)
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+RawIndices::_internal_indices() const {
+ return _impl_.indices_;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >&
+RawIndices::indices() const {
+ // @@protoc_insertion_point(field_list:mozilla.safebrowsing.RawIndices.indices)
+ return _internal_indices();
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+RawIndices::_internal_mutable_indices() {
+ return &_impl_.indices_;
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >*
+RawIndices::mutable_indices() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.safebrowsing.RawIndices.indices)
+ return _internal_mutable_indices();
+}
+
+// -------------------------------------------------------------------
+
+// RawHashes
+
+// optional int32 prefix_size = 1;
+inline bool RawHashes::_internal_has_prefix_size() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ return value;
+}
+inline bool RawHashes::has_prefix_size() const {
+ return _internal_has_prefix_size();
+}
+inline void RawHashes::clear_prefix_size() {
+ _impl_.prefix_size_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline int32_t RawHashes::_internal_prefix_size() const {
+ return _impl_.prefix_size_;
+}
+inline int32_t RawHashes::prefix_size() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.RawHashes.prefix_size)
+ return _internal_prefix_size();
+}
+inline void RawHashes::_internal_set_prefix_size(int32_t value) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.prefix_size_ = value;
+}
+inline void RawHashes::set_prefix_size(int32_t value) {
+ _internal_set_prefix_size(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.RawHashes.prefix_size)
+}
+
+// optional bytes raw_hashes = 2;
+inline bool RawHashes::_internal_has_raw_hashes() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool RawHashes::has_raw_hashes() const {
+ return _internal_has_raw_hashes();
+}
+inline void RawHashes::clear_raw_hashes() {
+ _impl_.raw_hashes_.ClearToEmpty();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& RawHashes::raw_hashes() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.RawHashes.raw_hashes)
+ return _internal_raw_hashes();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void RawHashes::set_raw_hashes(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.raw_hashes_.SetBytes(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.RawHashes.raw_hashes)
+}
+inline std::string* RawHashes::mutable_raw_hashes() {
+ std::string* _s = _internal_mutable_raw_hashes();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.RawHashes.raw_hashes)
+ return _s;
+}
+inline const std::string& RawHashes::_internal_raw_hashes() const {
+ return _impl_.raw_hashes_.Get();
+}
+inline void RawHashes::_internal_set_raw_hashes(const std::string& value) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.raw_hashes_.Set(value, GetArenaForAllocation());
+}
+inline std::string* RawHashes::_internal_mutable_raw_hashes() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ return _impl_.raw_hashes_.Mutable(GetArenaForAllocation());
+}
+inline std::string* RawHashes::release_raw_hashes() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.RawHashes.raw_hashes)
+ if (!_internal_has_raw_hashes()) {
+ return nullptr;
+ }
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ auto* p = _impl_.raw_hashes_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.raw_hashes_.IsDefault()) {
+ _impl_.raw_hashes_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ return p;
+}
+inline void RawHashes::set_allocated_raw_hashes(std::string* raw_hashes) {
+ if (raw_hashes != nullptr) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.raw_hashes_.SetAllocated(raw_hashes, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.raw_hashes_.IsDefault()) {
+ _impl_.raw_hashes_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.RawHashes.raw_hashes)
+}
+
+// -------------------------------------------------------------------
+
+// RiceDeltaEncoding
+
+// optional int64 first_value = 1;
+inline bool RiceDeltaEncoding::_internal_has_first_value() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ return value;
+}
+inline bool RiceDeltaEncoding::has_first_value() const {
+ return _internal_has_first_value();
+}
+inline void RiceDeltaEncoding::clear_first_value() {
+ _impl_.first_value_ = int64_t{0};
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline int64_t RiceDeltaEncoding::_internal_first_value() const {
+ return _impl_.first_value_;
+}
+inline int64_t RiceDeltaEncoding::first_value() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.RiceDeltaEncoding.first_value)
+ return _internal_first_value();
+}
+inline void RiceDeltaEncoding::_internal_set_first_value(int64_t value) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.first_value_ = value;
+}
+inline void RiceDeltaEncoding::set_first_value(int64_t value) {
+ _internal_set_first_value(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.RiceDeltaEncoding.first_value)
+}
+
+// optional int32 rice_parameter = 2;
+inline bool RiceDeltaEncoding::_internal_has_rice_parameter() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+ return value;
+}
+inline bool RiceDeltaEncoding::has_rice_parameter() const {
+ return _internal_has_rice_parameter();
+}
+inline void RiceDeltaEncoding::clear_rice_parameter() {
+ _impl_.rice_parameter_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline int32_t RiceDeltaEncoding::_internal_rice_parameter() const {
+ return _impl_.rice_parameter_;
+}
+inline int32_t RiceDeltaEncoding::rice_parameter() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.RiceDeltaEncoding.rice_parameter)
+ return _internal_rice_parameter();
+}
+inline void RiceDeltaEncoding::_internal_set_rice_parameter(int32_t value) {
+ _impl_._has_bits_[0] |= 0x00000004u;
+ _impl_.rice_parameter_ = value;
+}
+inline void RiceDeltaEncoding::set_rice_parameter(int32_t value) {
+ _internal_set_rice_parameter(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.RiceDeltaEncoding.rice_parameter)
+}
+
+// optional int32 num_entries = 3;
+inline bool RiceDeltaEncoding::_internal_has_num_entries() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000008u) != 0;
+ return value;
+}
+inline bool RiceDeltaEncoding::has_num_entries() const {
+ return _internal_has_num_entries();
+}
+inline void RiceDeltaEncoding::clear_num_entries() {
+ _impl_.num_entries_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000008u;
+}
+inline int32_t RiceDeltaEncoding::_internal_num_entries() const {
+ return _impl_.num_entries_;
+}
+inline int32_t RiceDeltaEncoding::num_entries() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.RiceDeltaEncoding.num_entries)
+ return _internal_num_entries();
+}
+inline void RiceDeltaEncoding::_internal_set_num_entries(int32_t value) {
+ _impl_._has_bits_[0] |= 0x00000008u;
+ _impl_.num_entries_ = value;
+}
+inline void RiceDeltaEncoding::set_num_entries(int32_t value) {
+ _internal_set_num_entries(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.RiceDeltaEncoding.num_entries)
+}
+
+// optional bytes encoded_data = 4;
+inline bool RiceDeltaEncoding::_internal_has_encoded_data() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool RiceDeltaEncoding::has_encoded_data() const {
+ return _internal_has_encoded_data();
+}
+inline void RiceDeltaEncoding::clear_encoded_data() {
+ _impl_.encoded_data_.ClearToEmpty();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& RiceDeltaEncoding::encoded_data() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.RiceDeltaEncoding.encoded_data)
+ return _internal_encoded_data();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void RiceDeltaEncoding::set_encoded_data(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.encoded_data_.SetBytes(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.RiceDeltaEncoding.encoded_data)
+}
+inline std::string* RiceDeltaEncoding::mutable_encoded_data() {
+ std::string* _s = _internal_mutable_encoded_data();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.RiceDeltaEncoding.encoded_data)
+ return _s;
+}
+inline const std::string& RiceDeltaEncoding::_internal_encoded_data() const {
+ return _impl_.encoded_data_.Get();
+}
+inline void RiceDeltaEncoding::_internal_set_encoded_data(const std::string& value) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.encoded_data_.Set(value, GetArenaForAllocation());
+}
+inline std::string* RiceDeltaEncoding::_internal_mutable_encoded_data() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ return _impl_.encoded_data_.Mutable(GetArenaForAllocation());
+}
+inline std::string* RiceDeltaEncoding::release_encoded_data() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.RiceDeltaEncoding.encoded_data)
+ if (!_internal_has_encoded_data()) {
+ return nullptr;
+ }
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ auto* p = _impl_.encoded_data_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.encoded_data_.IsDefault()) {
+ _impl_.encoded_data_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ return p;
+}
+inline void RiceDeltaEncoding::set_allocated_encoded_data(std::string* encoded_data) {
+ if (encoded_data != nullptr) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.encoded_data_.SetAllocated(encoded_data, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.encoded_data_.IsDefault()) {
+ _impl_.encoded_data_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.RiceDeltaEncoding.encoded_data)
+}
+
+// -------------------------------------------------------------------
+
+// ThreatEntryMetadata_MetadataEntry
+
+// optional bytes key = 1;
+inline bool ThreatEntryMetadata_MetadataEntry::_internal_has_key() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool ThreatEntryMetadata_MetadataEntry::has_key() const {
+ return _internal_has_key();
+}
+inline void ThreatEntryMetadata_MetadataEntry::clear_key() {
+ _impl_.key_.ClearToEmpty();
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline const std::string& ThreatEntryMetadata_MetadataEntry::key() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry.key)
+ return _internal_key();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void ThreatEntryMetadata_MetadataEntry::set_key(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.key_.SetBytes(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry.key)
+}
+inline std::string* ThreatEntryMetadata_MetadataEntry::mutable_key() {
+ std::string* _s = _internal_mutable_key();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry.key)
+ return _s;
+}
+inline const std::string& ThreatEntryMetadata_MetadataEntry::_internal_key() const {
+ return _impl_.key_.Get();
+}
+inline void ThreatEntryMetadata_MetadataEntry::_internal_set_key(const std::string& value) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.key_.Set(value, GetArenaForAllocation());
+}
+inline std::string* ThreatEntryMetadata_MetadataEntry::_internal_mutable_key() {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ return _impl_.key_.Mutable(GetArenaForAllocation());
+}
+inline std::string* ThreatEntryMetadata_MetadataEntry::release_key() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry.key)
+ if (!_internal_has_key()) {
+ return nullptr;
+ }
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ auto* p = _impl_.key_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.key_.IsDefault()) {
+ _impl_.key_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ return p;
+}
+inline void ThreatEntryMetadata_MetadataEntry::set_allocated_key(std::string* key) {
+ if (key != nullptr) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000001u;
+ }
+ _impl_.key_.SetAllocated(key, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.key_.IsDefault()) {
+ _impl_.key_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry.key)
+}
+
+// optional bytes value = 2;
+inline bool ThreatEntryMetadata_MetadataEntry::_internal_has_value() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ return value;
+}
+inline bool ThreatEntryMetadata_MetadataEntry::has_value() const {
+ return _internal_has_value();
+}
+inline void ThreatEntryMetadata_MetadataEntry::clear_value() {
+ _impl_.value_.ClearToEmpty();
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline const std::string& ThreatEntryMetadata_MetadataEntry::value() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry.value)
+ return _internal_value();
+}
+template <typename ArgT0, typename... ArgT>
+inline PROTOBUF_ALWAYS_INLINE
+void ThreatEntryMetadata_MetadataEntry::set_value(ArgT0&& arg0, ArgT... args) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.value_.SetBytes(static_cast<ArgT0 &&>(arg0), args..., GetArenaForAllocation());
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry.value)
+}
+inline std::string* ThreatEntryMetadata_MetadataEntry::mutable_value() {
+ std::string* _s = _internal_mutable_value();
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry.value)
+ return _s;
+}
+inline const std::string& ThreatEntryMetadata_MetadataEntry::_internal_value() const {
+ return _impl_.value_.Get();
+}
+inline void ThreatEntryMetadata_MetadataEntry::_internal_set_value(const std::string& value) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.value_.Set(value, GetArenaForAllocation());
+}
+inline std::string* ThreatEntryMetadata_MetadataEntry::_internal_mutable_value() {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ return _impl_.value_.Mutable(GetArenaForAllocation());
+}
+inline std::string* ThreatEntryMetadata_MetadataEntry::release_value() {
+ // @@protoc_insertion_point(field_release:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry.value)
+ if (!_internal_has_value()) {
+ return nullptr;
+ }
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ auto* p = _impl_.value_.Release();
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.value_.IsDefault()) {
+ _impl_.value_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ return p;
+}
+inline void ThreatEntryMetadata_MetadataEntry::set_allocated_value(std::string* value) {
+ if (value != nullptr) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ } else {
+ _impl_._has_bits_[0] &= ~0x00000002u;
+ }
+ _impl_.value_.SetAllocated(value, GetArenaForAllocation());
+#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ if (_impl_.value_.IsDefault()) {
+ _impl_.value_.Set("", GetArenaForAllocation());
+ }
+#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING
+ // @@protoc_insertion_point(field_set_allocated:mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry.value)
+}
+
+// -------------------------------------------------------------------
+
+// ThreatEntryMetadata
+
+// repeated .mozilla.safebrowsing.ThreatEntryMetadata.MetadataEntry entries = 1;
+inline int ThreatEntryMetadata::_internal_entries_size() const {
+ return _impl_.entries_.size();
+}
+inline int ThreatEntryMetadata::entries_size() const {
+ return _internal_entries_size();
+}
+inline void ThreatEntryMetadata::clear_entries() {
+ _impl_.entries_.Clear();
+}
+inline ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry* ThreatEntryMetadata::mutable_entries(int index) {
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ThreatEntryMetadata.entries)
+ return _impl_.entries_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry >*
+ThreatEntryMetadata::mutable_entries() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.safebrowsing.ThreatEntryMetadata.entries)
+ return &_impl_.entries_;
+}
+inline const ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry& ThreatEntryMetadata::_internal_entries(int index) const {
+ return _impl_.entries_.Get(index);
+}
+inline const ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry& ThreatEntryMetadata::entries(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatEntryMetadata.entries)
+ return _internal_entries(index);
+}
+inline ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry* ThreatEntryMetadata::_internal_add_entries() {
+ return _impl_.entries_.Add();
+}
+inline ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry* ThreatEntryMetadata::add_entries() {
+ ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry* _add = _internal_add_entries();
+ // @@protoc_insertion_point(field_add:mozilla.safebrowsing.ThreatEntryMetadata.entries)
+ return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatEntryMetadata_MetadataEntry >&
+ThreatEntryMetadata::entries() const {
+ // @@protoc_insertion_point(field_list:mozilla.safebrowsing.ThreatEntryMetadata.entries)
+ return _impl_.entries_;
+}
+
+// -------------------------------------------------------------------
+
+// ThreatListDescriptor
+
+// optional .mozilla.safebrowsing.ThreatType threat_type = 1;
+inline bool ThreatListDescriptor::_internal_has_threat_type() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool ThreatListDescriptor::has_threat_type() const {
+ return _internal_has_threat_type();
+}
+inline void ThreatListDescriptor::clear_threat_type() {
+ _impl_.threat_type_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline ::mozilla::safebrowsing::ThreatType ThreatListDescriptor::_internal_threat_type() const {
+ return static_cast< ::mozilla::safebrowsing::ThreatType >(_impl_.threat_type_);
+}
+inline ::mozilla::safebrowsing::ThreatType ThreatListDescriptor::threat_type() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatListDescriptor.threat_type)
+ return _internal_threat_type();
+}
+inline void ThreatListDescriptor::_internal_set_threat_type(::mozilla::safebrowsing::ThreatType value) {
+ assert(::mozilla::safebrowsing::ThreatType_IsValid(value));
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.threat_type_ = value;
+}
+inline void ThreatListDescriptor::set_threat_type(::mozilla::safebrowsing::ThreatType value) {
+ _internal_set_threat_type(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatListDescriptor.threat_type)
+}
+
+// optional .mozilla.safebrowsing.PlatformType platform_type = 2;
+inline bool ThreatListDescriptor::_internal_has_platform_type() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ return value;
+}
+inline bool ThreatListDescriptor::has_platform_type() const {
+ return _internal_has_platform_type();
+}
+inline void ThreatListDescriptor::clear_platform_type() {
+ _impl_.platform_type_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline ::mozilla::safebrowsing::PlatformType ThreatListDescriptor::_internal_platform_type() const {
+ return static_cast< ::mozilla::safebrowsing::PlatformType >(_impl_.platform_type_);
+}
+inline ::mozilla::safebrowsing::PlatformType ThreatListDescriptor::platform_type() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatListDescriptor.platform_type)
+ return _internal_platform_type();
+}
+inline void ThreatListDescriptor::_internal_set_platform_type(::mozilla::safebrowsing::PlatformType value) {
+ assert(::mozilla::safebrowsing::PlatformType_IsValid(value));
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.platform_type_ = value;
+}
+inline void ThreatListDescriptor::set_platform_type(::mozilla::safebrowsing::PlatformType value) {
+ _internal_set_platform_type(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatListDescriptor.platform_type)
+}
+
+// optional .mozilla.safebrowsing.ThreatEntryType threat_entry_type = 3;
+inline bool ThreatListDescriptor::_internal_has_threat_entry_type() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000004u) != 0;
+ return value;
+}
+inline bool ThreatListDescriptor::has_threat_entry_type() const {
+ return _internal_has_threat_entry_type();
+}
+inline void ThreatListDescriptor::clear_threat_entry_type() {
+ _impl_.threat_entry_type_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000004u;
+}
+inline ::mozilla::safebrowsing::ThreatEntryType ThreatListDescriptor::_internal_threat_entry_type() const {
+ return static_cast< ::mozilla::safebrowsing::ThreatEntryType >(_impl_.threat_entry_type_);
+}
+inline ::mozilla::safebrowsing::ThreatEntryType ThreatListDescriptor::threat_entry_type() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ThreatListDescriptor.threat_entry_type)
+ return _internal_threat_entry_type();
+}
+inline void ThreatListDescriptor::_internal_set_threat_entry_type(::mozilla::safebrowsing::ThreatEntryType value) {
+ assert(::mozilla::safebrowsing::ThreatEntryType_IsValid(value));
+ _impl_._has_bits_[0] |= 0x00000004u;
+ _impl_.threat_entry_type_ = value;
+}
+inline void ThreatListDescriptor::set_threat_entry_type(::mozilla::safebrowsing::ThreatEntryType value) {
+ _internal_set_threat_entry_type(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.ThreatListDescriptor.threat_entry_type)
+}
+
+// -------------------------------------------------------------------
+
+// ListThreatListsResponse
+
+// repeated .mozilla.safebrowsing.ThreatListDescriptor threat_lists = 1;
+inline int ListThreatListsResponse::_internal_threat_lists_size() const {
+ return _impl_.threat_lists_.size();
+}
+inline int ListThreatListsResponse::threat_lists_size() const {
+ return _internal_threat_lists_size();
+}
+inline void ListThreatListsResponse::clear_threat_lists() {
+ _impl_.threat_lists_.Clear();
+}
+inline ::mozilla::safebrowsing::ThreatListDescriptor* ListThreatListsResponse::mutable_threat_lists(int index) {
+ // @@protoc_insertion_point(field_mutable:mozilla.safebrowsing.ListThreatListsResponse.threat_lists)
+ return _impl_.threat_lists_.Mutable(index);
+}
+inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatListDescriptor >*
+ListThreatListsResponse::mutable_threat_lists() {
+ // @@protoc_insertion_point(field_mutable_list:mozilla.safebrowsing.ListThreatListsResponse.threat_lists)
+ return &_impl_.threat_lists_;
+}
+inline const ::mozilla::safebrowsing::ThreatListDescriptor& ListThreatListsResponse::_internal_threat_lists(int index) const {
+ return _impl_.threat_lists_.Get(index);
+}
+inline const ::mozilla::safebrowsing::ThreatListDescriptor& ListThreatListsResponse::threat_lists(int index) const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.ListThreatListsResponse.threat_lists)
+ return _internal_threat_lists(index);
+}
+inline ::mozilla::safebrowsing::ThreatListDescriptor* ListThreatListsResponse::_internal_add_threat_lists() {
+ return _impl_.threat_lists_.Add();
+}
+inline ::mozilla::safebrowsing::ThreatListDescriptor* ListThreatListsResponse::add_threat_lists() {
+ ::mozilla::safebrowsing::ThreatListDescriptor* _add = _internal_add_threat_lists();
+ // @@protoc_insertion_point(field_add:mozilla.safebrowsing.ListThreatListsResponse.threat_lists)
+ return _add;
+}
+inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::mozilla::safebrowsing::ThreatListDescriptor >&
+ListThreatListsResponse::threat_lists() const {
+ // @@protoc_insertion_point(field_list:mozilla.safebrowsing.ListThreatListsResponse.threat_lists)
+ return _impl_.threat_lists_;
+}
+
+// -------------------------------------------------------------------
+
+// Duration
+
+// optional int64 seconds = 1;
+inline bool Duration::_internal_has_seconds() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000001u) != 0;
+ return value;
+}
+inline bool Duration::has_seconds() const {
+ return _internal_has_seconds();
+}
+inline void Duration::clear_seconds() {
+ _impl_.seconds_ = int64_t{0};
+ _impl_._has_bits_[0] &= ~0x00000001u;
+}
+inline int64_t Duration::_internal_seconds() const {
+ return _impl_.seconds_;
+}
+inline int64_t Duration::seconds() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.Duration.seconds)
+ return _internal_seconds();
+}
+inline void Duration::_internal_set_seconds(int64_t value) {
+ _impl_._has_bits_[0] |= 0x00000001u;
+ _impl_.seconds_ = value;
+}
+inline void Duration::set_seconds(int64_t value) {
+ _internal_set_seconds(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.Duration.seconds)
+}
+
+// optional int32 nanos = 2;
+inline bool Duration::_internal_has_nanos() const {
+ bool value = (_impl_._has_bits_[0] & 0x00000002u) != 0;
+ return value;
+}
+inline bool Duration::has_nanos() const {
+ return _internal_has_nanos();
+}
+inline void Duration::clear_nanos() {
+ _impl_.nanos_ = 0;
+ _impl_._has_bits_[0] &= ~0x00000002u;
+}
+inline int32_t Duration::_internal_nanos() const {
+ return _impl_.nanos_;
+}
+inline int32_t Duration::nanos() const {
+ // @@protoc_insertion_point(field_get:mozilla.safebrowsing.Duration.nanos)
+ return _internal_nanos();
+}
+inline void Duration::_internal_set_nanos(int32_t value) {
+ _impl_._has_bits_[0] |= 0x00000002u;
+ _impl_.nanos_ = value;
+}
+inline void Duration::set_nanos(int32_t value) {
+ _internal_set_nanos(value);
+ // @@protoc_insertion_point(field_set:mozilla.safebrowsing.Duration.nanos)
+}
+
+#ifdef __GNUC__
+ #pragma GCC diagnostic pop
+#endif // __GNUC__
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+// -------------------------------------------------------------------
+
+
+// @@protoc_insertion_point(namespace_scope)
+
+} // namespace safebrowsing
+} // namespace mozilla
+
+PROTOBUF_NAMESPACE_OPEN
+
+template <> struct is_proto_enum< ::mozilla::safebrowsing::FetchThreatListUpdatesResponse_ListUpdateResponse_ResponseType> : ::std::true_type {};
+template <> struct is_proto_enum< ::mozilla::safebrowsing::ThreatHit_ThreatSourceType> : ::std::true_type {};
+template <> struct is_proto_enum< ::mozilla::safebrowsing::ChromeClientInfo_SafeBrowsingReportingPopulation> : ::std::true_type {};
+template <> struct is_proto_enum< ::mozilla::safebrowsing::ThreatType> : ::std::true_type {};
+template <> struct is_proto_enum< ::mozilla::safebrowsing::PlatformType> : ::std::true_type {};
+template <> struct is_proto_enum< ::mozilla::safebrowsing::CompressionType> : ::std::true_type {};
+template <> struct is_proto_enum< ::mozilla::safebrowsing::ThreatEntryType> : ::std::true_type {};
+
+PROTOBUF_NAMESPACE_CLOSE
+
+// @@protoc_insertion_point(global_scope)
+
+#include <google/protobuf/port_undef.inc>
+#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_safebrowsing_2eproto
diff --git a/toolkit/components/url-classifier/chromium/safebrowsing.proto b/toolkit/components/url-classifier/chromium/safebrowsing.proto
new file mode 100644
index 0000000000..207429bbd6
--- /dev/null
+++ b/toolkit/components/url-classifier/chromium/safebrowsing.proto
@@ -0,0 +1,540 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// This file includes Safe Browsing V4 API blacklist request and response
+// protocol buffers. They should be kept in sync with the server implementation.
+
+syntax = "proto2";
+
+option optimize_for = LITE_RUNTIME;
+
+package mozilla.safebrowsing;
+
+message ThreatInfo {
+ // The threat types to be checked.
+ repeated ThreatType threat_types = 1;
+
+ // The platform types to be checked.
+ repeated PlatformType platform_types = 2;
+
+ // The entry types to be checked.
+ repeated ThreatEntryType threat_entry_types = 4;
+
+ // The threat entries to be checked.
+ repeated ThreatEntry threat_entries = 3;
+}
+
+// A match when checking a threat entry in the Safe Browsing threat lists.
+message ThreatMatch {
+ // The threat type matching this threat.
+ optional ThreatType threat_type = 1;
+
+ // The platform type matching this threat.
+ optional PlatformType platform_type = 2;
+
+ // The threat entry type matching this threat.
+ optional ThreatEntryType threat_entry_type = 6;
+
+ // The threat matching this threat.
+ optional ThreatEntry threat = 3;
+
+ // Optional metadata associated with this threat.
+ optional ThreatEntryMetadata threat_entry_metadata = 4;
+
+ // The cache lifetime for the returned match. Clients must not cache this
+ // response for more than this duration to avoid false positives.
+ optional Duration cache_duration = 5;
+}
+
+// Request to check entries against lists.
+message FindThreatMatchesRequest {
+ // The client metadata.
+ optional ClientInfo client = 1;
+
+ // The lists and entries to be checked for matches.
+ optional ThreatInfo threat_info = 2;
+}
+
+// Response type for requests to find threat matches.
+message FindThreatMatchesResponse {
+ // The threat list matches.
+ repeated ThreatMatch matches = 1;
+}
+
+// Describes a Safe Browsing API update request. Clients can request updates for
+// multiple lists in a single request.
+message FetchThreatListUpdatesRequest {
+ // The client metadata.
+ optional ClientInfo client = 1;
+
+ // A single list update request.
+ message ListUpdateRequest {
+ // The type of threat posed by entries present in the list.
+ optional ThreatType threat_type = 1;
+
+ // The type of platform at risk by entries present in the list.
+ optional PlatformType platform_type = 2;
+
+ // The types of entries present in the list.
+ optional ThreatEntryType threat_entry_type = 5;
+
+ // The current state of the client for the requested list (the encrypted
+ // ClientState that was sent to the client from the previous update
+ // request).
+ optional bytes state = 3;
+
+ // The constraints for this update.
+ message Constraints {
+ // The maximum size in number of entries. The update will not contain more
+ // entries than this value. This should be a power of 2 between 2**10 and
+ // 2**20. If zero, no update size limit is set.
+ optional int32 max_update_entries = 1;
+
+ // Sets the maxmimum number of entries that the client is willing to have
+ // in the local database. This should be a power of 2 between 2**10 and
+ // 2**20. If zero, no database size limit is set.
+ optional int32 max_database_entries = 2;
+
+ // Requests the list for a specific geographic location. If not set the
+ // server may pick that value based on the user's IP address. Expects ISO
+ // 3166-1 alpha-2 format.
+ optional string region = 3;
+
+ // The compression types supported by the client.
+ repeated CompressionType supported_compressions = 4;
+ }
+
+ // The constraints associated with this request.
+ optional Constraints constraints = 4;
+ }
+
+ // The requested threat list updates.
+ repeated ListUpdateRequest list_update_requests = 3;
+
+ // Chrome-specific client information.
+ optional ChromeClientInfo chrome_client_info = 4;
+}
+
+// Response type for threat list update requests.
+message FetchThreatListUpdatesResponse {
+ // An update to an individual list.
+ message ListUpdateResponse {
+ // The threat type for which data is returned.
+ optional ThreatType threat_type = 1;
+
+ // The format of the threats.
+ optional ThreatEntryType threat_entry_type = 2;
+
+ // The platform type for which data is returned.
+ optional PlatformType platform_type = 3;
+
+ // The type of response sent to the client.
+ enum ResponseType {
+ // Unknown.
+ RESPONSE_TYPE_UNSPECIFIED = 0;
+
+ // Partial updates are applied to the client's existing local database.
+ PARTIAL_UPDATE = 1;
+
+ // Full updates replace the client's entire local database. This means
+ // that either the client was seriously out-of-date or the client is
+ // believed to be corrupt.
+ FULL_UPDATE = 2;
+ }
+
+ // The type of response. This may indicate that an action is required by the
+ // client when the response is received.
+ optional ResponseType response_type = 4;
+
+ // A set of entries to add to a local threat type's list. Repeated to allow
+ // for a combination of compressed and raw data to be sent in a single
+ // response.
+ repeated ThreatEntrySet additions = 5;
+
+ // A set of entries to remove from a local threat type's list. In practice,
+ // this field is empty or contains exactly one ThreatEntrySet.
+ repeated ThreatEntrySet removals = 6;
+
+ // The new client state, in encrypted format. Opaque to clients.
+ optional bytes new_client_state = 7;
+
+ // The expected SHA256 hash of the client state; that is, of the sorted list
+ // of all hashes present in the database after applying the provided update.
+ // If the client state doesn't match the expected state, the client must
+ // disregard this update and retry later.
+ optional Checksum checksum = 8;
+ }
+
+ // The list updates requested by the clients.
+ repeated ListUpdateResponse list_update_responses = 1;
+
+ // The minimum duration the client must wait before issuing any update
+ // request. If this field is not set clients may update as soon as they want.
+ optional Duration minimum_wait_duration = 2;
+}
+
+// Request to return full hashes matched by the provided hash prefixes.
+message FindFullHashesRequest {
+ // The client metadata.
+ optional ClientInfo client = 1;
+
+ // The current client states for each of the client's local threat lists.
+ repeated bytes client_states = 2;
+
+ // The lists and hashes to be checked.
+ optional ThreatInfo threat_info = 3;
+}
+
+// Response type for requests to find full hashes.
+message FindFullHashesResponse {
+ // The full hashes that matched the requested prefixes.
+ repeated ThreatMatch matches = 1;
+
+ // The minimum duration the client must wait before issuing any find hashes
+ // request. If this field is not set, clients can issue a request as soon as
+ // they want.
+ optional Duration minimum_wait_duration = 2;
+
+ // For requested entities that did not match the threat list, how long to
+ // cache the response.
+ optional Duration negative_cache_duration = 3;
+}
+
+// A hit comprised of multiple resources; one is the threat list entry that was
+// encountered by the client, while others give context as to how the client
+// arrived at the unsafe entry.
+message ThreatHit {
+ // The threat type reported.
+ optional ThreatType threat_type = 1;
+
+ // The platform type reported.
+ optional PlatformType platform_type = 2;
+
+ // The threat entry responsible for the hit. Full hash should be reported for
+ // hash-based hits.
+ optional ThreatEntry entry = 3;
+
+ // Types of resources reported by the client as part of a single hit.
+ enum ThreatSourceType {
+ // Unknown.
+ THREAT_SOURCE_TYPE_UNSPECIFIED = 0;
+ // The URL that matched the threat list (for which GetFullHash returned a
+ // valid hash).
+ MATCHING_URL = 1;
+ // The final top-level URL of the tab that the client was browsing when the
+ // match occurred.
+ TAB_URL = 2;
+ // A redirect URL that was fetched before hitting the final TAB_URL.
+ TAB_REDIRECT = 3;
+ // A resource loaded within the final TAB_URL.
+ TAB_RESOURCE = 4;
+ }
+
+ // A single resource related to a threat hit.
+ message ThreatSource {
+ // The URL of the resource.
+ optional string url = 1;
+
+ // The type of source reported.
+ optional ThreatSourceType type = 2;
+
+ // The remote IP of the resource in ASCII format. Either IPv4 or IPv6.
+ optional string remote_ip = 3;
+
+ // Referrer of the resource. Only set if the referrer is available.
+ optional string referrer = 4;
+ }
+
+ // The resources related to the threat hit.
+ repeated ThreatSource resources = 4;
+
+ // Client-reported identification.
+ optional ClientInfo client_info = 5;
+
+ // Details about the user that encountered the threat.
+ message UserInfo {
+ // The UN M.49 region code associated with the user's location.
+ optional string region_code = 1;
+
+ // Unique ID stable over a week or two
+ optional bytes user_id = 2;
+ }
+
+ // Details about the user that encountered the threat.
+ optional UserInfo user_info = 6;
+}
+
+// Types of threats.
+enum ThreatType {
+ // Unknown.
+ THREAT_TYPE_UNSPECIFIED = 0;
+
+ // Malware threat type.
+ MALWARE_THREAT = 1;
+
+ // Social engineering threat type.
+ SOCIAL_ENGINEERING_PUBLIC = 2;
+
+ // Unwanted software threat type.
+ UNWANTED_SOFTWARE = 3;
+
+ // Potentially harmful application threat type.
+ POTENTIALLY_HARMFUL_APPLICATION = 4;
+
+ // Social engineering threat type for internal use.
+ SOCIAL_ENGINEERING = 5;
+
+ // API abuse threat type.
+ API_ABUSE = 6;
+
+ // Malicious binary threat type.
+ MALICIOUS_BINARY = 7;
+
+ // Client side detection whitelist threat type.
+ CSD_WHITELIST = 8;
+
+ // Client side download detection whitelist threat type.
+ CSD_DOWNLOAD_WHITELIST = 9;
+
+ // Client incident threat type.
+ CLIENT_INCIDENT = 10;
+
+ // Patterns to be used for activating the subresource filter. Interstitial
+ // will not be shown for patterns from this list.
+ SUBRESOURCE_FILTER = 13;
+}
+
+// Types of platforms.
+enum PlatformType {
+ // Unknown platform.
+ PLATFORM_TYPE_UNSPECIFIED = 0;
+
+ // Threat posed to Windows.
+ WINDOWS_PLATFORM = 1;
+
+ // Threat posed to Linux.
+ LINUX_PLATFORM = 2;
+
+ // Threat posed to Android.
+ // This cannot be ANDROID because that symbol is defined for android builds
+ // here: build/config/android/BUILD.gn line21.
+ ANDROID_PLATFORM = 3;
+
+ // Threat posed to OSX.
+ OSX_PLATFORM = 4;
+
+ // Threat posed to iOS.
+ IOS_PLATFORM = 5;
+
+ // Threat posed to at least one of the defined platforms.
+ ANY_PLATFORM = 6;
+
+ // Threat posed to all defined platforms.
+ ALL_PLATFORMS = 7;
+
+ // Threat posed to Chrome.
+ CHROME_PLATFORM = 8;
+}
+
+// The client metadata associated with Safe Browsing API requests.
+message ClientInfo {
+ // A client ID that (hopefully) uniquely identifies the client implementation
+ // of the Safe Browsing API.
+ optional string client_id = 1;
+
+ // The version of the client implementation.
+ optional string client_version = 2;
+}
+
+// The client metadata associated with Safe Browsing API requests specific to
+// users of Chrome.
+message ChromeClientInfo {
+ // Safe Browsing reporting populations in Chrome.
+ enum SafeBrowsingReportingPopulation {
+ // Unspecified reporting verbosity.
+ UNSPECIFIED = 0;
+
+ // Client is opted out of reporting.
+ OPT_OUT = 1;
+
+ // Legacy extended reporting population.
+ EXTENDED = 2;
+
+ // Scout reporting population.
+ SCOUT = 3;
+ }
+
+ // The reporting population of the user.
+ optional SafeBrowsingReportingPopulation reporting_population = 1;
+}
+
+// The expected state of a client's local database.
+message Checksum {
+ // The SHA256 hash of the client state; that is, of the sorted list of all
+ // hashes present in the database.
+ optional bytes sha256 = 1;
+}
+
+// The ways in which threat entry sets can be compressed.
+enum CompressionType {
+ // Unknown.
+ COMPRESSION_TYPE_UNSPECIFIED = 0;
+
+ // Raw, uncompressed data.
+ RAW = 1;
+
+ // Rice-Golomb encoded data.
+ RICE = 2;
+}
+
+// An individual threat; for example, a malicious URL or its hash
+// representation. Only one of these fields should be set.
+message ThreatEntry {
+ // A variable-length SHA256 hash with size between 4 and 32 bytes inclusive.
+ optional bytes hash = 1;
+
+ // A URL.
+ optional string url = 2;
+}
+
+// Types of entries that pose threats. Threat lists are collections of entries
+// of a single type.
+enum ThreatEntryType {
+ // Unspecified.
+ THREAT_ENTRY_TYPE_UNSPECIFIED = 0;
+
+ // A host-suffix/path-prefix URL expression; for example, "foo.bar.com/baz/".
+ URL = 1;
+
+ // An executable program.
+ EXECUTABLE = 2;
+
+ // An IP range.
+ IP_RANGE = 3;
+
+ // Chrome extension.
+ CHROME_EXTENSION = 4;
+
+ // Filename.
+ FILENAME = 5;
+
+ // CERT.
+ CERT = 6;
+}
+
+// A set of threats that should be added or removed from a client's local
+// database.
+message ThreatEntrySet {
+ // The compression type for the entries in this set.
+ optional CompressionType compression_type = 1;
+
+ // At most one of the following fields should be set.
+
+ // The raw SHA256-formatted entries.
+ optional RawHashes raw_hashes = 2;
+
+ // The raw removal indices for a local list.
+ optional RawIndices raw_indices = 3;
+
+ // The encoded 4-byte prefixes of SHA256-formatted entries, using a
+ // Golomb-Rice encoding.
+ optional RiceDeltaEncoding rice_hashes = 4;
+
+ // The encoded local, lexicographically-sorted list indices, using a
+ // Golomb-Rice encoding. Used for sending compressed removal indicies.
+ optional RiceDeltaEncoding rice_indices = 5;
+}
+
+// A set of raw indicies to remove from a local list.
+message RawIndices {
+ // The indicies to remove from a lexicographically-sorted local list.
+ repeated int32 indices = 1;
+}
+
+// The uncompressed threat entries in hash format of a particular prefix length.
+// Hashes can be anywhere from 4 to 32 bytes in size. A large majority are 4
+// bytes, but some hashes are lengthened if they collide with the hash of a
+// popular URL.
+//
+// Used for sending ThreatEntrySet to clients that do not support compression,
+// or when sending non-4-byte hashes to clients that do support compression.
+message RawHashes {
+ // The number of bytes for each prefix encoded below. This field can be
+ // anywhere from 4 (shortest prefix) to 32 (full SHA256 hash).
+ optional int32 prefix_size = 1;
+
+ // The hashes, all concatenated into one long string. Each hash has a prefix
+ // size of |prefix_size| above. Hashes are sorted in lexicographic order.
+ optional bytes raw_hashes = 2;
+}
+
+// The Rice-Golomb encoded data. Used for sending compressed 4-byte hashes or
+// compressed removal indices.
+message RiceDeltaEncoding {
+ // The offset of the first entry in the encoded data, or, if only a single
+ // integer was encoded, that single integer's value.
+ optional int64 first_value = 1;
+
+ // The Golomb-Rice parameter which is a number between 2 and 28. This field
+ // is missing (that is, zero) if num_entries is zero.
+ optional int32 rice_parameter = 2;
+
+ // The number of entries that are delta encoded in the encoded data. If only a
+ // single integer was encoded, this will be zero and the single value will be
+ // stored in first_value.
+ optional int32 num_entries = 3;
+
+ // The encoded deltas that are encoded using the Golomb-Rice coder.
+ optional bytes encoded_data = 4;
+}
+
+// The metadata associated with a specific threat entry. The client is expected
+// to know the metadata key/value pairs associated with each threat type.
+message ThreatEntryMetadata {
+ // A single metadata entry.
+ message MetadataEntry {
+ // The metadata entry key.
+ optional bytes key = 1;
+
+ // The metadata entry value.
+ optional bytes value = 2;
+ }
+
+ // The metadata entries.
+ repeated MetadataEntry entries = 1;
+}
+
+// Describes an individual threat list. A list is defined by three parameters:
+// the type of threat posed, the type of platform targeted by the threat, and
+// the type of entries in the list.
+message ThreatListDescriptor {
+ // The threat type posed by the list's entries.
+ optional ThreatType threat_type = 1;
+
+ // The platform type targeted by the list's entries.
+ optional PlatformType platform_type = 2;
+
+ // The entry types contained in the list.
+ optional ThreatEntryType threat_entry_type = 3;
+}
+
+// A collection of lists available for download.
+message ListThreatListsResponse {
+ // The lists available for download.
+ repeated ThreatListDescriptor threat_lists = 1;
+}
+
+message Duration {
+ // Signed seconds of the span of time. Must be from -315,576,000,000
+ // to +315,576,000,000 inclusive.
+ optional int64 seconds = 1;
+
+ // Signed fractions of a second at nanosecond resolution of the span
+ // of time. Durations less than one second are represented with a 0
+ // `seconds` field and a positive or negative `nanos` field. For durations
+ // of one second or more, a non-zero value for the `nanos` field must be
+ // of the same sign as the `seconds` field. Must be from -999,999,999
+ // to +999,999,999 inclusive.
+ optional int32 nanos = 2;
+}
diff --git a/toolkit/components/url-classifier/components.conf b/toolkit/components/url-classifier/components.conf
new file mode 100644
index 0000000000..1be90b11e7
--- /dev/null
+++ b/toolkit/components/url-classifier/components.conf
@@ -0,0 +1,32 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+ {
+ 'cid': '{26a4a019-2827-4a89-a85c-5931a678823a}',
+ 'contract_ids': ['@mozilla.org/url-classifier/jslib;1'],
+ 'esModule': 'resource://gre/modules/UrlClassifierLib.sys.mjs',
+ 'constructor': 'UrlClassifierLib',
+ },
+ {
+ 'cid': '{ca168834-cc00-48f9-b83c-fd018e58cae3}',
+ 'contract_ids': ['@mozilla.org/url-classifier/listmanager;1'],
+ 'esModule': 'resource://gre/modules/UrlClassifierListManager.sys.mjs',
+ 'constructor': 'RegistrationData',
+ },
+ {
+ 'cid': '{9111de73-9322-4bfc-8b65-2b727f3e6ec8}',
+ 'contract_ids': ['@mozilla.org/url-classifier/hashcompleter;1'],
+ 'esModule': 'resource://gre/modules/UrlClassifierHashCompleter.sys.mjs',
+ 'constructor': 'HashCompleter',
+ },
+ {
+ 'cid': '{1980624c-c50b-4b46-a91c-dfaba7792706}',
+ 'contract_ids': ['@mozilla.org/url-classifier/list-service;1'],
+ 'esModule': 'resource://gre/modules/UrlClassifierRemoteSettingsService.sys.mjs',
+ 'constructor': 'UrlClassifierRemoteSettingsService',
+ },
+]
diff --git a/toolkit/components/url-classifier/docs/flash-block-lists.rst b/toolkit/components/url-classifier/docs/flash-block-lists.rst
new file mode 100644
index 0000000000..f24fc0d2c0
--- /dev/null
+++ b/toolkit/components/url-classifier/docs/flash-block-lists.rst
@@ -0,0 +1,38 @@
+=========================
+List Based Flash Blocking
+=========================
+
+List based Flash blocking currently uses six lists.
+The lists specify what domains/subdomains Flash is allowed to or denied from loading on.
+The domains specified by the lists indicate the domain of the document that the Flash is loaded in, not the domain hosting the Flash content itself.
+
+* Allow List
+* Allow Exceptions List
+* Deny List
+* Deny Exceptions List
+* Sub-Document Deny List
+* Sub-Document Deny Exceptions List
+
+If a page is on a list and the corresponding "Exceptions List", it is treated as though it is not on that list.
+
+Classification
+==============
+
+Documents can be classified as Allow, Deny or Unknown.
+Documents with an Allow classification may load Flash normally.
+Documents with a Deny classification may not load Flash at all.
+A Deny classification overrides an Allow classification.
+The Unknown classification is the fall-through classification; it essentially just means that the document did not receive an Allow or Deny classification.
+Documents with an Unknown classification will have Flash set to Click To Activate.
+
+If the document is at the top level (its address is in the URL bar), then the Deny List is checked first followed by the Allow List to determine its classification.
+
+If the document is not at the top level, it will receive a Deny classification if the classification of the parent document is Deny or if the document is on the Deny List.
+It will also receive a Deny classification if the sub-document is not same-origin and the document is on the Sub-Document Deny List.
+If the document did not receive a Deny classification, it can receive an Allow classification if it is on the Allow List or if the parent document received an Allow classification.
+
+If for any reason, the document has a null principal, it will receive a Deny classification.
+Some examples of documents that would have a null principal are:
+
+* Data URIs <https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs> loaded directly from the URL bar. Data URIs loaded by a page should inherit the loading page's permissions.
+* URIs that are rendered with the JSON viewer
diff --git a/toolkit/components/url-classifier/docs/index.rst b/toolkit/components/url-classifier/docs/index.rst
new file mode 100644
index 0000000000..01f0f58ba3
--- /dev/null
+++ b/toolkit/components/url-classifier/docs/index.rst
@@ -0,0 +1,8 @@
+==============
+URL Classifier
+==============
+
+.. toctree::
+ :maxdepth: 1
+
+ flash-block-lists
diff --git a/toolkit/components/url-classifier/moz.build b/toolkit/components/url-classifier/moz.build
new file mode 100644
index 0000000000..3b1431dc2c
--- /dev/null
+++ b/toolkit/components/url-classifier/moz.build
@@ -0,0 +1,101 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Toolkit", "Safe Browsing")
+
+TEST_DIRS += ["tests"]
+
+XPIDL_SOURCES += [
+ "IUrlClassifierUITelemetry.idl",
+ "nsIUrlClassifierDBService.idl",
+ "nsIUrlClassifierHashCompleter.idl",
+ "nsIUrlClassifierInfo.idl",
+ "nsIUrlClassifierPrefixSet.idl",
+ "nsIUrlClassifierRemoteSettingsService.idl",
+ "nsIUrlClassifierStreamUpdater.idl",
+ "nsIUrlClassifierUtils.idl",
+ "nsIUrlListManager.idl",
+]
+
+XPIDL_MODULE = "url-classifier"
+
+DEFINES["GOOGLE_PROTOBUF_NO_RTTI"] = True
+DEFINES["GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER"] = True
+
+UNIFIED_SOURCES += [
+ "chromium/safebrowsing.pb.cc",
+ "ChunkSet.cpp",
+ "Classifier.cpp",
+ "LookupCache.cpp",
+ "LookupCacheV4.cpp",
+ "nsCheckSummedOutputStream.cpp",
+ "nsUrlClassifierDBService.cpp",
+ "nsUrlClassifierInfo.cpp",
+ "nsUrlClassifierProxies.cpp",
+ "nsUrlClassifierUtils.cpp",
+ "ProtocolParser.cpp",
+ "RiceDeltaDecoder.cpp",
+ "UrlClassifierTelemetryUtils.cpp",
+]
+
+# define conflicting LOG() macros
+SOURCES += [
+ "nsUrlClassifierPrefixSet.cpp",
+ "nsUrlClassifierStreamUpdater.cpp",
+ "VariableLengthPrefixSet.cpp",
+]
+
+# contains variables that conflict with LookupCache.cpp
+SOURCES += [
+ "HashStore.cpp",
+]
+
+EXTRA_JS_MODULES += [
+ "SafeBrowsing.sys.mjs",
+ "UrlClassifierHashCompleter.sys.mjs",
+ "UrlClassifierLib.sys.mjs",
+ "UrlClassifierListManager.sys.mjs",
+ "UrlClassifierRemoteSettingsService.sys.mjs",
+]
+
+XPCOM_MANIFESTS += [
+ "components.conf",
+]
+
+EXPORTS += [
+ "chromium/safebrowsing.pb.h",
+ "Entries.h",
+ "LookupCache.h",
+ "LookupCacheV4.h",
+ "nsUrlClassifierPrefixSet.h",
+ "VariableLengthPrefixSet.h",
+]
+
+FINAL_LIBRARY = "xul"
+
+LOCAL_INCLUDES += [
+ "../build",
+ "/xpcom/io",
+]
+
+CXXFLAGS += CONFIG["SQLITE_CFLAGS"]
+
+# Suppress warnings in third-party code.
+if CONFIG["CC_TYPE"] == "gcc":
+ CXXFLAGS += [
+ "-Wno-maybe-uninitialized",
+ ]
+
+if CONFIG["NIGHTLY_BUILD"] or CONFIG["MOZ_DEBUG"]:
+ DEFINES["MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES"] = True
+
+SPHINX_TREES["url-classifier"] = "docs"
+
+with Files("docs/**"):
+ SCHEDULES.exclusive = ["docs"]
+
+include("/ipc/chromium/chromium-config.mozbuild")
diff --git a/toolkit/components/url-classifier/nsCheckSummedOutputStream.cpp b/toolkit/components/url-classifier/nsCheckSummedOutputStream.cpp
new file mode 100644
index 0000000000..472dd71390
--- /dev/null
+++ b/toolkit/components/url-classifier/nsCheckSummedOutputStream.cpp
@@ -0,0 +1,87 @@
+//* -*- 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 "nsCRT.h"
+#include "nsComponentManagerUtils.h"
+#include "nsISupportsImpl.h"
+#include "nsCheckSummedOutputStream.h"
+#include "crc32c.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// nsCheckSummedOutputStream
+
+NS_IMPL_ISUPPORTS_INHERITED(nsCheckSummedOutputStream, nsBufferedOutputStream,
+ nsISafeOutputStream)
+
+NS_IMETHODIMP
+nsCheckSummedOutputStream::Init(nsIOutputStream* stream, uint32_t bufferSize) {
+ nsresult rv;
+ mHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mHash->Init(nsICryptoHash::MD5);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return nsBufferedOutputStream::Init(stream, bufferSize);
+}
+
+NS_IMETHODIMP
+nsCheckSummedOutputStream::Finish() {
+ nsresult rv = mHash->Finish(false, mCheckSum);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t written;
+ rv = nsBufferedOutputStream::Write(
+ reinterpret_cast<const char*>(mCheckSum.BeginReading()),
+ mCheckSum.Length(), &written);
+ NS_ASSERTION(written == mCheckSum.Length(), "Error writing stream checksum");
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return nsBufferedOutputStream::Finish();
+}
+
+NS_IMETHODIMP
+nsCheckSummedOutputStream::Write(const char* buf, uint32_t count,
+ uint32_t* result) {
+ nsresult rv = mHash->Update(reinterpret_cast<const uint8_t*>(buf), count);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return nsBufferedOutputStream::Write(buf, count, result);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsCrc32CheckSumedOutputStream
+NS_IMPL_ISUPPORTS_INHERITED(nsCrc32CheckSumedOutputStream,
+ nsBufferedOutputStream, nsISafeOutputStream)
+
+NS_IMETHODIMP
+nsCrc32CheckSumedOutputStream::Init(nsIOutputStream* stream,
+ uint32_t bufferSize) {
+ mCheckSum = ~0;
+
+ return nsBufferedOutputStream::Init(stream, bufferSize);
+}
+
+NS_IMETHODIMP
+nsCrc32CheckSumedOutputStream::Finish() {
+ uint32_t written;
+ nsresult rv = nsBufferedOutputStream::Write(
+ reinterpret_cast<const char*>(&mCheckSum), sizeof(mCheckSum), &written);
+ NS_ASSERTION(written == sizeof(mCheckSum), "Error writing stream checksum");
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return nsBufferedOutputStream::Finish();
+}
+
+NS_IMETHODIMP
+nsCrc32CheckSumedOutputStream::Write(const char* buf, uint32_t count,
+ uint32_t* result) {
+ mCheckSum =
+ ComputeCrc32c(mCheckSum, reinterpret_cast<const uint8_t*>(buf), count);
+
+ return nsBufferedOutputStream::Write(buf, count, result);
+}
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/toolkit/components/url-classifier/nsCheckSummedOutputStream.h b/toolkit/components/url-classifier/nsCheckSummedOutputStream.h
new file mode 100644
index 0000000000..0aa1324024
--- /dev/null
+++ b/toolkit/components/url-classifier/nsCheckSummedOutputStream.h
@@ -0,0 +1,88 @@
+//* -*- 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/. */
+
+#ifndef nsCheckSummedOutputStream_h__
+#define nsCheckSummedOutputStream_h__
+
+#include "nsIFile.h"
+#include "nsIOutputStream.h"
+#include "nsICryptoHash.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsString.h"
+#include "../../../netwerk/base/nsBufferedStreams.h"
+#include "prio.h"
+
+class nsCheckSummedOutputStream : public nsBufferedOutputStream {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // Size of MD5 hash in bytes
+ static const uint32_t CHECKSUM_SIZE = 16;
+ static const uint32_t MAX_BUFFER_SIZE = 64 * 1024;
+
+ nsCheckSummedOutputStream() = default;
+
+ NS_IMETHOD Finish() override;
+ NS_IMETHOD Write(const char* buf, uint32_t count, uint32_t* result) override;
+ NS_IMETHOD Init(nsIOutputStream* stream, uint32_t bufferSize) override;
+
+ protected:
+ virtual ~nsCheckSummedOutputStream() { nsBufferedOutputStream::Close(); }
+
+ nsCOMPtr<nsICryptoHash> mHash;
+ nsCString mCheckSum;
+};
+
+// returns a file output stream which can be QI'ed to nsIFileOutputStream.
+inline nsresult NS_NewCheckSummedOutputStream(nsIOutputStream** result,
+ nsIFile* file) {
+ nsCOMPtr<nsIOutputStream> localOutFile;
+ nsresult rv =
+ NS_NewSafeLocalFileOutputStream(getter_AddRefs(localOutFile), file,
+ PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIBufferedOutputStream> out = new nsCheckSummedOutputStream();
+ rv = out->Init(localOutFile, nsCheckSummedOutputStream::MAX_BUFFER_SIZE);
+ if (NS_SUCCEEDED(rv)) {
+ out.forget(result);
+ }
+ return rv;
+}
+
+class nsCrc32CheckSumedOutputStream : public nsBufferedOutputStream {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+
+ static const uint32_t CHECKSUM_SIZE = 4;
+
+ nsCrc32CheckSumedOutputStream() = default;
+
+ NS_IMETHOD Finish() override;
+ NS_IMETHOD Write(const char* buf, uint32_t count, uint32_t* result) override;
+ NS_IMETHOD Init(nsIOutputStream* stream, uint32_t bufferSize) override;
+
+ protected:
+ virtual ~nsCrc32CheckSumedOutputStream() { nsBufferedOutputStream::Close(); }
+
+ uint32_t mCheckSum;
+};
+
+inline nsresult NS_NewCrc32OutputStream(
+ nsIOutputStream** aResult, already_AddRefed<nsIOutputStream> aOutput,
+ uint32_t aBufferSize) {
+ nsCOMPtr<nsIOutputStream> out = std::move(aOutput);
+
+ nsCOMPtr<nsIBufferedOutputStream> bufferOutput =
+ new nsCrc32CheckSumedOutputStream();
+ nsresult rv = bufferOutput->Init(out, aBufferSize);
+ if (NS_SUCCEEDED(rv)) {
+ bufferOutput.forget(aResult);
+ }
+ return rv;
+}
+
+#endif
diff --git a/toolkit/components/url-classifier/nsIUrlClassifierDBService.idl b/toolkit/components/url-classifier/nsIUrlClassifierDBService.idl
new file mode 100644
index 0000000000..5a20360672
--- /dev/null
+++ b/toolkit/components/url-classifier/nsIUrlClassifierDBService.idl
@@ -0,0 +1,245 @@
+/* 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"
+
+%{C++
+#include "Entries.h"
+#include "LookupCache.h"
+#include "mozilla/UniquePtr.h"
+%}
+native ResultArray(mozilla::UniquePtr<mozilla::safebrowsing::LookupResultArray>);
+
+interface nsIUrlClassifierHashCompleter;
+interface nsIPrincipal;
+
+// Interface for JS function callbacks
+[scriptable, function, uuid(4ca27b6b-a674-4b3d-ab30-d21e2da2dffb)]
+interface nsIUrlClassifierCallback : nsISupports {
+ void handleEvent(in ACString value);
+};
+
+/**
+ * The nsIUrlClassifierUpdateObserver interface is implemented by
+ * clients streaming updates to the url-classifier (usually
+ * nsUrlClassifierStreamUpdater.
+ */
+[scriptable, uuid(9fa11561-5816-4e1b-bcc9-b629ca05cce6)]
+interface nsIUrlClassifierUpdateObserver : nsISupports {
+ /**
+ * The update requested a new URL whose contents should be downloaded
+ * and sent to the classifier as a new stream.
+ *
+ * @param url The url that was requested.
+ * @param table The table name that this URL's contents will be associated
+ * with. This should be passed back to beginStream().
+ */
+ void updateUrlRequested(in ACString url,
+ in ACString table);
+
+ /**
+ * A stream update has completed.
+ *
+ * @param status The state of the update process.
+ * @param delay The amount of time the updater should wait to fetch the
+ * next URL in ms.
+ */
+ void streamFinished(in nsresult status, in unsigned long delay);
+
+ /* The update has encountered an error and should be cancelled */
+ void updateError(in nsresult error);
+
+ /**
+ * The update has completed successfully.
+ *
+ * @param requestedTimeout The number of seconds that the caller should
+ * wait before trying to update again.
+ **/
+ void updateSuccess(in unsigned long requestedTimeout);
+};
+
+/**
+ * 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(7a258022-6765-11e5-b379-b37b1f2354be)]
+interface nsIUrlClassifierDBService : nsISupports
+{
+ /**
+ * Looks up a URI in the specified tables.
+ *
+ * @param principal: The principal containing the URI to search.
+ * @param c: The callback will be called with a comma-separated list
+ * of tables to which the key belongs.
+ */
+ void lookup(in nsIPrincipal principal,
+ in ACString tables,
+ in nsIUrlClassifierCallback c);
+
+ /**
+ * Lists the tables along with their meta info in the following format:
+ *
+ * tablename;[metadata]\n
+ * tablename2;[metadata]\n
+ *
+ * For v2 tables, the metadata is the chunks info such as
+ *
+ * goog-phish-shavar;a:10,14,30-40s:56,67
+ * goog-unwanted-shavar;a:1-3,5
+ *
+ * For v4 tables, base64 encoded state is currently the only info in the
+ * metadata (can be extended whenever necessary). For exmaple,
+ *
+ * goog-phish-proto;Cg0IARAGGAEiAzAwMTABEKqTARoCGAjT1gDD:oCGAjT1gDD\n
+ * goog-malware-proto;Cg0IAhAGGAEiAzAwMTABENCQARoCGAjx5Yty:BENCQARoCGAj\n
+ *
+ * Note that the metadata is colon-separated.
+ *
+ */
+ void getTables(in nsIUrlClassifierCallback c);
+
+ /**
+ * Set the nsIUrlClassifierCompleter object for a given table. This
+ * object will be used to request complete versions of partial
+ * hashes.
+ */
+ void setHashCompleter(in ACString tableName,
+ in nsIUrlClassifierHashCompleter completer);
+
+ /**
+ * Forget the results that were used in the last DB update.
+ */
+ void clearLastResults();
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Incremental update methods.
+ //
+ // An update to the database has the following steps:
+ //
+ // 1) The update process is started with beginUpdate(). The client
+ // passes an nsIUrlClassifierUpdateObserver object which will be
+ // notified as the update is processed by the dbservice.
+ // 2) The client sends an initial update stream to the dbservice,
+ // using beginStream/updateStream/finishStream.
+ // 3) While reading this initial update stream, the dbservice may
+ // request additional streams from the client as requested by the
+ // update stream.
+ // 4) For each additional update stream, the client feeds the
+ // contents to the dbservice using beginStream/updateStream/endStream.
+ // 5) Once all streams have been processed, the client calls
+ // finishUpdate. When the dbservice has finished processing
+ // all streams, it will notify the observer that the update process
+ // is complete.
+
+ /**
+ * Begin an update process. Will throw NS_ERROR_NOT_AVAILABLE if there
+ * is already an update in progress.
+ *
+ * @param updater The update observer tied to this update.
+ * @param tables A comma-separated list of tables included in this update.
+ */
+ void beginUpdate(in nsIUrlClassifierUpdateObserver updater,
+ in ACString tables);
+
+ /**
+ * Begin a stream update. This should be called once per url being
+ * fetched.
+ *
+ * @param table The table the contents of this stream will be associated
+ * with, or empty for the initial stream.
+ */
+ void beginStream(in ACString table);
+
+ /**
+ * Update the table incrementally.
+ */
+ void updateStream(in ACString updateChunk);
+
+ // It would be nice to have an updateFromStream method to round out the
+ // interface, but it's tricky because of XPCOM proxies.
+
+ /**
+ * Finish an individual stream update. Must be called for every
+ * beginStream() call, before the next beginStream() or finishUpdate().
+ *
+ * The update observer's streamFinished will be called once the
+ * stream has been processed.
+ */
+ void finishStream();
+
+ /**
+ * Finish an incremental update. This will attempt to commit any
+ * pending changes and resets the update interface.
+ *
+ * The update observer's updateSucceeded or updateError methods
+ * will be called when the update has been processed.
+ */
+ void finishUpdate();
+
+ /**
+ * Cancel an incremental update. This rolls back any pending changes.
+ * and resets the update interface.
+ *
+ * The update observer's updateError method will be called when the
+ * update has been rolled back.
+ */
+ void cancelUpdate();
+
+ /**
+ * Reset the url-classifier database. This call will delete the existing
+ * database, emptying all tables. Mostly intended for use in unit tests.
+ */
+ 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.
+ */
+[uuid(b903dc8f-dff1-42fe-894b-36e7a59bb801)]
+interface nsIUrlClassifierLookupCallback : nsISupports
+{
+ /**
+ * The lookup process is complete.
+ *
+ * @param results
+ * If this parameter is null, there were no results found.
+ * If not, it contains an array of nsUrlClassifierEntry objects
+ * with possible matches. The callee is responsible for freeing
+ * this array.
+ */
+ void lookupComplete(in ResultArray results);
+};
+
+/**
+ * This is an internal helper interface which is called after each
+ * classify completes to provide and handle a set of possible results,
+ * which the main thread may need to expand using an nsIURIClassifierCallback.
+ */
+[uuid(091adf98-28a5-473d-8dec-5b34b4e62496)]
+interface nsIUrlClassifierClassifyCallback : nsISupports
+{
+ /**
+ * The function is called each time the URL matches a Safe Browsing list
+ * The function could be called multiple times if URL matches multiple lists
+ *
+ */
+ void handleResult(in ACString aList,
+ in ACString aPrefix);
+};
diff --git a/toolkit/components/url-classifier/nsIUrlClassifierHashCompleter.idl b/toolkit/components/url-classifier/nsIUrlClassifierHashCompleter.idl
new file mode 100644
index 0000000000..64aaffae30
--- /dev/null
+++ b/toolkit/components/url-classifier/nsIUrlClassifierHashCompleter.idl
@@ -0,0 +1,103 @@
+/* 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"
+
+interface nsIArray;
+
+/**
+ * This interface contains feilds in Matches object of FullHashResponse(V4).
+ * Reference from:
+ * https://developers.google.com/safe-browsing/v4/update-api#http-post-response_2
+ */
+[scriptable, uuid(aabeb50e-d9f7-418e-9469-2cd9608958c0)]
+interface nsIFullHashMatch : nsISupports
+{
+ readonly attribute ACString tableName;
+
+ readonly attribute ACString fullHash;
+
+ readonly attribute uint32_t cacheDuration;
+};
+
+/**
+ * This interface is implemented by nsIUrlClassifierHashCompleter clients.
+ */
+[scriptable, uuid(da16de40-df26-414d-bde7-c4faf4504868)]
+interface nsIUrlClassifierHashCompleterCallback : nsISupports
+{
+ /**
+ * A complete hash has been found that matches the partial hash.
+ * This method may be called 0-n times for a given
+ * nsIUrlClassifierCompleter::complete() call.
+ *
+ * @param hash
+ * The 256-bit hash that was discovered.
+ * @param table
+ * The name of the table that this hash belongs to.
+ * @param chunkId
+ * The database chunk that this hash belongs to.
+ */
+ void completionV2(in ACString hash,
+ in ACString table,
+ in uint32_t chunkId);
+
+ /**
+ * This will be called when a fullhash response is received and parsed
+ * no matter if any full hash has been found.
+ *
+ * @param partialHash
+ * The hash that was sent for completion.
+ * @param table
+ * The name of the table that this hash belongs to.
+ * @param negativeCacheDuration
+ * The negative cache duration in millisecond.
+ * @param fullHashes
+ * Array of fullhashes that match the prefix.
+ */
+ void completionV4(in ACString partialHash,
+ in ACString table,
+ in uint32_t negativeCacheDuration,
+ in nsIArray fullHashes);
+
+ /**
+ * The completion is complete. This method is called once per
+ * nsIUrlClassifierCompleter::complete() call, after all completion()
+ * calls are finished.
+ *
+ * @param status
+ * NS_OK if the request completed successfully, or an error code.
+ */
+ void completionFinished(in nsresult status);
+};
+
+/**
+ * Clients updating the url-classifier database have the option of sending
+ * partial (32-bit) hashes of URL fragments to be blocklisted. If the
+ * url-classifier encounters one of these truncated hashes, it will ask an
+ * nsIUrlClassifierCompleter instance to asynchronously provide the complete
+ * hash, along with some associated metadata.
+ * This is only ever used for testing and should absolutely be deleted (I
+ * think).
+ */
+[scriptable, uuid(231fb2ad-ea8a-4e63-a331-eafc3b434811)]
+interface nsIUrlClassifierHashCompleter : nsISupports
+{
+ /**
+ * Request a completed hash from the given gethash url.
+ *
+ * @param partialHash
+ * The 32-bit hash encountered by the url-classifier.
+ * @param gethashUrl
+ * The gethash url to use.
+ * @param tableName
+ * The table where we matched the partial hash.
+ * @param callback
+ * An nsIUrlClassifierCompleterCallback instance.
+ */
+ void complete(in ACString partialHash,
+ in ACString gethashUrl,
+ in ACString tableName,
+ in nsIUrlClassifierHashCompleterCallback callback);
+};
diff --git a/toolkit/components/url-classifier/nsIUrlClassifierInfo.idl b/toolkit/components/url-classifier/nsIUrlClassifierInfo.idl
new file mode 100644
index 0000000000..f57c66aee5
--- /dev/null
+++ b/toolkit/components/url-classifier/nsIUrlClassifierInfo.idl
@@ -0,0 +1,80 @@
+ /* -*- 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, uuid(69384f24-d9c5-4462-b24e-351c69e3b46a)]
+interface nsIUrlClassifierCacheInfo : nsISupports {
+ /**
+ * Table name.
+ */
+ readonly attribute ACString table;
+
+ /*
+ * An array of nsIUrlClassifierCacheEntry.
+ */
+ readonly attribute nsIArray entries;
+};
+
+[scriptable, function, uuid(26e12ea4-14ff-4c77-858f-6745998b7659)]
+interface nsIUrlClassifierGetCacheCallback : nsISupports {
+
+ void onGetCacheComplete(in nsIUrlClassifierCacheInfo info);
+};
+
+/**
+ * Interface to query url-classifier information.
+ */
+[scriptable, uuid(411bbff4-1b88-4687-aa36-e2bbdd93f6e8)]
+interface nsIUrlClassifierInfo : nsISupports {
+ /**
+ * An asynchronous call to return cache information for the table.
+ */
+ void getCacheInfo(in ACString table,
+ in nsIUrlClassifierGetCacheCallback callback);
+};
diff --git a/toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl b/toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl
new file mode 100644
index 0000000000..6ccb5ae7d1
--- /dev/null
+++ b/toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl
@@ -0,0 +1,27 @@
+/* -*- Mode: IDL; tab-width: 2; 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 "nsISupports.idl"
+#include "nsIFile.idl"
+
+// Note that the PrefixSet name is historical and we do properly support
+// duplicated values, so it's really a Prefix Trie.
+// All methods are thread-safe.
+[scriptable, uuid(3d8579f0-75fa-4e00-ba41-38661d5b5d17)]
+interface nsIUrlClassifierPrefixSet : nsISupports
+{
+ // Initialize the PrefixSet. Give it a name for memory reporting.
+ void init(in ACString aName);
+ // Fills the PrefixSet with the given array of prefixes.
+ // Can send an empty Array to clear the tree.
+ // Requires array to be sorted.
+ void setPrefixes([const, array, size_is(aLength)] in unsigned long aPrefixes,
+ in unsigned long aLength);
+ void getPrefixes(out unsigned long aCount,
+ [array, size_is(aCount), retval] out unsigned long aPrefixes);
+ // Do a lookup in the PrefixSet, return whether the value is present.
+ boolean contains(in unsigned long aPrefix);
+ boolean isEmpty();
+};
diff --git a/toolkit/components/url-classifier/nsIUrlClassifierRemoteSettingsService.idl b/toolkit/components/url-classifier/nsIUrlClassifierRemoteSettingsService.idl
new file mode 100644
index 0000000000..dc38b8eba0
--- /dev/null
+++ b/toolkit/components/url-classifier/nsIUrlClassifierRemoteSettingsService.idl
@@ -0,0 +1,32 @@
+/* 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"
+
+interface nsIStreamListener;
+
+/**
+ * A service that serves Safe Browsing list data (V2 protocol) via
+ * Remote Settings.
+ */
+[scriptable, uuid(26a445a4-0d00-4b20-ba5f-1297d3344a25)]
+interface nsIUrlClassifierRemoteSettingsService : nsISupports
+{
+ /**
+ * Fetch the Safe Browsing list data from the service. The service
+ * returns the response data by simulating how the data is sent over a
+ * stream listener when a HTTP request is made.
+ *
+ *
+ * @param aPayload the request payload for list data request
+ * @param aListener An nsIStreamListener object
+ */
+ void fetchList(in ACString aPayload, in nsIStreamListener aListener);
+
+ /**
+ * Clear all data in the service.
+ * This API is for testing only.
+ */
+ void clear();
+};
diff --git a/toolkit/components/url-classifier/nsIUrlClassifierStreamUpdater.idl b/toolkit/components/url-classifier/nsIUrlClassifierStreamUpdater.idl
new file mode 100644
index 0000000000..50844d0e03
--- /dev/null
+++ b/toolkit/components/url-classifier/nsIUrlClassifierStreamUpdater.idl
@@ -0,0 +1,39 @@
+/* -*- 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 "nsIUrlClassifierDBService.idl"
+
+/**
+ * This is a class to manage large table updates from the server. Rather than
+ * downloading the whole update and then updating the sqlite database, we
+ * update tables as the data is streaming in.
+ */
+[scriptable, uuid(e1797597-f4d6-4dd3-a1e1-745ad352cd80)]
+interface nsIUrlClassifierStreamUpdater : nsISupports
+{
+ /**
+ * Try to download updates from updateUrl. If an update is already in
+ * progress, queues the requested update. This is used in nsIUrlListManager
+ * as well as in testing.
+ * @param aRequestTables Comma-separated list of tables included in this
+ * update.
+ * @param aRequestPayload The payload for the request.
+ * @param aIsPostRequest Whether the request should be sent by POST method.
+ * Should be 'true' for v2 usage.
+ * @param aUpdateUrl The plaintext url from which to request updates.
+ * @param aSuccessCallback Called after a successful update.
+ * @param aUpdateErrorCallback Called for problems applying the update
+ * @param aDownloadErrorCallback Called if we get an http error or a
+ * connection refused error.
+ */
+ boolean downloadUpdates(in ACString aRequestTables,
+ in ACString aRequestPayload,
+ in boolean aIsPostRequest,
+ in ACString aUpdateUrl,
+ in nsIUrlClassifierCallback aSuccessCallback,
+ in nsIUrlClassifierCallback aUpdateErrorCallback,
+ in nsIUrlClassifierCallback aDownloadErrorCallback);
+};
diff --git a/toolkit/components/url-classifier/nsIUrlClassifierUtils.idl b/toolkit/components/url-classifier/nsIUrlClassifierUtils.idl
new file mode 100644
index 0000000000..d98340e9a7
--- /dev/null
+++ b/toolkit/components/url-classifier/nsIUrlClassifierUtils.idl
@@ -0,0 +1,158 @@
+/* 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"
+/**
+ * Some utility methods used by the url classifier.
+ */
+
+interface nsIURI;
+interface nsIChannel;
+
+/**
+ * Interface for parseFindFullHashResponseV4 callback
+ */
+[scriptable, uuid(fbb9684a-a0aa-11e6-88b0-08606e456b8a)]
+interface nsIUrlClassifierParseFindFullHashCallback : nsISupports {
+ /**
+ * Callback when a match is found in full hash response. This callback may be
+ * called multiple times when there are more than one matches in response.
+ *
+ * @param aCompleteHash A 32-byte complete hash string.
+ * @param aTableNames The table names that this complete hash is associated with.
+ * Since the server responded with a threat type, multiple
+ * list names can be returned. The caller is reponsible
+ * for filtering out the unrequested table names.
+ * See |convertThreatTypeToListNames| for the format.
+ * @param aPerHashCacheDuration See "FindFullHashesResponse" in safebrowsing.proto.
+ *
+ */
+ void onCompleteHashFound(in ACString aCompleteHash,
+ in ACString aTableNames,
+ in unsigned long aPerHashCacheDuration);
+
+ /**
+ * Callback when full hash response is received.
+ *
+ * @param aMinWaitDuration See "FindFullHashesResponse" in safebrowsing.proto.
+ * @param aNegCacheDuration See "FindFullHashesResponse" in safebrowsing.proto.
+ *
+ */
+ void onResponseParsed(in unsigned long aMinWaitDuration,
+ in unsigned long aNegCacheDuration);
+};
+
+[scriptable, uuid(e4f0e59c-b922-48b0-a7b6-1735c1f96fed)]
+interface nsIUrlClassifierUtils : nsISupports
+{
+ /**
+ * Get the lookup string for a given URI. This normalizes the hostname,
+ * url-decodes the string, and strips off the protocol.
+ *
+ * @param uri URI to get the lookup key for.
+ *
+ * @returns String containing the canonicalized URI.
+ */
+ ACString getKeyForURI(in nsIURI uri);
+
+ /**
+ * Get the provider by table name.
+ *
+ * @param tableName The table name that we want to lookup
+ *
+ * @returns the provider name that the given table belongs.
+ */
+ ACString getProvider(in ACString tableName);
+
+ /**
+ * Get the provider used for Telemetry.
+ * Because recording Telemetry will leak user-controlled strings,
+ * only built-in providers should be recorded.
+ *
+ * @param tableName The table name that we want to lookup
+ *
+ * @returns the filtered provider for telemetry.
+ *
+ */
+ ACString getTelemetryProvider(in ACString tableName);
+
+ /**
+ * Get the protocol version for the given provider.
+ *
+ * @param provider String the provider name. e.g. "google"
+ *
+ * @returns String to indicate the protocol version. e.g. "2.2"
+ */
+ ACString getProtocolVersion(in ACString provider);
+
+ /**
+ * Convert threat type to list name.
+ *
+ * @param Integer to indicate threat type.
+ *
+ * @returns The list names separated by ','. For example,
+ * 'goog-phish-proto,test-phish-proto'.
+ */
+ ACString convertThreatTypeToListNames(in uint32_t threatType);
+
+ /**
+ * Convert list name to threat type.
+ *
+ * @param The list name.
+ *
+ * @returns The threat type in integer.
+ */
+ uint32_t convertListNameToThreatType(in ACString listName);
+
+ /**
+ * Make update request for given lists and their states.
+ *
+ * @param aListNames An array of list name represented in string.
+ * @param aState An array of states (encoded in base64 format) for each list.
+ *
+ * The two argument arrays must be the same length.
+ *
+ * @returns A base64url encoded string.
+ */
+ ACString makeUpdateRequestV4(in Array<ACString> aListNames,
+ in Array<ACString> aStatesBase64);
+
+ /**
+ * Make "find full hash" request by for the given prefixes.
+ *
+ * @param aListNames An array of list names represented in string.
+ * @param aListStatesBase64 An array of list states represented in base64.
+ * @param aPrefixes An array of prefixes for which we'd like to find full hashes..
+ *
+ * The aListNames and aListStatesBase64 arrays must be the same length.
+ *
+ * @returns A base64url encoded string.
+ */
+ ACString makeFindFullHashRequestV4(in Array<ACString> aListNames,
+ in Array<ACString> aListStatesBase64,
+ in Array<ACString> aPrefixes);
+
+ /**
+ * Make ThreatHit report request body.
+ *
+ * @param aChannel channel which encountered the threat.
+ * @param aListName listname represented in string.
+ * @param aHashBase64 hash-based hit represented in base64.
+ *
+ * @returns A base64 encoded string.
+ */
+ ACString makeThreatHitReport(in nsIChannel aChannel,
+ in ACString aListName,
+ in ACString aHashBase64);
+
+ /**
+ * Parse V4 FindFullHash response.
+ *
+ * @param aResponse Byte stream from the server.
+ * @param aCallback The callback function on each complete hash parsed.
+ * Can be called multiple times in one parsing.
+ */
+ void parseFindFullHashResponseV4(in ACString aResponse,
+ in nsIUrlClassifierParseFindFullHashCallback aCallback);
+};
diff --git a/toolkit/components/url-classifier/nsIUrlListManager.idl b/toolkit/components/url-classifier/nsIUrlListManager.idl
new file mode 100644
index 0000000000..6bf01020ee
--- /dev/null
+++ b/toolkit/components/url-classifier/nsIUrlListManager.idl
@@ -0,0 +1,94 @@
+/* -*- 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"
+
+interface nsIPrincipal;
+
+/**
+ * Interface for a class that manages updates of the url classifier database.
+ */
+
+[scriptable, uuid(d60a08ee-5c83-4eb6-bdfb-79fd0716501e)]
+interface nsIUrlListManager : nsISupports
+{
+ /**
+ * Get the gethash url for this table
+ */
+ ACString getGethashUrl(in ACString tableName);
+
+ /**
+ * Get the update url for this table
+ */
+ ACString getUpdateUrl(in ACString tableName);
+
+ /**
+ * Add a table to the list of tables we are managing. The name is a
+ * string of the format provider_name-semantic_type-table_type. For
+ * @param tableName A string of the format
+ * provider_name-semantic_type-table_type. For example,
+ * goog-white-enchash or goog-black-url.
+ * @param providerName The name of the entity providing the list.
+ * @param updateUrl The URL from which to fetch updates.
+ * @param gethashUrl The URL from which to fetch hash completions.
+ */
+ boolean registerTable(in ACString tableName,
+ in ACString providerName,
+ in ACString updateUrl,
+ in ACString gethashUrl);
+
+ /**
+ * Unregister table from the list
+ */
+ void unregisterTable(in ACString tableName);
+
+ /**
+ * Turn on update checking for a table. I.e., during the next server
+ * check, download updates for this table.
+ */
+ void enableUpdate(in ACString tableName);
+
+ /**
+ * Turn off update checking for all tables.
+ */
+ void disableAllUpdates();
+
+ /**
+ * Turn off update checking for a single table. Only used in tests.
+ */
+ void disableUpdate(in ACString tableName);
+
+ /**
+ * Toggle update checking, if necessary.
+ */
+ void maybeToggleUpdateChecking();
+
+ /**
+ * This is currently used by about:url-classifier to force an update
+ * for the update url. Update may still fail because of backoff algorithm.
+ */
+ boolean checkForUpdates(in ACString updateUrl);
+
+ /**
+ * Force updates for the given tables, updates are still restricted to
+ * backoff algorithm.
+ * @param tables A string lists all the tables that we want to trigger updates.
+ * table names are separated with ','.
+ */
+ boolean forceUpdates(in ACString tableNames);
+
+ /**
+ * This is currently used by about:url-classifier to get back-off time
+ * (in millisecond since epoch) for the given provider. Return 0 if we
+ * are not in back-off mode.
+ */
+ uint64_t getBackOffTime(in ACString provider);
+
+ /**
+ * Return true if someone registers a table, this is used by testcase
+ * to figure out it SafeBrowsing.jsm is initialized.
+ */
+ boolean isRegistered();
+};
diff --git a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
new file mode 100644
index 0000000000..929517aa6e
--- /dev/null
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -0,0 +1,2613 @@
+/* -*- 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 "nsCOMPtr.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsArrayUtils.h"
+#include "nsCRT.h"
+#include "nsIObserverService.h"
+#include "nsIPermissionManager.h"
+#include "nsIPrefBranch.h"
+#include "nsIXULRuntime.h"
+#include "nsToolkitCompsCID.h"
+#include "nsUrlClassifierDBService.h"
+#include "nsUrlClassifierUtils.h"
+#include "nsUrlClassifierProxies.h"
+#include "nsURILoader.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsTArray.h"
+#include "nsNetCID.h"
+#include "nsThreadUtils.h"
+#include "nsProxyRelease.h"
+#include "nsString.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/Components.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/ErrorNames.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Unused.h"
+#include "mozilla/Logging.h"
+#include "prnetdb.h"
+#include "Entries.h"
+#include "Classifier.h"
+#include "ProtocolParser.h"
+#include "mozilla/Attributes.h"
+#include "nsIHttpChannel.h"
+#include "nsIPrincipal.h"
+#include "nsIUrlListManager.h"
+#include "Classifier.h"
+#include "ProtocolParser.h"
+#include "nsContentUtils.h"
+#include "mozilla/Components.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/PermissionMessageUtils.h"
+#include "mozilla/dom/URLClassifierChild.h"
+#include "mozilla/net/UrlClassifierFeatureFactory.h"
+#include "mozilla/net/UrlClassifierFeatureResult.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "mozilla/SyncRunnable.h"
+#include "UrlClassifierTelemetryUtils.h"
+#include "nsIURLFormatter.h"
+#include "nsIUploadChannel.h"
+#include "nsStringStream.h"
+#include "nsNetUtil.h"
+#include "nsToolkitCompsCID.h"
+
+namespace mozilla {
+namespace safebrowsing {
+
+nsresult TablesToResponse(const nsACString& tables) {
+ if (tables.IsEmpty()) {
+ return NS_OK;
+ }
+
+ // We don't check mCheckMalware and friends because disabled tables are
+ // never included
+ if (FindInReadable("-malware-"_ns, tables)) {
+ return NS_ERROR_MALWARE_URI;
+ }
+ if (FindInReadable("-harmful-"_ns, tables)) {
+ return NS_ERROR_HARMFUL_URI;
+ }
+ if (FindInReadable("-phish-"_ns, tables)) {
+ return NS_ERROR_PHISHING_URI;
+ }
+ if (FindInReadable("-unwanted-"_ns, tables)) {
+ return NS_ERROR_UNWANTED_URI;
+ }
+ if (FindInReadable("-track-"_ns, tables)) {
+ return NS_ERROR_TRACKING_URI;
+ }
+ if (FindInReadable("-block-"_ns, tables)) {
+ return NS_ERROR_BLOCKED_URI;
+ }
+ return NS_OK;
+}
+
+} // namespace safebrowsing
+} // namespace mozilla
+
+// This class holds a list of features, their tables, and it stores the lookup
+// results.
+class nsUrlClassifierDBService::FeatureHolder final {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FeatureHolder);
+
+ // In order to avoid multiple lookup for the same table, we have a special
+ // array for tables and their results. The Features are stored in a separate
+ // array together with the references to their tables.
+
+ class TableData {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(
+ nsUrlClassifierDBService::FeatureHolder::TableData);
+
+ explicit TableData(const nsACString& aTable) : mTable(aTable) {}
+
+ nsCString mTable;
+ LookupResultArray mResults;
+
+ private:
+ ~TableData() = default;
+ };
+
+ struct FeatureData {
+ RefPtr<nsIUrlClassifierFeature> mFeature;
+ nsTArray<RefPtr<TableData>> mTables;
+ };
+
+ static already_AddRefed<FeatureHolder> Create(
+ nsIURI* aURI, const nsTArray<RefPtr<nsIUrlClassifierFeature>>& aFeatures,
+ nsIUrlClassifierFeature::listType aListType) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aURI);
+
+ RefPtr<FeatureHolder> holder = new FeatureHolder(aURI);
+
+ for (nsIUrlClassifierFeature* feature : aFeatures) {
+ FeatureData* featureData = holder->mFeatureData.AppendElement();
+ MOZ_ASSERT(featureData);
+
+ featureData->mFeature = feature;
+ nsTArray<nsCString> tables;
+ nsresult rv = feature->GetTables(aListType, tables);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+
+ for (const nsCString& table : tables) {
+ TableData* tableData = holder->GetOrCreateTableData(table);
+ MOZ_ASSERT(tableData);
+
+ featureData->mTables.AppendElement(tableData);
+ }
+ }
+
+ return holder.forget();
+ }
+
+ nsresult DoLocalLookup(const nsACString& aSpec,
+ nsUrlClassifierDBServiceWorker* aWorker) {
+ MOZ_ASSERT(!NS_IsMainThread());
+ MOZ_ASSERT(aWorker);
+
+ mozilla::Telemetry::AutoTimer<
+ mozilla::Telemetry::URLCLASSIFIER_CL_CHECK_TIME>
+ timer;
+
+ // Get the set of fragments based on the url. This is necessary because we
+ // only look up at most 5 URLs per aSpec, even if aSpec has more than 5
+ // components.
+ nsTArray<nsCString> fragments;
+ nsresult rv = LookupCache::GetLookupFragments(aSpec, &fragments);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (TableData* tableData : mTableData) {
+ rv = aWorker->DoSingleLocalLookupWithURIFragments(
+ fragments, tableData->mTable, tableData->mResults);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ return NS_OK;
+ }
+
+ // This method is used to convert the LookupResultArray from
+ // ::DoSingleLocalLookupWithURIFragments to nsIUrlClassifierFeatureResult
+ void GetResults(nsTArray<RefPtr<nsIUrlClassifierFeatureResult>>& aResults) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // For each table, we must concatenate the results of the corresponding
+ // tables.
+
+ for (FeatureData& featureData : mFeatureData) {
+ nsAutoCString list;
+ for (TableData* tableData : featureData.mTables) {
+ for (uint32_t i = 0; i < tableData->mResults.Length(); ++i) {
+ if (!list.IsEmpty()) {
+ list.AppendLiteral(",");
+ }
+ list.Append(tableData->mResults[i]->mTableName);
+ }
+ }
+
+ if (list.IsEmpty()) {
+ continue;
+ }
+
+ RefPtr<mozilla::net::UrlClassifierFeatureResult> result =
+ new mozilla::net::UrlClassifierFeatureResult(
+ mURI, featureData.mFeature, list);
+ aResults.AppendElement(result);
+ }
+ }
+
+ mozilla::UniquePtr<LookupResultArray> GetTableResults() const {
+ mozilla::UniquePtr<LookupResultArray> results =
+ mozilla::MakeUnique<LookupResultArray>();
+ if (NS_WARN_IF(!results)) {
+ return nullptr;
+ }
+
+ for (TableData* tableData : mTableData) {
+ results->AppendElements(tableData->mResults);
+ }
+
+ return results;
+ }
+
+ private:
+ explicit FeatureHolder(nsIURI* aURI) : mURI(aURI) {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ ~FeatureHolder() {
+ for (FeatureData& featureData : mFeatureData) {
+ NS_ReleaseOnMainThread("FeatureHolder:mFeatureData",
+ featureData.mFeature.forget());
+ }
+
+ NS_ReleaseOnMainThread("FeatureHolder:mURI", mURI.forget());
+ }
+
+ TableData* GetOrCreateTableData(const nsACString& aTable) {
+ for (TableData* tableData : mTableData) {
+ if (tableData->mTable == aTable) {
+ return tableData;
+ }
+ }
+
+ RefPtr<TableData> tableData = new TableData(aTable);
+ mTableData.AppendElement(tableData);
+ return tableData;
+ }
+
+ nsCOMPtr<nsIURI> mURI;
+ nsTArray<FeatureData> mFeatureData;
+ nsTArray<RefPtr<TableData>> mTableData;
+};
+
+using namespace mozilla;
+using namespace mozilla::safebrowsing;
+
+// MOZ_LOG=UrlClassifierDbService:5
+LazyLogModule gUrlClassifierDbServiceLog("UrlClassifierDbService");
+#define LOG(args) \
+ MOZ_LOG(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() \
+ MOZ_LOG_TEST(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug)
+
+#define GETHASH_NOISE_PREF "urlclassifier.gethashnoise"
+#define GETHASH_NOISE_DEFAULT 4
+
+// 30 minutes as the maximum negative cache duration.
+#define MAXIMUM_NEGATIVE_CACHE_DURATION_SEC (30 * 60 * 1000)
+
+class nsUrlClassifierDBServiceWorker;
+
+// Singleton instance.
+static nsUrlClassifierDBService* sUrlClassifierDBService;
+
+nsIThread* nsUrlClassifierDBService::gDbBackgroundThread = nullptr;
+
+// Once we've committed to shutting down, don't do work in the background
+// thread.
+static Atomic<bool> gShuttingDownThread(false);
+
+NS_IMPL_ISUPPORTS(nsUrlClassifierDBServiceWorker, nsIUrlClassifierDBService)
+
+nsUrlClassifierDBServiceWorker::nsUrlClassifierDBServiceWorker()
+ : mUpdateObserverLock("nsUrlClassifierDBServerWorker.mUpdateObserverLock"),
+ mInStream(false),
+ mGethashNoise(0),
+ mPendingLookupLock("nsUrlClassifierDBServerWorker.mPendingLookupLock") {}
+
+nsUrlClassifierDBServiceWorker::~nsUrlClassifierDBServiceWorker() {
+ NS_ASSERTION(!mClassifier,
+ "Db connection not closed, leaking memory! Call CloseDb "
+ "to close the connection.");
+}
+
+nsresult nsUrlClassifierDBServiceWorker::Init(
+ uint32_t aGethashNoise, nsCOMPtr<nsIFile> aCacheDir,
+ nsUrlClassifierDBService* aDBService) {
+ mGethashNoise = aGethashNoise;
+ mCacheDir = aCacheDir;
+ mDBService = aDBService;
+
+ ResetUpdate();
+
+ return NS_OK;
+}
+
+nsresult nsUrlClassifierDBServiceWorker::QueueLookup(
+ const nsACString& aKey,
+ nsUrlClassifierDBService::FeatureHolder* aFeatureHolder,
+ nsIUrlClassifierLookupCallback* aCallback) {
+ MOZ_ASSERT(aFeatureHolder);
+ MOZ_ASSERT(aCallback);
+
+ MutexAutoLock lock(mPendingLookupLock);
+ if (gShuttingDownThread) {
+ return NS_ERROR_ABORT;
+ }
+
+ PendingLookup* lookup = mPendingLookups.AppendElement(fallible);
+ if (NS_WARN_IF(!lookup)) return NS_ERROR_OUT_OF_MEMORY;
+
+ lookup->mStartTime = TimeStamp::Now();
+ lookup->mKey = aKey;
+ lookup->mCallback = aCallback;
+ lookup->mFeatureHolder = aFeatureHolder;
+
+ return NS_OK;
+}
+
+nsresult nsUrlClassifierDBServiceWorker::DoSingleLocalLookupWithURIFragments(
+ const nsTArray<nsCString>& aSpecFragments, const nsACString& aTable,
+ LookupResultArray& aResults) {
+ if (gShuttingDownThread) {
+ return NS_ERROR_ABORT;
+ }
+
+ MOZ_ASSERT(
+ !NS_IsMainThread(),
+ "DoSingleLocalLookupWithURIFragments must be on background thread");
+
+ // Bail if we haven't been initialized on the background thread.
+ if (!mClassifier) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsresult rv =
+ mClassifier->CheckURIFragments(aSpecFragments, aTable, aResults);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ LOG(("Found %zu results.", aResults.Length()));
+ return NS_OK;
+}
+
+/**
+ * Lookup up a key in the database is a two step process:
+ *
+ * a) First we look for any Entries in the database that might apply to this
+ * url. For each URL there are one or two possible domain names to check:
+ * the two-part domain name (example.com) and the three-part name
+ * (www.example.com). We check the database for both of these.
+ * b) If we find any entries, we check the list of fragments for that entry
+ * against the possible subfragments of the URL as described in the
+ * "Simplified Regular Expression Lookup" section of the protocol doc.
+ */
+nsresult nsUrlClassifierDBServiceWorker::DoLookup(
+ const nsACString& spec,
+ nsUrlClassifierDBService::FeatureHolder* aFeatureHolder,
+ nsIUrlClassifierLookupCallback* c) {
+ // Make sure the callback is invoked when a failure occurs,
+ // otherwise we will not be able to load any url.
+ auto scopeExit = MakeScopeExit([&c]() { c->LookupComplete(nullptr); });
+
+ if (gShuttingDownThread) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ PRIntervalTime clockStart = 0;
+ if (LOG_ENABLED()) {
+ clockStart = PR_IntervalNow();
+ }
+
+ nsresult rv = aFeatureHolder->DoLocalLookup(spec, this);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (LOG_ENABLED()) {
+ PRIntervalTime clockEnd = PR_IntervalNow();
+ LOG(("query took %dms\n",
+ PR_IntervalToMilliseconds(clockEnd - clockStart)));
+ }
+
+ UniquePtr<LookupResultArray> results = aFeatureHolder->GetTableResults();
+ if (NS_WARN_IF(!results)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ LOG(("Found %zu results.", results->Length()));
+
+ for (const RefPtr<const LookupResult> lookupResult : *results) {
+ if (!lookupResult->Confirmed() &&
+ mDBService->CanComplete(lookupResult->mTableName)) {
+ // We're going to be doing a gethash request, add some extra entries.
+ // Note that we cannot pass the first two by reference, because we
+ // add to completes, which can cause completes to reallocate and move.
+ AddNoise(lookupResult->hash.fixedLengthPrefix, lookupResult->mTableName,
+ mGethashNoise, *results);
+ break;
+ }
+ }
+
+ // At this point ownership of 'results' is handed to the callback.
+ scopeExit.release();
+ c->LookupComplete(std::move(results));
+
+ return NS_OK;
+}
+
+nsresult nsUrlClassifierDBServiceWorker::HandlePendingLookups() {
+ if (gShuttingDownThread) {
+ return NS_ERROR_ABORT;
+ }
+
+ MutexAutoLock lock(mPendingLookupLock);
+ while (mPendingLookups.Length() > 0) {
+ PendingLookup lookup = mPendingLookups[0];
+ mPendingLookups.RemoveElementAt(0);
+ {
+ MutexAutoUnlock unlock(mPendingLookupLock);
+ DoLookup(lookup.mKey, lookup.mFeatureHolder, lookup.mCallback);
+ }
+ double lookupTime = (TimeStamp::Now() - lookup.mStartTime).ToMilliseconds();
+ Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LOOKUP_TIME_2,
+ static_cast<uint32_t>(lookupTime));
+ }
+
+ return NS_OK;
+}
+
+nsresult nsUrlClassifierDBServiceWorker::AddNoise(const Prefix aPrefix,
+ const nsCString tableName,
+ uint32_t aCount,
+ LookupResultArray& results) {
+ if (gShuttingDownThread) {
+ return NS_ERROR_ABORT;
+ }
+
+ if (aCount < 1) {
+ return NS_OK;
+ }
+
+ PrefixArray noiseEntries;
+ nsresult rv =
+ mClassifier->ReadNoiseEntries(aPrefix, tableName, aCount, noiseEntries);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (const auto noiseEntry : noiseEntries) {
+ RefPtr<LookupResult> result = new LookupResult;
+ results.AppendElement(result);
+
+ result->hash.fixedLengthPrefix = noiseEntry;
+ result->mNoise = true;
+ result->mPartialHashLength = PREFIX_SIZE; // Noise is always 4-byte,
+ result->mTableName.Assign(tableName);
+ }
+
+ return NS_OK;
+}
+
+// Lookup a key in the db.
+NS_IMETHODIMP
+nsUrlClassifierDBServiceWorker::Lookup(nsIPrincipal* aPrincipal,
+ const nsACString& aTables,
+ nsIUrlClassifierCallback* c) {
+ if (gShuttingDownThread) {
+ return NS_ERROR_ABORT;
+ }
+
+ return HandlePendingLookups();
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBServiceWorker::GetTables(nsIUrlClassifierCallback* c) {
+ if (gShuttingDownThread) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ nsresult rv = OpenDb();
+ if (NS_FAILED(rv)) {
+ NS_ERROR("Unable to open SafeBrowsing database");
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString response;
+ mClassifier->TableRequest(response);
+ LOG(("GetTables: %s", response.get()));
+ c->HandleEvent(response);
+
+ return rv;
+}
+
+void nsUrlClassifierDBServiceWorker::ResetStream() {
+ LOG(("ResetStream"));
+ mInStream = false;
+ mProtocolParser = nullptr;
+}
+
+void nsUrlClassifierDBServiceWorker::ResetUpdate() {
+ LOG(("ResetUpdate"));
+ mUpdateWaitSec = 0;
+ mUpdateStatus = NS_OK;
+ {
+ MutexAutoLock lock(mUpdateObserverLock);
+ mUpdateObserver = nullptr;
+ }
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBServiceWorker::SetHashCompleter(
+ const nsACString& tableName, nsIUrlClassifierHashCompleter* completer) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBServiceWorker::BeginUpdate(
+ nsIUrlClassifierUpdateObserver* observer, const nsACString& tables) {
+ LOG(("nsUrlClassifierDBServiceWorker::BeginUpdate [%s]",
+ PromiseFlatCString(tables).get()));
+
+ if (gShuttingDownThread) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ {
+ MutexAutoLock lock(mUpdateObserverLock);
+ NS_ENSURE_STATE(!mUpdateObserver);
+
+ nsresult rv = OpenDb();
+ if (NS_FAILED(rv)) {
+ NS_ERROR("Unable to open SafeBrowsing database");
+ return NS_ERROR_FAILURE;
+ }
+
+ mUpdateStatus = NS_OK;
+ MOZ_ASSERT(mTableUpdates.IsEmpty(),
+ "mTableUpdates should have been cleared in FinishUpdate()");
+ mUpdateObserver = observer;
+ }
+ Classifier::SplitTables(tables, mUpdateTables);
+
+ return NS_OK;
+}
+
+// Called from the stream updater.
+NS_IMETHODIMP
+nsUrlClassifierDBServiceWorker::BeginStream(const nsACString& table) {
+ LOG(("nsUrlClassifierDBServiceWorker::BeginStream"));
+ MOZ_ASSERT(!NS_IsMainThread(), "Streaming must be on the background thread");
+
+ if (gShuttingDownThread) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ {
+ MutexAutoLock lock(mUpdateObserverLock);
+ NS_ENSURE_STATE(mUpdateObserver);
+ }
+ NS_ENSURE_STATE(!mInStream);
+
+ mInStream = true;
+
+ NS_ASSERTION(!mProtocolParser, "Should not have a protocol parser.");
+
+ // Check if we should use protobuf to parse the update.
+ bool useProtobuf = false;
+ for (size_t i = 0; i < mUpdateTables.Length(); i++) {
+ bool isCurProtobuf = StringEndsWith(mUpdateTables[i], "-proto"_ns);
+
+ if (0 == i) {
+ // Use the first table name to decice if all the subsequent tables
+ // should be '-proto'.
+ useProtobuf = isCurProtobuf;
+ continue;
+ }
+
+ if (useProtobuf != isCurProtobuf) {
+ NS_WARNING(
+ "Cannot mix 'proto' tables with other types "
+ "within the same provider.");
+ break;
+ }
+ }
+
+ if (useProtobuf) {
+ mProtocolParser.reset(new (fallible) ProtocolParserProtobuf());
+ } else {
+ mProtocolParser.reset(new (fallible) ProtocolParserV2());
+ }
+ if (!mProtocolParser) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return mProtocolParser->Begin(table, mUpdateTables);
+}
+
+/**
+ * Updating the database:
+ *
+ * The Update() method takes a series of chunks separated with control data,
+ * as described in
+ * http://code.google.com/p/google-safe-browsing/wiki/Protocolv2Spec
+ *
+ * It will iterate through the control data until it reaches a chunk. By
+ * the time it reaches a chunk, it should have received
+ * a) the table to which this chunk applies
+ * b) the type of chunk (add, delete, expire add, expire delete).
+ * c) the chunk ID
+ * d) the length of the chunk.
+ *
+ * For add and subtract chunks, it needs to read the chunk data (expires
+ * don't have any data). Chunk data is a list of URI fragments whose
+ * encoding depends on the type of table (which is indicated by the end
+ * of the table name):
+ * a) tables ending with -exp are a zlib-compressed list of URI fragments
+ * separated by newlines.
+ * b) tables ending with -sha128 have the form
+ * [domain][N][frag0]...[fragN]
+ * 16 1 16 16
+ * If N is 0, the domain is reused as a fragment.
+ * c) any other tables are assumed to be a plaintext list of URI fragments
+ * separated by newlines.
+ *
+ * Update() can be fed partial data; It will accumulate data until there is
+ * enough to act on. Finish() should be called when there will be no more
+ * data.
+ */
+NS_IMETHODIMP
+nsUrlClassifierDBServiceWorker::UpdateStream(const nsACString& chunk) {
+ if (gShuttingDownThread) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ MOZ_ASSERT(mProtocolParser);
+
+ NS_ENSURE_STATE(mInStream);
+
+ HandlePendingLookups();
+
+ // Feed the chunk to the parser.
+ return mProtocolParser->AppendStream(chunk);
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBServiceWorker::FinishStream() {
+ if (gShuttingDownThread) {
+ LOG(("shutting down"));
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ MutexAutoLock lock(mUpdateObserverLock);
+
+ MOZ_ASSERT(mProtocolParser);
+
+ NS_ENSURE_STATE(mInStream);
+ NS_ENSURE_STATE(mUpdateObserver);
+
+ mInStream = false;
+
+ mProtocolParser->End();
+
+ if (NS_SUCCEEDED(mProtocolParser->Status())) {
+ if (mProtocolParser->UpdateWaitSec()) {
+ mUpdateWaitSec = mProtocolParser->UpdateWaitSec();
+ }
+ // XXX: Only allow forwards from the initial update?
+ const nsTArray<ProtocolParser::ForwardedUpdate>& forwards =
+ mProtocolParser->Forwards();
+ for (uint32_t i = 0; i < forwards.Length(); i++) {
+ const ProtocolParser::ForwardedUpdate& forward = forwards[i];
+ mUpdateObserver->UpdateUrlRequested(forward.url, forward.table);
+ }
+ // Hold on to any TableUpdate objects that were created by the
+ // parser.
+ mTableUpdates.AppendElements(mProtocolParser->GetTableUpdates());
+ mProtocolParser->ForgetTableUpdates();
+
+#ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+ // The assignment involves no string copy since the source string is
+ // sharable.
+ mRawTableUpdates = mProtocolParser->GetRawTableUpdates();
+#endif
+ } else {
+ LOG(
+ ("nsUrlClassifierDBService::FinishStream Failed to parse the stream "
+ "using mProtocolParser."));
+ mUpdateStatus = mProtocolParser->Status();
+ }
+ mUpdateObserver->StreamFinished(mProtocolParser->Status(), 0);
+
+ if (NS_SUCCEEDED(mUpdateStatus)) {
+ if (mProtocolParser->ResetRequested()) {
+ mClassifier->ResetTables(Classifier::Clear_All,
+ mProtocolParser->TablesToReset());
+ }
+ }
+
+ mProtocolParser = nullptr;
+
+ return mUpdateStatus;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBServiceWorker::FinishUpdate() {
+ LOG(("nsUrlClassifierDBServiceWorker::FinishUpdate"));
+
+ MOZ_ASSERT(!NS_IsMainThread(),
+ "nsUrlClassifierDBServiceWorker::FinishUpdate "
+ "NUST NOT be on the main thread.");
+
+ if (gShuttingDownThread) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ MOZ_ASSERT(!mProtocolParser,
+ "Should have been nulled out in FinishStream() "
+ "or never created.");
+
+ {
+ MutexAutoLock lock(mUpdateObserverLock);
+ NS_ENSURE_STATE(mUpdateObserver);
+ }
+
+ if (NS_FAILED(mUpdateStatus)) {
+ LOG(
+ ("nsUrlClassifierDBServiceWorker::FinishUpdate() Not running "
+ "ApplyUpdate() since the update has already failed."));
+ mTableUpdates.Clear();
+ return NotifyUpdateObserver(mUpdateStatus);
+ }
+
+ if (mTableUpdates.IsEmpty()) {
+ LOG(("Nothing to update. Just notify update observer."));
+ return NotifyUpdateObserver(NS_OK);
+ }
+
+ RefPtr<nsUrlClassifierDBServiceWorker> self = this;
+ nsresult rv = mClassifier->AsyncApplyUpdates(
+ mTableUpdates, [self](nsresult aRv) -> void {
+#ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+ if (NS_FAILED(aRv) && NS_ERROR_OUT_OF_MEMORY != aRv &&
+ NS_ERROR_UC_UPDATE_SHUTDOWNING != aRv) {
+ self->mClassifier->DumpRawTableUpdates(self->mRawTableUpdates);
+ }
+ // Invalidate the raw table updates.
+ self->mRawTableUpdates.Truncate();
+#endif
+
+ self->NotifyUpdateObserver(aRv);
+ });
+ mTableUpdates.Clear(); // Classifier is working on its copy.
+
+ if (NS_FAILED(rv)) {
+ LOG(("Failed to start async update. Notify immediately."));
+ NotifyUpdateObserver(rv);
+ }
+
+ return rv;
+}
+
+nsresult nsUrlClassifierDBServiceWorker::NotifyUpdateObserver(
+ nsresult aUpdateStatus) {
+ MOZ_ASSERT(!NS_IsMainThread(),
+ "nsUrlClassifierDBServiceWorker::NotifyUpdateObserver "
+ "NUST NOT be on the main thread.");
+
+ LOG(("nsUrlClassifierDBServiceWorker::NotifyUpdateObserver"));
+
+ // We've either
+ // 1) failed starting a download stream
+ // 2) succeeded in starting a download stream but failed to obtain
+ // table updates
+ // 3) succeeded in obtaining table updates but failed to build new
+ // tables.
+ // 4) succeeded in building new tables but failed to take them.
+ // 5) succeeded in taking new tables.
+
+ mUpdateStatus = aUpdateStatus;
+
+ nsUrlClassifierUtils* urlUtil = nsUrlClassifierUtils::GetInstance();
+ if (NS_WARN_IF(!urlUtil)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCString provider;
+ // Assume that all the tables in update should have the same provider.
+ urlUtil->GetTelemetryProvider(mUpdateTables.SafeElementAt(0, ""_ns),
+ provider);
+
+ nsresult updateStatus = mUpdateStatus;
+ if (NS_FAILED(mUpdateStatus)) {
+ updateStatus =
+ NS_ERROR_GET_MODULE(mUpdateStatus) == NS_ERROR_MODULE_URL_CLASSIFIER
+ ? mUpdateStatus
+ : NS_ERROR_UC_UPDATE_UNKNOWN;
+ }
+
+ // Do not record telemetry for testing tables.
+ if (!provider.EqualsLiteral(TESTING_TABLE_PROVIDER_NAME)) {
+ Telemetry::Accumulate(Telemetry::URLCLASSIFIER_UPDATE_ERROR, provider,
+ NS_ERROR_GET_CODE(updateStatus));
+ }
+
+ MutexAutoLock lock(mUpdateObserverLock);
+
+ if (!mUpdateObserver) {
+ // In the normal shutdown process, CancelUpdate() would NOT be
+ // called prior to NotifyUpdateObserver(). However, CancelUpdate()
+ // is a public API which can be called in the test case at any point.
+ // If the call sequence is FinishUpdate() then CancelUpdate(), the later
+ // might be executed before NotifyUpdateObserver() which is triggered
+ // by the update thread. In this case, we will get null mUpdateObserver.
+ NS_WARNING(
+ "CancelUpdate() is called before we asynchronously call "
+ "NotifyUpdateObserver() in FinishUpdate().");
+
+ // The DB cleanup will be done in CancelUpdate() so we can just return.
+ return NS_OK;
+ }
+
+ // Null out mUpdateObserver before notifying so that BeginUpdate()
+ // becomes available prior to callback.
+ nsCOMPtr<nsIUrlClassifierUpdateObserver> updateObserver = nullptr;
+ updateObserver.swap(mUpdateObserver);
+
+ if (NS_SUCCEEDED(mUpdateStatus)) {
+ LOG(("Notifying success: %d", mUpdateWaitSec));
+ updateObserver->UpdateSuccess(mUpdateWaitSec);
+ } else {
+ if (LOG_ENABLED()) {
+ nsAutoCString errorName;
+ mozilla::GetErrorName(mUpdateStatus, errorName);
+ LOG(("Notifying error: %s (%" PRIu32 ")", errorName.get(),
+ static_cast<uint32_t>(mUpdateStatus)));
+ }
+
+ updateObserver->UpdateError(mUpdateStatus);
+ /*
+ * mark the tables as spoiled(clear cache in LookupCache), we don't want to
+ * block hosts longer than normal because our update failed
+ */
+ mClassifier->ResetTables(Classifier::Clear_Cache, mUpdateTables);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBServiceWorker::ResetDatabase() {
+ nsresult rv = OpenDb();
+
+ if (NS_SUCCEEDED(rv)) {
+ mClassifier->Reset();
+ }
+
+ rv = CloseDb();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBServiceWorker::ReloadDatabase() {
+ // This will null out mClassifier
+ nsresult rv = CloseDb();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // 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"));
+
+ {
+ MutexAutoLock lock(mUpdateObserverLock);
+ if (!mUpdateObserver) {
+ LOG(("No UpdateObserver, nothing to cancel"));
+
+ return NS_OK;
+ }
+
+ LOG(("UpdateObserver exists, cancelling"));
+
+ mUpdateStatus = NS_BINDING_ABORTED;
+
+ mUpdateObserver->UpdateError(mUpdateStatus);
+ }
+
+ /*
+ * mark the tables as spoiled(clear cache in LookupCache), we don't want to
+ * block hosts longer than normal because our update failed
+ */
+ mClassifier->ResetTables(Classifier::Clear_Cache, mUpdateTables);
+
+ ResetStream();
+ ResetUpdate();
+
+ return NS_OK;
+}
+
+void nsUrlClassifierDBServiceWorker::FlushAndDisableAsyncUpdate() {
+ LOG(("nsUrlClassifierDBServiceWorker::FlushAndDisableAsyncUpdate()"));
+
+ if (mClassifier) {
+ mClassifier->FlushAndDisableAsyncUpdate();
+ }
+}
+
+// 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.
+nsresult nsUrlClassifierDBServiceWorker::CloseDb() {
+ if (mClassifier) {
+ mClassifier->Close();
+ mClassifier = nullptr;
+ }
+
+ // Clear last completion result when close db so we will still cache
+ // completion result next time we re-open it.
+ mLastResults.Clear();
+
+ LOG(("urlclassifier db closed\n"));
+
+ return NS_OK;
+}
+
+nsresult nsUrlClassifierDBServiceWorker::PreShutdown() {
+ if (mClassifier) {
+ // Classifier close will release all lookup caches which may be a
+ // time-consuming job. See Bug 1408631.
+ mClassifier->Close();
+ }
+
+ // WARNING: nothing we put here should affect an ongoing update thread. When
+ // in doubt, put things in Shutdown() instead.
+ return NS_OK;
+}
+
+nsresult nsUrlClassifierDBServiceWorker::CacheCompletions(
+ const ConstCacheResultArray& aResults) {
+ if (gShuttingDownThread) {
+ return NS_ERROR_ABORT;
+ }
+
+ LOG(("nsUrlClassifierDBServiceWorker::CacheCompletions [%p]", this));
+ if (!mClassifier) {
+ return NS_OK;
+ }
+
+ if (aResults.Length() == 0) {
+ return NS_OK;
+ }
+
+ if (IsSameAsLastResults(aResults)) {
+ LOG(("Skipping completions that have just been cached already."));
+ return NS_OK;
+ }
+
+ // Only cache results for tables that we have, don't take
+ // in tables we might accidentally have hit during a completion.
+ // This happens due to goog vs googpub lists existing.
+ nsTArray<nsCString> tables;
+ nsresult rv = mClassifier->ActiveTables(tables);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (LOG_ENABLED()) {
+ nsCString s;
+ for (size_t i = 0; i < tables.Length(); i++) {
+ if (!s.IsEmpty()) {
+ s += ",";
+ }
+ s += tables[i];
+ }
+ LOG(("Active tables: %s", s.get()));
+ }
+
+ ConstTableUpdateArray updates;
+
+ for (const auto& result : aResults) {
+ bool activeTable = false;
+
+ for (uint32_t table = 0; table < tables.Length(); table++) {
+ if (tables[table].Equals(result->table)) {
+ activeTable = true;
+ break;
+ }
+ }
+ if (activeTable) {
+ UniquePtr<ProtocolParser> pParse;
+ if (result->Ver() == CacheResult::V2) {
+ pParse.reset(new ProtocolParserV2());
+ } else {
+ pParse.reset(new ProtocolParserProtobuf());
+ }
+
+ RefPtr<TableUpdate> tu = pParse->GetTableUpdate(result->table);
+
+ rv = CacheResultToTableUpdate(result, tu);
+ if (NS_FAILED(rv)) {
+ // We can bail without leaking here because ForgetTableUpdates
+ // hasn't been called yet.
+ return rv;
+ }
+ updates.AppendElement(tu);
+ pParse->ForgetTableUpdates();
+ } else {
+ LOG(("Completion received, but table %s is not active, so not caching.",
+ result->table.get()));
+ }
+ }
+
+ rv = mClassifier->ApplyFullHashes(updates);
+ if (NS_SUCCEEDED(rv)) {
+ mLastResults = aResults.Clone();
+ }
+ return rv;
+}
+
+nsresult nsUrlClassifierDBServiceWorker::CacheResultToTableUpdate(
+ RefPtr<const CacheResult> aCacheResult, RefPtr<TableUpdate> aUpdate) {
+ RefPtr<TableUpdateV2> tuV2 = TableUpdate::Cast<TableUpdateV2>(aUpdate);
+ if (tuV2) {
+ RefPtr<const CacheResultV2> result =
+ CacheResult::Cast<const CacheResultV2>(aCacheResult);
+ MOZ_ASSERT(result);
+
+ if (result->miss) {
+ return tuV2->NewMissPrefix(result->prefix);
+ } else {
+ LOG(("CacheCompletion hash %X, Addchunk %d",
+ result->completion.ToUint32(), result->addChunk));
+
+ nsresult rv = tuV2->NewAddComplete(result->addChunk, result->completion);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ return tuV2->NewAddChunk(result->addChunk);
+ }
+ }
+
+ RefPtr<TableUpdateV4> tuV4 = TableUpdate::Cast<TableUpdateV4>(aUpdate);
+ if (tuV4) {
+ RefPtr<const CacheResultV4> result =
+ CacheResult::Cast<const CacheResultV4>(aCacheResult);
+ MOZ_ASSERT(result);
+
+ if (LOG_ENABLED()) {
+ const FullHashExpiryCache& fullHashes = result->response.fullHashes;
+ for (const auto& entry : fullHashes) {
+ Completion completion;
+ completion.Assign(entry.GetKey());
+ LOG(("CacheCompletion(v4) hash %X, CacheExpireTime %" PRId64,
+ completion.ToUint32(), entry.GetData()));
+ }
+ }
+
+ tuV4->NewFullHashResponse(result->prefix, result->response);
+ return NS_OK;
+ }
+
+ // tableUpdate object should be either V2 or V4.
+ return NS_ERROR_FAILURE;
+}
+
+nsresult nsUrlClassifierDBServiceWorker::OpenDb() {
+ if (gShuttingDownThread) {
+ return NS_ERROR_ABORT;
+ }
+
+ MOZ_ASSERT(!NS_IsMainThread(), "Must initialize DB on background thread");
+ // Connection already open, don't do anything.
+ if (mClassifier) {
+ return NS_OK;
+ }
+
+ nsresult rv;
+ RefPtr<Classifier> classifier = new (fallible) Classifier();
+ if (!classifier) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ rv = classifier->Open(*mCacheDir);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mClassifier = classifier;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBServiceWorker::ClearLastResults() {
+ MOZ_ASSERT(!NS_IsMainThread(), "Must be on the background thread");
+ 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(
+ const ConstCacheResultArray& aResult) const {
+ if (mLastResults.Length() != aResult.Length()) {
+ return false;
+ }
+
+ bool equal = true;
+ for (uint32_t i = 0; i < mLastResults.Length() && equal; i++) {
+ RefPtr<const CacheResult> lhs = mLastResults[i];
+ RefPtr<const CacheResult> rhs = aResult[i];
+
+ if (lhs->Ver() != rhs->Ver()) {
+ return false;
+ }
+
+ if (lhs->Ver() == CacheResult::V2) {
+ equal = *(CacheResult::Cast<const CacheResultV2>(lhs)) ==
+ *(CacheResult::Cast<const CacheResultV2>(rhs));
+ } else if (lhs->Ver() == CacheResult::V4) {
+ equal = *(CacheResult::Cast<const CacheResultV4>(lhs)) ==
+ *(CacheResult::Cast<const CacheResultV4>(rhs));
+ }
+ }
+
+ return equal;
+}
+
+// -------------------------------------------------------------------------
+// nsUrlClassifierLookupCallback
+//
+// This class takes the results of a lookup found on the worker thread
+// and handles any necessary partial hash expansions before calling
+// the client callback.
+
+class nsUrlClassifierLookupCallback final
+ : public nsIUrlClassifierLookupCallback,
+ public nsIUrlClassifierHashCompleterCallback {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURLCLASSIFIERLOOKUPCALLBACK
+ NS_DECL_NSIURLCLASSIFIERHASHCOMPLETERCALLBACK
+
+ nsUrlClassifierLookupCallback(nsUrlClassifierDBService* dbservice,
+ nsIUrlClassifierCallback* c)
+ : mDBService(dbservice),
+ mResults(nullptr),
+ mPendingCompletions(0),
+ mCallback(c) {}
+
+ private:
+ ~nsUrlClassifierLookupCallback();
+
+ nsresult HandleResults();
+ nsresult ProcessComplete(RefPtr<CacheResult> aCacheResult);
+ nsresult CacheMisses();
+
+ RefPtr<nsUrlClassifierDBService> mDBService;
+ UniquePtr<LookupResultArray> mResults;
+
+ // Completed results to send back to the worker for caching.
+ ConstCacheResultArray mCacheResults;
+
+ uint32_t mPendingCompletions;
+ nsCOMPtr<nsIUrlClassifierCallback> mCallback;
+};
+
+NS_IMPL_ISUPPORTS(nsUrlClassifierLookupCallback, nsIUrlClassifierLookupCallback,
+ nsIUrlClassifierHashCompleterCallback)
+
+nsUrlClassifierLookupCallback::~nsUrlClassifierLookupCallback() {
+ if (mCallback) {
+ NS_ReleaseOnMainThread("nsUrlClassifierLookupCallback::mCallback",
+ mCallback.forget());
+ }
+}
+
+NS_IMETHODIMP
+nsUrlClassifierLookupCallback::LookupComplete(
+ UniquePtr<LookupResultArray> results) {
+ NS_ASSERTION(
+ mResults == nullptr,
+ "Should only get one set of results per nsUrlClassifierLookupCallback!");
+
+ if (!results) {
+ HandleResults();
+ return NS_OK;
+ }
+
+ mResults = std::move(results);
+
+ // Check the results entries that need to be completed.
+ for (const auto& result : *mResults) {
+ // We will complete partial matches and matches that are stale.
+ if (!result->Confirmed()) {
+ nsCOMPtr<nsIUrlClassifierHashCompleter> completer;
+ nsCString gethashUrl;
+ nsresult rv;
+ nsCOMPtr<nsIUrlListManager> listManager =
+ do_GetService("@mozilla.org/url-classifier/listmanager;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = listManager->GetGethashUrl(result->mTableName, gethashUrl);
+ NS_ENSURE_SUCCESS(rv, rv);
+ LOG(("The match from %s needs to be completed at %s",
+ result->mTableName.get(), gethashUrl.get()));
+ // gethashUrls may be empty in 2 cases: test tables, and on startup where
+ // we may have found a prefix in an existing table before the listmanager
+ // has registered the table. In the second case we should not call
+ // complete.
+ if ((!gethashUrl.IsEmpty() ||
+ nsUrlClassifierUtils::IsTestTable(result->mTableName)) &&
+ mDBService->GetCompleter(result->mTableName,
+ getter_AddRefs(completer))) {
+ // Bug 1323953 - Send the first 4 bytes for completion no matter how
+ // long we matched the prefix.
+ nsresult rv = completer->Complete(result->PartialHash(), gethashUrl,
+ result->mTableName, this);
+ if (NS_SUCCEEDED(rv)) {
+ mPendingCompletions++;
+ }
+ } else {
+ // For tables with no hash completer, a complete hash match is
+ // good enough, we'll consider it is valid.
+ if (result->Complete()) {
+ result->mConfirmed = true;
+ LOG(("Skipping completion in a table without a valid completer (%s).",
+ result->mTableName.get()));
+ } else {
+ NS_WARNING(
+ "Partial match in a table without a valid completer, ignoring "
+ "partial match.");
+ }
+ }
+ }
+ }
+
+ LOG(
+ ("nsUrlClassifierLookupCallback::LookupComplete [%p] "
+ "%u pending completions",
+ this, mPendingCompletions));
+ if (mPendingCompletions == 0) {
+ // All results were complete, we're ready!
+ HandleResults();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierLookupCallback::CompletionFinished(nsresult status) {
+ if (LOG_ENABLED()) {
+ nsAutoCString errorName;
+ mozilla::GetErrorName(status, errorName);
+ LOG(("nsUrlClassifierLookupCallback::CompletionFinished [%p, %s]", this,
+ errorName.get()));
+ }
+
+ mPendingCompletions--;
+ if (mPendingCompletions == 0) {
+ HandleResults();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierLookupCallback::CompletionV2(const nsACString& aCompleteHash,
+ const nsACString& aTableName,
+ uint32_t aChunkId) {
+ LOG(("nsUrlClassifierLookupCallback::Completion [%p, %s, %d]", this,
+ PromiseFlatCString(aTableName).get(), aChunkId));
+
+ MOZ_ASSERT(!StringEndsWith(aTableName, "-proto"_ns));
+
+ RefPtr<CacheResultV2> result = new CacheResultV2();
+
+ result->table = aTableName;
+ result->prefix.Assign(aCompleteHash);
+ result->completion.Assign(aCompleteHash);
+ result->addChunk = aChunkId;
+
+ return ProcessComplete(result);
+}
+
+NS_IMETHODIMP
+nsUrlClassifierLookupCallback::CompletionV4(const nsACString& aPartialHash,
+ const nsACString& aTableName,
+ uint32_t aNegativeCacheDuration,
+ nsIArray* aFullHashes) {
+ LOG(("nsUrlClassifierLookupCallback::CompletionV4 [%p, %s, %d]", this,
+ PromiseFlatCString(aTableName).get(), aNegativeCacheDuration));
+
+ MOZ_ASSERT(StringEndsWith(aTableName, "-proto"_ns));
+
+ if (!aFullHashes) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (aNegativeCacheDuration > MAXIMUM_NEGATIVE_CACHE_DURATION_SEC) {
+ LOG(
+ ("Negative cache duration too large, clamping it down to"
+ "a reasonable value."));
+ aNegativeCacheDuration = MAXIMUM_NEGATIVE_CACHE_DURATION_SEC;
+ }
+
+ RefPtr<CacheResultV4> result = new CacheResultV4();
+
+ int64_t nowSec = PR_Now() / PR_USEC_PER_SEC;
+
+ result->table = aTableName;
+ result->prefix.Assign(aPartialHash);
+ result->response.negativeCacheExpirySec = nowSec + aNegativeCacheDuration;
+
+ // Fill in positive cache entries.
+ uint32_t fullHashCount = 0;
+ nsresult rv = aFullHashes->GetLength(&fullHashCount);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ for (uint32_t i = 0; i < fullHashCount; i++) {
+ nsCOMPtr<nsIFullHashMatch> match = do_QueryElementAt(aFullHashes, i);
+
+ nsCString fullHash;
+ match->GetFullHash(fullHash);
+
+ uint32_t duration;
+ match->GetCacheDuration(&duration);
+
+ result->response.fullHashes.InsertOrUpdate(fullHash, nowSec + duration);
+ }
+
+ return ProcessComplete(result);
+}
+
+nsresult nsUrlClassifierLookupCallback::ProcessComplete(
+ RefPtr<CacheResult> aCacheResult) {
+ NS_ENSURE_ARG_POINTER(mResults);
+
+ if (!mCacheResults.AppendElement(aCacheResult, fallible)) {
+ // OK if this failed, we just won't cache the item.
+ }
+
+ // Check if this matched any of our results.
+ for (const auto& result : *mResults) {
+ // Now, see if it verifies a lookup
+ if (!result->mNoise && result->mTableName.Equals(aCacheResult->table) &&
+ aCacheResult->findCompletion(result->CompleteHash())) {
+ result->mProtocolConfirmed = true;
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult nsUrlClassifierLookupCallback::HandleResults() {
+ if (!mResults) {
+ // No results, this URI is clean.
+ LOG(("nsUrlClassifierLookupCallback::HandleResults [%p, no results]",
+ this));
+ return mCallback->HandleEvent(""_ns);
+ }
+ MOZ_ASSERT(mPendingCompletions == 0,
+ "HandleResults() should never be "
+ "called while there are pending completions");
+
+ LOG(("nsUrlClassifierLookupCallback::HandleResults [%p, %zu results]", this,
+ mResults->Length()));
+
+ nsCOMPtr<nsIUrlClassifierClassifyCallback> classifyCallback =
+ do_QueryInterface(mCallback);
+
+ nsTArray<nsCString> tables;
+ // Build a stringified list of result tables.
+ for (const auto& result : *mResults) {
+ // Leave out results that weren't confirmed, as their existence on
+ // the list can't be verified. Also leave out randomly-generated
+ // noise.
+ if (result->mNoise) {
+ LOG(("Skipping result %s from table %s (noise)",
+ result->PartialHashHex().get(), result->mTableName.get()));
+ continue;
+ }
+
+ if (!result->Confirmed()) {
+ LOG(("Skipping result %s from table %s (not confirmed)",
+ result->PartialHashHex().get(), result->mTableName.get()));
+ continue;
+ }
+
+ LOG(("Confirmed result %s from table %s", result->PartialHashHex().get(),
+ result->mTableName.get()));
+
+ if (tables.IndexOf(result->mTableName) == nsTArray<nsCString>::NoIndex) {
+ tables.AppendElement(result->mTableName);
+ }
+
+ if (classifyCallback) {
+ nsCString fullHashString;
+ result->hash.complete.ToString(fullHashString);
+ classifyCallback->HandleResult(result->mTableName, fullHashString);
+ }
+ }
+
+ // Some parts of this gethash request generated no hits at all.
+ // Save the prefixes we checked to prevent repeated requests.
+ CacheMisses();
+
+ // This hands ownership of the cache results array back to the worker
+ // thread.
+ mDBService->CacheCompletions(mCacheResults);
+ mCacheResults.Clear();
+
+ return mCallback->HandleEvent(StringJoin(","_ns, tables));
+}
+
+nsresult nsUrlClassifierLookupCallback::CacheMisses() {
+ MOZ_ASSERT(mResults);
+
+ for (const RefPtr<const LookupResult> result : *mResults) {
+ // Skip V4 because cache information is already included in the
+ // fullhash response so we don't need to manually add it here.
+ if (!result->mProtocolV2 || result->Confirmed() || result->mNoise) {
+ continue;
+ }
+
+ RefPtr<CacheResultV2> cacheResult = new CacheResultV2();
+
+ cacheResult->table = result->mTableName;
+ cacheResult->prefix = result->hash.fixedLengthPrefix;
+ cacheResult->miss = true;
+ if (!mCacheResults.AppendElement(cacheResult, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ return NS_OK;
+}
+
+struct Provider {
+ nsCString name;
+ uint8_t priority;
+};
+
+// Order matters
+// Provider which is not included in this table has the lowest priority 0
+static const Provider kBuiltInProviders[] = {
+ {"mozilla"_ns, 1},
+ {"google4"_ns, 2},
+ {"google"_ns, 3},
+};
+
+// -------------------------------------------------------------------------
+// Helper class for nsIURIClassifier implementation, handle classify result and
+// send back to nsIURIClassifier
+
+class nsUrlClassifierClassifyCallback final
+ : public nsIUrlClassifierCallback,
+ public nsIUrlClassifierClassifyCallback {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURLCLASSIFIERCALLBACK
+ NS_DECL_NSIURLCLASSIFIERCLASSIFYCALLBACK
+
+ explicit nsUrlClassifierClassifyCallback(nsIURIClassifierCallback* c)
+ : mCallback(c) {}
+
+ private:
+ struct ClassifyMatchedInfo {
+ nsCString table;
+ nsCString fullhash;
+ Provider provider;
+ nsresult errorCode;
+ };
+
+ ~nsUrlClassifierClassifyCallback() = default;
+
+ nsCOMPtr<nsIURIClassifierCallback> mCallback;
+ nsTArray<ClassifyMatchedInfo> mMatchedArray;
+};
+
+NS_IMPL_ISUPPORTS(nsUrlClassifierClassifyCallback, nsIUrlClassifierCallback,
+ nsIUrlClassifierClassifyCallback)
+
+NS_IMETHODIMP
+nsUrlClassifierClassifyCallback::HandleEvent(const nsACString& tables) {
+ nsresult response = TablesToResponse(tables);
+ ClassifyMatchedInfo* matchedInfo = nullptr;
+
+ if (NS_FAILED(response)) {
+ // Filter all matched info which has correct response
+ // In the case multiple tables found, use the higher priority provider
+ nsTArray<ClassifyMatchedInfo> matches;
+ for (uint32_t i = 0; i < mMatchedArray.Length(); i++) {
+ if (mMatchedArray[i].errorCode == response &&
+ (!matchedInfo || matchedInfo->provider.priority <
+ mMatchedArray[i].provider.priority)) {
+ matchedInfo = &mMatchedArray[i];
+ }
+ }
+ }
+
+ nsCString provider = matchedInfo ? matchedInfo->provider.name : ""_ns;
+ nsCString fullhash = matchedInfo ? matchedInfo->fullhash : ""_ns;
+ nsCString table = matchedInfo ? matchedInfo->table : ""_ns;
+
+ mCallback->OnClassifyComplete(response, table, provider, fullhash);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierClassifyCallback::HandleResult(const nsACString& aTable,
+ const nsACString& aFullHash) {
+ LOG(
+ ("nsUrlClassifierClassifyCallback::HandleResult [%p, table %s full hash "
+ "%s]",
+ this, PromiseFlatCString(aTable).get(),
+ PromiseFlatCString(aFullHash).get()));
+
+ if (NS_WARN_IF(aTable.IsEmpty()) || NS_WARN_IF(aFullHash.IsEmpty())) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ ClassifyMatchedInfo* matchedInfo = mMatchedArray.AppendElement();
+ matchedInfo->table = aTable;
+ matchedInfo->fullhash = aFullHash;
+
+ nsUrlClassifierUtils* urlUtil = nsUrlClassifierUtils::GetInstance();
+ if (NS_WARN_IF(!urlUtil)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCString provider;
+ nsresult rv = urlUtil->GetProvider(aTable, provider);
+
+ matchedInfo->provider.name = NS_SUCCEEDED(rv) ? provider : ""_ns;
+ matchedInfo->provider.priority = 0;
+ for (uint8_t i = 0; i < ArrayLength(kBuiltInProviders); i++) {
+ if (kBuiltInProviders[i].name.Equals(matchedInfo->provider.name)) {
+ matchedInfo->provider.priority = kBuiltInProviders[i].priority;
+ }
+ }
+ matchedInfo->errorCode = TablesToResponse(aTable);
+
+ return NS_OK;
+}
+
+// -------------------------------------------------------------------------
+// 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 */
+already_AddRefed<nsUrlClassifierDBService>
+nsUrlClassifierDBService::GetInstance(nsresult* result) {
+ *result = NS_OK;
+ if (!sUrlClassifierDBService) {
+ sUrlClassifierDBService = new (fallible) nsUrlClassifierDBService();
+ if (!sUrlClassifierDBService) {
+ *result = NS_ERROR_OUT_OF_MEMORY;
+ return nullptr;
+ }
+
+ *result = sUrlClassifierDBService->Init();
+ if (NS_FAILED(*result)) {
+ return nullptr;
+ }
+ }
+ return do_AddRef(sUrlClassifierDBService);
+}
+
+nsUrlClassifierDBService::nsUrlClassifierDBService() : mInUpdate(false) {}
+
+nsUrlClassifierDBService::~nsUrlClassifierDBService() {
+ sUrlClassifierDBService = nullptr;
+}
+
+nsresult nsUrlClassifierDBService::ReadDisallowCompletionsTablesFromPrefs() {
+ nsAutoCString tables;
+
+ Preferences::GetCString(DISALLOW_COMPLETION_TABLE_PREF, tables);
+ Classifier::SplitTables(tables, mDisallowCompletionsTables);
+
+ return NS_OK;
+}
+
+nsresult nsUrlClassifierDBService::Init() {
+ MOZ_ASSERT(NS_IsMainThread(), "Must initialize DB service on main thread");
+
+ switch (XRE_GetProcessType()) {
+ case GeckoProcessType_Default:
+ // The parent process is supported.
+ break;
+ case GeckoProcessType_Content:
+ // In a content process, we simply forward all requests to the parent
+ // process, so we can skip the initialization steps here. Note that since
+ // we never register an observer, Shutdown() will also never be called in
+ // the content process.
+ return NS_OK;
+ default:
+ // No other process type is supported!
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ uint32_t hashNoise =
+ Preferences::GetUint(GETHASH_NOISE_PREF, GETHASH_NOISE_DEFAULT);
+ ReadDisallowCompletionsTablesFromPrefs();
+
+ // Force nsUrlClassifierUtils loading on main thread.
+ if (NS_WARN_IF(!nsUrlClassifierUtils::GetInstance())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Directory providers must also be accessed on the main thread.
+ nsresult rv;
+ nsCOMPtr<nsIFile> cacheDir;
+ rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
+ getter_AddRefs(cacheDir));
+ if (NS_FAILED(rv)) {
+ rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+ getter_AddRefs(cacheDir));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ // Start the background thread.
+ rv = NS_NewNamedThread("URL Classifier", &gDbBackgroundThread);
+ if (NS_FAILED(rv)) return rv;
+
+ mWorker = new (fallible) nsUrlClassifierDBServiceWorker();
+ if (!mWorker) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ rv = mWorker->Init(hashNoise, cacheDir, this);
+ if (NS_FAILED(rv)) {
+ mWorker = nullptr;
+ return rv;
+ }
+
+ // Proxy for calling the worker on the background thread
+ mWorkerProxy = new UrlClassifierDBServiceWorkerProxy(mWorker);
+ rv = mWorkerProxy->OpenDb();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // Add an observer for shutdown
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (!observerService) return NS_ERROR_FAILURE;
+
+ // The application is about to quit
+ observerService->AddObserver(this, "quit-application", false);
+ observerService->AddObserver(this, "profile-before-change", false);
+
+ Preferences::AddStrongObserver(this, DISALLOW_COMPLETION_TABLE_PREF);
+
+ return NS_OK;
+}
+
+// nsChannelClassifier is the only consumer of this interface.
+NS_IMETHODIMP
+nsUrlClassifierDBService::Classify(nsIPrincipal* aPrincipal,
+ nsIURIClassifierCallback* c, bool* aResult) {
+ NS_ENSURE_ARG(aPrincipal);
+ MOZ_ASSERT(c);
+ NS_ENSURE_ARG(aResult);
+
+ if (aPrincipal->IsSystemPrincipal()) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ if (XRE_IsContentProcess()) {
+ using namespace mozilla::dom;
+
+ ContentChild* content = ContentChild::GetSingleton();
+ MOZ_ASSERT(content);
+
+ auto actor = static_cast<URLClassifierChild*>(
+ content->AllocPURLClassifierChild(aPrincipal, aResult));
+ MOZ_ASSERT(actor);
+
+ if (!content->SendPURLClassifierConstructor(actor, aPrincipal, aResult)) {
+ *aResult = false;
+ return NS_ERROR_FAILURE;
+ }
+
+ actor->SetCallback(c);
+ return NS_OK;
+ }
+
+ NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
+
+ nsCOMPtr<nsIPermissionManager> permissionManager =
+ components::PermissionManager::Service();
+ if (NS_WARN_IF(!permissionManager)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ uint32_t perm;
+ nsresult rv = permissionManager->TestPermissionFromPrincipal(
+ aPrincipal, "safe-browsing"_ns, &perm);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (perm == nsIPermissionManager::ALLOW_ACTION) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ nsTArray<RefPtr<nsIUrlClassifierFeature>> features;
+ mozilla::net::UrlClassifierFeatureFactory::GetPhishingProtectionFeatures(
+ features);
+ if (features.IsEmpty()) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ // Casting to BasePrincipal, as we can't get InnerMost URI otherwise
+ auto* basePrincipal = BasePrincipal::Cast(aPrincipal);
+ rv = basePrincipal->GetURI(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
+
+ // Let's keep the features alive and release them on the correct thread.
+ RefPtr<FeatureHolder> holder =
+ FeatureHolder::Create(uri, features, nsIUrlClassifierFeature::blocklist);
+ if (NS_WARN_IF(!holder)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ uri = NS_GetInnermostURI(uri);
+ NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
+
+ nsUrlClassifierUtils* utilsService = nsUrlClassifierUtils::GetInstance();
+ if (NS_WARN_IF(!utilsService)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Canonicalize the url
+ nsAutoCString key;
+ rv = utilsService->GetKeyForURI(uri, key);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<nsUrlClassifierClassifyCallback> callback =
+ new (fallible) nsUrlClassifierClassifyCallback(c);
+ if (NS_WARN_IF(!callback)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // The rest is done async.
+ rv = LookupURI(key, holder, callback);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aResult = true;
+ return NS_OK;
+}
+
+class ThreatHitReportListener final : public nsIStreamListener {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+
+ ThreatHitReportListener() = default;
+
+ private:
+ ~ThreatHitReportListener() = default;
+};
+
+NS_IMPL_ISUPPORTS(ThreatHitReportListener, nsIStreamListener,
+ nsIRequestObserver)
+
+NS_IMETHODIMP
+ThreatHitReportListener::OnStartRequest(nsIRequest* aRequest) {
+ if (!LOG_ENABLED()) {
+ return NS_OK; // Nothing to do!
+ }
+
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
+ NS_ENSURE_TRUE(httpChannel, NS_OK);
+
+ nsresult rv, status;
+ rv = httpChannel->GetStatus(&status);
+ NS_ENSURE_SUCCESS(rv, NS_OK);
+ nsAutoCString errorName;
+ mozilla::GetErrorName(status, errorName);
+
+ uint32_t requestStatus;
+ rv = httpChannel->GetResponseStatus(&requestStatus);
+ NS_ENSURE_SUCCESS(rv, NS_OK);
+
+ nsAutoCString spec;
+ nsCOMPtr<nsIURI> uri;
+ rv = httpChannel->GetURI(getter_AddRefs(uri));
+ if (NS_SUCCEEDED(rv) && uri) {
+ uri->GetAsciiSpec(spec);
+ }
+ nsCOMPtr<nsIURLFormatter> urlFormatter =
+ do_GetService("@mozilla.org/toolkit/URLFormatterService;1");
+ nsAutoString trimmed;
+ rv = urlFormatter->TrimSensitiveURLs(NS_ConvertUTF8toUTF16(spec), trimmed);
+ NS_ENSURE_SUCCESS(rv, NS_OK);
+
+ LOG(
+ ("ThreatHitReportListener::OnStartRequest "
+ "(status=%s, code=%d, uri=%s, this=%p)",
+ errorName.get(), requestStatus, NS_ConvertUTF16toUTF8(trimmed).get(),
+ this));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ThreatHitReportListener::OnDataAvailable(nsIRequest* aRequest,
+ nsIInputStream* aInputStream,
+ uint64_t aOffset, uint32_t aCount) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ThreatHitReportListener::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
+ NS_ENSURE_TRUE(httpChannel, aStatus);
+
+ uint8_t netErrCode =
+ NS_FAILED(aStatus) ? mozilla::safebrowsing::NetworkErrorToBucket(aStatus)
+ : 0;
+ mozilla::Telemetry::Accumulate(
+ mozilla::Telemetry::URLCLASSIFIER_THREATHIT_NETWORK_ERROR, netErrCode);
+
+ uint32_t requestStatus;
+ nsresult rv = httpChannel->GetResponseStatus(&requestStatus);
+ NS_ENSURE_SUCCESS(rv, aStatus);
+ mozilla::Telemetry::Accumulate(
+ mozilla::Telemetry::URLCLASSIFIER_THREATHIT_REMOTE_STATUS,
+ mozilla::safebrowsing::HTTPStatusToBucket(requestStatus));
+
+ if (LOG_ENABLED()) {
+ nsAutoCString errorName;
+ mozilla::GetErrorName(aStatus, errorName);
+
+ nsAutoCString spec;
+ nsCOMPtr<nsIURI> uri;
+ rv = httpChannel->GetURI(getter_AddRefs(uri));
+ if (NS_SUCCEEDED(rv) && uri) {
+ uri->GetAsciiSpec(spec);
+ }
+ nsCOMPtr<nsIURLFormatter> urlFormatter =
+ do_GetService("@mozilla.org/toolkit/URLFormatterService;1");
+ nsString trimmed;
+ rv = urlFormatter->TrimSensitiveURLs(NS_ConvertUTF8toUTF16(spec), trimmed);
+ NS_ENSURE_SUCCESS(rv, aStatus);
+
+ LOG(
+ ("ThreatHitReportListener::OnStopRequest "
+ "(status=%s, code=%d, uri=%s, this=%p)",
+ errorName.get(), requestStatus, NS_ConvertUTF16toUTF8(trimmed).get(),
+ this));
+ }
+
+ return aStatus;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBService::SendThreatHitReport(nsIChannel* aChannel,
+ const nsACString& aProvider,
+ const nsACString& aList,
+ const nsACString& aFullHash) {
+ NS_ENSURE_ARG_POINTER(aChannel);
+
+ if (aProvider.IsEmpty()) {
+ LOG(("nsUrlClassifierDBService::SendThreatHitReport missing provider"));
+ return NS_ERROR_FAILURE;
+ }
+ if (aList.IsEmpty()) {
+ LOG(("nsUrlClassifierDBService::SendThreatHitReport missing list"));
+ return NS_ERROR_FAILURE;
+ }
+ if (aFullHash.IsEmpty()) {
+ LOG(("nsUrlClassifierDBService::SendThreatHitReport missing fullhash"));
+ return NS_ERROR_FAILURE;
+ }
+
+ nsPrintfCString reportUrlPref(
+ "browser.safebrowsing.provider.%s.dataSharingURL",
+ PromiseFlatCString(aProvider).get());
+
+ nsCOMPtr<nsIURLFormatter> formatter(
+ do_GetService("@mozilla.org/toolkit/URLFormatterService;1"));
+ if (!formatter) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsString urlStr;
+ nsresult rv =
+ formatter->FormatURLPref(NS_ConvertUTF8toUTF16(reportUrlPref), urlStr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (urlStr.IsEmpty() || u"about:blank"_ns.Equals(urlStr)) {
+ LOG(("%s is missing a ThreatHit data reporting URL.",
+ PromiseFlatCString(aProvider).get()));
+ return NS_OK;
+ }
+
+ nsUrlClassifierUtils* utilsService = nsUrlClassifierUtils::GetInstance();
+ if (NS_WARN_IF(!utilsService)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoCString reportBody;
+ rv =
+ utilsService->MakeThreatHitReport(aChannel, aList, aFullHash, reportBody);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIStringInputStream> sis(
+ do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID));
+ rv = sis->SetData(reportBody.get(), reportBody.Length());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ LOG(("Sending the following ThreatHit report to %s about %s: %s",
+ PromiseFlatCString(aProvider).get(), PromiseFlatCString(aList).get(),
+ reportBody.get()));
+
+ nsCOMPtr<nsIURI> reportURI;
+ rv = NS_NewURI(getter_AddRefs(reportURI), urlStr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t loadFlags = nsIRequest::LOAD_ANONYMOUS | // no cookies
+ nsIChannel::INHIBIT_CACHING |
+ nsIChannel::LOAD_BYPASS_CACHE;
+
+ nsCOMPtr<nsIChannel> reportChannel;
+ rv = NS_NewChannel(getter_AddRefs(reportChannel), reportURI,
+ nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER,
+ nullptr, // nsICookieJarSettings
+ nullptr, // aPerformanceStorage
+ nullptr, // aLoadGroup
+ nullptr, loadFlags);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsILoadInfo> loadInfo = reportChannel->LoadInfo();
+ mozilla::OriginAttributes attrs;
+ attrs.mFirstPartyDomain.AssignLiteral(NECKO_SAFEBROWSING_FIRST_PARTY_DOMAIN);
+ loadInfo->SetOriginAttributes(attrs);
+
+ nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(reportChannel));
+ NS_ENSURE_TRUE(uploadChannel, NS_ERROR_FAILURE);
+ rv = uploadChannel->SetUploadStream(sis, "application/x-protobuf"_ns, -1);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(reportChannel));
+ NS_ENSURE_TRUE(httpChannel, NS_ERROR_FAILURE);
+ rv = httpChannel->SetRequestMethod("POST"_ns);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // Disable keepalive.
+ rv = httpChannel->SetRequestHeader("Connection"_ns, "close"_ns, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<ThreatHitReportListener> listener = new ThreatHitReportListener();
+ rv = reportChannel->AsyncOpen(listener);
+ if (NS_FAILED(rv)) {
+ LOG(("Failure to send Safe Browsing ThreatHit report"));
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBService::Lookup(nsIPrincipal* aPrincipal,
+ const nsACString& aTables,
+ nsIUrlClassifierCallback* aCallback) {
+ // We don't expect someone with SystemPrincipal calls this API(See Bug
+ // 813897).
+ MOZ_ASSERT(!aPrincipal->IsSystemPrincipal());
+
+ NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
+
+ nsTArray<nsCString> tableArray;
+ Classifier::SplitTables(aTables, tableArray);
+
+ nsCOMPtr<nsIUrlClassifierFeature> feature;
+ nsresult rv = CreateFeatureWithTables(
+ "lookup"_ns, tableArray, nsTArray<nsCString>(), getter_AddRefs(feature));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIURI> uri;
+ // Casting to BasePrincipal, as we can't get InnerMost URI otherwise
+ auto* basePrincipal = BasePrincipal::Cast(aPrincipal);
+ rv = basePrincipal->GetURI(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
+
+ nsTArray<RefPtr<nsIUrlClassifierFeature>> features;
+ features.AppendElement(feature.get());
+
+ // Let's keep the features alive and release them on the correct thread.
+ RefPtr<FeatureHolder> holder =
+ FeatureHolder::Create(uri, features, nsIUrlClassifierFeature::blocklist);
+ if (NS_WARN_IF(!holder)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ uri = NS_GetInnermostURI(uri);
+ NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
+
+ nsUrlClassifierUtils* utilsService = nsUrlClassifierUtils::GetInstance();
+ if (NS_WARN_IF(!utilsService)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoCString key;
+ // Canonicalize the url
+ rv = utilsService->GetKeyForURI(uri, key);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return LookupURI(key, holder, aCallback);
+}
+
+nsresult nsUrlClassifierDBService::LookupURI(
+ const nsACString& aKey, FeatureHolder* aHolder,
+ nsIUrlClassifierCallback* aCallback) {
+ MOZ_ASSERT(aHolder);
+ MOZ_ASSERT(aCallback);
+
+ NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
+
+ // Create an nsUrlClassifierLookupCallback object. This object will
+ // take care of confirming partial hash matches if necessary before
+ // calling the client's callback.
+ nsCOMPtr<nsIUrlClassifierLookupCallback> callback =
+ new nsUrlClassifierLookupCallback(this, aCallback);
+
+ nsCOMPtr<nsIUrlClassifierLookupCallback> proxyCallback =
+ new UrlClassifierLookupCallbackProxy(callback);
+
+ // Queue this lookup and call the lookup function to flush the queue if
+ // necessary.
+ nsresult rv = mWorker->QueueLookup(aKey, aHolder, proxyCallback);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // This seems to just call HandlePendingLookups.
+ nsAutoCString dummy;
+ return mWorkerProxy->Lookup(nullptr, dummy, nullptr);
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBService::GetTables(nsIUrlClassifierCallback* c) {
+ NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
+
+ // The proxy callback uses the current thread.
+ nsCOMPtr<nsIUrlClassifierCallback> proxyCallback =
+ new UrlClassifierCallbackProxy(c);
+
+ return mWorkerProxy->GetTables(proxyCallback);
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBService::SetHashCompleter(
+ const nsACString& tableName, nsIUrlClassifierHashCompleter* completer) {
+ if (completer) {
+ mCompleters.InsertOrUpdate(tableName, completer);
+ } else {
+ mCompleters.Remove(tableName);
+ }
+ ClearLastResults();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBService::ClearLastResults() {
+ NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
+
+ return mWorkerProxy->ClearLastResults();
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBService::BeginUpdate(nsIUrlClassifierUpdateObserver* observer,
+ const nsACString& updateTables) {
+ NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
+
+ if (mInUpdate) {
+ LOG(("Already updating, not available"));
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ if (mWorker->IsBusyUpdating()) {
+ // |mInUpdate| used to work well because "notifying update observer"
+ // is synchronously done in Worker::FinishUpdate(). Even if the
+ // update observer hasn't been notified at this point, we can still
+ // dispatch BeginUpdate() since it will NOT be run until the
+ // previous Worker::FinishUpdate() returns.
+ //
+ // However, some tasks in Worker::FinishUpdate() have been moved to
+ // another thread. The update observer will NOT be notified when
+ // Worker::FinishUpdate() returns. If we only check |mInUpdate|,
+ // the following sequence might happen on worker thread:
+ //
+ // Worker::FinishUpdate() // for update 1
+ // Worker::BeginUpdate() // for update 2
+ // Worker::NotifyUpdateObserver() // for update 1
+ //
+ // So, we have to find out a way to reject BeginUpdate() right here
+ // if the previous update observer hasn't been notified.
+ //
+ // Directly probing the worker's state is the most lightweight solution.
+ // No lock is required since Worker::BeginUpdate() and
+ // Worker::NotifyUpdateObserver() are by nature mutual exclusive.
+ // (both run on worker thread.)
+ LOG(("The previous update observer hasn't been notified."));
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ mInUpdate = true;
+
+ // The proxy observer uses the current thread
+ nsCOMPtr<nsIUrlClassifierUpdateObserver> proxyObserver =
+ new UrlClassifierUpdateObserverProxy(observer);
+
+ return mWorkerProxy->BeginUpdate(proxyObserver, updateTables);
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBService::BeginStream(const nsACString& table) {
+ NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
+
+ return mWorkerProxy->BeginStream(table);
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBService::UpdateStream(const nsACString& aUpdateChunk) {
+ NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
+
+ return mWorkerProxy->UpdateStream(aUpdateChunk);
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBService::FinishStream() {
+ NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
+
+ return mWorkerProxy->FinishStream();
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBService::FinishUpdate() {
+ NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
+
+ mInUpdate = false;
+
+ return mWorkerProxy->FinishUpdate();
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBService::CancelUpdate() {
+ NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
+
+ mInUpdate = false;
+
+ return mWorkerProxy->CancelUpdate();
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBService::ResetDatabase() {
+ NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
+
+ if (mWorker->IsBusyUpdating()) {
+ LOG(("Failed to ResetDatabase because of the unfinished update."));
+ return NS_ERROR_FAILURE;
+ }
+
+ return mWorkerProxy->ResetDatabase();
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBService::ReloadDatabase() {
+ NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
+
+ 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, nsIUrlClassifierGetCacheCallback* aCallback) {
+ NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
+
+ return mWorkerProxy->GetCacheInfo(aTable, aCallback);
+}
+
+nsresult nsUrlClassifierDBService::CacheCompletions(
+ const ConstCacheResultArray& results) {
+ NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
+
+ return mWorkerProxy->CacheCompletions(results);
+}
+
+bool nsUrlClassifierDBService::CanComplete(const nsACString& aTableName) {
+ return !mDisallowCompletionsTables.Contains(aTableName);
+}
+
+bool nsUrlClassifierDBService::GetCompleter(
+ const nsACString& tableName, nsIUrlClassifierHashCompleter** completer) {
+ // If we have specified a completer, go ahead and query it. This is only
+ // used by tests.
+ if (mCompleters.Get(tableName, completer)) {
+ return true;
+ }
+
+ if (!CanComplete(tableName)) {
+ return false;
+ }
+
+ // Otherwise, call gethash to find the hash completions.
+ return NS_SUCCEEDED(
+ CallGetService(NS_URLCLASSIFIERHASHCOMPLETER_CONTRACTID, completer));
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBService::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
+ ReadDisallowCompletionsTablesFromPrefs();
+ } else if (!strcmp(aTopic, "quit-application")) {
+ // Tell the update thread to finish as soon as possible.
+ gShuttingDownThread = true;
+
+ // The code in ::Shutdown() is run on a 'profile-before-change' event and
+ // ensures that objects are freed by blocking on this freeing.
+ // We can however speed up the shutdown time by using the worker thread to
+ // release, in an earlier event, any objects that cannot affect an ongoing
+ // update on the update thread.
+ PreShutdown();
+ } else if (!strcmp(aTopic, "profile-before-change")) {
+ gShuttingDownThread = true;
+ Shutdown();
+ } else {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
+
+// Post a PreShutdown task to worker thread to release objects without blocking
+// main-thread. Notice that shutdown process may still be blocked by PreShutdown
+// task when ::Shutdown() is executed and synchronously waits for worker thread
+// to finish PreShutdown event.
+nsresult nsUrlClassifierDBService::PreShutdown() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (mWorkerProxy) {
+ mWorkerProxy->PreShutdown();
+ }
+
+ return NS_OK;
+}
+
+// Join the background thread if it exists.
+nsresult nsUrlClassifierDBService::Shutdown() {
+ LOG(("shutting down db service\n"));
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (!gDbBackgroundThread) {
+ return NS_OK;
+ }
+
+ Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_SHUTDOWN_TIME> timer;
+
+ mCompleters.Clear();
+
+ nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ if (prefs) {
+ prefs->RemoveObserver(DISALLOW_COMPLETION_TABLE_PREF, this);
+ }
+
+ // 1. Synchronize with worker thread and update thread by
+ // *synchronously* dispatching an event to worker thread
+ // for shutting down the update thread. The reason not
+ // shutting down update thread directly from main thread
+ // is to avoid racing for Classifier::mUpdateThread
+ // between main thread and the worker thread. (Both threads
+ // would access Classifier::mUpdateThread.)
+ // This event is dispatched unconditionally to avoid
+ // accessing mWorker->mClassifier on the main thread, which
+ // would be a race.
+ using Worker = nsUrlClassifierDBServiceWorker;
+ RefPtr<nsIRunnable> r = NewRunnableMethod(
+ "nsUrlClassifierDBServiceWorker::FlushAndDisableAsyncUpdate", mWorker,
+ &Worker::FlushAndDisableAsyncUpdate);
+ SyncRunnable::DispatchToThread(gDbBackgroundThread, r);
+ // At this point the update thread has been shut down and
+ // the worker thread should only have at most one event,
+ // which is the callback event.
+
+ // 2. Send CancelUpdate() event to notify the dangling update.
+ // (i.e. BeginUpdate is called but FinishUpdate is not.)
+ // and CloseDb() to clear mClassifier. They will be the last two
+ // events on the worker thread in the shutdown process.
+ DebugOnly<nsresult> rv;
+ rv = mWorkerProxy->CancelUpdate();
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to post 'cancel update' event");
+ rv = mWorkerProxy->CloseDb();
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to post 'close db' event");
+ mWorkerProxy = nullptr;
+
+ // 3. Invalidate XPCOM APIs by nulling out gDbBackgroundThread
+ // since every API checks gDbBackgroundThread first. This has
+ // to be done before calling nsIThread.shutdown because it
+ // will cause the pending events on the joining thread to
+ // be processed.
+ nsIThread* backgroundThread = nullptr;
+ std::swap(backgroundThread, gDbBackgroundThread);
+
+ // 4. Wait until the worker thread is down.
+ if (backgroundThread) {
+ backgroundThread->Shutdown();
+ NS_RELEASE(backgroundThread);
+ }
+
+ mWorker = nullptr;
+ return NS_OK;
+}
+
+nsIThread* nsUrlClassifierDBService::BackgroundThread() {
+ return gDbBackgroundThread;
+}
+
+// static
+bool nsUrlClassifierDBService::ShutdownHasStarted() {
+ return gShuttingDownThread;
+}
+
+// static
+nsUrlClassifierDBServiceWorker* nsUrlClassifierDBService::GetWorker() {
+ nsresult rv;
+ RefPtr<nsUrlClassifierDBService> service =
+ nsUrlClassifierDBService::GetInstance(&rv);
+ if (!service) {
+ return nullptr;
+ }
+
+ return service->mWorker;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBService::AsyncClassifyLocalWithFeatures(
+ nsIURI* aURI, const nsTArray<RefPtr<nsIUrlClassifierFeature>>& aFeatures,
+ nsIUrlClassifierFeature::listType aListType,
+ nsIUrlClassifierFeatureCallback* aCallback) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (gShuttingDownThread) {
+ return NS_ERROR_ABORT;
+ }
+
+ nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
+ NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
+
+ // Let's try to use the preferences.
+ if (AsyncClassifyLocalWithFeaturesUsingPreferences(uri, aFeatures, aListType,
+ aCallback)) {
+ return NS_OK;
+ }
+
+ nsUrlClassifierUtils* utilsService = nsUrlClassifierUtils::GetInstance();
+ if (NS_WARN_IF(!utilsService)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoCString key;
+ // Canonicalize the url
+ nsresult rv = utilsService->GetKeyForURI(uri, key);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (XRE_IsContentProcess()) {
+ using namespace mozilla::dom;
+ using namespace mozilla::ipc;
+
+ ContentChild* content = ContentChild::GetSingleton();
+ if (NS_WARN_IF(!content || content->IsShuttingDown())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ auto actor = new URLClassifierLocalChild();
+
+ nsTArray<IPCURLClassifierFeature> ipcFeatures;
+ for (nsIUrlClassifierFeature* feature : aFeatures) {
+ nsAutoCString name;
+ rv = feature->GetName(name);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ continue;
+ }
+
+ nsTArray<nsCString> tables;
+ rv = feature->GetTables(aListType, tables);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ continue;
+ }
+
+ nsAutoCString exceptionHostList;
+ if (aListType == nsIUrlClassifierFeature::blocklist) {
+ rv = feature->GetExceptionHostList(exceptionHostList);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ continue;
+ }
+ }
+
+ ipcFeatures.AppendElement(
+ IPCURLClassifierFeature(name, tables, exceptionHostList));
+ }
+
+ if (!content->SendPURLClassifierLocalConstructor(actor, aURI,
+ ipcFeatures)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ actor->SetFeaturesAndCallback(aFeatures, aCallback);
+ return NS_OK;
+ }
+
+ using namespace mozilla::Telemetry;
+ auto startTime = TimeStamp::Now(); // For telemetry.
+
+ // Let's keep the features alive and release them on the correct thread.
+ RefPtr<FeatureHolder> holder =
+ FeatureHolder::Create(aURI, aFeatures, aListType);
+ if (NS_WARN_IF(!holder)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ auto worker = mWorker;
+
+ // Since aCallback will be passed around threads...
+ nsMainThreadPtrHandle<nsIUrlClassifierFeatureCallback> callback(
+ new nsMainThreadPtrHolder<nsIUrlClassifierFeatureCallback>(
+ "nsIURIClassifierFeatureCallback", aCallback));
+
+ nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+ "nsUrlClassifierDBService::AsyncClassifyLocalWithFeatures",
+ [worker, key, holder, callback, startTime]() -> void {
+ holder->DoLocalLookup(key, worker);
+
+ nsCOMPtr<nsIRunnable> cbRunnable = NS_NewRunnableFunction(
+ "nsUrlClassifierDBService::AsyncClassifyLocalWithFeatures",
+ [callback, holder, startTime]() -> void {
+ // Measure the time diff between calling and callback.
+ AccumulateTimeDelta(
+ Telemetry::URLCLASSIFIER_ASYNC_CLASSIFYLOCAL_TIME, startTime);
+
+ nsTArray<RefPtr<nsIUrlClassifierFeatureResult>> results;
+ holder->GetResults(results);
+
+ // |callback| is captured as const value so ...
+ auto cb =
+ const_cast<nsIUrlClassifierFeatureCallback*>(callback.get());
+ cb->OnClassifyComplete(results);
+ });
+
+ NS_DispatchToMainThread(cbRunnable);
+ });
+
+ return gDbBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL);
+}
+
+bool nsUrlClassifierDBService::AsyncClassifyLocalWithFeaturesUsingPreferences(
+ nsIURI* aURI, const nsTArray<RefPtr<nsIUrlClassifierFeature>>& aFeatures,
+ nsIUrlClassifierFeature::listType aListType,
+ nsIUrlClassifierFeatureCallback* aCallback) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsAutoCString host;
+ nsresult rv = aURI->GetHost(host);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ nsTArray<RefPtr<nsIUrlClassifierFeatureResult>> results;
+
+ // Let's see if we have special entries set by prefs.
+ for (nsIUrlClassifierFeature* feature : aFeatures) {
+ bool found = false;
+
+ nsAutoCString tableName;
+ rv = feature->HasHostInPreferences(host, aListType, tableName, &found);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ if (found) {
+ MOZ_ASSERT(!tableName.IsEmpty());
+ LOG(("URI found in preferences. Table: %s", tableName.get()));
+
+ RefPtr<mozilla::net::UrlClassifierFeatureResult> result =
+ new mozilla::net::UrlClassifierFeatureResult(aURI, feature,
+ tableName);
+ results.AppendElement(result);
+ }
+ }
+
+ if (results.IsEmpty()) {
+ return false;
+ }
+
+ // If we have some match using the preferences, we don't need to continue.
+ nsCOMPtr<nsIUrlClassifierFeatureCallback> callback(aCallback);
+ nsCOMPtr<nsIRunnable> cbRunnable = NS_NewRunnableFunction(
+ "nsUrlClassifierDBService::AsyncClassifyLocalWithFeatures",
+ [callback, results = std::move(results)]() {
+ callback->OnClassifyComplete(results);
+ });
+
+ NS_DispatchToMainThread(cbRunnable);
+ return true;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBService::GetFeatureByName(const nsACString& aFeatureName,
+ nsIUrlClassifierFeature** aFeature) {
+ NS_ENSURE_ARG_POINTER(aFeature);
+ nsCOMPtr<nsIUrlClassifierFeature> feature =
+ mozilla::net::UrlClassifierFeatureFactory::GetFeatureByName(aFeatureName);
+ if (NS_WARN_IF(!feature)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ feature.forget(aFeature);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBService::GetFeatureNames(nsTArray<nsCString>& aArray) {
+ mozilla::net::UrlClassifierFeatureFactory::GetFeatureNames(aArray);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierDBService::CreateFeatureWithTables(
+ const nsACString& aName, const nsTArray<nsCString>& aBlocklistTables,
+ const nsTArray<nsCString>& aEntitylistTables,
+ nsIUrlClassifierFeature** aFeature) {
+ NS_ENSURE_ARG_POINTER(aFeature);
+ nsCOMPtr<nsIUrlClassifierFeature> feature =
+ mozilla::net::UrlClassifierFeatureFactory::CreateFeatureWithTables(
+ aName, aBlocklistTables, aEntitylistTables);
+ if (NS_WARN_IF(!feature)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ feature.forget(aFeature);
+ return NS_OK;
+}
diff --git a/toolkit/components/url-classifier/nsUrlClassifierDBService.h b/toolkit/components/url-classifier/nsUrlClassifierDBService.h
new file mode 100644
index 0000000000..462d9a7b9d
--- /dev/null
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.h
@@ -0,0 +1,284 @@
+//* -*- 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/. */
+
+#ifndef nsUrlClassifierDBService_h_
+#define nsUrlClassifierDBService_h_
+
+#include <nsISupportsUtils.h>
+
+#include "nsID.h"
+#include "nsInterfaceHashtable.h"
+#include "nsIObserver.h"
+#include "nsUrlClassifierPrefixSet.h"
+#include "nsIUrlClassifierHashCompleter.h"
+#include "nsIUrlClassifierDBService.h"
+#include "nsIUrlClassifierInfo.h"
+#include "nsIURIClassifier.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/TimeStamp.h"
+
+#include "Entries.h"
+#include "LookupCache.h"
+#include "HashStore.h"
+
+// The hash length for a domain key.
+#define DOMAIN_LENGTH 4
+
+// The hash length of a partial hash entry.
+#define PARTIAL_LENGTH 4
+
+// The hash length of a complete hash entry.
+#define COMPLETE_LENGTH 32
+
+// Comma-separated lists
+#define DISALLOW_COMPLETION_TABLE_PREF "urlclassifier.disallow_completions"
+
+using namespace mozilla::safebrowsing;
+
+class nsUrlClassifierDBServiceWorker;
+class nsIThread;
+class nsIURI;
+class UrlClassifierDBServiceWorkerProxy;
+
+namespace mozilla {
+
+namespace safebrowsing {
+class Classifier;
+class ProtocolParser;
+
+nsresult TablesToResponse(const nsACString& tables);
+
+} // namespace safebrowsing
+
+namespace net {
+class AsyncUrlChannelClassifier;
+}
+
+} // namespace mozilla
+
+// This is a proxy class that just creates a background thread and delegates
+// calls to the background thread.
+class nsUrlClassifierDBService final : public nsIUrlClassifierDBService,
+ public nsIURIClassifier,
+ public nsIUrlClassifierInfo,
+ public nsIObserver {
+ friend class mozilla::net::AsyncUrlChannelClassifier;
+
+ public:
+ class FeatureHolder;
+
+ // This is thread safe. It throws an exception if the thread is busy.
+ nsUrlClassifierDBService();
+
+ nsresult Init();
+
+ static already_AddRefed<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(
+ const mozilla::safebrowsing::ConstCacheResultArray& results);
+
+ static nsIThread* BackgroundThread();
+
+ static bool ShutdownHasStarted();
+
+ private:
+ // This method is used only by AsyncUrlChannelClassifier. If you want to use
+ // it, please contact a safebrowsing/URL-Classifier peer.
+ static nsUrlClassifierDBServiceWorker* GetWorker();
+
+ // No subclassing
+ ~nsUrlClassifierDBService();
+
+ // Disallow copy constructor
+ nsUrlClassifierDBService(nsUrlClassifierDBService&);
+
+ nsresult LookupURI(const nsACString& aKey, FeatureHolder* aHolder,
+ nsIUrlClassifierCallback* c);
+
+ // Post an event to worker thread to release objects when receive
+ // 'quit-application'
+ nsresult PreShutdown();
+
+ // Close db connection and join the background thread if it exists.
+ nsresult Shutdown();
+
+ nsresult ReadDisallowCompletionsTablesFromPrefs();
+
+ // This method checks if the classification can be done just using
+ // preferences. It returns true if the operation has been completed.
+ bool AsyncClassifyLocalWithFeaturesUsingPreferences(
+ nsIURI* aURI, const nsTArray<RefPtr<nsIUrlClassifierFeature>>& aFeatures,
+ nsIUrlClassifierFeature::listType aListType,
+ nsIUrlClassifierFeatureCallback* aCallback);
+
+ RefPtr<nsUrlClassifierDBServiceWorker> mWorker;
+ RefPtr<UrlClassifierDBServiceWorkerProxy> mWorkerProxy;
+
+ nsInterfaceHashtable<nsCStringHashKey, nsIUrlClassifierHashCompleter>
+ mCompleters;
+
+ // TRUE if a BeginUpdate() has been called without an accompanying
+ // CancelUpdate()/FinishUpdate(). This is used to prevent competing
+ // updates, not to determine whether an update is still being
+ // processed.
+ bool mInUpdate;
+
+ // The list of tables that should never be hash completed.
+ nsTArray<nsCString> mDisallowCompletionsTables;
+
+ // Thread that we do the updates on.
+ static nsIThread* gDbBackgroundThread;
+};
+
+class nsUrlClassifierDBServiceWorker final : public nsIUrlClassifierDBService {
+ public:
+ nsUrlClassifierDBServiceWorker();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURLCLASSIFIERDBSERVICE
+
+ nsresult Init(uint32_t aGethashNoise, nsCOMPtr<nsIFile> aCacheDir,
+ nsUrlClassifierDBService* aDBService);
+
+ // Queue a lookup for the worker to perform, called in the main thread.
+ nsresult QueueLookup(const nsACString& aLookupKey,
+ nsUrlClassifierDBService::FeatureHolder* aFeatureHolder,
+ nsIUrlClassifierLookupCallback* aLallback);
+
+ // Handle any queued-up lookups. We call this function during long-running
+ // update operations to prevent lookups from blocking for too long.
+ nsresult HandlePendingLookups();
+
+ // Perform a blocking classifier lookup for a given url fragments set.
+ // Can be called on either the main thread or the worker thread.
+ nsresult DoSingleLocalLookupWithURIFragments(
+ const nsTArray<nsCString>& aSpecFragments, const nsACString& aTable,
+ LookupResultArray& aResults);
+
+ // Open the DB connection
+ nsresult OpenDb();
+
+ // Provide a way to forcibly close the db connection.
+ nsresult CloseDb();
+
+ nsresult PreShutdown();
+
+ nsresult CacheCompletions(const ConstCacheResultArray& aEntries);
+
+ // Used to probe the state of the worker thread. When the update begins,
+ // mUpdateObserver will be set. When the update finished, mUpdateObserver
+ // will be nulled out in NotifyUpdateObserver.
+ bool IsBusyUpdating() {
+ mozilla::MutexAutoLock lock(mUpdateObserverLock);
+ 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);
+
+ // Reset the in-progress update stream
+ void ResetStream();
+
+ // Reset the in-progress update
+ void ResetUpdate();
+
+ // Perform a classifier lookup for a given url.
+ nsresult DoLookup(const nsACString& spec,
+ nsUrlClassifierDBService::FeatureHolder* aFeatureHolder,
+ nsIUrlClassifierLookupCallback* c);
+
+ nsresult AddNoise(const Prefix aPrefix, const nsCString tableName,
+ uint32_t aCount, LookupResultArray& results);
+
+ nsresult CacheResultToTableUpdate(RefPtr<const CacheResult> aCacheResult,
+ RefPtr<TableUpdate> aUpdate);
+
+ bool IsSameAsLastResults(const ConstCacheResultArray& aResult) const;
+
+ RefPtr<mozilla::safebrowsing::Classifier> mClassifier;
+ // The class that actually parses the update chunks.
+ mozilla::UniquePtr<ProtocolParser> mProtocolParser;
+
+ // Directory where to store the SB databases.
+ nsCOMPtr<nsIFile> mCacheDir;
+
+ RefPtr<nsUrlClassifierDBService> mDBService;
+
+ TableUpdateArray mTableUpdates;
+
+ uint32_t mUpdateWaitSec;
+
+ // Stores the last results that triggered a table update.
+ ConstCacheResultArray mLastResults;
+
+ nsresult mUpdateStatus;
+ nsTArray<nsCString> mUpdateTables;
+
+ // The mUpdateObserver will be accessed by both the main thread and the worker
+ // thread. The lock is used to protect the mUpdateObserver.
+ mozilla::Mutex mUpdateObserverLock;
+
+ nsCOMPtr<nsIUrlClassifierUpdateObserver> mUpdateObserver
+ MOZ_GUARDED_BY(mUpdateObserverLock);
+ bool mInStream;
+
+ // The number of noise entries to add to the set of lookup results.
+ uint32_t mGethashNoise;
+
+ // Pending lookups are stored in a queue for processing. The queue
+ // is protected by mPendingLookupLock.
+ mozilla::Mutex mPendingLookupLock MOZ_UNANNOTATED;
+
+ class PendingLookup {
+ public:
+ mozilla::TimeStamp mStartTime;
+ nsCString mKey;
+ RefPtr<nsUrlClassifierDBService::FeatureHolder> mFeatureHolder;
+ nsCOMPtr<nsIUrlClassifierLookupCallback> mCallback;
+ };
+
+ // list of pending lookups
+ nsTArray<PendingLookup> mPendingLookups;
+
+#ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+ // The raw update response for debugging.
+ nsCString mRawTableUpdates;
+#endif
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsUrlClassifierDBService,
+ NS_URLCLASSIFIERDBSERVICE_CID)
+
+#endif // nsUrlClassifierDBService_h_
diff --git a/toolkit/components/url-classifier/nsUrlClassifierInfo.cpp b/toolkit/components/url-classifier/nsUrlClassifierInfo.cpp
new file mode 100644
index 0000000000..afc9d1d920
--- /dev/null
+++ b/toolkit/components/url-classifier/nsUrlClassifierInfo.cpp
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "nsComponentManagerUtils.h"
+#include "nsIMutableArray.h"
+#include "nsUrlClassifierInfo.h"
+
+NS_IMPL_ISUPPORTS(nsUrlClassifierPositiveCacheEntry,
+ nsIUrlClassifierPositiveCacheEntry)
+
+nsUrlClassifierPositiveCacheEntry::nsUrlClassifierPositiveCacheEntry()
+ : expirySec(-1) {}
+
+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() : expirySec(-1) {}
+
+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]);
+ }
+
+ NS_ADDREF(*aMatches = array);
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsUrlClassifierCacheInfo, nsIUrlClassifierCacheInfo)
+
+nsUrlClassifierCacheInfo::nsUrlClassifierCacheInfo() = default;
+
+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]);
+ }
+
+ NS_ADDREF(*aEntries = array);
+
+ return NS_OK;
+}
diff --git a/toolkit/components/url-classifier/nsUrlClassifierInfo.h b/toolkit/components/url-classifier/nsUrlClassifierInfo.h
new file mode 100644
index 0000000000..3eec17bce8
--- /dev/null
+++ b/toolkit/components/url-classifier/nsUrlClassifierInfo.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef nsUrlClassifierInfo_h_
+#define nsUrlClassifierInfo_h_
+
+#include "nsIUrlClassifierInfo.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsTArray.h"
+
+class nsUrlClassifierPositiveCacheEntry final
+ : public nsIUrlClassifierPositiveCacheEntry {
+ public:
+ nsUrlClassifierPositiveCacheEntry();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURLCLASSIFIERPOSITIVECACHEENTRY
+
+ private:
+ ~nsUrlClassifierPositiveCacheEntry() = default;
+
+ public:
+ nsCString fullhash;
+
+ int64_t expirySec;
+};
+
+class nsUrlClassifierCacheEntry final : public nsIUrlClassifierCacheEntry {
+ public:
+ nsUrlClassifierCacheEntry();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURLCLASSIFIERCACHEENTRY
+
+ private:
+ ~nsUrlClassifierCacheEntry() = default;
+
+ 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() = default;
+
+ public:
+ nsCString table;
+
+ nsTArray<nsCOMPtr<nsIUrlClassifierCacheEntry>> entries;
+};
+
+#endif // nsUrlClassifierInfo_h_
diff --git a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
new file mode 100644
index 0000000000..20b55ea150
--- /dev/null
+++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
@@ -0,0 +1,597 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "nsUrlClassifierPrefixSet.h"
+#include "nsIUrlClassifierPrefixSet.h"
+#include "nsCOMPtr.h"
+#include "nsDebug.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsTArray.h"
+#include "nsString.h"
+#include "mozilla/Logging.h"
+#include "mozilla/StaticPrefs_browser.h"
+
+using namespace mozilla;
+
+// MOZ_LOG=UrlClassifierPrefixSet:5
+static LazyLogModule gUrlClassifierPrefixSetLog("UrlClassifierPrefixSet");
+#define LOG(args) \
+ MOZ_LOG(gUrlClassifierPrefixSetLog, mozilla::LogLevel::Debug, args)
+#define LOG_ENABLED() \
+ MOZ_LOG_TEST(gUrlClassifierPrefixSetLog, mozilla::LogLevel::Debug)
+
+NS_IMPL_ISUPPORTS(nsUrlClassifierPrefixSet, nsIUrlClassifierPrefixSet)
+
+nsUrlClassifierPrefixSet::nsUrlClassifierPrefixSet()
+ : mLock("nsUrlClassifierPrefixSet.mLock"), mTotalPrefixes(0) {}
+
+NS_IMETHODIMP
+nsUrlClassifierPrefixSet::Init(const nsACString& aName) {
+ mName = aName;
+
+ return NS_OK;
+}
+
+nsUrlClassifierPrefixSet::~nsUrlClassifierPrefixSet() {
+ for (uint32_t i = 0; i < mIndexDeltas.Length(); i++) {
+ mIndexDeltas[i].Clear();
+ }
+ mIndexDeltas.Clear();
+ mIndexPrefixes.Clear();
+}
+
+void nsUrlClassifierPrefixSet::Clear() {
+ LOG(("[%s] Clearing PrefixSet", mName.get()));
+ mIndexDeltas.Clear();
+ mIndexPrefixes.Clear();
+ mTotalPrefixes = 0;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierPrefixSet::SetPrefixes(const uint32_t* aArray,
+ uint32_t aLength) {
+ MutexAutoLock lock(mLock);
+
+ nsresult rv = NS_OK;
+ Clear();
+
+ if (aLength > 0) {
+ rv = MakePrefixSet(aArray, aLength);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ Clear(); // clear out any leftovers
+ }
+ }
+
+ MOZ_ASSERT_IF(mIndexDeltas.IsEmpty(),
+ mIndexPrefixes.Length() == mTotalPrefixes);
+ MOZ_ASSERT_IF(!mIndexDeltas.IsEmpty(),
+ mIndexPrefixes.Length() == mIndexDeltas.Length());
+ return rv;
+}
+
+nsresult nsUrlClassifierPrefixSet::MakePrefixSet(const uint32_t* aPrefixes,
+ uint32_t aLength) {
+ mLock.AssertCurrentThreadOwns();
+
+ MOZ_ASSERT(aPrefixes);
+ MOZ_ASSERT(aLength > 0);
+
+#ifdef DEBUG
+ for (uint32_t i = 1; i < aLength; i++) {
+ MOZ_ASSERT(aPrefixes[i] >= aPrefixes[i - 1]);
+ }
+#endif
+
+ uint32_t totalDeltas = 0;
+
+ // Request one memory space to store all the prefixes may lead to
+ // memory allocation failure on certain platforms(See Bug 1046038).
+ // So if required size to store all the prefixes exceeds defined
+ // threshold(512k), we divide prefixes into delta chunks instead. Note that
+ // the potential overhead of this approach is that it may reuqire more memory
+ // compared to store all prefixes in one array because of jemalloc's
+ // implementation.
+ if (aLength * sizeof(uint32_t) <
+ StaticPrefs::browser_safebrowsing_prefixset_max_array_size()) {
+ // Not over the threshold, store all prefixes into mIndexPrefixes.
+ // mIndexDeltas is empty in this case.
+ mIndexPrefixes.SetCapacity(aLength);
+ for (uint32_t i = 0; i < aLength; i++) {
+ mIndexPrefixes.AppendElement(aPrefixes[i]);
+ }
+ } else {
+ // Apply delta algorithm to split prefixes into smaller delta chunk.
+
+ // We estimate the capacity of mIndexPrefixes & mIndexDeltas by assuming
+ // each element in mIndexDeltas stores DELTAS_LIMITS deltas, so the
+ // number of indexed prefixes is round up of
+ // TotalPrefixes / (DELTA_LIMIT + 1)
+ // The estimation only works when the number of prefixes are over a
+ // certain limit, which means, arrays in mIndexDeltas are always full.
+ uint32_t estimateCapacity =
+ (aLength + (DELTAS_LIMIT + 1) - 1) / (DELTAS_LIMIT + 1);
+ mIndexPrefixes.SetCapacity(estimateCapacity);
+ mIndexDeltas.SetCapacity(estimateCapacity);
+
+ mIndexPrefixes.AppendElement(aPrefixes[0]);
+ mIndexDeltas.AppendElement();
+ mIndexDeltas.LastElement().SetCapacity(DELTAS_LIMIT);
+
+ uint32_t numOfDeltas = 0;
+ uint32_t previousItem = aPrefixes[0];
+ for (uint32_t i = 1; i < aLength; i++) {
+ if ((numOfDeltas >= DELTAS_LIMIT) ||
+ (aPrefixes[i] - previousItem >= MAX_INDEX_DIFF)) {
+ // Compact the previous element.
+ // Note there is always at least one element when we get here,
+ // because we created the first element before the loop.
+ mIndexDeltas.LastElement().Compact();
+ if (!mIndexDeltas.AppendElement(fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ mIndexDeltas.LastElement().SetCapacity(DELTAS_LIMIT);
+
+ if (!mIndexPrefixes.AppendElement(aPrefixes[i], fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ numOfDeltas = 0;
+ } else {
+ uint16_t delta = aPrefixes[i] - previousItem;
+ if (!mIndexDeltas.LastElement().AppendElement(delta, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ numOfDeltas++;
+ totalDeltas++;
+ }
+ previousItem = aPrefixes[i];
+ }
+
+ mIndexDeltas.LastElement().Compact();
+ mIndexDeltas.Compact();
+ mIndexPrefixes.Compact();
+
+ MOZ_ASSERT(mIndexPrefixes.Length() == mIndexDeltas.Length());
+ }
+
+ if (totalDeltas == 0) {
+ // We have to clear mIndexDeltas here because it is still possible
+ // that the delta generation algorithm produces no deltas at all. When that
+ // happens, mIndexDeltas is not empty, which conflicts with the assumption
+ // that when there is no delta, mIndexDeltas is empty.
+ mIndexDeltas.Clear();
+ }
+ mTotalPrefixes = aLength;
+
+ LOG(("Total number of indices: %d", aLength));
+ LOG(("Total number of deltas: %d", totalDeltas));
+ LOG(("Total number of delta chunks: %zu", mIndexDeltas.Length()));
+
+ return NS_OK;
+}
+
+nsresult nsUrlClassifierPrefixSet::GetPrefixesNative(
+ FallibleTArray<uint32_t>& aOutArray) {
+ MutexAutoLock lock(mLock);
+
+ if (!aOutArray.SetLength(mTotalPrefixes, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ uint32_t prefixIdxLength = mIndexPrefixes.Length();
+ uint32_t prefixCnt = 0;
+
+ for (uint32_t i = 0; i < prefixIdxLength; i++) {
+ uint32_t prefix = mIndexPrefixes[i];
+
+ if (prefixCnt >= mTotalPrefixes) {
+ return NS_ERROR_FAILURE;
+ }
+ aOutArray[prefixCnt++] = prefix;
+
+ if (mIndexDeltas.IsEmpty()) {
+ continue;
+ }
+
+ for (uint32_t j = 0; j < mIndexDeltas[i].Length(); j++) {
+ prefix += mIndexDeltas[i][j];
+ if (prefixCnt >= mTotalPrefixes) {
+ return NS_ERROR_FAILURE;
+ }
+ aOutArray[prefixCnt++] = prefix;
+ }
+ }
+
+ NS_ASSERTION(mTotalPrefixes == prefixCnt, "Lengths are inconsistent");
+ return NS_OK;
+}
+
+nsresult nsUrlClassifierPrefixSet::GetPrefixByIndex(
+ uint32_t aIndex, uint32_t* aOutPrefix) const {
+ NS_ENSURE_ARG_POINTER(aOutPrefix);
+
+ MutexAutoLock lock(mLock);
+ MOZ_ASSERT(aIndex < mTotalPrefixes);
+
+ // We can directly get the target index if the delta algorithm didn't apply.
+ if (mIndexDeltas.IsEmpty()) {
+ *aOutPrefix = mIndexPrefixes[aIndex];
+ return NS_OK;
+ }
+
+ // The prefix set was compressed by the delta algorithm, we have to iterate
+ // the delta index to find out the right bucket.
+ for (uint32_t i = 0; i < mIndexDeltas.Length(); i++) {
+ // The target index is in the current delta bucket.
+ if (aIndex <= mIndexDeltas[i].Length()) {
+ MOZ_ASSERT(aIndex <= DELTAS_LIMIT);
+
+ uint32_t prefix = mIndexPrefixes[i];
+
+ for (uint32_t j = 0; j < aIndex; j++) {
+ prefix += mIndexDeltas[i][j];
+ }
+
+ *aOutPrefix = prefix;
+ break;
+ }
+
+ aIndex -= mIndexDeltas[i].Length() + 1;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierPrefixSet::GetPrefixes(uint32_t* aCount, uint32_t** aPrefixes) {
+ // No need to get mLock here because this function does not directly touch
+ // the class's data members. (GetPrefixesNative() will get mLock, however.)
+
+ NS_ENSURE_ARG_POINTER(aCount);
+ *aCount = 0;
+ NS_ENSURE_ARG_POINTER(aPrefixes);
+ *aPrefixes = nullptr;
+
+ FallibleTArray<uint32_t> prefixes;
+ nsresult rv = GetPrefixesNative(prefixes);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ uint64_t itemCount = prefixes.Length();
+ uint32_t* prefixArray =
+ static_cast<uint32_t*>(moz_xmalloc(itemCount * sizeof(uint32_t)));
+
+ memcpy(prefixArray, prefixes.Elements(), sizeof(uint32_t) * itemCount);
+
+ *aCount = itemCount;
+ *aPrefixes = prefixArray;
+
+ return NS_OK;
+}
+
+uint32_t nsUrlClassifierPrefixSet::BinSearch(uint32_t start, uint32_t end,
+ uint32_t target) const {
+ mLock.AssertCurrentThreadOwns();
+
+ while (start != end && end >= start) {
+ uint32_t i = start + ((end - start) >> 1);
+ uint32_t value = mIndexPrefixes[i];
+ if (value < target) {
+ start = i + 1;
+ } else if (value > target) {
+ end = i - 1;
+ } else {
+ return i;
+ }
+ }
+ return end;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierPrefixSet::Contains(uint32_t aPrefix, bool* aFound) {
+ MutexAutoLock lock(mLock);
+
+ *aFound = false;
+
+ if (IsEmptyInternal()) {
+ return NS_OK;
+ }
+
+ uint32_t target = aPrefix;
+
+ // We want to do a "Price is Right" binary search, that is, we want to find
+ // the index of the value either equal to the target or the closest value
+ // that is less than the target.
+ //
+ if (target < mIndexPrefixes[0]) {
+ return NS_OK;
+ }
+
+ // |binsearch| does not necessarily return the correct index (when the
+ // target is not found) but rather it returns an index at least one away
+ // from the correct index.
+ // Because of this, we need to check if the target lies before the beginning
+ // of the indices.
+
+ uint32_t i = BinSearch(0, mIndexPrefixes.Length() - 1, target);
+ if (mIndexPrefixes[i] > target && i > 0) {
+ i--;
+ }
+
+ // Now search through the deltas for the target.
+ uint32_t diff = target - mIndexPrefixes[i];
+
+ if (!mIndexDeltas.IsEmpty()) {
+ uint32_t deltaSize = mIndexDeltas[i].Length();
+ uint32_t deltaIndex = 0;
+
+ while (diff > 0 && deltaIndex < deltaSize) {
+ diff -= mIndexDeltas[i][deltaIndex];
+ deltaIndex++;
+ }
+ }
+
+ if (diff == 0) {
+ *aFound = true;
+ }
+
+ return NS_OK;
+}
+
+size_t nsUrlClassifierPrefixSet::SizeOfIncludingThis(
+ mozilla::MallocSizeOf aMallocSizeOf) const {
+ MutexAutoLock lock(mLock);
+
+ size_t n = 0;
+ n += aMallocSizeOf(this);
+ n += mIndexDeltas.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ for (uint32_t i = 0; i < mIndexDeltas.Length(); i++) {
+ n += mIndexDeltas[i].ShallowSizeOfExcludingThis(aMallocSizeOf);
+ }
+ n += mIndexPrefixes.ShallowSizeOfExcludingThis(aMallocSizeOf);
+ return n;
+}
+
+bool nsUrlClassifierPrefixSet::IsEmptyInternal() const {
+ if (mIndexPrefixes.IsEmpty()) {
+ MOZ_ASSERT(mIndexDeltas.IsEmpty() && mTotalPrefixes == 0,
+ "If we're empty, there should be no leftovers.");
+ return true;
+ }
+
+ MOZ_ASSERT(mTotalPrefixes >= mIndexPrefixes.Length());
+ return false;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierPrefixSet::IsEmpty(bool* aEmpty) {
+ MutexAutoLock lock(mLock);
+
+ *aEmpty = IsEmptyInternal();
+ return NS_OK;
+}
+
+nsresult nsUrlClassifierPrefixSet::LoadPrefixes(nsCOMPtr<nsIInputStream>& in) {
+ MutexAutoLock lock(mLock);
+
+ mCanary.Check();
+ Clear();
+
+ uint32_t magic;
+ uint32_t read;
+
+ nsresult rv =
+ in->Read(reinterpret_cast<char*>(&magic), sizeof(uint32_t), &read);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE);
+
+ if (magic == PREFIXSET_VERSION_MAGIC) {
+ // Read the number of indexed prefixes
+ uint32_t indexSize;
+ rv = in->Read(reinterpret_cast<char*>(&indexSize), sizeof(uint32_t), &read);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE);
+
+ // Read the number of delta prefixes
+ uint32_t deltaSize;
+ rv = in->Read(reinterpret_cast<char*>(&deltaSize), sizeof(uint32_t), &read);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE);
+
+ if (indexSize == 0) {
+ LOG(("[%s] Stored PrefixSet is empty!", mName.get()));
+ return NS_OK;
+ }
+
+ if (!mIndexPrefixes.SetLength(indexSize, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ mTotalPrefixes = indexSize;
+ if (deltaSize > (indexSize * DELTAS_LIMIT)) {
+ return NS_ERROR_FILE_CORRUPTED;
+ }
+
+ // Read index prefixes
+ uint32_t toRead = indexSize * sizeof(uint32_t);
+ rv = in->Read(reinterpret_cast<char*>(mIndexPrefixes.Elements()), toRead,
+ &read);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(read == toRead, NS_ERROR_FAILURE);
+
+ if (deltaSize) {
+ nsTArray<uint32_t> indexStarts;
+
+ if (!indexStarts.SetLength(indexSize, fallible) ||
+ !mIndexDeltas.SetLength(indexSize, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Read index start array to construct mIndexDeltas
+ rv = in->Read(reinterpret_cast<char*>(indexStarts.Elements()), toRead,
+ &read);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(read == toRead, NS_ERROR_FAILURE);
+
+ if (indexStarts[0] != 0) {
+ return NS_ERROR_FILE_CORRUPTED;
+ }
+
+ for (uint32_t i = 0; i < indexSize; i++) {
+ uint32_t numInDelta = i == indexSize - 1
+ ? deltaSize - indexStarts[i]
+ : indexStarts[i + 1] - indexStarts[i];
+ if (numInDelta > DELTAS_LIMIT) {
+ return NS_ERROR_FILE_CORRUPTED;
+ }
+ if (numInDelta > 0) {
+ if (!mIndexDeltas[i].SetLength(numInDelta, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ mTotalPrefixes += numInDelta;
+ toRead = numInDelta * sizeof(uint16_t);
+ rv = in->Read(reinterpret_cast<char*>(mIndexDeltas[i].Elements()),
+ toRead, &read);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(read == toRead, NS_ERROR_FAILURE);
+ }
+ }
+ } else {
+ mIndexDeltas.Clear();
+ }
+ } else {
+ LOG(("[%s] Version magic mismatch, not loading", mName.get()));
+ return NS_ERROR_FILE_CORRUPTED;
+ }
+
+ MOZ_ASSERT_IF(mIndexDeltas.IsEmpty(),
+ mIndexPrefixes.Length() == mTotalPrefixes);
+ MOZ_ASSERT_IF(!mIndexDeltas.IsEmpty(),
+ mIndexPrefixes.Length() == mIndexDeltas.Length());
+ LOG(("[%s] Loading PrefixSet successful (%u total prefixes)", mName.get(),
+ mTotalPrefixes));
+
+ return NS_OK;
+}
+
+uint32_t nsUrlClassifierPrefixSet::CalculatePreallocateSize() const {
+ uint32_t fileSize = 4 * sizeof(uint32_t);
+ MutexAutoLock lock(mLock);
+
+ MOZ_RELEASE_ASSERT(mTotalPrefixes >= mIndexPrefixes.Length());
+ uint32_t deltas = mTotalPrefixes - mIndexPrefixes.Length();
+ fileSize += mIndexPrefixes.Length() * sizeof(uint32_t);
+ if (deltas) {
+ MOZ_ASSERT(mIndexPrefixes.Length() == mIndexDeltas.Length());
+
+ fileSize += mIndexPrefixes.Length() * sizeof(uint32_t);
+ fileSize += mIndexDeltas.Length() * sizeof(uint32_t);
+ fileSize += deltas * sizeof(uint16_t);
+ }
+ return fileSize;
+}
+
+uint32_t nsUrlClassifierPrefixSet::Length() const {
+ MutexAutoLock lock(mLock);
+
+ return mTotalPrefixes;
+}
+
+nsresult nsUrlClassifierPrefixSet::WritePrefixes(
+ nsCOMPtr<nsIOutputStream>& out) const {
+ MutexAutoLock lock(mLock);
+
+ MOZ_ASSERT_IF(mIndexDeltas.IsEmpty(),
+ mIndexPrefixes.Length() == mTotalPrefixes);
+ MOZ_ASSERT_IF(!mIndexDeltas.IsEmpty(),
+ mIndexPrefixes.Length() == mIndexDeltas.Length());
+
+ mCanary.Check();
+
+ uint32_t written;
+ uint32_t writelen = sizeof(uint32_t);
+ const uint32_t magic = PREFIXSET_VERSION_MAGIC;
+ nsresult rv =
+ out->Write(reinterpret_cast<const char*>(&magic), writelen, &written);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
+
+ const uint32_t indexSize = mIndexPrefixes.Length();
+ if (mIndexDeltas.IsEmpty()) {
+ if (NS_WARN_IF(mTotalPrefixes != indexSize)) {
+ LOG(("[%s] mIndexPrefixes doesn't have the same length as mTotalPrefixes",
+ mName.get()));
+ return NS_ERROR_FAILURE;
+ }
+ } else {
+ if (NS_WARN_IF(mIndexDeltas.Length() != indexSize)) {
+ LOG(("[%s] mIndexPrefixes doesn't have the same length as mIndexDeltas",
+ mName.get()));
+ return NS_ERROR_FAILURE;
+ }
+ }
+ uint32_t totalDeltas = 0;
+
+ // Store the shape of mIndexDeltas by noting at which "count" of total
+ // indexes a new subarray starts. This is slightly cumbersome but keeps
+ // file format compatibility.
+ // If we ever update the format, we can gain space by storing the delta
+ // subarray sizes, which fit in bytes.
+ nsTArray<uint32_t> indexStarts;
+ if (!mIndexDeltas.IsEmpty()) {
+ if (!indexStarts.SetCapacity(indexSize + 1, fallible)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ indexStarts.AppendElement(0);
+
+ for (uint32_t i = 0; i < indexSize; i++) {
+ uint32_t deltaLength = mIndexDeltas[i].Length();
+ totalDeltas += deltaLength;
+ indexStarts.AppendElement(totalDeltas);
+ }
+ indexStarts.RemoveLastElement(); // we don't use the last element
+ MOZ_ASSERT(indexStarts.Length() == indexSize);
+ }
+
+ rv =
+ out->Write(reinterpret_cast<const char*>(&indexSize), writelen, &written);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
+
+ rv = out->Write(reinterpret_cast<const char*>(&totalDeltas), writelen,
+ &written);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
+
+ writelen = indexSize * sizeof(uint32_t);
+ rv = out->Write(reinterpret_cast<const char*>(mIndexPrefixes.Elements()),
+ writelen, &written);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
+
+ if (!mIndexDeltas.IsEmpty()) {
+ MOZ_ASSERT(!indexStarts.IsEmpty() && totalDeltas > 0);
+ rv = out->Write(reinterpret_cast<const char*>(indexStarts.Elements()),
+ writelen, &written);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
+
+ for (uint32_t i = 0; i < indexSize; i++) {
+ writelen = mIndexDeltas[i].Length() * sizeof(uint16_t);
+ rv = out->Write(reinterpret_cast<const char*>(mIndexDeltas[i].Elements()),
+ writelen, &written);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
+ }
+ }
+
+ LOG(("[%s] Writing PrefixSet successful", mName.get()));
+
+ return NS_OK;
+}
diff --git a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h
new file mode 100644
index 0000000000..dac631359e
--- /dev/null
+++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h
@@ -0,0 +1,90 @@
+//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef nsUrlClassifierPrefixSet_h_
+#define nsUrlClassifierPrefixSet_h_
+
+#include "nsIUrlClassifierPrefixSet.h"
+#include "nsISupports.h"
+#include "nsToolkitCompsCID.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "mozilla/FileUtils.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Poison.h"
+
+class nsIInputStream;
+class nsIOutputStream;
+
+namespace mozilla {
+namespace safebrowsing {
+
+class VariableLengthPrefixSet;
+
+} // namespace safebrowsing
+} // namespace mozilla
+
+class nsUrlClassifierPrefixSet final : public nsIUrlClassifierPrefixSet {
+ public:
+ nsUrlClassifierPrefixSet();
+
+ NS_IMETHOD Init(const nsACString& aName) override;
+ NS_IMETHOD SetPrefixes(const uint32_t* aArray, uint32_t aLength) override;
+ NS_IMETHOD GetPrefixes(uint32_t* aCount, uint32_t** aPrefixes) override;
+ NS_IMETHOD Contains(uint32_t aPrefix, bool* aFound) override;
+ NS_IMETHOD IsEmpty(bool* aEmpty) override;
+
+ nsresult GetPrefixesNative(FallibleTArray<uint32_t>& aOutArray);
+ nsresult GetPrefixByIndex(uint32_t aIndex, uint32_t* aOutPrefix) const;
+ nsresult WritePrefixes(nsCOMPtr<nsIOutputStream>& out) const;
+ nsresult LoadPrefixes(nsCOMPtr<nsIInputStream>& in);
+ uint32_t CalculatePreallocateSize() const;
+ uint32_t Length() const;
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ friend class mozilla::safebrowsing::VariableLengthPrefixSet;
+
+ private:
+ virtual ~nsUrlClassifierPrefixSet();
+
+ static const uint32_t DELTAS_LIMIT = 120;
+ static const uint32_t MAX_INDEX_DIFF = (1 << 16);
+ static const uint32_t PREFIXSET_VERSION_MAGIC = 1;
+
+ void Clear() MOZ_REQUIRES(mLock);
+ nsresult MakePrefixSet(const uint32_t* aArray, uint32_t aLength)
+ MOZ_REQUIRES(mLock);
+ uint32_t BinSearch(uint32_t start, uint32_t end, uint32_t target) const
+ MOZ_REQUIRES(mLock);
+ bool IsEmptyInternal() const MOZ_REQUIRES(mLock);
+
+ // Lock to prevent races between the url-classifier thread (which does most
+ // of the operations) and the main thread (which does memory reporting).
+ // It should be held for all operations between Init() and destruction that
+ // touch this class's data members.
+ mutable mozilla::Mutex mLock;
+ // list of fully stored prefixes, that also form the
+ // start of a run of deltas in mIndexDeltas.
+ nsTArray<uint32_t> mIndexPrefixes MOZ_GUARDED_BY(mLock);
+ // array containing arrays of deltas from indices.
+ // Index to the place that matches the closest lower
+ // prefix from mIndexPrefix. Then every "delta" corresponds
+ // to a prefix in the PrefixSet.
+ // This array could be empty when we decide to store all the prefixes
+ // in mIndexPrefixes.
+ nsTArray<nsTArray<uint16_t> > mIndexDeltas MOZ_GUARDED_BY(mLock);
+
+ // how many prefixes we have.
+ uint32_t mTotalPrefixes MOZ_GUARDED_BY(mLock);
+
+ nsCString mName; // Set in Init() only
+ mozilla::CorruptionCanary mCanary;
+};
+
+#endif
diff --git a/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp b/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp
new file mode 100644
index 0000000000..85caeaa728
--- /dev/null
+++ b/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp
@@ -0,0 +1,302 @@
+/* -*- 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 "nsUrlClassifierProxies.h"
+#include "nsUrlClassifierDBService.h"
+
+#include "mozilla/SyncRunnable.h"
+#include "Classifier.h"
+
+using namespace mozilla;
+using namespace mozilla::safebrowsing;
+using mozilla::NewRunnableMethod;
+
+static nsresult DispatchToWorkerThread(nsIRunnable* r) {
+ nsIThread* t = nsUrlClassifierDBService::BackgroundThread();
+ if (!t) return NS_ERROR_FAILURE;
+
+ return t->Dispatch(r, NS_DISPATCH_NORMAL);
+}
+
+NS_IMPL_ISUPPORTS(UrlClassifierDBServiceWorkerProxy, nsIUrlClassifierDBService)
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::Lookup(nsIPrincipal* aPrincipal,
+ const nsACString& aTables,
+ nsIUrlClassifierCallback* aCB) {
+ nsCOMPtr<nsIRunnable> r =
+ new LookupRunnable(mTarget, aPrincipal, aTables, aCB);
+ return DispatchToWorkerThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::LookupRunnable::Run() {
+ (void)mTarget->Lookup(mPrincipal, mLookupTables, mCB);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::GetTables(nsIUrlClassifierCallback* aCB) {
+ nsCOMPtr<nsIRunnable> r = new GetTablesRunnable(mTarget, aCB);
+ return DispatchToWorkerThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::GetTablesRunnable::Run() {
+ mTarget->GetTables(mCB);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::SetHashCompleter(
+ const nsACString&, nsIUrlClassifierHashCompleter*) {
+ MOZ_ASSERT_UNREACHABLE("This method should not be called!");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::BeginUpdate(
+ nsIUrlClassifierUpdateObserver* aUpdater, const nsACString& aTables) {
+ nsCOMPtr<nsIRunnable> r = new BeginUpdateRunnable(mTarget, aUpdater, aTables);
+ return DispatchToWorkerThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::BeginUpdateRunnable::Run() {
+ mTarget->BeginUpdate(mUpdater, mTables);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::BeginStream(const nsACString& aTable) {
+ nsCOMPtr<nsIRunnable> r = new BeginStreamRunnable(mTarget, aTable);
+ return DispatchToWorkerThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::BeginStreamRunnable::Run() {
+ mTarget->BeginStream(mTable);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::UpdateStream(
+ const nsACString& aUpdateChunk) {
+ nsCOMPtr<nsIRunnable> r = new UpdateStreamRunnable(mTarget, aUpdateChunk);
+ return DispatchToWorkerThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::UpdateStreamRunnable::Run() {
+ mTarget->UpdateStream(mUpdateChunk);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::FinishStream() {
+ nsCOMPtr<nsIRunnable> r =
+ NewRunnableMethod("nsUrlClassifierDBServiceWorker::FinishStream", mTarget,
+ &nsUrlClassifierDBServiceWorker::FinishStream);
+ return DispatchToWorkerThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::FinishUpdate() {
+ nsCOMPtr<nsIRunnable> r =
+ NewRunnableMethod("nsUrlClassifierDBServiceWorker::FinishUpdate", mTarget,
+ &nsUrlClassifierDBServiceWorker::FinishUpdate);
+ return DispatchToWorkerThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::CancelUpdate() {
+ nsCOMPtr<nsIRunnable> r =
+ NewRunnableMethod("nsUrlClassifierDBServiceWorker::CancelUpdate", mTarget,
+ &nsUrlClassifierDBServiceWorker::CancelUpdate);
+ return DispatchToWorkerThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::ResetDatabase() {
+ nsCOMPtr<nsIRunnable> r = NewRunnableMethod(
+ "nsUrlClassifierDBServiceWorker::ResetDatabase", mTarget,
+ &nsUrlClassifierDBServiceWorker::ResetDatabase);
+ return DispatchToWorkerThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::ReloadDatabase() {
+ nsCOMPtr<nsIRunnable> r = NewRunnableMethod(
+ "nsUrlClassifierDBServiceWorker::ReloadDatabase", mTarget,
+ &nsUrlClassifierDBServiceWorker::ReloadDatabase);
+ return DispatchToWorkerThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::ClearCache() {
+ nsCOMPtr<nsIRunnable> r =
+ NewRunnableMethod("nsUrlClassifierDBServiceWorker::ClearCache", mTarget,
+ &nsUrlClassifierDBServiceWorker::ClearCache);
+ return DispatchToWorkerThread(r);
+}
+
+nsresult UrlClassifierDBServiceWorkerProxy::OpenDb() const {
+ nsCOMPtr<nsIRunnable> r =
+ NewRunnableMethod("nsUrlClassifierDBServiceWorker::OpenDb", mTarget,
+ &nsUrlClassifierDBServiceWorker::OpenDb);
+ return DispatchToWorkerThread(r);
+}
+
+nsresult UrlClassifierDBServiceWorkerProxy::CloseDb() const {
+ nsCOMPtr<nsIRunnable> r =
+ NewRunnableMethod("nsUrlClassifierDBServiceWorker::CloseDb", mTarget,
+ &nsUrlClassifierDBServiceWorker::CloseDb);
+ return DispatchToWorkerThread(r);
+}
+
+nsresult UrlClassifierDBServiceWorkerProxy::PreShutdown() const {
+ nsCOMPtr<nsIRunnable> r =
+ NewRunnableMethod("nsUrlClassifierDBServiceWorker::PreShutdown", mTarget,
+ &nsUrlClassifierDBServiceWorker::PreShutdown);
+ return DispatchToWorkerThread(r);
+}
+
+nsresult UrlClassifierDBServiceWorkerProxy::CacheCompletions(
+ const ConstCacheResultArray& aEntries) const {
+ nsCOMPtr<nsIRunnable> r = new CacheCompletionsRunnable(mTarget, aEntries);
+ return DispatchToWorkerThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::CacheCompletionsRunnable::Run() {
+ mTarget->CacheCompletions(mEntries);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::ClearLastResults() {
+ nsCOMPtr<nsIRunnable> r = new ClearLastResultsRunnable(mTarget);
+ return DispatchToWorkerThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::ClearLastResultsRunnable::Run() {
+ return mTarget->ClearLastResults();
+}
+
+nsresult UrlClassifierDBServiceWorkerProxy::GetCacheInfo(
+ const nsACString& aTable,
+ nsIUrlClassifierGetCacheCallback* aCallback) const {
+ nsCOMPtr<nsIRunnable> r =
+ new GetCacheInfoRunnable(mTarget, aTable, aCallback);
+ return DispatchToWorkerThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::GetCacheInfoRunnable::Run() {
+ MOZ_ASSERT(mCallback);
+
+ mTarget->GetCacheInfo(mTable, getter_AddRefs(mCache));
+
+ nsCOMPtr<nsIRunnable> r = new GetCacheInfoCallbackRunnable(mCache, mCallback);
+ return NS_DispatchToMainThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::GetCacheInfoCallbackRunnable::Run() {
+ MOZ_ASSERT(NS_IsMainThread(), "Must be called on main thread");
+ MOZ_ASSERT(mCallback);
+
+ mCallback->OnGetCacheComplete(mCache);
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(UrlClassifierLookupCallbackProxy,
+ nsIUrlClassifierLookupCallback)
+
+NS_IMETHODIMP
+UrlClassifierLookupCallbackProxy::LookupComplete(
+ UniquePtr<LookupResultArray> aResults) {
+ nsCOMPtr<nsIRunnable> r =
+ new LookupCompleteRunnable(mTarget, std::move(aResults));
+ return NS_DispatchToMainThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierLookupCallbackProxy::LookupCompleteRunnable::Run() {
+ mTarget->LookupComplete(std::move(mResults));
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(UrlClassifierCallbackProxy, nsIUrlClassifierCallback)
+
+NS_IMETHODIMP
+UrlClassifierCallbackProxy::HandleEvent(const nsACString& aValue) {
+ nsCOMPtr<nsIRunnable> r = new HandleEventRunnable(mTarget, aValue);
+ return NS_DispatchToMainThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierCallbackProxy::HandleEventRunnable::Run() {
+ mTarget->HandleEvent(mValue);
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(UrlClassifierUpdateObserverProxy,
+ nsIUrlClassifierUpdateObserver)
+
+NS_IMETHODIMP
+UrlClassifierUpdateObserverProxy::UpdateUrlRequested(const nsACString& aURL,
+ const nsACString& aTable) {
+ nsCOMPtr<nsIRunnable> r =
+ new UpdateUrlRequestedRunnable(mTarget, aURL, aTable);
+ return NS_DispatchToMainThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierUpdateObserverProxy::UpdateUrlRequestedRunnable::Run() {
+ mTarget->UpdateUrlRequested(mURL, mTable);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UrlClassifierUpdateObserverProxy::StreamFinished(nsresult aStatus,
+ uint32_t aDelay) {
+ nsCOMPtr<nsIRunnable> r =
+ new StreamFinishedRunnable(mTarget, aStatus, aDelay);
+ return NS_DispatchToMainThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierUpdateObserverProxy::StreamFinishedRunnable::Run() {
+ mTarget->StreamFinished(mStatus, mDelay);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UrlClassifierUpdateObserverProxy::UpdateError(nsresult aError) {
+ nsCOMPtr<nsIRunnable> r = new UpdateErrorRunnable(mTarget, aError);
+ return NS_DispatchToMainThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierUpdateObserverProxy::UpdateErrorRunnable::Run() {
+ mTarget->UpdateError(mError);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+UrlClassifierUpdateObserverProxy::UpdateSuccess(uint32_t aRequestedTimeout) {
+ nsCOMPtr<nsIRunnable> r =
+ new UpdateSuccessRunnable(mTarget, aRequestedTimeout);
+ return NS_DispatchToMainThread(r);
+}
+
+NS_IMETHODIMP
+UrlClassifierUpdateObserverProxy::UpdateSuccessRunnable::Run() {
+ mTarget->UpdateSuccess(mRequestedTimeout);
+ return NS_OK;
+}
diff --git a/toolkit/components/url-classifier/nsUrlClassifierProxies.h b/toolkit/components/url-classifier/nsUrlClassifierProxies.h
new file mode 100644
index 0000000000..5739d7bf21
--- /dev/null
+++ b/toolkit/components/url-classifier/nsUrlClassifierProxies.h
@@ -0,0 +1,359 @@
+/* -*- 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/. */
+
+#ifndef nsUrlClassifierProxies_h
+#define nsUrlClassifierProxies_h
+
+#include "nsIUrlClassifierDBService.h"
+#include "nsUrlClassifierDBService.h"
+#include "nsProxyRelease.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Attributes.h"
+#include "nsIPrincipal.h"
+#include "LookupCache.h"
+
+/**
+ * Thread proxy from the main thread to the worker thread.
+ */
+class UrlClassifierDBServiceWorkerProxy final
+ : public nsIUrlClassifierDBService {
+ public:
+ explicit UrlClassifierDBServiceWorkerProxy(
+ nsUrlClassifierDBServiceWorker* aTarget)
+ : mTarget(aTarget) {}
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURLCLASSIFIERDBSERVICE
+
+ class LookupRunnable : public mozilla::Runnable {
+ public:
+ LookupRunnable(nsUrlClassifierDBServiceWorker* aTarget,
+ nsIPrincipal* aPrincipal, const nsACString& aTables,
+ nsIUrlClassifierCallback* aCB)
+ : mozilla::Runnable(
+ "UrlClassifierDBServiceWorkerProxy::LookupRunnable"),
+ mTarget(aTarget),
+ mPrincipal(aPrincipal),
+ mLookupTables(aTables),
+ mCB(aCB) {}
+
+ NS_DECL_NSIRUNNABLE
+
+ private:
+ const RefPtr<nsUrlClassifierDBServiceWorker> mTarget;
+ const nsCOMPtr<nsIPrincipal> mPrincipal;
+ const nsCString mLookupTables;
+ const nsCOMPtr<nsIUrlClassifierCallback> mCB;
+ };
+
+ class GetTablesRunnable : public mozilla::Runnable {
+ public:
+ GetTablesRunnable(nsUrlClassifierDBServiceWorker* aTarget,
+ nsIUrlClassifierCallback* aCB)
+ : mozilla::Runnable(
+ "UrlClassifierDBServiceWorkerProxy::GetTablesRunnable"),
+ mTarget(aTarget),
+ mCB(aCB) {}
+
+ NS_DECL_NSIRUNNABLE
+
+ private:
+ const RefPtr<nsUrlClassifierDBServiceWorker> mTarget;
+ const nsCOMPtr<nsIUrlClassifierCallback> mCB;
+ };
+
+ class BeginUpdateRunnable : public mozilla::Runnable {
+ public:
+ BeginUpdateRunnable(nsUrlClassifierDBServiceWorker* aTarget,
+ nsIUrlClassifierUpdateObserver* aUpdater,
+ const nsACString& aTables)
+ : mozilla::Runnable(
+ "UrlClassifierDBServiceWorkerProxy::BeginUpdateRunnable"),
+ mTarget(aTarget),
+ mUpdater(aUpdater),
+ mTables(aTables) {}
+
+ NS_DECL_NSIRUNNABLE
+
+ private:
+ const RefPtr<nsUrlClassifierDBServiceWorker> mTarget;
+ const nsCOMPtr<nsIUrlClassifierUpdateObserver> mUpdater;
+ const nsCString mTables;
+ };
+
+ class BeginStreamRunnable : public mozilla::Runnable {
+ public:
+ BeginStreamRunnable(nsUrlClassifierDBServiceWorker* aTarget,
+ const nsACString& aTable)
+ : mozilla::Runnable(
+ "UrlClassifierDBServiceWorkerProxy::BeginStreamRunnable"),
+ mTarget(aTarget),
+ mTable(aTable) {}
+
+ NS_DECL_NSIRUNNABLE
+
+ private:
+ const RefPtr<nsUrlClassifierDBServiceWorker> mTarget;
+ const nsCString mTable;
+ };
+
+ class UpdateStreamRunnable : public mozilla::Runnable {
+ public:
+ UpdateStreamRunnable(nsUrlClassifierDBServiceWorker* aTarget,
+ const nsACString& aUpdateChunk)
+ : mozilla::Runnable(
+ "UrlClassifierDBServiceWorkerProxy::UpdateStreamRunnable"),
+ mTarget(aTarget),
+ mUpdateChunk(aUpdateChunk) {}
+
+ NS_DECL_NSIRUNNABLE
+
+ private:
+ const RefPtr<nsUrlClassifierDBServiceWorker> mTarget;
+ const nsCString mUpdateChunk;
+ };
+
+ class CacheCompletionsRunnable : public mozilla::Runnable {
+ public:
+ CacheCompletionsRunnable(
+ nsUrlClassifierDBServiceWorker* aTarget,
+ const mozilla::safebrowsing::ConstCacheResultArray& aEntries)
+ : mozilla::Runnable(
+ "UrlClassifierDBServiceWorkerProxy::CacheCompletionsRunnable"),
+ mTarget(aTarget),
+ mEntries(aEntries.Clone()) {}
+
+ NS_DECL_NSIRUNNABLE
+
+ private:
+ const RefPtr<nsUrlClassifierDBServiceWorker> mTarget;
+ const mozilla::safebrowsing::ConstCacheResultArray mEntries;
+ };
+
+ class ClearLastResultsRunnable : public mozilla::Runnable {
+ public:
+ explicit ClearLastResultsRunnable(nsUrlClassifierDBServiceWorker* aTarget)
+ : mozilla::Runnable(
+ "UrlClassifierDBServiceWorkerProxy::ClearLastResultsRunnable"),
+ mTarget(aTarget) {}
+
+ NS_DECL_NSIRUNNABLE
+ private:
+ const RefPtr<nsUrlClassifierDBServiceWorker> mTarget;
+ };
+
+ class GetCacheInfoRunnable : public mozilla::Runnable {
+ public:
+ explicit GetCacheInfoRunnable(nsUrlClassifierDBServiceWorker* aTarget,
+ const nsACString& aTable,
+ nsIUrlClassifierGetCacheCallback* aCallback)
+ : mozilla::Runnable(
+ "UrlClassifierDBServiceWorkerProxy::GetCacheInfoRunnable"),
+ mTarget(aTarget),
+ mTable(aTable),
+ mCache(nullptr),
+ mCallback(new nsMainThreadPtrHolder<nsIUrlClassifierGetCacheCallback>(
+ "nsIUrlClassifierGetCacheCallback", aCallback)) {}
+
+ NS_DECL_NSIRUNNABLE
+ private:
+ const RefPtr<nsUrlClassifierDBServiceWorker> mTarget;
+ const nsCString mTable;
+ nsCOMPtr<nsIUrlClassifierCacheInfo> mCache;
+ const nsMainThreadPtrHandle<nsIUrlClassifierGetCacheCallback> mCallback;
+ };
+
+ class GetCacheInfoCallbackRunnable : public mozilla::Runnable {
+ public:
+ explicit GetCacheInfoCallbackRunnable(
+ nsIUrlClassifierCacheInfo* aCache,
+ const nsMainThreadPtrHandle<nsIUrlClassifierGetCacheCallback>&
+ aCallback)
+ : mozilla::Runnable(
+ "UrlClassifierDBServiceWorkerProxy::"
+ "GetCacheInfoCallbackRunnable"),
+ mCache(aCache),
+ mCallback(aCallback) {}
+
+ NS_DECL_NSIRUNNABLE
+ private:
+ nsCOMPtr<nsIUrlClassifierCacheInfo> mCache;
+ const nsMainThreadPtrHandle<nsIUrlClassifierGetCacheCallback> mCallback;
+ };
+
+ public:
+ nsresult OpenDb() const;
+ nsresult CloseDb() const;
+ nsresult PreShutdown() const;
+
+ nsresult CacheCompletions(
+ const mozilla::safebrowsing::ConstCacheResultArray& aEntries) const;
+
+ nsresult GetCacheInfo(const nsACString& aTable,
+ nsIUrlClassifierGetCacheCallback* aCallback) const;
+
+ private:
+ ~UrlClassifierDBServiceWorkerProxy() = default;
+
+ const RefPtr<nsUrlClassifierDBServiceWorker> mTarget;
+};
+
+// The remaining classes here are all proxies to the main thread
+
+class UrlClassifierLookupCallbackProxy final
+ : public nsIUrlClassifierLookupCallback {
+ public:
+ explicit UrlClassifierLookupCallbackProxy(
+ nsIUrlClassifierLookupCallback* aTarget)
+ : mTarget(new nsMainThreadPtrHolder<nsIUrlClassifierLookupCallback>(
+ "UrlClassifierLookupCallbackProxy::mTarget", aTarget)) {}
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURLCLASSIFIERLOOKUPCALLBACK
+
+ class LookupCompleteRunnable : public mozilla::Runnable {
+ public:
+ LookupCompleteRunnable(
+ const nsMainThreadPtrHandle<nsIUrlClassifierLookupCallback>& aTarget,
+ mozilla::UniquePtr<mozilla::safebrowsing::LookupResultArray> aResults)
+ : mozilla::Runnable(
+ "UrlClassifierLookupCallbackProxy::LookupCompleteRunnable"),
+ mTarget(aTarget),
+ mResults(std::move(aResults)) {}
+
+ NS_DECL_NSIRUNNABLE
+
+ private:
+ const nsMainThreadPtrHandle<nsIUrlClassifierLookupCallback> mTarget;
+ mozilla::UniquePtr<mozilla::safebrowsing::LookupResultArray> mResults;
+ };
+
+ private:
+ ~UrlClassifierLookupCallbackProxy() = default;
+
+ const nsMainThreadPtrHandle<nsIUrlClassifierLookupCallback> mTarget;
+};
+
+class UrlClassifierCallbackProxy final : public nsIUrlClassifierCallback {
+ public:
+ explicit UrlClassifierCallbackProxy(nsIUrlClassifierCallback* aTarget)
+ : mTarget(new nsMainThreadPtrHolder<nsIUrlClassifierCallback>(
+ "UrlClassifierCallbackProxy::mTarget", aTarget)) {}
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURLCLASSIFIERCALLBACK
+
+ class HandleEventRunnable : public mozilla::Runnable {
+ public:
+ HandleEventRunnable(
+ const nsMainThreadPtrHandle<nsIUrlClassifierCallback>& aTarget,
+ const nsACString& aValue)
+ : mozilla::Runnable("UrlClassifierCallbackProxy::HandleEventRunnable"),
+ mTarget(aTarget),
+ mValue(aValue) {}
+
+ NS_DECL_NSIRUNNABLE
+
+ private:
+ const nsMainThreadPtrHandle<nsIUrlClassifierCallback> mTarget;
+ const nsCString mValue;
+ };
+
+ private:
+ ~UrlClassifierCallbackProxy() = default;
+
+ const nsMainThreadPtrHandle<nsIUrlClassifierCallback> mTarget;
+};
+
+class UrlClassifierUpdateObserverProxy final
+ : public nsIUrlClassifierUpdateObserver {
+ public:
+ explicit UrlClassifierUpdateObserverProxy(
+ nsIUrlClassifierUpdateObserver* aTarget)
+ : mTarget(new nsMainThreadPtrHolder<nsIUrlClassifierUpdateObserver>(
+ "UrlClassifierUpdateObserverProxy::mTarget", aTarget)) {}
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURLCLASSIFIERUPDATEOBSERVER
+
+ class UpdateUrlRequestedRunnable : public mozilla::Runnable {
+ public:
+ UpdateUrlRequestedRunnable(
+ const nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver>& aTarget,
+ const nsACString& aURL, const nsACString& aTable)
+ : mozilla::Runnable(
+ "UrlClassifierUpdateObserverProxy::UpdateUrlRequestedRunnable"),
+ mTarget(aTarget),
+ mURL(aURL),
+ mTable(aTable) {}
+
+ NS_DECL_NSIRUNNABLE
+
+ private:
+ const nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver> mTarget;
+ const nsCString mURL;
+ const nsCString mTable;
+ };
+
+ class StreamFinishedRunnable : public mozilla::Runnable {
+ public:
+ StreamFinishedRunnable(
+ const nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver>& aTarget,
+ nsresult aStatus, uint32_t aDelay)
+ : mozilla::Runnable(
+ "UrlClassifierUpdateObserverProxy::StreamFinishedRunnable"),
+ mTarget(aTarget),
+ mStatus(aStatus),
+ mDelay(aDelay) {}
+
+ NS_DECL_NSIRUNNABLE
+
+ private:
+ const nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver> mTarget;
+ const nsresult mStatus;
+ const uint32_t mDelay;
+ };
+
+ class UpdateErrorRunnable : public mozilla::Runnable {
+ public:
+ UpdateErrorRunnable(
+ const nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver>& aTarget,
+ nsresult aError)
+ : mozilla::Runnable(
+ "UrlClassifierUpdateObserverProxy::UpdateErrorRunnable"),
+ mTarget(aTarget),
+ mError(aError) {}
+
+ NS_DECL_NSIRUNNABLE
+
+ private:
+ const nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver> mTarget;
+ const nsresult mError;
+ };
+
+ class UpdateSuccessRunnable : public mozilla::Runnable {
+ public:
+ UpdateSuccessRunnable(
+ const nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver>& aTarget,
+ uint32_t aRequestedTimeout)
+ : mozilla::Runnable(
+ "UrlClassifierUpdateObserverProxy::UpdateSuccessRunnable"),
+ mTarget(aTarget),
+ mRequestedTimeout(aRequestedTimeout) {}
+
+ NS_DECL_NSIRUNNABLE
+
+ private:
+ const nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver> mTarget;
+ const uint32_t mRequestedTimeout;
+ };
+
+ private:
+ ~UrlClassifierUpdateObserverProxy() = default;
+
+ const nsMainThreadPtrHandle<nsIUrlClassifierUpdateObserver> mTarget;
+};
+
+#endif // nsUrlClassifierProxies_h
diff --git a/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp
new file mode 100644
index 0000000000..641339c3ea
--- /dev/null
+++ b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp
@@ -0,0 +1,881 @@
+//* -*- 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 "mozilla/Components.h"
+#include "nsCRT.h"
+#include "nsIHttpChannel.h"
+#include "nsIObserverService.h"
+#include "nsIStringStream.h"
+#include "nsIUploadChannel.h"
+#include "nsIURI.h"
+#include "nsIUrlClassifierDBService.h"
+#include "nsIUrlClassifierRemoteSettingsService.h"
+#include "nsUrlClassifierUtils.h"
+#include "nsNetUtil.h"
+#include "nsStreamUtils.h"
+#include "nsStringStream.h"
+#include "nsUrlClassifierStreamUpdater.h"
+#include "mozilla/ErrorNames.h"
+#include "mozilla/Logging.h"
+#include "nsIInterfaceRequestor.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Try.h"
+#include "nsContentUtils.h"
+#include "nsIURLFormatter.h"
+#include "Classifier.h"
+#include "UrlClassifierTelemetryUtils.h"
+#include "mozilla/StaticPrefs_urlclassifier.h"
+
+using namespace mozilla::safebrowsing;
+using namespace mozilla;
+
+#define MIN_TIMEOUT_MS (60 * 1000)
+
+static const char* gQuitApplicationMessage = "quit-application";
+
+// Limit the list file size to 32mb
+const uint32_t MAX_FILE_SIZE = (32 * 1024 * 1024);
+
+// Retry delay when we failed to DownloadUpdate() if due to
+// DBService busy.
+const uint32_t FETCH_NEXT_REQUEST_RETRY_DELAY_MS = 1000;
+
+#undef LOG
+
+// MOZ_LOG=UrlClassifierStreamUpdater:5
+static mozilla::LazyLogModule gUrlClassifierStreamUpdaterLog(
+ "UrlClassifierStreamUpdater");
+#define LOG(args) TrimAndLog args
+#define LOG_ENABLED() \
+ MOZ_LOG_TEST(gUrlClassifierStreamUpdaterLog, mozilla::LogLevel::Debug)
+
+// Calls nsIURLFormatter::TrimSensitiveURLs to remove sensitive
+// info from the logging message.
+static MOZ_FORMAT_PRINTF(1, 2) void TrimAndLog(const char* aFmt, ...) {
+ nsString raw;
+
+ va_list ap;
+ va_start(ap, aFmt);
+ raw.AppendVprintf(aFmt, ap);
+ va_end(ap);
+
+ nsCOMPtr<nsIURLFormatter> urlFormatter =
+ do_GetService("@mozilla.org/toolkit/URLFormatterService;1");
+
+ nsString trimmed;
+ nsresult rv = urlFormatter->TrimSensitiveURLs(raw, trimmed);
+ if (NS_FAILED(rv)) {
+ trimmed.Truncate();
+ }
+
+ // Use %s so we aren't exposing random strings to printf interpolation.
+ MOZ_LOG(gUrlClassifierStreamUpdaterLog, mozilla::LogLevel::Debug,
+ ("%s", NS_ConvertUTF16toUTF8(trimmed).get()));
+}
+
+// This class does absolutely nothing, except pass requests onto the DBService.
+
+///////////////////////////////////////////////////////////////////////////////
+// nsIUrlClassiferStreamUpdater implementation
+// Handles creating/running the stream listener
+
+nsUrlClassifierStreamUpdater::nsUrlClassifierStreamUpdater()
+ : mIsUpdating(false),
+ mInitialized(false),
+ mDownloadError(false),
+ mBeganStream(false),
+ mChannel(nullptr),
+ mTelemetryClockStart(0) {
+ LOG(("nsUrlClassifierStreamUpdater init [this=%p]", this));
+}
+
+NS_IMPL_ISUPPORTS(nsUrlClassifierStreamUpdater, nsIUrlClassifierStreamUpdater,
+ nsIUrlClassifierUpdateObserver, nsIRequestObserver,
+ nsIStreamListener, nsIObserver, nsIInterfaceRequestor,
+ nsITimerCallback, nsINamed)
+
+/**
+ * Clear out the update.
+ */
+void nsUrlClassifierStreamUpdater::DownloadDone() {
+ LOG(("nsUrlClassifierStreamUpdater::DownloadDone [this=%p]", this));
+ mIsUpdating = false;
+
+ mPendingUpdates.Clear();
+ mDownloadError = false;
+ mCurrentRequest = nullptr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsIUrlClassifierStreamUpdater implementation
+
+nsresult nsUrlClassifierStreamUpdater::FetchUpdate(
+ nsIURI* aUpdateUrl, const nsACString& aRequestPayload, bool aIsPostRequest,
+ const nsACString& aStreamTable) {
+ mBeganStream = false;
+ nsresult rv;
+ // moz-sbrs is a customed scheme used by Safe Browsing. When the scheme is
+ // present in the update url, we'll fetch the data from
+ // UrlClassifierRemoteSettingsService.
+ if (aUpdateUrl->SchemeIs("moz-sbrs")) {
+#ifdef DEBUG
+ LOG(("Fetching update %s from RemoteSettings", aRequestPayload.Data()));
+#endif
+ nsCOMPtr<nsIUrlClassifierRemoteSettingsService> rsService =
+ do_GetService("@mozilla.org/url-classifier/list-service;1");
+ if (!rsService) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = rsService->FetchList(aRequestPayload, this);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ } else {
+#ifdef DEBUG
+ LOG(("Fetching update %s from %s", aRequestPayload.Data(),
+ aUpdateUrl->GetSpecOrDefault().get()));
+#endif
+ uint32_t loadFlags = nsIChannel::INHIBIT_CACHING |
+ nsIChannel::LOAD_BYPASS_CACHE |
+ nsIChannel::LOAD_BYPASS_URL_CLASSIFIER;
+
+ // SafeBrowsing update request should never be classified to make sure
+ // we can recover from a bad SafeBrowsing database.
+ rv = NS_NewChannel(getter_AddRefs(mChannel), aUpdateUrl,
+ nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER,
+ nullptr, // nsICookieJarSettings
+ nullptr, // aPerformanceStorage
+ nullptr, // aLoadGroup
+ this, // aInterfaceRequestor
+ loadFlags);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
+ mozilla::OriginAttributes attrs;
+ attrs.mFirstPartyDomain.AssignLiteral(
+ NECKO_SAFEBROWSING_FIRST_PARTY_DOMAIN);
+ loadInfo->SetOriginAttributes(attrs);
+ // allow deprecated HTTP request from SystemPrincipal
+ loadInfo->SetAllowDeprecatedSystemRequests(true);
+
+ if (!aIsPostRequest) {
+ // We use POST method to send our request in v2. In v4, the request
+ // needs to be embedded to the URL and use GET method to send.
+ // However, from the Chromium source code, a extended HTTP header has
+ // to be sent along with the request to make the request succeed.
+ // The following description is from Chromium source code:
+ //
+ // "The following header informs the envelope server (which sits in
+ // front of Google's stubby server) that the received GET request should
+ // be interpreted as a POST."
+ //
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = httpChannel->SetRequestHeader("X-HTTP-Method-Override"_ns, "POST"_ns,
+ false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else if (!aRequestPayload.IsEmpty()) {
+ rv = AddRequestBody(aRequestPayload);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Set the appropriate content type for file/data URIs, for unit testing
+ // purposes.
+ // This is only used for testing and should be deleted.
+ if (aUpdateUrl->SchemeIs("file") || aUpdateUrl->SchemeIs("data")) {
+ mChannel->SetContentType("application/vnd.google.safebrowsing-update"_ns);
+ } else {
+ // We assume everything else is an HTTP request.
+
+ // Disable keepalive.
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = httpChannel->SetRequestHeader("Connection"_ns, "close"_ns, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Make the request.
+ rv = mChannel->AsyncOpen(this);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ mTelemetryClockStart = PR_IntervalNow();
+ mStreamTable = aStreamTable;
+
+ if (StaticPrefs::urlclassifier_update_response_timeout_ms() >
+ StaticPrefs::urlclassifier_update_timeout_ms()) {
+ NS_WARNING(
+ "Safe Browsing response timeout is greater than the general "
+ "timeout. Disabling these update timeouts.");
+ return NS_OK;
+ }
+ MOZ_TRY_VAR(mResponseTimeoutTimer,
+ NS_NewTimerWithCallback(
+ this, StaticPrefs::urlclassifier_update_response_timeout_ms(),
+ nsITimer::TYPE_ONE_SHOT));
+
+ MOZ_TRY_VAR(mTimeoutTimer,
+ NS_NewTimerWithCallback(
+ this, StaticPrefs::urlclassifier_update_timeout_ms(),
+ nsITimer::TYPE_ONE_SHOT));
+
+ if (StaticPrefs::urlclassifier_update_timeout_ms() < MIN_TIMEOUT_MS) {
+ LOG(("Download update timeout %d ms (< %d ms) would be too small",
+ StaticPrefs::urlclassifier_update_timeout_ms(), MIN_TIMEOUT_MS));
+ }
+
+ return NS_OK;
+}
+
+nsresult nsUrlClassifierStreamUpdater::FetchUpdate(
+ const nsACString& aUpdateUrl, const nsACString& aRequestPayload,
+ bool aIsPostRequest, const nsACString& aStreamTable) {
+ LOG(("(pre) Fetching update from %s\n",
+ PromiseFlatCString(aUpdateUrl).get()));
+
+ nsCString updateUrl(aUpdateUrl);
+ if (!aIsPostRequest) {
+ updateUrl.AppendPrintf("&$req=%s", nsCString(aRequestPayload).get());
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), updateUrl);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString urlSpec;
+ uri->GetAsciiSpec(urlSpec);
+
+ LOG(("(post) Fetching update from %s\n", urlSpec.get()));
+
+ return FetchUpdate(uri, aRequestPayload, aIsPostRequest, aStreamTable);
+}
+
+NS_IMETHODIMP
+nsUrlClassifierStreamUpdater::DownloadUpdates(
+ const nsACString& aRequestTables, const nsACString& aRequestPayload,
+ bool aIsPostRequest, const nsACString& aUpdateUrl,
+ nsIUrlClassifierCallback* aSuccessCallback,
+ nsIUrlClassifierCallback* aUpdateErrorCallback,
+ nsIUrlClassifierCallback* aDownloadErrorCallback, bool* _retval) {
+ NS_ENSURE_ARG(aSuccessCallback);
+ NS_ENSURE_ARG(aUpdateErrorCallback);
+ NS_ENSURE_ARG(aDownloadErrorCallback);
+
+ if (mIsUpdating) {
+ LOG(("Already updating, queueing update %s from %s", aRequestPayload.Data(),
+ aUpdateUrl.Data()));
+ *_retval = false;
+ UpdateRequest* request = mPendingRequests.AppendElement(fallible);
+ if (!request) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ BuildUpdateRequest(aRequestTables, aRequestPayload, aIsPostRequest,
+ aUpdateUrl, aSuccessCallback, aUpdateErrorCallback,
+ aDownloadErrorCallback, request);
+ return NS_OK;
+ }
+
+ if (aUpdateUrl.IsEmpty()) {
+ NS_ERROR("updateUrl not set");
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ nsresult rv;
+
+ if (!mInitialized) {
+ // Add an observer for shutdown so we can cancel any pending list
+ // downloads. quit-application is the same event that the download
+ // manager listens for and uses to cancel pending downloads.
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (!observerService) return NS_ERROR_FAILURE;
+
+ observerService->AddObserver(this, gQuitApplicationMessage, false);
+
+ mDBService = mozilla::components::UrlClassifierDB::Service(&rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mInitialized = true;
+ }
+
+ rv = mDBService->BeginUpdate(this, aRequestTables);
+ if (rv == NS_ERROR_NOT_AVAILABLE) {
+ LOG(("Service busy, already updating, queuing update %s from %s",
+ aRequestPayload.Data(), aUpdateUrl.Data()));
+ *_retval = false;
+ UpdateRequest* request = mPendingRequests.AppendElement(fallible);
+ if (!request) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ BuildUpdateRequest(aRequestTables, aRequestPayload, aIsPostRequest,
+ aUpdateUrl, aSuccessCallback, aUpdateErrorCallback,
+ aDownloadErrorCallback, request);
+
+ // We cannot guarantee that we will be notified when DBService is done
+ // processing the current update, so we fire a retry timer on our own.
+ MOZ_TRY_VAR(mFetchNextRequestTimer,
+ NS_NewTimerWithCallback(this, FETCH_NEXT_REQUEST_RETRY_DELAY_MS,
+ nsITimer::TYPE_ONE_SHOT));
+
+ return NS_OK;
+ }
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsUrlClassifierUtils* urlUtil = nsUrlClassifierUtils::GetInstance();
+ if (NS_WARN_IF(!urlUtil)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsTArray<nsCString> tables;
+ mozilla::safebrowsing::Classifier::SplitTables(aRequestTables, tables);
+ urlUtil->GetTelemetryProvider(tables.SafeElementAt(0, ""_ns),
+ mTelemetryProvider);
+
+ mCurrentRequest = MakeUnique<UpdateRequest>();
+ BuildUpdateRequest(aRequestTables, aRequestPayload, aIsPostRequest,
+ aUpdateUrl, aSuccessCallback, aUpdateErrorCallback,
+ aDownloadErrorCallback, mCurrentRequest.get());
+
+ mIsUpdating = true;
+ *_retval = true;
+
+ LOG(("FetchUpdate: %s", mCurrentRequest->mUrl.Data()));
+
+ return FetchUpdate(aUpdateUrl, aRequestPayload, aIsPostRequest, ""_ns);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsIUrlClassifierUpdateObserver implementation
+
+NS_IMETHODIMP
+nsUrlClassifierStreamUpdater::UpdateUrlRequested(const nsACString& aUrl,
+ const nsACString& aTable) {
+ LOG(("Queuing requested update from %s\n", PromiseFlatCString(aUrl).get()));
+
+ PendingUpdate* update = mPendingUpdates.AppendElement(fallible);
+ if (!update) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Allow data: and file: urls for unit testing purposes, otherwise assume http
+ if (StringBeginsWith(aUrl, "data:"_ns) ||
+ StringBeginsWith(aUrl, "file:"_ns)) {
+ update->mUrl = aUrl;
+ } else {
+ // For unittesting update urls to localhost should use http, not https
+ // (otherwise the connection will fail silently, since there will be no
+ // cert available).
+ if (!StringBeginsWith(aUrl, "localhost"_ns)) {
+ update->mUrl = "https://"_ns + aUrl;
+ } else {
+ update->mUrl = "http://"_ns + aUrl;
+ }
+ }
+ update->mTable = aTable;
+
+ return NS_OK;
+}
+
+nsresult nsUrlClassifierStreamUpdater::FetchNext() {
+ if (mPendingUpdates.Length() == 0) {
+ return NS_OK;
+ }
+
+ PendingUpdate& update = mPendingUpdates[0];
+ LOG(("Fetching update url: %s\n", update.mUrl.get()));
+ nsresult rv =
+ FetchUpdate(update.mUrl, ""_ns,
+ true, // This method is for v2 and v2 is always a POST.
+ update.mTable);
+ if (NS_FAILED(rv)) {
+ nsAutoCString errorName;
+ mozilla::GetErrorName(rv, errorName);
+ LOG(("Error (%s) fetching update url: %s\n", errorName.get(),
+ update.mUrl.get()));
+ // We can commit the urls that we've applied so far. This is
+ // probably a transient server problem, so trigger backoff.
+ mDownloadError = true;
+ mDBService->FinishUpdate();
+ return rv;
+ }
+
+ mPendingUpdates.RemoveElementAt(0);
+
+ return NS_OK;
+}
+
+nsresult nsUrlClassifierStreamUpdater::FetchNextRequest() {
+ if (mPendingRequests.Length() == 0) {
+ LOG(("No more requests, returning"));
+ return NS_OK;
+ }
+
+ UpdateRequest request = mPendingRequests[0];
+ mPendingRequests.RemoveElementAt(0);
+ LOG(("Stream updater: fetching next request: %s, %s", request.mTables.get(),
+ request.mUrl.get()));
+ bool dummy;
+ DownloadUpdates(request.mTables, request.mRequestPayload,
+ request.mIsPostRequest, request.mUrl,
+ request.mSuccessCallback, request.mUpdateErrorCallback,
+ request.mDownloadErrorCallback, &dummy);
+ return NS_OK;
+}
+
+void nsUrlClassifierStreamUpdater::BuildUpdateRequest(
+ const nsACString& aRequestTables, const nsACString& aRequestPayload,
+ bool aIsPostRequest, const nsACString& aUpdateUrl,
+ nsIUrlClassifierCallback* aSuccessCallback,
+ nsIUrlClassifierCallback* aUpdateErrorCallback,
+ nsIUrlClassifierCallback* aDownloadErrorCallback, UpdateRequest* aRequest) {
+ MOZ_ASSERT(aRequest);
+
+ aRequest->mTables = aRequestTables;
+ aRequest->mRequestPayload = aRequestPayload;
+ aRequest->mIsPostRequest = aIsPostRequest;
+ aRequest->mUrl = aUpdateUrl;
+ aRequest->mSuccessCallback = aSuccessCallback;
+ aRequest->mUpdateErrorCallback = aUpdateErrorCallback;
+ aRequest->mDownloadErrorCallback = aDownloadErrorCallback;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierStreamUpdater::StreamFinished(nsresult status,
+ uint32_t requestedDelay) {
+ // We are a service and may not be reset with Init between calls, so reset
+ // mBeganStream manually.
+ mBeganStream = false;
+ if (LOG_ENABLED()) {
+ nsAutoCString errorName;
+ mozilla::GetErrorName(status, errorName);
+ LOG(("nsUrlClassifierStreamUpdater::StreamFinished [%s, %d]",
+ errorName.get(), requestedDelay));
+ }
+ if (NS_FAILED(status) || mPendingUpdates.Length() == 0) {
+ // We're done.
+ LOG(("nsUrlClassifierStreamUpdater::Done [this=%p]", this));
+ mDBService->FinishUpdate();
+ return NS_OK;
+ }
+
+ // This timer is for fetching indirect updates ("forwards") from any "u:"
+ // lines that we encountered while processing the server response. It is NOT
+ // for scheduling the next time we pull the list from the server. That's a
+ // different timer in listmanager.js (see bug 1110891).
+ nsresult rv;
+ rv = NS_NewTimerWithCallback(getter_AddRefs(mFetchIndirectUpdatesTimer), this,
+ requestedDelay, nsITimer::TYPE_ONE_SHOT);
+
+ if (NS_FAILED(rv)) {
+ NS_WARNING(
+ "Unable to initialize timer, fetching next safebrowsing item "
+ "immediately");
+ return FetchNext();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierStreamUpdater::UpdateSuccess(uint32_t requestedTimeout) {
+ LOG(("nsUrlClassifierStreamUpdater::UpdateSuccess [this=%p]", this));
+ if (mPendingUpdates.Length() != 0) {
+ NS_WARNING("Didn't fetch all safebrowsing update redirects");
+ }
+
+ // DownloadDone() clears mSuccessCallback, so we save it off here.
+ nsCOMPtr<nsIUrlClassifierCallback> successCallback =
+ mDownloadError ? nullptr : mCurrentRequest->mSuccessCallback.get();
+ nsCOMPtr<nsIUrlClassifierCallback> downloadErrorCallback =
+ mDownloadError ? mCurrentRequest->mDownloadErrorCallback.get() : nullptr;
+
+ DownloadDone();
+
+ nsAutoCString strTimeout;
+ strTimeout.AppendInt(requestedTimeout);
+ if (successCallback) {
+ LOG(("nsUrlClassifierStreamUpdater::UpdateSuccess callback [this=%p]",
+ this));
+ successCallback->HandleEvent(strTimeout);
+ } else if (downloadErrorCallback) {
+ downloadErrorCallback->HandleEvent(mDownloadErrorStatusStr);
+ mDownloadErrorStatusStr.Truncate();
+ LOG(("Notify download error callback in UpdateSuccess [this=%p]", this));
+ }
+ // Now fetch the next request
+ LOG(("stream updater: calling into fetch next request"));
+ FetchNextRequest();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierStreamUpdater::UpdateError(nsresult result) {
+ LOG(("nsUrlClassifierStreamUpdater::UpdateError [this=%p]", this));
+
+ // DownloadDone() clears mUpdateErrorCallback, so we save it off here.
+ nsCOMPtr<nsIUrlClassifierCallback> errorCallback =
+ mDownloadError ? nullptr : mCurrentRequest->mUpdateErrorCallback.get();
+ nsCOMPtr<nsIUrlClassifierCallback> downloadErrorCallback =
+ mDownloadError ? mCurrentRequest->mDownloadErrorCallback.get() : nullptr;
+ DownloadDone();
+
+ if (errorCallback) {
+ nsAutoCString strResult;
+ mozilla::GetErrorName(result, strResult);
+ errorCallback->HandleEvent(strResult);
+ } else if (downloadErrorCallback) {
+ LOG(("Notify download error callback in UpdateError [this=%p]", this));
+ downloadErrorCallback->HandleEvent(mDownloadErrorStatusStr);
+ mDownloadErrorStatusStr.Truncate();
+ }
+
+ return NS_OK;
+}
+
+nsresult nsUrlClassifierStreamUpdater::AddRequestBody(
+ const nsACString& aRequestBody) {
+ nsresult rv;
+ nsCOMPtr<nsIStringInputStream> strStream =
+ do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = strStream->SetData(aRequestBody.BeginReading(), aRequestBody.Length());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIUploadChannel> uploadChannel = do_QueryInterface(mChannel, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = uploadChannel->SetUploadStream(strStream, "text/plain"_ns, -1);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = httpChannel->SetRequestMethod("POST"_ns);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsIStreamListenerObserver implementation
+
+NS_IMETHODIMP
+nsUrlClassifierStreamUpdater::OnStartRequest(nsIRequest* request) {
+ nsresult rv;
+ bool downloadError = false;
+ nsAutoCString strStatus;
+ nsresult status = NS_OK;
+
+ // Only update if we got http success header
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
+ if (httpChannel) {
+ rv = httpChannel->GetStatus(&status);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (LOG_ENABLED()) {
+ nsAutoCString errorName, spec;
+ mozilla::GetErrorName(status, errorName);
+ nsCOMPtr<nsIURI> uri;
+ rv = httpChannel->GetURI(getter_AddRefs(uri));
+ if (NS_SUCCEEDED(rv) && uri) {
+ uri->GetAsciiSpec(spec);
+ }
+ LOG(
+ ("nsUrlClassifierStreamUpdater::OnStartRequest "
+ "(status=%s, uri=%s, this=%p)",
+ errorName.get(), spec.get(), this));
+ }
+ if (mTelemetryClockStart > 0) {
+ uint32_t msecs =
+ PR_IntervalToMilliseconds(PR_IntervalNow() - mTelemetryClockStart);
+ mozilla::Telemetry::Accumulate(
+ mozilla::Telemetry::URLCLASSIFIER_UPDATE_SERVER_RESPONSE_TIME,
+ mTelemetryProvider, msecs);
+ }
+
+ if (mResponseTimeoutTimer) {
+ mResponseTimeoutTimer->Cancel();
+ mResponseTimeoutTimer = nullptr;
+ }
+
+ uint8_t netErrCode = NS_FAILED(status) ? NetworkErrorToBucket(status) : 0;
+ mozilla::Telemetry::Accumulate(
+ mozilla::Telemetry::URLCLASSIFIER_UPDATE_REMOTE_NETWORK_ERROR,
+ mTelemetryProvider, netErrCode);
+
+ if (NS_FAILED(status)) {
+ // Assume we're overloading the server and trigger backoff.
+ downloadError = true;
+ } else {
+ bool succeeded = false;
+ rv = httpChannel->GetRequestSucceeded(&succeeded);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t requestStatus;
+ rv = httpChannel->GetResponseStatus(&requestStatus);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mozilla::Telemetry::Accumulate(
+ mozilla::Telemetry::URLCLASSIFIER_UPDATE_REMOTE_STATUS2,
+ mTelemetryProvider, HTTPStatusToBucket(requestStatus));
+ if (requestStatus == 400) {
+ printf_stderr(
+ "Safe Browsing server returned a 400 during update:"
+ "request url = %s, payload = %s\n",
+ mCurrentRequest->mUrl.get(),
+ mCurrentRequest->mRequestPayload.get());
+ }
+
+ LOG(("nsUrlClassifierStreamUpdater::OnStartRequest %s (%d)",
+ succeeded ? "succeeded" : "failed", requestStatus));
+ if (!succeeded) {
+ // 404 or other error, pass error status back
+ strStatus.AppendInt(requestStatus);
+ downloadError = true;
+ }
+ }
+ }
+
+ if (downloadError) {
+ LOG(("nsUrlClassifierStreamUpdater::Download error [this=%p]", this));
+ mDownloadError = true;
+ mDownloadErrorStatusStr = strStatus;
+ status = NS_ERROR_ABORT;
+ } else if (NS_SUCCEEDED(status)) {
+ MOZ_ASSERT(mCurrentRequest->mDownloadErrorCallback);
+ mBeganStream = true;
+ LOG(("nsUrlClassifierStreamUpdater::Beginning stream [this=%p]", this));
+ rv = mDBService->BeginStream(mStreamTable);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ mStreamTable.Truncate();
+
+ return status;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierStreamUpdater::OnDataAvailable(nsIRequest* request,
+ nsIInputStream* aIStream,
+ uint64_t aSourceOffset,
+ uint32_t aLength) {
+ if (!mDBService) return NS_ERROR_NOT_INITIALIZED;
+
+ LOG(("OnDataAvailable (%d bytes)", aLength));
+
+ if (aSourceOffset > MAX_FILE_SIZE) {
+ LOG((
+ "OnDataAvailable::Abort because exceeded the maximum file size(%" PRIu64
+ ")",
+ aSourceOffset));
+ return NS_ERROR_FILE_TOO_BIG;
+ }
+
+ nsresult rv;
+
+ // Copy the data into a nsCString
+ nsCString chunk;
+ rv = NS_ConsumeStream(aIStream, aLength, chunk);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // LOG(("Chunk (%d): %s\n\n", chunk.Length(), chunk.get()));
+ rv = mDBService->UpdateStream(chunk);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierStreamUpdater::OnStopRequest(nsIRequest* request,
+ nsresult aStatus) {
+ if (!mDBService) return NS_ERROR_NOT_INITIALIZED;
+
+ if (LOG_ENABLED()) {
+ nsAutoCString errorName;
+ mozilla::GetErrorName(aStatus, errorName);
+ LOG(("OnStopRequest (status %s, beganStream %s, this=%p)", errorName.get(),
+ mBeganStream ? "true" : "false", this));
+ }
+
+ nsresult rv;
+
+ if (NS_SUCCEEDED(aStatus)) {
+ // Success, finish this stream and move on to the next.
+ rv = mDBService->FinishStream();
+ } else if (mBeganStream) {
+ LOG(("OnStopRequest::Canceling update [this=%p]", this));
+ // We began this stream and couldn't finish it. We have to cancel the
+ // update, it's not in a consistent state.
+ mDownloadError = true;
+ rv = mDBService->CancelUpdate();
+ } else {
+ LOG(("OnStopRequest::Finishing update [this=%p]", this));
+ // The fetch failed, but we didn't start the stream (probably a
+ // server or connection error). We can commit what we've applied
+ // so far, and request again later.
+ rv = mDBService->FinishUpdate();
+ }
+
+ if (mResponseTimeoutTimer) {
+ mResponseTimeoutTimer->Cancel();
+ mResponseTimeoutTimer = nullptr;
+ }
+
+ // mResponseTimeoutTimer may be cleared in OnStartRequest, so we check
+ // mTimeoutTimer to see whether the update was has timed out
+ if (mTimeoutTimer) {
+ mozilla::Telemetry::Accumulate(
+ mozilla::Telemetry::URLCLASSIFIER_UPDATE_TIMEOUT, mTelemetryProvider,
+ static_cast<uint8_t>(eNoTimeout));
+ mTimeoutTimer->Cancel();
+ mTimeoutTimer = nullptr;
+ }
+
+ mTelemetryProvider.Truncate();
+ mTelemetryClockStart = 0;
+ mChannel = nullptr;
+
+ // If the fetch failed, return the network status rather than NS_OK, the
+ // result of finishing a possibly-empty update
+ if (NS_SUCCEEDED(aStatus)) {
+ return rv;
+ }
+ return aStatus;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsIObserver implementation
+
+NS_IMETHODIMP
+nsUrlClassifierStreamUpdater::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ if (nsCRT::strcmp(aTopic, gQuitApplicationMessage) == 0) {
+ if (mIsUpdating && mChannel) {
+ LOG(("Cancel download"));
+ nsresult rv;
+ rv = mChannel->Cancel(NS_ERROR_ABORT);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mIsUpdating = false;
+ mChannel = nullptr;
+ mTelemetryClockStart = 0;
+ }
+ if (mFetchIndirectUpdatesTimer) {
+ mFetchIndirectUpdatesTimer->Cancel();
+ mFetchIndirectUpdatesTimer = nullptr;
+ }
+ if (mFetchNextRequestTimer) {
+ mFetchNextRequestTimer->Cancel();
+ mFetchNextRequestTimer = nullptr;
+ }
+ if (mResponseTimeoutTimer) {
+ mResponseTimeoutTimer->Cancel();
+ mResponseTimeoutTimer = nullptr;
+ }
+ if (mTimeoutTimer) {
+ mTimeoutTimer->Cancel();
+ mTimeoutTimer = nullptr;
+ }
+ }
+ return NS_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsIInterfaceRequestor implementation
+
+NS_IMETHODIMP
+nsUrlClassifierStreamUpdater::GetInterface(const nsIID& eventSinkIID,
+ void** _retval) {
+ return QueryInterface(eventSinkIID, _retval);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsITimerCallback implementation
+NS_IMETHODIMP
+nsUrlClassifierStreamUpdater::Notify(nsITimer* timer) {
+ LOG(("nsUrlClassifierStreamUpdater::Notify [%p]", this));
+
+ if (timer == mFetchNextRequestTimer) {
+ mFetchNextRequestTimer = nullptr;
+ FetchNextRequest();
+ return NS_OK;
+ }
+
+ if (timer == mFetchIndirectUpdatesTimer) {
+ mFetchIndirectUpdatesTimer = nullptr;
+ // Start the update process up again.
+ FetchNext();
+ return NS_OK;
+ }
+
+ bool updateFailed = false;
+ if (timer == mResponseTimeoutTimer) {
+ mResponseTimeoutTimer = nullptr;
+ if (mTimeoutTimer) {
+ mTimeoutTimer->Cancel();
+ mTimeoutTimer = nullptr;
+ }
+ mDownloadError = true; // Trigger backoff
+ updateFailed = true;
+ MOZ_LOG(gUrlClassifierStreamUpdaterLog, mozilla::LogLevel::Error,
+ ("Safe Browsing timed out while waiting for the update server to "
+ "respond."));
+ mozilla::Telemetry::Accumulate(
+ mozilla::Telemetry::URLCLASSIFIER_UPDATE_TIMEOUT, mTelemetryProvider,
+ static_cast<uint8_t>(eResponseTimeout));
+ }
+
+ if (timer == mTimeoutTimer) {
+ mTimeoutTimer = nullptr;
+ // No backoff since the connection may just be temporarily slow.
+ updateFailed = true;
+ MOZ_LOG(gUrlClassifierStreamUpdaterLog, mozilla::LogLevel::Error,
+ ("Safe Browsing timed out while waiting for the update server to "
+ "finish."));
+ mozilla::Telemetry::Accumulate(
+ mozilla::Telemetry::URLCLASSIFIER_UPDATE_TIMEOUT, mTelemetryProvider,
+ static_cast<uint8_t>(eDownloadTimeout));
+ }
+
+ if (updateFailed) {
+ // Cancelling the channel will trigger OnStopRequest.
+ if (mChannel) {
+ mozilla::Unused << mChannel->Cancel(NS_ERROR_ABORT);
+ mChannel = nullptr;
+ }
+ mTelemetryClockStart = 0;
+
+ if (mFetchIndirectUpdatesTimer) {
+ mFetchIndirectUpdatesTimer->Cancel();
+ mFetchIndirectUpdatesTimer = nullptr;
+ }
+ if (mFetchNextRequestTimer) {
+ mFetchNextRequestTimer->Cancel();
+ mFetchNextRequestTimer = nullptr;
+ }
+
+ return NS_OK;
+ }
+
+ MOZ_ASSERT_UNREACHABLE("A timer is fired from nowhere.");
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+//// nsINamed
+
+NS_IMETHODIMP
+nsUrlClassifierStreamUpdater::GetName(nsACString& aName) {
+ aName.AssignLiteral("nsUrlClassifierStreamUpdater");
+ return NS_OK;
+}
diff --git a/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.h b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.h
new file mode 100644
index 0000000000..cc5508c3e6
--- /dev/null
+++ b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.h
@@ -0,0 +1,129 @@
+//* -*- 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/. */
+
+#ifndef nsUrlClassifierStreamUpdater_h_
+#define nsUrlClassifierStreamUpdater_h_
+
+#include "nsISupports.h"
+#include "nsCOMPtr.h"
+#include "nsINamed.h"
+#include "nsIObserver.h"
+#include "nsIUrlClassifierStreamUpdater.h"
+#include "nsIStreamListener.h"
+#include "nsIChannel.h"
+#include "nsTArray.h"
+#include "nsITimer.h"
+
+// Forward declare pointers
+class nsIURI;
+
+class nsUrlClassifierStreamUpdater final
+ : public nsIUrlClassifierStreamUpdater,
+ public nsIUrlClassifierUpdateObserver,
+ public nsIStreamListener,
+ public nsIObserver,
+ public nsIInterfaceRequestor,
+ public nsITimerCallback,
+ public nsINamed {
+ public:
+ nsUrlClassifierStreamUpdater();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURLCLASSIFIERSTREAMUPDATER
+ NS_DECL_NSIURLCLASSIFIERUPDATEOBSERVER
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSITIMERCALLBACK
+ NS_DECL_NSINAMED
+
+ private:
+ // No subclassing
+ ~nsUrlClassifierStreamUpdater() = default;
+
+ // When the dbservice sends an UpdateComplete or UpdateFailure, we call this
+ // to reset the stream updater.
+ void DownloadDone();
+
+ // Disallow copy constructor
+ nsUrlClassifierStreamUpdater(nsUrlClassifierStreamUpdater&);
+
+ nsresult AddRequestBody(const nsACString& aRequestBody);
+
+ // Fetches an update for a single table.
+ nsresult FetchUpdate(nsIURI* aURI, const nsACString& aRequest,
+ bool aIsPostRequest, const nsACString& aTable);
+ // Dumb wrapper so we don't have to create URIs.
+ nsresult FetchUpdate(const nsACString& aURI, const nsACString& aRequest,
+ bool aIsPostRequest, const nsACString& aTable);
+
+ // Fetches the next table, from mPendingUpdates.
+ nsresult FetchNext();
+ // Fetches the next request, from mPendingRequests
+ nsresult FetchNextRequest();
+
+ struct UpdateRequest {
+ nsCString mTables;
+ nsCString mRequestPayload;
+ bool mIsPostRequest;
+ nsCString mUrl;
+ nsCOMPtr<nsIUrlClassifierCallback> mSuccessCallback;
+ nsCOMPtr<nsIUrlClassifierCallback> mUpdateErrorCallback;
+ nsCOMPtr<nsIUrlClassifierCallback> mDownloadErrorCallback;
+ };
+ // Utility function to create an update request.
+ void BuildUpdateRequest(const nsACString& aRequestTables,
+ const nsACString& aRequestPayload,
+ bool aIsPostRequest, const nsACString& aUpdateUrl,
+ nsIUrlClassifierCallback* aSuccessCallback,
+ nsIUrlClassifierCallback* aUpdateErrorCallback,
+ nsIUrlClassifierCallback* aDownloadErrorCallback,
+ UpdateRequest* aRequest);
+
+ bool mIsUpdating;
+ bool mInitialized;
+ bool mDownloadError;
+ bool mBeganStream;
+
+ nsCString mDownloadErrorStatusStr;
+
+ // Note that mStreamTable is only used by v2, it is empty for v4 update.
+ nsCString mStreamTable;
+
+ nsCOMPtr<nsIChannel> mChannel;
+ nsCOMPtr<nsIUrlClassifierDBService> mDBService;
+
+ // In v2, a update response might contain redirection and this
+ // timer is for fetching the redirected update.
+ nsCOMPtr<nsITimer> mFetchIndirectUpdatesTimer;
+
+ // When we DownloadUpdate(), the DBService might be busy on processing
+ // request issused outside of StreamUpdater. We have to fire a timer to
+ // retry on our own.
+ nsCOMPtr<nsITimer> mFetchNextRequestTimer;
+
+ // Timer to abort the download if the server takes too long to respond.
+ nsCOMPtr<nsITimer> mResponseTimeoutTimer;
+
+ // Timer to abort the download if it takes too long.
+ nsCOMPtr<nsITimer> mTimeoutTimer;
+
+ mozilla::UniquePtr<UpdateRequest> mCurrentRequest;
+ nsTArray<UpdateRequest> mPendingRequests;
+
+ struct PendingUpdate {
+ nsCString mUrl;
+ nsCString mTable;
+ };
+ nsTArray<PendingUpdate> mPendingUpdates;
+
+ // The provider for current update request and should be only used by
+ // telemetry since it would show up as "other" for any other providers.
+ nsCString mTelemetryProvider;
+ PRIntervalTime mTelemetryClockStart;
+};
+
+#endif // nsUrlClassifierStreamUpdater_h_
diff --git a/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp b/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp
new file mode 100644
index 0000000000..da5e9cd451
--- /dev/null
+++ b/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp
@@ -0,0 +1,1125 @@
+/* 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 "chromium/safebrowsing.pb.h"
+#include "nsEscape.h"
+#include "nsString.h"
+#include "nsIURI.h"
+#include "nsIURIMutator.h"
+#include "nsIURL.h"
+#include "nsIXULRuntime.h"
+#include "nsUrlClassifierUtils.h"
+#include "nsTArray.h"
+#include "nsReadableUtils.h"
+#include "plbase64.h"
+#include "nsPrintfCString.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Mutex.h"
+#include "nsIRedirectHistoryEntry.h"
+#include "nsIHttpChannelInternal.h"
+#include "mozIThirdPartyUtil.h"
+#include "nsIDocShell.h"
+#include "mozilla/TextUtils.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/Telemetry.h"
+#include "nsNetUtil.h"
+#include "nsIHttpChannel.h"
+#include "nsIObserverService.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
+#include "nsPIDOMWindow.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadManager.h"
+#include "nsTHashSet.h"
+#include "Classifier.h"
+#include "Entries.h"
+#include "prprf.h"
+#include "prtime.h"
+
+#define DEFAULT_PROTOCOL_VERSION "2.2"
+
+using namespace mozilla;
+using namespace mozilla::safebrowsing;
+
+static mozilla::StaticRefPtr<nsUrlClassifierUtils> gUrlClassifierUtils;
+
+static char int_to_hex_digit(int32_t i) {
+ NS_ASSERTION((i >= 0) && (i <= 15), "int too big in int_to_hex_digit");
+ return static_cast<char>(((i < 10) ? (i + '0') : ((i - 10) + 'A')));
+}
+
+static bool IsDecimal(const nsACString& num) {
+ for (uint32_t i = 0; i < num.Length(); i++) {
+ if (!mozilla::IsAsciiDigit(num[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool IsHex(const nsACString& num) {
+ if (num.Length() < 3) {
+ return false;
+ }
+
+ if (num[0] != '0' || !(num[1] == 'x' || num[1] == 'X')) {
+ return false;
+ }
+
+ for (uint32_t i = 2; i < num.Length(); i++) {
+ if (!mozilla::IsAsciiHexDigit(num[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool IsOctal(const nsACString& num) {
+ if (num.Length() < 2) {
+ return false;
+ }
+
+ if (num[0] != '0') {
+ return false;
+ }
+
+ for (uint32_t i = 1; i < num.Length(); i++) {
+ if (!mozilla::IsAsciiDigit(num[i]) || num[i] == '8' || num[i] == '9') {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////
+// SafeBrowsing V4 related utits.
+
+namespace mozilla {
+namespace safebrowsing {
+
+static PlatformType GetPlatformType() {
+#if defined(ANDROID)
+ return ANDROID_PLATFORM;
+#elif defined(XP_MACOSX)
+ return OSX_PLATFORM;
+#elif defined(XP_LINUX)
+ return LINUX_PLATFORM;
+#elif defined(XP_WIN)
+ return WINDOWS_PLATFORM;
+#else
+ // Default to Linux for other platforms (see bug 1362501).
+ return LINUX_PLATFORM;
+#endif
+}
+
+typedef FetchThreatListUpdatesRequest_ListUpdateRequest ListUpdateRequest;
+typedef FetchThreatListUpdatesRequest_ListUpdateRequest_Constraints Constraints;
+
+static void InitListUpdateRequest(ThreatType aThreatType,
+ const nsCString& aStateBase64,
+ ListUpdateRequest* aListUpdateRequest) {
+ aListUpdateRequest->set_threat_type(aThreatType);
+ PlatformType platform = GetPlatformType();
+#if defined(ANDROID)
+ // Temporary hack to fix bug 1441345.
+ if ((aThreatType == SOCIAL_ENGINEERING_PUBLIC) ||
+ (aThreatType == SOCIAL_ENGINEERING)) {
+ platform = LINUX_PLATFORM;
+ }
+#endif
+ aListUpdateRequest->set_platform_type(platform);
+ aListUpdateRequest->set_threat_entry_type(URL);
+
+ Constraints* contraints = new Constraints();
+ contraints->add_supported_compressions(RICE);
+ aListUpdateRequest->set_allocated_constraints(contraints);
+
+ // Only set non-empty state.
+ if (!aStateBase64.IsEmpty()) {
+ nsCString stateBinary;
+ nsresult rv = Base64Decode(aStateBase64, stateBinary);
+ if (NS_SUCCEEDED(rv)) {
+ aListUpdateRequest->set_state(stateBinary.get(), stateBinary.Length());
+ }
+ }
+}
+
+static ClientInfo* CreateClientInfo() {
+ ClientInfo* c = new ClientInfo();
+
+ nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
+
+ nsAutoCString clientId;
+ nsresult rv = prefBranch->GetCharPref("browser.safebrowsing.id", clientId);
+
+ if (NS_FAILED(rv)) {
+ clientId = "Firefox"; // Use "Firefox" as fallback.
+ }
+
+ c->set_client_id(clientId.get());
+
+ return c;
+}
+
+static bool IsAllowedOnCurrentPlatform(uint32_t aThreatType) {
+ PlatformType platform = GetPlatformType();
+
+ switch (aThreatType) {
+ case POTENTIALLY_HARMFUL_APPLICATION:
+ // Bug 1388582 - Google server would respond 404 error if the request
+ // contains PHA on non-mobile platform.
+ return ANDROID_PLATFORM == platform;
+ case MALICIOUS_BINARY:
+ case CSD_DOWNLOAD_WHITELIST:
+ // Bug 1392204 - 'goog-downloadwhite-proto' and 'goog-badbinurl-proto'
+ // are not available on android.
+ return ANDROID_PLATFORM != platform;
+ }
+ // We allow every threat type not listed in the switch cases.
+ return true;
+}
+
+} // end of namespace safebrowsing.
+} // end of namespace mozilla.
+
+// static
+already_AddRefed<nsUrlClassifierUtils>
+nsUrlClassifierUtils::GetXPCOMSingleton() {
+ if (gUrlClassifierUtils) {
+ return do_AddRef(gUrlClassifierUtils);
+ }
+
+ RefPtr<nsUrlClassifierUtils> utils = new nsUrlClassifierUtils();
+ if (NS_WARN_IF(NS_FAILED(utils->Init()))) {
+ return nullptr;
+ }
+
+ // Note: This is cleared in the nsUrlClassifierUtils destructor.
+ gUrlClassifierUtils = utils.get();
+ ClearOnShutdown(&gUrlClassifierUtils);
+ return utils.forget();
+}
+
+// static
+nsUrlClassifierUtils* nsUrlClassifierUtils::GetInstance() {
+ if (!gUrlClassifierUtils) {
+ RefPtr<nsUrlClassifierUtils> utils = GetXPCOMSingleton();
+ }
+
+ return gUrlClassifierUtils;
+}
+
+nsUrlClassifierUtils::nsUrlClassifierUtils()
+ : mProviderDictLock("nsUrlClassifierUtils.mProviderDictLock") {}
+
+nsUrlClassifierUtils::~nsUrlClassifierUtils() {
+ if (gUrlClassifierUtils) {
+ MOZ_ASSERT(gUrlClassifierUtils == this);
+ gUrlClassifierUtils = nullptr;
+ }
+}
+
+nsresult nsUrlClassifierUtils::Init() {
+ // nsIUrlClassifierUtils is a thread-safe service so it's
+ // allowed to use on non-main threads. However, building
+ // the provider dictionary must be on the main thread.
+ // We forcefully load nsUrlClassifierUtils in
+ // nsUrlClassifierDBService::Init() to ensure we must
+ // now be on the main thread.
+ nsresult rv = ReadProvidersFromPrefs(mProviderDict);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Add an observer for shutdown
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (!observerService) return NS_ERROR_FAILURE;
+
+ observerService->AddObserver(this, "xpcom-shutdown-threads", false);
+ mozilla::Preferences::AddStrongObserver(this, "browser.safebrowsing");
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsUrlClassifierUtils, nsIUrlClassifierUtils, nsIObserver)
+
+/////////////////////////////////////////////////////////////////////////////
+// nsIUrlClassifierUtils
+
+NS_IMETHODIMP
+nsUrlClassifierUtils::GetKeyForURI(nsIURI* uri, nsACString& _retval) {
+ nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
+ if (!innerURI) innerURI = uri;
+
+ nsAutoCString host;
+ innerURI->GetAsciiHost(host);
+
+ if (host.IsEmpty()) {
+ return NS_ERROR_MALFORMED_URI;
+ }
+
+ nsresult rv = CanonicalizeHostname(host, _retval);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString path;
+ rv = innerURI->GetPathQueryRef(path);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Strip fragment and query because canonicalization only applies to path
+ int32_t ref = path.FindChar('#');
+ if (ref != kNotFound) {
+ path.SetLength(ref);
+ }
+
+ int32_t query = path.FindChar('?');
+ if (query != kNotFound) {
+ path.SetLength(query);
+ }
+
+ nsAutoCString temp;
+ rv = CanonicalizePath(path, temp);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ _retval.Append(temp);
+
+ if (query != kNotFound) {
+ nsAutoCString query;
+ rv = innerURI->GetQuery(query);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ _retval.AppendPrintf("?%s", query.get());
+ }
+
+ return NS_OK;
+}
+
+// We use "goog-*-proto" as the list name for v4, where "proto" indicates
+// it's updated (as well as hash completion) via protobuf.
+//
+// In the mozilla official build, we are allowed to use the
+// private phishing list (goog-phish-proto). See Bug 1288840.
+static const struct {
+ const char* mListName;
+ uint32_t mThreatType;
+} THREAT_TYPE_CONV_TABLE[] = {
+ {"goog-malware-proto", MALWARE_THREAT}, // 1
+ {"googpub-phish-proto", SOCIAL_ENGINEERING_PUBLIC}, // 2
+ {"goog-unwanted-proto", UNWANTED_SOFTWARE}, // 3
+ {"goog-harmful-proto", POTENTIALLY_HARMFUL_APPLICATION}, // 4
+ {"goog-phish-proto", SOCIAL_ENGINEERING}, // 5
+
+ // For application reputation
+ {"goog-badbinurl-proto", MALICIOUS_BINARY}, // 7
+ {"goog-downloadwhite-proto", CSD_DOWNLOAD_WHITELIST}, // 9
+
+ // For testing purpose.
+ {"moztest-phish-proto", SOCIAL_ENGINEERING_PUBLIC}, // 2
+ {"test-phish-proto", SOCIAL_ENGINEERING_PUBLIC}, // 2
+ {"moztest-unwanted-proto", UNWANTED_SOFTWARE}, // 3
+ {"test-unwanted-proto", UNWANTED_SOFTWARE}, // 3
+};
+
+NS_IMETHODIMP
+nsUrlClassifierUtils::ConvertThreatTypeToListNames(uint32_t aThreatType,
+ nsACString& aListNames) {
+ for (uint32_t i = 0; i < ArrayLength(THREAT_TYPE_CONV_TABLE); i++) {
+ if (aThreatType == THREAT_TYPE_CONV_TABLE[i].mThreatType) {
+ if (!aListNames.IsEmpty()) {
+ aListNames.AppendLiteral(",");
+ }
+ aListNames += THREAT_TYPE_CONV_TABLE[i].mListName;
+ }
+ }
+
+ return aListNames.IsEmpty() ? NS_ERROR_FAILURE : NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierUtils::ConvertListNameToThreatType(const nsACString& aListName,
+ uint32_t* aThreatType) {
+ for (uint32_t i = 0; i < ArrayLength(THREAT_TYPE_CONV_TABLE); i++) {
+ if (aListName.EqualsASCII(THREAT_TYPE_CONV_TABLE[i].mListName)) {
+ *aThreatType = THREAT_TYPE_CONV_TABLE[i].mThreatType;
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierUtils::GetProvider(const nsACString& aTableName,
+ nsACString& aProvider) {
+ MutexAutoLock lock(mProviderDictLock);
+ nsCString* provider = nullptr;
+
+ if (IsTestTable(aTableName)) {
+ aProvider = nsLiteralCString(TESTING_TABLE_PROVIDER_NAME);
+ } else if (mProviderDict.Get(aTableName, &provider)) {
+ aProvider = provider ? *provider : ""_ns;
+ } else {
+ aProvider.Truncate();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierUtils::GetTelemetryProvider(const nsACString& aTableName,
+ nsACString& aProvider) {
+ GetProvider(aTableName, aProvider);
+ // Exceptionlist known providers to avoid reporting on private ones.
+ // An empty provider is treated as "other"
+ if (!"mozilla"_ns.Equals(aProvider) && !"google"_ns.Equals(aProvider) &&
+ !"google4"_ns.Equals(aProvider) && !"baidu"_ns.Equals(aProvider) &&
+ !"mozcn"_ns.Equals(aProvider) && !"yandex"_ns.Equals(aProvider) &&
+ !nsLiteralCString(TESTING_TABLE_PROVIDER_NAME).Equals(aProvider)) {
+ aProvider.AssignLiteral("other");
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierUtils::GetProtocolVersion(const nsACString& aProvider,
+ nsACString& aVersion) {
+ nsCOMPtr<nsIPrefBranch> prefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ if (prefBranch) {
+ nsPrintfCString prefName("browser.safebrowsing.provider.%s.pver",
+ nsCString(aProvider).get());
+ nsAutoCString version;
+ nsresult rv = prefBranch->GetCharPref(prefName.get(), version);
+
+ aVersion = NS_SUCCEEDED(rv) ? version.get() : DEFAULT_PROTOCOL_VERSION;
+ } else {
+ aVersion = DEFAULT_PROTOCOL_VERSION;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierUtils::MakeUpdateRequestV4(
+ const nsTArray<nsCString>& aListNames,
+ const nsTArray<nsCString>& aStatesBase64, nsACString& aRequest) {
+ using namespace mozilla::safebrowsing;
+
+ if (aListNames.Length() != aStatesBase64.Length()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ FetchThreatListUpdatesRequest r;
+ r.set_allocated_client(CreateClientInfo());
+
+ for (uint32_t i = 0; i < aListNames.Length(); i++) {
+ uint32_t threatType;
+ nsresult rv = ConvertListNameToThreatType(aListNames[i], &threatType);
+ if (NS_FAILED(rv)) {
+ continue; // Unknown list name.
+ }
+ if (!IsAllowedOnCurrentPlatform(threatType)) {
+ NS_WARNING(
+ nsPrintfCString(
+ "Threat type %d (%s) is unsupported on current platform: %d",
+ threatType, aListNames[i].get(), GetPlatformType())
+ .get());
+ continue; // Some threat types are not available on some platforms.
+ }
+ auto lur = r.mutable_list_update_requests()->Add();
+ InitListUpdateRequest(static_cast<ThreatType>(threatType), aStatesBase64[i],
+ lur);
+ }
+
+ // Then serialize.
+ std::string s;
+ r.SerializeToString(&s);
+
+ nsCString out;
+ nsresult rv = Base64URLEncode(s.size(), (const uint8_t*)s.c_str(),
+ Base64URLEncodePaddingPolicy::Include, out);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aRequest = out;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierUtils::MakeFindFullHashRequestV4(
+ const nsTArray<nsCString>& aListNames,
+ const nsTArray<nsCString>& aListStatesBase64,
+ const nsTArray<nsCString>& aPrefixesBase64, nsACString& aRequest) {
+ if (aListNames.Length() != aListStatesBase64.Length()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ FindFullHashesRequest r;
+ r.set_allocated_client(CreateClientInfo());
+
+ nsresult rv;
+
+ //-------------------------------------------------------------------
+ // Set up FindFullHashesRequest.threat_info.
+ auto threatInfo = r.mutable_threat_info();
+
+ PlatformType platform = GetPlatformType();
+
+ // 1) Set threat types.
+ for (uint32_t i = 0; i < aListNames.Length(); i++) {
+ // Add threat types.
+ uint32_t threatType;
+ rv = ConvertListNameToThreatType(aListNames[i], &threatType);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!IsAllowedOnCurrentPlatform(threatType)) {
+ NS_WARNING(
+ nsPrintfCString(
+ "Threat type %d (%s) is unsupported on current platform: %d",
+ threatType, aListNames[i].get(), GetPlatformType())
+ .get());
+ continue;
+ }
+ threatInfo->add_threat_types((ThreatType)threatType);
+
+#if defined(ANDROID)
+ // Temporary hack to fix bug 1441345.
+ if (((ThreatType)threatType == SOCIAL_ENGINEERING_PUBLIC) ||
+ ((ThreatType)threatType == SOCIAL_ENGINEERING)) {
+ platform = LINUX_PLATFORM;
+ }
+#endif
+
+ // Add client states for index 'i' only when the threat type is available
+ // on current platform.
+ nsCString stateBinary;
+ rv = Base64Decode(aListStatesBase64[i], stateBinary);
+ NS_ENSURE_SUCCESS(rv, rv);
+ r.add_client_states(stateBinary.get(), stateBinary.Length());
+ }
+
+ // 2) Set platform type.
+ threatInfo->add_platform_types(platform);
+
+ // 3) Set threat entry type.
+ threatInfo->add_threat_entry_types(URL);
+
+ // 4) Set threat entries.
+ for (const nsCString& prefix : aPrefixesBase64) {
+ nsCString prefixBinary;
+ rv = Base64Decode(prefix, prefixBinary);
+ threatInfo->add_threat_entries()->set_hash(prefixBinary.get(),
+ prefixBinary.Length());
+ }
+ //-------------------------------------------------------------------
+
+ // Then serialize.
+ std::string s;
+ r.SerializeToString(&s);
+
+ nsCString out;
+ rv = Base64URLEncode(s.size(), (const uint8_t*)s.c_str(),
+ Base64URLEncodePaddingPolicy::Include, out);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aRequest = out;
+
+ return NS_OK;
+}
+
+// Remove ref, query, userpass, anypart which may contain sensitive data
+static nsresult GetSpecWithoutSensitiveData(nsIURI* aUri, nsACString& aSpec) {
+ if (NS_WARN_IF(!aUri)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIURL> url(do_QueryInterface(aUri));
+ if (url) {
+ nsCOMPtr<nsIURI> clone;
+ rv = NS_MutateURI(url)
+ .SetQuery(""_ns)
+ .SetRef(""_ns)
+ .SetUserPass(""_ns)
+ .Finalize(clone);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = clone->GetAsciiSpec(aSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ return NS_OK;
+}
+
+static nsresult AddThreatSourceFromChannel(ThreatHit& aHit,
+ nsIChannel* aChannel,
+ ThreatHit_ThreatSourceType aType) {
+ if (NS_WARN_IF(!aChannel)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsresult rv;
+
+ auto matchingSource = aHit.add_resources();
+ matchingSource->set_type(aType);
+
+ nsCOMPtr<nsIURI> uri;
+ rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString spec;
+ rv = GetSpecWithoutSensitiveData(uri, spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ matchingSource->set_url(spec.get());
+
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
+ if (httpChannel) {
+ nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
+ if (referrerInfo) {
+ nsAutoCString referrerSpec;
+ nsCOMPtr<nsIURI> referrer = referrerInfo->GetComputedReferrer();
+ if (referrer) {
+ rv = GetSpecWithoutSensitiveData(referrer, referrerSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ matchingSource->set_referrer(referrerSpec.get());
+ }
+ }
+ }
+
+ nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
+ do_QueryInterface(aChannel);
+ if (httpChannelInternal) {
+ nsCString remoteIp;
+ rv = httpChannelInternal->GetRemoteAddress(remoteIp);
+ if (NS_SUCCEEDED(rv) && !remoteIp.IsEmpty()) {
+ matchingSource->set_remote_ip(remoteIp.get());
+ }
+ }
+ return NS_OK;
+}
+static nsresult AddThreatSourceFromRedirectEntry(
+ ThreatHit& aHit, nsIRedirectHistoryEntry* aRedirectEntry,
+ ThreatHit_ThreatSourceType aType) {
+ if (NS_WARN_IF(!aRedirectEntry)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsresult rv;
+
+ nsCOMPtr<nsIPrincipal> principal;
+ rv = aRedirectEntry->GetPrincipal(getter_AddRefs(principal));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCString spec;
+ rv = principal->GetExposableSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ auto source = aHit.add_resources();
+ source->set_url(spec.get());
+ source->set_type(aType);
+
+ nsCOMPtr<nsIURI> referrer;
+ rv = aRedirectEntry->GetReferrerURI(getter_AddRefs(referrer));
+ if (NS_SUCCEEDED(rv) && referrer) {
+ nsCString referrerSpec;
+ rv = GetSpecWithoutSensitiveData(referrer, referrerSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ source->set_referrer(referrerSpec.get());
+ }
+
+ nsCString remoteIp;
+ rv = aRedirectEntry->GetRemoteAddress(remoteIp);
+ if (NS_SUCCEEDED(rv) && !remoteIp.IsEmpty()) {
+ source->set_remote_ip(remoteIp.get());
+ }
+ return NS_OK;
+}
+
+// Add top level tab url and redirect threatsources to threatHit message
+static nsresult AddTabThreatSources(ThreatHit& aHit, nsIChannel* aChannel) {
+ if (NS_WARN_IF(!aChannel)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsresult rv;
+ nsCOMPtr<mozIDOMWindowProxy> win;
+ nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
+ do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = thirdPartyUtil->GetTopWindowForChannel(aChannel, nullptr,
+ getter_AddRefs(win));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ auto* pwin = nsPIDOMWindowOuter::From(win);
+ nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
+ if (!docShell) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIChannel> topChannel;
+ docShell->GetCurrentDocumentChannel(getter_AddRefs(topChannel));
+ if (!topChannel) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ rv = aChannel->GetURI(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIURI> topUri;
+ rv = topChannel->GetURI(getter_AddRefs(topUri));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isTopUri = false;
+ rv = topUri->Equals(uri, &isTopUri);
+ if (NS_SUCCEEDED(rv) && !isTopUri) {
+ nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+ if (loadInfo->RedirectChain().Length()) {
+ AddThreatSourceFromRedirectEntry(aHit, loadInfo->RedirectChain()[0],
+ ThreatHit_ThreatSourceType_TAB_RESOURCE);
+ }
+ }
+
+ // Set top level tab_url threat source
+ rv = AddThreatSourceFromChannel(aHit, topChannel,
+ ThreatHit_ThreatSourceType_TAB_URL);
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+
+ // Set tab_redirect threat sources if there's any
+ nsCOMPtr<nsILoadInfo> topLoadInfo = topChannel->LoadInfo();
+ nsIRedirectHistoryEntry* redirectEntry;
+ size_t length = topLoadInfo->RedirectChain().Length();
+ for (size_t i = 0; i < length; i++) {
+ redirectEntry = topLoadInfo->RedirectChain()[i];
+ AddThreatSourceFromRedirectEntry(aHit, redirectEntry,
+ ThreatHit_ThreatSourceType_TAB_REDIRECT);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierUtils::MakeThreatHitReport(nsIChannel* aChannel,
+ const nsACString& aListName,
+ const nsACString& aHashBase64,
+ nsACString& aRequest) {
+ if (NS_WARN_IF(aListName.IsEmpty()) || NS_WARN_IF(aHashBase64.IsEmpty()) ||
+ NS_WARN_IF(!aChannel)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ ThreatHit hit;
+ nsresult rv;
+
+ uint32_t threatType;
+ rv = ConvertListNameToThreatType(aListName, &threatType);
+ NS_ENSURE_SUCCESS(rv, rv);
+ hit.set_threat_type(static_cast<ThreatType>(threatType));
+
+ hit.set_platform_type(GetPlatformType());
+
+ nsCString hash;
+ rv = Base64Decode(aHashBase64, hash);
+ if (NS_FAILED(rv) || hash.Length() != COMPLETE_SIZE) {
+ return NS_ERROR_FAILURE;
+ }
+
+ auto threatEntry = hit.mutable_entry();
+ threatEntry->set_hash(hash.get(), hash.Length());
+
+ // Set matching source
+ rv = AddThreatSourceFromChannel(hit, aChannel,
+ ThreatHit_ThreatSourceType_MATCHING_URL);
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+ // Set tab url, tab resource url and redirect sources
+ rv = AddTabThreatSources(hit, aChannel);
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+
+ hit.set_allocated_client_info(CreateClientInfo());
+
+ std::string s;
+ hit.SerializeToString(&s);
+
+ nsCString out;
+ rv = Base64URLEncode(s.size(), reinterpret_cast<const uint8_t*>(s.c_str()),
+ Base64URLEncodePaddingPolicy::Include, out);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aRequest = out;
+
+ return NS_OK;
+}
+
+static uint32_t DurationToMs(const Duration& aDuration) {
+ // Seconds precision is good enough. Ignore nanoseconds like Chrome does.
+ return aDuration.seconds() * 1000;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierUtils::ParseFindFullHashResponseV4(
+ const nsACString& aResponse,
+ nsIUrlClassifierParseFindFullHashCallback* aCallback) {
+ enum CompletionErrorType {
+ SUCCESS = 0,
+ PARSING_FAILURE = 1,
+ UNKNOWN_THREAT_TYPE = 2,
+ };
+
+ FindFullHashesResponse r;
+ if (!r.ParseFromArray(aResponse.BeginReading(), aResponse.Length())) {
+ NS_WARNING("Invalid response");
+ Telemetry::Accumulate(Telemetry::URLCLASSIFIER_COMPLETION_ERROR,
+ PARSING_FAILURE);
+ return NS_ERROR_FAILURE;
+ }
+
+ bool hasUnknownThreatType = false;
+
+ for (auto& m : r.matches()) {
+ nsCString tableNames;
+ nsresult rv = ConvertThreatTypeToListNames(m.threat_type(), tableNames);
+ if (NS_FAILED(rv)) {
+ hasUnknownThreatType = true;
+ continue; // Ignore un-convertable threat type.
+ }
+ auto& hash = m.threat().hash();
+ auto cacheDurationSec = m.cache_duration().seconds();
+ aCallback->OnCompleteHashFound(
+ nsDependentCString(hash.c_str(), hash.length()), tableNames,
+ cacheDurationSec);
+ }
+
+ auto minWaitDuration = DurationToMs(r.minimum_wait_duration());
+ auto negCacheDurationSec = r.negative_cache_duration().seconds();
+
+ aCallback->OnResponseParsed(minWaitDuration, negCacheDurationSec);
+
+ Telemetry::Accumulate(Telemetry::URLCLASSIFIER_COMPLETION_ERROR,
+ hasUnknownThreatType ? UNKNOWN_THREAT_TYPE : SUCCESS);
+ return NS_OK;
+}
+
+//////////////////////////////////////////////////////////
+// nsIObserver
+
+NS_IMETHODIMP
+nsUrlClassifierUtils::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ if (0 == strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
+ MutexAutoLock lock(mProviderDictLock);
+ return ReadProvidersFromPrefs(mProviderDict);
+ }
+
+ if (0 == strcmp(aTopic, "xpcom-shutdown-threads")) {
+ nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ NS_ENSURE_TRUE(prefs, NS_ERROR_FAILURE);
+ return prefs->RemoveObserver("browser.safebrowsing", this);
+ }
+
+ return NS_ERROR_UNEXPECTED;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// non-interface methods
+
+nsresult nsUrlClassifierUtils::ReadProvidersFromPrefs(ProviderDictType& aDict) {
+ MOZ_ASSERT(NS_IsMainThread(),
+ "ReadProvidersFromPrefs must be on main thread");
+
+ nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+ NS_ENSURE_TRUE(prefs, NS_ERROR_FAILURE);
+ nsCOMPtr<nsIPrefBranch> prefBranch;
+ nsresult rv = prefs->GetBranch("browser.safebrowsing.provider.",
+ getter_AddRefs(prefBranch));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // We've got a pref branch for "browser.safebrowsing.provider.".
+ // Enumerate all children prefs and parse providers.
+ nsTArray<nsCString> childArray;
+ rv = prefBranch->GetChildList("", childArray);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Collect providers from childArray.
+ nsTHashSet<nsCString> providers;
+ for (auto& child : childArray) {
+ auto dotPos = child.FindChar('.');
+ if (dotPos < 0) {
+ continue;
+ }
+
+ nsDependentCSubstring provider = Substring(child, 0, dotPos);
+
+ providers.Insert(provider);
+ }
+
+ // Now we have all providers. Check which one owns |aTableName|.
+ // e.g. The owning lists of provider "google" is defined in
+ // "browser.safebrowsing.provider.google.lists".
+ for (const auto& provider : providers) {
+ nsPrintfCString owninListsPref("%s.lists",
+ nsPromiseFlatCString{provider}.get());
+
+ nsAutoCString owningLists;
+ nsresult rv = prefBranch->GetCharPref(owninListsPref.get(), owningLists);
+ if (NS_FAILED(rv)) {
+ continue;
+ }
+
+ // We've got the owning lists (represented as string) of |provider|.
+ // Build the dictionary for the owning list and the current provider.
+ nsTArray<nsCString> tables;
+ Classifier::SplitTables(owningLists, tables);
+ for (auto tableName : tables) {
+ aDict.InsertOrUpdate(tableName, MakeUnique<nsCString>(provider));
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult nsUrlClassifierUtils::CanonicalizeHostname(const nsACString& hostname,
+ nsACString& _retval) {
+ nsAutoCString unescaped;
+ if (!NS_UnescapeURL(PromiseFlatCString(hostname).get(),
+ PromiseFlatCString(hostname).Length(), 0, unescaped)) {
+ unescaped.Assign(hostname);
+ }
+
+ nsAutoCString cleaned;
+ CleanupHostname(unescaped, cleaned);
+
+ nsAutoCString temp;
+ ParseIPAddress(cleaned, temp);
+ if (!temp.IsEmpty()) {
+ cleaned.Assign(temp);
+ }
+
+ ToLowerCase(cleaned);
+ SpecialEncode(cleaned, false, _retval);
+
+ return NS_OK;
+}
+
+nsresult nsUrlClassifierUtils::CanonicalizePath(const nsACString& path,
+ nsACString& _retval) {
+ _retval.Truncate();
+
+ nsAutoCString decodedPath(path);
+ nsAutoCString temp;
+ while (NS_UnescapeURL(decodedPath.get(), decodedPath.Length(), 0, temp)) {
+ decodedPath.Assign(temp);
+ temp.Truncate();
+ }
+
+ SpecialEncode(decodedPath, true, _retval);
+ // XXX: lowercase the path?
+
+ return NS_OK;
+}
+
+void nsUrlClassifierUtils::CleanupHostname(const nsACString& hostname,
+ nsACString& _retval) {
+ _retval.Truncate();
+
+ const char* curChar = hostname.BeginReading();
+ const char* end = hostname.EndReading();
+ char lastChar = '\0';
+ while (curChar != end) {
+ unsigned char c = static_cast<unsigned char>(*curChar);
+ if (c == '.' && (lastChar == '\0' || lastChar == '.')) {
+ // skip
+ } else {
+ _retval.Append(*curChar);
+ }
+ lastChar = c;
+ ++curChar;
+ }
+
+ // cut off trailing dots
+ while (_retval.Length() > 0 && _retval[_retval.Length() - 1] == '.') {
+ _retval.SetLength(_retval.Length() - 1);
+ }
+}
+
+void nsUrlClassifierUtils::ParseIPAddress(const nsACString& host,
+ nsACString& _retval) {
+ _retval.Truncate();
+ nsACString::const_iterator iter, end;
+ host.BeginReading(iter);
+ host.EndReading(end);
+
+ if (host.Length() <= 15) {
+ // The Windows resolver allows a 4-part dotted decimal IP address to
+ // have a space followed by any old rubbish, so long as the total length
+ // of the string doesn't get above 15 characters. So, "10.192.95.89 xy"
+ // is resolved to 10.192.95.89.
+ // If the string length is greater than 15 characters, e.g.
+ // "10.192.95.89 xy.wildcard.example.com", it will be resolved through
+ // DNS.
+
+ if (FindCharInReadable(' ', iter, end)) {
+ end = iter;
+ }
+ }
+
+ for (host.BeginReading(iter); iter != end; iter++) {
+ if (!(mozilla::IsAsciiHexDigit(*iter) || *iter == 'x' || *iter == 'X' ||
+ *iter == '.')) {
+ // not an IP
+ return;
+ }
+ }
+
+ host.BeginReading(iter);
+ nsTArray<nsCString> parts;
+ ParseString(PromiseFlatCString(Substring(iter, end)), '.', parts);
+ if (parts.Length() > 4) {
+ return;
+ }
+
+ // If any potentially-octal numbers (start with 0 but not hex) have
+ // non-octal digits, no part of the ip can be in octal
+ // XXX: this came from the old javascript implementation, is it really
+ // supposed to be like this?
+ bool allowOctal = true;
+ uint32_t i;
+
+ for (i = 0; i < parts.Length(); i++) {
+ const nsCString& part = parts[i];
+ if (part[0] == '0') {
+ for (uint32_t j = 1; j < part.Length(); j++) {
+ if (part[j] == 'x') {
+ break;
+ }
+ if (part[j] == '8' || part[j] == '9') {
+ allowOctal = false;
+ break;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < parts.Length(); i++) {
+ nsAutoCString canonical;
+
+ if (i == parts.Length() - 1) {
+ CanonicalNum(parts[i], 5 - parts.Length(), allowOctal, canonical);
+ } else {
+ CanonicalNum(parts[i], 1, allowOctal, canonical);
+ }
+
+ if (canonical.IsEmpty()) {
+ _retval.Truncate();
+ return;
+ }
+
+ if (_retval.IsEmpty()) {
+ _retval.Assign(canonical);
+ } else {
+ _retval.Append('.');
+ _retval.Append(canonical);
+ }
+ }
+}
+
+void nsUrlClassifierUtils::CanonicalNum(const nsACString& num, uint32_t bytes,
+ bool allowOctal, nsACString& _retval) {
+ _retval.Truncate();
+
+ if (num.Length() < 1) {
+ return;
+ }
+
+ uint32_t val;
+ if (allowOctal && IsOctal(num)) {
+ if (PR_sscanf(PromiseFlatCString(num).get(), "%o", &val) != 1) {
+ return;
+ }
+ } else if (IsDecimal(num)) {
+ if (PR_sscanf(PromiseFlatCString(num).get(), "%u", &val) != 1) {
+ return;
+ }
+ } else if (IsHex(num)) {
+ if (PR_sscanf(PromiseFlatCString(num).get(),
+ num[1] == 'X' ? "0X%x" : "0x%x", &val) != 1) {
+ return;
+ }
+ } else {
+ return;
+ }
+
+ while (bytes--) {
+ char buf[20];
+ SprintfLiteral(buf, "%u", val & 0xff);
+ if (_retval.IsEmpty()) {
+ _retval.Assign(buf);
+ } else {
+ _retval = nsDependentCString(buf) + "."_ns + _retval;
+ }
+ val >>= 8;
+ }
+}
+
+// This function will encode all "special" characters in typical url
+// encoding, that is %hh where h is a valid hex digit. It will also fold
+// any duplicated slashes.
+bool nsUrlClassifierUtils::SpecialEncode(const nsACString& url,
+ bool foldSlashes,
+ nsACString& _retval) {
+ bool changed = false;
+ const char* curChar = url.BeginReading();
+ const char* end = url.EndReading();
+
+ unsigned char lastChar = '\0';
+ while (curChar != end) {
+ unsigned char c = static_cast<unsigned char>(*curChar);
+ if (ShouldURLEscape(c)) {
+ _retval.Append('%');
+ _retval.Append(int_to_hex_digit(c / 16));
+ _retval.Append(int_to_hex_digit(c % 16));
+
+ changed = true;
+ } else if (foldSlashes && (c == '/' && lastChar == '/')) {
+ // skip
+ } else {
+ _retval.Append(*curChar);
+ }
+ lastChar = c;
+ curChar++;
+ }
+ return changed;
+}
+
+bool nsUrlClassifierUtils::ShouldURLEscape(const unsigned char c) const {
+ return c <= 32 || c == '%' || c == '#' || c >= 127;
+}
+
+// moztest- tables are built-in created in LookupCache, they contain hardcoded
+// url entries in it. moztest tables don't support updates.
+// static
+bool nsUrlClassifierUtils::IsMozTestTable(const nsACString& aTableName) {
+ return StringBeginsWith(aTableName, "moztest-"_ns);
+}
+
+// test- tables are used by testcases and can add custom test entries
+// through update API.
+// static
+bool nsUrlClassifierUtils::IsTestTable(const nsACString& aTableName) {
+ return IsMozTestTable(aTableName) || StringBeginsWith(aTableName, "test"_ns);
+}
+
+bool nsUrlClassifierUtils::IsInSafeMode() {
+ static Maybe<bool> sIsInSafeMode;
+
+ if (!sIsInSafeMode.isSome()) {
+ nsCOMPtr<nsIXULRuntime> appInfo =
+ do_GetService("@mozilla.org/xre/runtime;1");
+ if (appInfo) {
+ bool inSafeMode = false;
+ appInfo->GetInSafeMode(&inSafeMode);
+ sIsInSafeMode.emplace(inSafeMode);
+ }
+ }
+
+ return sIsInSafeMode.value();
+}
diff --git a/toolkit/components/url-classifier/nsUrlClassifierUtils.h b/toolkit/components/url-classifier/nsUrlClassifierUtils.h
new file mode 100644
index 0000000000..5ff9d97fdc
--- /dev/null
+++ b/toolkit/components/url-classifier/nsUrlClassifierUtils.h
@@ -0,0 +1,72 @@
+/* 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/. */
+
+#ifndef nsUrlClassifierUtils_h_
+#define nsUrlClassifierUtils_h_
+
+#include "mozilla/Mutex.h"
+#include "mozilla/Base64.h"
+#include "nsIUrlClassifierUtils.h"
+#include "nsClassHashtable.h"
+#include "nsIObserver.h"
+
+#define TESTING_TABLE_PROVIDER_NAME "test"
+
+class nsUrlClassifierUtils final : public nsIUrlClassifierUtils,
+ public nsIObserver {
+ public:
+ typedef nsClassHashtable<nsCStringHashKey, nsCString> ProviderDictType;
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURLCLASSIFIERUTILS
+ NS_DECL_NSIOBSERVER
+
+ static already_AddRefed<nsUrlClassifierUtils> GetXPCOMSingleton();
+ static nsUrlClassifierUtils* GetInstance();
+
+ nsresult CanonicalizeHostname(const nsACString& hostname,
+ nsACString& _retval);
+ nsresult CanonicalizePath(const nsACString& url, nsACString& _retval);
+
+ // This function will encode all "special" characters in typical url encoding,
+ // that is %hh where h is a valid hex digit. The characters which are encoded
+ // by this function are any ascii characters under 32(control characters and
+ // space), 37(%), and anything 127 or above (special characters). Url is the
+ // string to encode, ret is the encoded string. Function returns true if
+ // ret != url.
+ bool SpecialEncode(const nsACString& url, bool foldSlashes,
+ nsACString& _retval);
+
+ void ParseIPAddress(const nsACString& host, nsACString& _retval);
+ void CanonicalNum(const nsACString& num, uint32_t bytes, bool allowOctal,
+ nsACString& _retval);
+
+ static bool IsMozTestTable(const nsACString& aTableName);
+
+ static bool IsTestTable(const nsACString& aTableName);
+
+ static bool IsInSafeMode();
+
+ private:
+ nsUrlClassifierUtils();
+ ~nsUrlClassifierUtils();
+
+ nsresult Init();
+
+ // Disallow copy constructor
+ nsUrlClassifierUtils(const nsUrlClassifierUtils&);
+
+ // Function to tell if we should encode a character.
+ bool ShouldURLEscape(const unsigned char c) const;
+
+ void CleanupHostname(const nsACString& host, nsACString& _retval);
+
+ nsresult ReadProvidersFromPrefs(ProviderDictType& aDict);
+
+ // The provider lookup table and its mutex.
+ ProviderDictType mProviderDict;
+ mozilla::Mutex mProviderDictLock MOZ_UNANNOTATED;
+};
+
+#endif // nsUrlClassifierUtils_h_
diff --git a/toolkit/components/url-classifier/tests/UrlClassifierTestUtils.sys.mjs b/toolkit/components/url-classifier/tests/UrlClassifierTestUtils.sys.mjs
new file mode 100644
index 0000000000..c69d0c24b4
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/UrlClassifierTestUtils.sys.mjs
@@ -0,0 +1,307 @@
+const ANNOTATION_TABLE_NAME = "mochitest1-track-simple";
+const ANNOTATION_TABLE_PREF = "urlclassifier.trackingAnnotationTable";
+const ANNOTATION_ENTITYLIST_TABLE_NAME = "mochitest1-trackwhite-simple";
+const ANNOTATION_ENTITYLIST_TABLE_PREF =
+ "urlclassifier.trackingAnnotationWhitelistTable";
+
+const TRACKING_TABLE_NAME = "mochitest2-track-simple";
+const TRACKING_TABLE_PREF = "urlclassifier.trackingTable";
+const ENTITYLIST_TABLE_NAME = "mochitest2-trackwhite-simple";
+const ENTITYLIST_TABLE_PREF = "urlclassifier.trackingWhitelistTable";
+
+const SOCIAL_ANNOTATION_TABLE_NAME = "mochitest3-track-simple";
+const SOCIAL_ANNOTATION_TABLE_PREF =
+ "urlclassifier.features.socialtracking.annotate.blacklistTables";
+const SOCIAL_TRACKING_TABLE_NAME = "mochitest4-track-simple";
+const SOCIAL_TRACKING_TABLE_PREF =
+ "urlclassifier.features.socialtracking.blacklistTables";
+const EMAIL_TRACKING_TABLE_NAME = "mochitest5-track-simple";
+const EMAIL_TRACKING_TABLE_PREF =
+ "urlclassifier.features.emailtracking.blocklistTables";
+
+let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+
+export var UrlClassifierTestUtils = {
+ addTestTrackers() {
+ // Add some URLs to the tracking databases
+ let annotationURL1 = "tracking.example.org/"; // only for annotations
+ let annotationURL2 = "itisatracker.org/";
+ let annotationURL3 = "trackertest.org/";
+ let annotationURL4 = "another-tracking.example.net/";
+ let annotationURL5 = "tlsresumptiontest.example.org/";
+ let annotationEntitylistedURL = "itisatrap.org/?resource=example.org";
+ let trackingURL1 = "tracking.example.com/"; // only for TP
+ let trackingURL2 = "itisatracker.org/";
+ let trackingURL3 = "trackertest.org/";
+ let entitylistedURL = "itisatrap.org/?resource=itisatracker.org";
+ let socialTrackingURL = "social-tracking.example.org/";
+ let emailTrackingURL = "email-tracking.example.org/";
+
+ let annotationUpdate =
+ "n:1000\ni:" +
+ ANNOTATION_TABLE_NAME +
+ "\nad:5\n" +
+ "a:1:32:" +
+ annotationURL1.length +
+ "\n" +
+ annotationURL1 +
+ "\n" +
+ "a:2:32:" +
+ annotationURL2.length +
+ "\n" +
+ annotationURL2 +
+ "\n" +
+ "a:3:32:" +
+ annotationURL3.length +
+ "\n" +
+ annotationURL3 +
+ "\n" +
+ "a:4:32:" +
+ annotationURL4.length +
+ "\n" +
+ annotationURL4 +
+ "\n" +
+ "a:5:32:" +
+ annotationURL5.length +
+ "\n" +
+ annotationURL5 +
+ "\n";
+ let socialAnnotationUpdate =
+ "n:1000\ni:" +
+ SOCIAL_ANNOTATION_TABLE_NAME +
+ "\nad:1\n" +
+ "a:1:32:" +
+ socialTrackingURL.length +
+ "\n" +
+ socialTrackingURL +
+ "\n";
+ let annotationEntitylistUpdate =
+ "n:1000\ni:" +
+ ANNOTATION_ENTITYLIST_TABLE_NAME +
+ "\nad:1\n" +
+ "a:1:32:" +
+ annotationEntitylistedURL.length +
+ "\n" +
+ annotationEntitylistedURL +
+ "\n";
+ let trackingUpdate =
+ "n:1000\ni:" +
+ TRACKING_TABLE_NAME +
+ "\nad:3\n" +
+ "a:1:32:" +
+ trackingURL1.length +
+ "\n" +
+ trackingURL1 +
+ "\n" +
+ "a:2:32:" +
+ trackingURL2.length +
+ "\n" +
+ trackingURL2 +
+ "\n" +
+ "a:3:32:" +
+ trackingURL3.length +
+ "\n" +
+ trackingURL3 +
+ "\n";
+ let socialTrackingUpdate =
+ "n:1000\ni:" +
+ SOCIAL_TRACKING_TABLE_NAME +
+ "\nad:1\n" +
+ "a:1:32:" +
+ socialTrackingURL.length +
+ "\n" +
+ socialTrackingURL +
+ "\n";
+ let emailTrackingUpdate =
+ "n:1000\ni:" +
+ EMAIL_TRACKING_TABLE_NAME +
+ "\nad:1\n" +
+ "a:1:32:" +
+ emailTrackingURL.length +
+ "\n" +
+ emailTrackingURL +
+ "\n";
+ let entitylistUpdate =
+ "n:1000\ni:" +
+ ENTITYLIST_TABLE_NAME +
+ "\nad:1\n" +
+ "a:1:32:" +
+ entitylistedURL.length +
+ "\n" +
+ entitylistedURL +
+ "\n";
+
+ var tables = [
+ {
+ pref: ANNOTATION_TABLE_PREF,
+ name: ANNOTATION_TABLE_NAME,
+ update: annotationUpdate,
+ },
+ {
+ pref: SOCIAL_ANNOTATION_TABLE_PREF,
+ name: SOCIAL_ANNOTATION_TABLE_NAME,
+ update: socialAnnotationUpdate,
+ },
+ {
+ pref: ANNOTATION_ENTITYLIST_TABLE_PREF,
+ name: ANNOTATION_ENTITYLIST_TABLE_NAME,
+ update: annotationEntitylistUpdate,
+ },
+ {
+ pref: TRACKING_TABLE_PREF,
+ name: TRACKING_TABLE_NAME,
+ update: trackingUpdate,
+ },
+ {
+ pref: SOCIAL_TRACKING_TABLE_PREF,
+ name: SOCIAL_TRACKING_TABLE_NAME,
+ update: socialTrackingUpdate,
+ },
+ {
+ pref: EMAIL_TRACKING_TABLE_PREF,
+ name: EMAIL_TRACKING_TABLE_NAME,
+ update: emailTrackingUpdate,
+ },
+ {
+ pref: ENTITYLIST_TABLE_PREF,
+ name: ENTITYLIST_TABLE_NAME,
+ update: entitylistUpdate,
+ },
+ ];
+
+ let tableIndex = 0;
+ let doOneUpdate = () => {
+ if (tableIndex == tables.length) {
+ return Promise.resolve();
+ }
+ return this.useTestDatabase(tables[tableIndex]).then(
+ () => {
+ tableIndex++;
+ return doOneUpdate();
+ },
+ aErrMsg => {
+ dump("Rejected: " + aErrMsg + ". Retry later.\n");
+ return new Promise(resolve => {
+ timer.initWithCallback(resolve, 100, Ci.nsITimer.TYPE_ONE_SHOT);
+ }).then(doOneUpdate);
+ }
+ );
+ };
+
+ return doOneUpdate();
+ },
+
+ cleanupTestTrackers() {
+ Services.prefs.clearUserPref(ANNOTATION_TABLE_PREF);
+ Services.prefs.clearUserPref(SOCIAL_ANNOTATION_TABLE_PREF);
+ Services.prefs.clearUserPref(ANNOTATION_ENTITYLIST_TABLE_PREF);
+ Services.prefs.clearUserPref(TRACKING_TABLE_PREF);
+ Services.prefs.clearUserPref(SOCIAL_TRACKING_TABLE_PREF);
+ Services.prefs.clearUserPref(EMAIL_TRACKING_TABLE_PREF);
+ Services.prefs.clearUserPref(ENTITYLIST_TABLE_PREF);
+ },
+
+ /**
+ * Add some entries to a test tracking protection database, and resets
+ * back to the default database after the test ends.
+ *
+ * @return {Promise}
+ */
+ useTestDatabase(table) {
+ Services.prefs.setCharPref(table.pref, table.name);
+
+ return new Promise((resolve, reject) => {
+ let dbService = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(
+ Ci.nsIUrlClassifierDBService
+ );
+ let listener = {
+ QueryInterface: iid => {
+ if (
+ iid.equals(Ci.nsISupports) ||
+ iid.equals(Ci.nsIUrlClassifierUpdateObserver)
+ ) {
+ return listener;
+ }
+
+ throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
+ },
+ updateUrlRequested: url => {},
+ streamFinished: status => {},
+ updateError: errorCode => {
+ reject("Got updateError when updating " + table.name);
+ },
+ updateSuccess: requestedTimeout => {
+ resolve();
+ },
+ };
+
+ try {
+ dbService.beginUpdate(listener, table.name, "");
+ dbService.beginStream("", "");
+ dbService.updateStream(table.update);
+ dbService.finishStream();
+ dbService.finishUpdate();
+ } catch (e) {
+ reject("Failed to update with dbService: " + table.name);
+ }
+ });
+ },
+
+ /**
+ * Handle the next "urlclassifier-before-block-channel" event.
+ * @param {Object} options
+ * @param {String} [options.filterOrigin] - Only handle event for channels
+ * with matching origin.
+ * @param {function} [options.onBeforeBlockChannel] - Optional callback for
+ * the event. Called before acting on the channel.
+ * @param {("allow"|"replace")} [options.action] - Whether to allow or replace
+ * the channel.
+ * @returns {Promise} - Resolves once event has been handled.
+ */
+ handleBeforeBlockChannel({
+ filterOrigin = null,
+ onBeforeBlockChannel,
+ action,
+ }) {
+ if (action && action != "allow" && action != "replace") {
+ throw new Error("Invalid action " + action);
+ }
+ let channelClassifierService = Cc[
+ "@mozilla.org/url-classifier/channel-classifier-service;1"
+ ].getService(Ci.nsIChannelClassifierService);
+
+ let resolver;
+ let promise = new Promise(resolve => {
+ resolver = resolve;
+ });
+
+ let observer = {
+ observe(subject, topic) {
+ if (topic != "urlclassifier-before-block-channel") {
+ return;
+ }
+ let channel = subject.QueryInterface(Ci.nsIUrlClassifierBlockedChannel);
+
+ if (filterOrigin) {
+ let { url } = channel;
+ let { origin } = new URL(url);
+ if (filterOrigin != origin) {
+ return;
+ }
+ }
+
+ if (onBeforeBlockChannel) {
+ onBeforeBlockChannel(channel);
+ }
+ if (action) {
+ channel[action]();
+ }
+
+ channelClassifierService.removeListener(observer);
+ resolver();
+ },
+ };
+ channelClassifierService.addListener(observer);
+ return promise;
+ },
+};
diff --git a/toolkit/components/url-classifier/tests/browser/browser.toml b/toolkit/components/url-classifier/tests/browser/browser.toml
new file mode 100644
index 0000000000..cd6be59720
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/browser/browser.toml
@@ -0,0 +1,7 @@
+[DEFAULT]
+support-files = [
+ "page.html",
+ "raptor.jpg",
+]
+
+["browser_emailtracking_telemetry.js"]
diff --git a/toolkit/components/url-classifier/tests/browser/browser_emailtracking_telemetry.js b/toolkit/components/url-classifier/tests/browser/browser_emailtracking_telemetry.js
new file mode 100644
index 0000000000..086ca49afe
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/browser/browser_emailtracking_telemetry.js
@@ -0,0 +1,423 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+"use strict";
+
+let { UrlClassifierTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/UrlClassifierTestUtils.sys.mjs"
+);
+
+const TEST_DOMAIN = "https://example.com/";
+const TEST_EMAIL_WEBAPP_DOMAIN = "https://test1.example.com/";
+const EMAIL_TRACKER_DOMAIN = "https://email-tracking.example.org/";
+const TEST_PATH = "browser/toolkit/components/url-classifier/tests/browser/";
+
+const TEST_PAGE = TEST_DOMAIN + TEST_PATH + "page.html";
+const TEST_EMAIL_WEBAPP_PAGE =
+ TEST_EMAIL_WEBAPP_DOMAIN + TEST_PATH + "page.html";
+
+const EMAIL_TRACKER_PAGE = EMAIL_TRACKER_DOMAIN + TEST_PATH + "page.html";
+const EMAIL_TRACKER_IMAGE = EMAIL_TRACKER_DOMAIN + TEST_PATH + "raptor.jpg";
+
+const TELEMETRY_EMAIL_TRACKER_COUNT = "EMAIL_TRACKER_COUNT";
+const TELEMETRY_EMAIL_TRACKER_EMBEDDED_PER_TAB =
+ "EMAIL_TRACKER_EMBEDDED_PER_TAB";
+
+const LABEL_BASE_NORMAL = 0;
+const LABEL_CONTENT_NORMAL = 1;
+const LABEL_BASE_EMAIL_WEBAPP = 2;
+const LABEL_CONTENT_EMAIL_WEBAPP = 3;
+
+const KEY_BASE_NORMAL = "base_normal";
+const KEY_CONTENT_NORMAL = "content_normal";
+const KEY_ALL_NORMAL = "all_normal";
+const KEY_BASE_EMAILAPP = "base_emailapp";
+const KEY_CONTENT_EMAILAPP = "content_emailapp";
+const KEY_ALL_EMAILAPP = "all_emailapp";
+
+async function clearTelemetry() {
+ Services.telemetry.getSnapshotForHistograms("main", true /* clear */);
+ Services.telemetry.getHistogramById(TELEMETRY_EMAIL_TRACKER_COUNT).clear();
+ Services.telemetry
+ .getKeyedHistogramById(TELEMETRY_EMAIL_TRACKER_EMBEDDED_PER_TAB)
+ .clear();
+}
+
+async function loadImage(browser, url) {
+ return SpecialPowers.spawn(browser, [url], page => {
+ return new Promise(resolve => {
+ let image = new content.Image();
+ image.src = page + "?" + Math.random();
+ image.onload = _ => resolve(true);
+ image.onerror = _ => resolve(false);
+ });
+ });
+}
+
+async function getTelemetryProbe(key, label, checkCntFn) {
+ let histogram;
+
+ // Wait until the telemetry probe appears.
+ await TestUtils.waitForCondition(() => {
+ let histograms = Services.telemetry.getSnapshotForHistograms(
+ "main",
+ false /* clear */
+ ).parent;
+
+ histogram = histograms[key];
+
+ let checkRes = false;
+
+ if (histogram) {
+ checkRes = checkCntFn ? checkCntFn(histogram.values[label]) : true;
+ }
+
+ return checkRes;
+ });
+
+ return histogram.values[label] || 0;
+}
+
+async function getKeyedHistogram(histogram_id, key, bucket, checkCntFn) {
+ let histogram;
+
+ // Wait until the telemetry probe appears.
+ await TestUtils.waitForCondition(() => {
+ let histograms = Services.telemetry.getSnapshotForKeyedHistograms(
+ "main",
+ false /* clear */
+ ).parent;
+
+ histogram = histograms[histogram_id];
+
+ let checkRes = false;
+
+ if (histogram && histogram[key]) {
+ checkRes = checkCntFn ? checkCntFn(histogram[key].values[bucket]) : true;
+ }
+
+ return checkRes;
+ });
+
+ return histogram[key].values[bucket] || 0;
+}
+
+async function checkTelemetryProbe(key, label, expectedCnt) {
+ let cnt = await getTelemetryProbe(key, label, cnt => {
+ if (cnt === undefined) {
+ cnt = 0;
+ }
+
+ return cnt == expectedCnt;
+ });
+
+ is(cnt, expectedCnt, "There should be expected count in telemetry.");
+}
+
+async function checkKeyedHistogram(histogram_id, key, bucket, expectedCnt) {
+ let cnt = await getKeyedHistogram(histogram_id, key, bucket, cnt => {
+ if (cnt === undefined) {
+ cnt = 0;
+ }
+
+ return cnt == expectedCnt;
+ });
+
+ is(cnt, expectedCnt, "There should be expected count in keyed telemetry.");
+}
+
+function checkNoTelemetryProbe(key) {
+ let histograms = Services.telemetry.getSnapshotForHistograms(
+ "main",
+ false /* clear */
+ ).parent;
+
+ let histogram = histograms[key];
+
+ ok(!histogram, `No Telemetry has been recorded for ${key}`);
+}
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [
+ "urlclassifier.features.emailtracking.datacollection.blocklistTables",
+ "mochitest5-track-simple",
+ ],
+ [
+ "urlclassifier.features.emailtracking.datacollection.allowlistTables",
+ "",
+ ],
+ [
+ "urlclassifier.features.emailtracking.blocklistTables",
+ "mochitest5-track-simple",
+ ],
+ ["urlclassifier.features.emailtracking.allowlistTables", ""],
+ ["privacy.trackingprotection.enabled", false],
+ ["privacy.trackingprotection.annotate_channels", false],
+ ["privacy.trackingprotection.cryptomining.enabled", false],
+ ["privacy.trackingprotection.emailtracking.enabled", true],
+ [
+ "privacy.trackingprotection.emailtracking.data_collection.enabled",
+ true,
+ ],
+ ["privacy.trackingprotection.fingerprinting.enabled", false],
+ ["privacy.trackingprotection.socialtracking.enabled", false],
+ [
+ "privacy.trackingprotection.emailtracking.webapp.domains",
+ "test1.example.com",
+ ],
+ ],
+ });
+
+ await UrlClassifierTestUtils.addTestTrackers();
+
+ registerCleanupFunction(function () {
+ UrlClassifierTestUtils.cleanupTestTrackers();
+ });
+
+ await clearTelemetry();
+});
+
+add_task(async function test_email_tracking_telemetry() {
+ // Open a non email webapp tab.
+ await BrowserTestUtils.withNewTab(TEST_PAGE, async browser => {
+ // Load a image from the email tracker
+ let res = await loadImage(browser, EMAIL_TRACKER_IMAGE);
+
+ is(res, false, "The image is blocked.");
+
+ // Verify the telemetry of the email tracker count.
+ await checkTelemetryProbe(
+ TELEMETRY_EMAIL_TRACKER_COUNT,
+ LABEL_BASE_NORMAL,
+ 1
+ );
+ await checkTelemetryProbe(
+ TELEMETRY_EMAIL_TRACKER_COUNT,
+ LABEL_CONTENT_NORMAL,
+ 0
+ );
+ await checkTelemetryProbe(
+ TELEMETRY_EMAIL_TRACKER_COUNT,
+ LABEL_BASE_EMAIL_WEBAPP,
+ 0
+ );
+ await checkTelemetryProbe(
+ TELEMETRY_EMAIL_TRACKER_COUNT,
+ LABEL_CONTENT_EMAIL_WEBAPP,
+ 0
+ );
+ });
+
+ // Open an email webapp tab.
+ await BrowserTestUtils.withNewTab(TEST_EMAIL_WEBAPP_PAGE, async browser => {
+ // Load a image from the email tracker
+ let res = await loadImage(browser, EMAIL_TRACKER_IMAGE);
+
+ is(res, false, "The image is blocked.");
+
+ // Verify the telemetry of the email tracker count.
+ await checkTelemetryProbe(
+ TELEMETRY_EMAIL_TRACKER_COUNT,
+ LABEL_BASE_NORMAL,
+ 1
+ );
+ await checkTelemetryProbe(
+ TELEMETRY_EMAIL_TRACKER_COUNT,
+ LABEL_CONTENT_NORMAL,
+ 0
+ );
+ await checkTelemetryProbe(
+ TELEMETRY_EMAIL_TRACKER_COUNT,
+ LABEL_BASE_EMAIL_WEBAPP,
+ 1
+ );
+ await checkTelemetryProbe(
+ TELEMETRY_EMAIL_TRACKER_COUNT,
+ LABEL_CONTENT_EMAIL_WEBAPP,
+ 0
+ );
+ });
+ // Make sure the tab was closed properly before clearing Telemetry.
+ await BrowserUtils.promiseObserved("window-global-destroyed");
+
+ await clearTelemetry();
+});
+
+add_task(async function test_no_telemetry_for_first_party_email_tracker() {
+ // Open a email tracker tab.
+ await BrowserTestUtils.withNewTab(EMAIL_TRACKER_PAGE, async browser => {
+ // Load a image from the first-party email tracker
+ let res = await loadImage(browser, EMAIL_TRACKER_IMAGE);
+
+ is(res, true, "The image is loaded.");
+
+ // Verify that there was no telemetry recorded.
+ checkNoTelemetryProbe(TELEMETRY_EMAIL_TRACKER_COUNT);
+ });
+ // Make sure the tab was closed properly before clearing Telemetry.
+ await BrowserUtils.promiseObserved("window-global-destroyed");
+
+ await clearTelemetry();
+});
+
+add_task(async function test_disable_email_data_collection() {
+ // Disable Email Tracking Data Collection.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [
+ "privacy.trackingprotection.emailtracking.data_collection.enabled",
+ false,
+ ],
+ ],
+ });
+
+ // Open an email webapp tab.
+ await BrowserTestUtils.withNewTab(TEST_EMAIL_WEBAPP_PAGE, async browser => {
+ // Load a image from the email tracker
+ let res = await loadImage(browser, EMAIL_TRACKER_IMAGE);
+
+ is(res, false, "The image is blocked.");
+
+ // Verify that there was no telemetry recorded.
+ checkNoTelemetryProbe(TELEMETRY_EMAIL_TRACKER_COUNT);
+ });
+ // Make sure the tab was closed properly before clearing Telemetry.
+ await BrowserUtils.promiseObserved("window-global-destroyed");
+
+ await SpecialPowers.popPrefEnv();
+ await clearTelemetry();
+});
+
+add_task(async function test_email_tracker_embedded_telemetry() {
+ // First, we open a page without loading any email trackers.
+ await BrowserTestUtils.withNewTab(TEST_PAGE, async _ => {});
+ // Make sure the tab was closed properly before checking Telemetry.
+ await BrowserUtils.promiseObserved("window-global-destroyed");
+
+ // Check that the telemetry has been record properly for normal page. The
+ // telemetry should show there was no email tracker loaded.
+ await checkKeyedHistogram(
+ TELEMETRY_EMAIL_TRACKER_EMBEDDED_PER_TAB,
+ KEY_BASE_NORMAL,
+ 0,
+ 1
+ );
+ await checkKeyedHistogram(
+ TELEMETRY_EMAIL_TRACKER_EMBEDDED_PER_TAB,
+ KEY_CONTENT_NORMAL,
+ 0,
+ 1
+ );
+ await checkKeyedHistogram(
+ TELEMETRY_EMAIL_TRACKER_EMBEDDED_PER_TAB,
+ KEY_ALL_NORMAL,
+ 0,
+ 1
+ );
+
+ // Second, Open a email webapp tab that doesn't a load email tracker.
+ await BrowserTestUtils.withNewTab(TEST_EMAIL_WEBAPP_PAGE, async _ => {});
+ // Make sure the tab was closed properly before checking Telemetry.
+ await BrowserUtils.promiseObserved("window-global-destroyed");
+
+ // Check that the telemetry has been record properly for the email webapp. The
+ // telemetry should show there was no email tracker loaded.
+ await checkKeyedHistogram(
+ TELEMETRY_EMAIL_TRACKER_EMBEDDED_PER_TAB,
+ KEY_BASE_EMAILAPP,
+ 0,
+ 1
+ );
+ await checkKeyedHistogram(
+ TELEMETRY_EMAIL_TRACKER_EMBEDDED_PER_TAB,
+ KEY_CONTENT_EMAILAPP,
+ 0,
+ 1
+ );
+ await checkKeyedHistogram(
+ TELEMETRY_EMAIL_TRACKER_EMBEDDED_PER_TAB,
+ KEY_ALL_EMAILAPP,
+ 0,
+ 1
+ );
+
+ // Third, open a page with one email tracker loaded.
+ await BrowserTestUtils.withNewTab(TEST_PAGE, async browser => {
+ // Load a image from the email tracker
+ let res = await loadImage(browser, EMAIL_TRACKER_IMAGE);
+
+ is(res, false, "The image is blocked.");
+ });
+ // Make sure the tab was closed properly before checking Telemetry.
+ await BrowserUtils.promiseObserved("window-global-destroyed");
+
+ // Verify that the telemetry has been record properly, The telemetry should
+ // show there was one base email tracker loaded.
+ await checkKeyedHistogram(
+ TELEMETRY_EMAIL_TRACKER_EMBEDDED_PER_TAB,
+ KEY_BASE_NORMAL,
+ 1,
+ 1
+ );
+ await checkKeyedHistogram(
+ TELEMETRY_EMAIL_TRACKER_EMBEDDED_PER_TAB,
+ KEY_CONTENT_NORMAL,
+ 0,
+ 2
+ );
+ await checkKeyedHistogram(
+ TELEMETRY_EMAIL_TRACKER_EMBEDDED_PER_TAB,
+ KEY_ALL_NORMAL,
+ 0,
+ 1
+ );
+ await checkKeyedHistogram(
+ TELEMETRY_EMAIL_TRACKER_EMBEDDED_PER_TAB,
+ KEY_ALL_NORMAL,
+ 1,
+ 1
+ );
+
+ // Open a page and load the same email tracker multiple times. There
+ // should be only one count for the same tracker.
+ await BrowserTestUtils.withNewTab(TEST_PAGE, async browser => {
+ // Load a image from the email tracker two times.
+ await loadImage(browser, EMAIL_TRACKER_IMAGE);
+ await loadImage(browser, EMAIL_TRACKER_IMAGE);
+ });
+ // Make sure the tab was closed properly before checking Telemetry.
+ await BrowserUtils.promiseObserved("window-global-destroyed");
+
+ // Verify that there is still only one count when loading the same tracker
+ // multiple times.
+ await checkKeyedHistogram(
+ TELEMETRY_EMAIL_TRACKER_EMBEDDED_PER_TAB,
+ KEY_BASE_NORMAL,
+ 1,
+ 2
+ );
+ await checkKeyedHistogram(
+ TELEMETRY_EMAIL_TRACKER_EMBEDDED_PER_TAB,
+ KEY_CONTENT_NORMAL,
+ 0,
+ 3
+ );
+ await checkKeyedHistogram(
+ TELEMETRY_EMAIL_TRACKER_EMBEDDED_PER_TAB,
+ KEY_ALL_NORMAL,
+ 0,
+ 1
+ );
+ await checkKeyedHistogram(
+ TELEMETRY_EMAIL_TRACKER_EMBEDDED_PER_TAB,
+ KEY_ALL_NORMAL,
+ 1,
+ 2
+ );
+
+ await clearTelemetry();
+});
diff --git a/toolkit/components/url-classifier/tests/browser/page.html b/toolkit/components/url-classifier/tests/browser/page.html
new file mode 100644
index 0000000000..a99e8be179
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/browser/page.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+ <title>Just a top-level page</title>
+</head>
+<body>
+ <h1>This is the top-level page</h1>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/browser/raptor.jpg b/toolkit/components/url-classifier/tests/browser/raptor.jpg
new file mode 100644
index 0000000000..243ba9e2d4
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/browser/raptor.jpg
Binary files differ
diff --git a/toolkit/components/url-classifier/tests/gtest/Common.cpp b/toolkit/components/url-classifier/tests/gtest/Common.cpp
new file mode 100644
index 0000000000..172ded051f
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/Common.cpp
@@ -0,0 +1,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;
+}
diff --git a/toolkit/components/url-classifier/tests/gtest/Common.h b/toolkit/components/url-classifier/tests/gtest/Common.h
new file mode 100644
index 0000000000..9c196abafa
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/Common.h
@@ -0,0 +1,147 @@
+/* -*- 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/. */
+
+#ifndef nsUrlClassifierGTestCommon_h__
+#define nsUrlClassifierGTestCommon_h__
+
+#include "Entries.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsIFile.h"
+#include "nsTArray.h"
+#include "nsIThread.h"
+#include "nsThreadUtils.h"
+#include "HashStore.h"
+
+#include "gtest/gtest.h"
+#include "mozilla/gtest/MozAssertions.h"
+#include "LookupCacheV4.h"
+
+using namespace mozilla::safebrowsing;
+
+namespace mozilla {
+namespace safebrowsing {
+class Classifier;
+class LookupCacheV4;
+class TableUpdate;
+} // namespace safebrowsing
+} // namespace mozilla
+
+#define GTEST_SAFEBROWSING_DIR "safebrowsing"_ns
+#define GTEST_TABLE_V4 "gtest-malware-proto"_ns
+#define GTEST_TABLE_V2 "gtest-malware-simple"_ns
+
+template <typename Function>
+void RunTestInNewThread(Function&& aFunction) {
+ nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+ "RunTestInNewThread", std::forward<Function>(aFunction));
+ nsCOMPtr<nsIThread> testingThread;
+ nsresult rv =
+ NS_NewNamedThread("Testing Thread", getter_AddRefs(testingThread), r);
+ ASSERT_EQ(rv, NS_OK);
+ testingThread->Shutdown();
+}
+
+// Synchronously apply updates by calling Classifier::AsyncApplyUpdates.
+nsresult SyncApplyUpdates(Classifier* aClassifier,
+ nsTArray<TableUpdate*>* aUpdates);
+nsresult SyncApplyUpdates(TableUpdateArray& aUpdates);
+
+// Return nsIFile with root directory - NS_APP_USER_PROFILE_50_DIR
+// Sub-directories are passed in path argument.
+already_AddRefed<nsIFile> GetFile(const nsTArray<nsString>& aPath);
+
+// ApplyUpdate will call |ApplyUpdates| of Classifier within a new thread
+void ApplyUpdate(nsTArray<TableUpdate*>& aUpdates);
+
+void ApplyUpdate(TableUpdate* aUpdate);
+
+/**
+ * Prefix processing utility functions
+ */
+
+typedef nsCString _Prefix;
+typedef nsTArray<nsCString> _PrefixArray;
+
+// This function converts a lexigraphic-sorted prefixes array
+// to a hash table keyed by prefix size(PrefixStringMap is defined in Entries.h)
+nsresult PrefixArrayToPrefixStringMap(const _PrefixArray& aPrefixArray,
+ PrefixStringMap& aOut);
+
+// This function converts a lexigraphic-sorted prefixes array
+// to an array containing AddPrefix(AddPrefix is defined in Entries.h)
+nsresult PrefixArrayToAddPrefixArray(const _PrefixArray& aPrefixArray,
+ AddPrefixArray& aOut);
+
+_Prefix CreatePrefixFromURL(const char* aURL, uint8_t aPrefixSize);
+
+_Prefix CreatePrefixFromURL(const nsCString& aURL, uint8_t aPrefixSize);
+
+// To test if the content is equal
+void CheckContent(LookupCacheV4* cache, const _PrefixArray& aPrefixArray);
+
+/**
+ * Utility function to generate safebrowsing internal structure
+ */
+
+static inline nsresult BuildCache(LookupCacheV2* cache,
+ const _PrefixArray& aPrefixArray) {
+ AddPrefixArray prefixes;
+ AddCompleteArray completions;
+ nsresult rv = PrefixArrayToAddPrefixArray(aPrefixArray, prefixes);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return cache->Build(prefixes, completions);
+}
+
+static inline nsresult BuildCache(LookupCacheV4* cache,
+ const _PrefixArray& aPrefixArray) {
+ PrefixStringMap map;
+ PrefixArrayToPrefixStringMap(aPrefixArray, map);
+ return cache->Build(map);
+}
+
+// Create a LookupCacheV4 object with sepecified prefix array.
+template <typename T>
+RefPtr<T> SetupLookupCache(const _PrefixArray& aPrefixArray,
+ nsCOMPtr<nsIFile>& aFile) {
+ RefPtr<T> cache = new T(GTEST_TABLE_V4, ""_ns, aFile);
+
+ nsresult rv = cache->Init();
+ EXPECT_EQ(rv, NS_OK);
+
+ rv = BuildCache(cache, aPrefixArray);
+ EXPECT_EQ(rv, NS_OK);
+
+ return cache;
+}
+
+template <typename T>
+RefPtr<T> SetupLookupCache(const _PrefixArray& aPrefixArray) {
+ nsCOMPtr<nsIFile> file;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
+
+ file->AppendNative(GTEST_SAFEBROWSING_DIR);
+
+ RefPtr<T> cache = new T(GTEST_TABLE_V4, ""_ns, file);
+ nsresult rv = cache->Init();
+ EXPECT_EQ(rv, NS_OK);
+
+ rv = BuildCache(cache, aPrefixArray);
+ EXPECT_EQ(rv, NS_OK);
+
+ return cache;
+}
+
+/**
+ * Retrieve Classifer class
+ */
+RefPtr<Classifier> GetClassifier();
+
+nsresult BuildLookupCache(const RefPtr<Classifier>& aClassifier,
+ const nsACString& aTable, _PrefixArray& aPrefixArray);
+
+#endif // nsUrlClassifierGTestCommon_h__
diff --git a/toolkit/components/url-classifier/tests/gtest/TestCaching.cpp b/toolkit/components/url-classifier/tests/gtest/TestCaching.cpp
new file mode 100644
index 0000000000..520eb35dcb
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/TestCaching.cpp
@@ -0,0 +1,271 @@
+/* -*- 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 "LookupCacheV4.h"
+
+#define EXPIRED_TIME_SEC (PR_Now() / PR_USEC_PER_SEC - 3600)
+#define NOTEXPIRED_TIME_SEC (PR_Now() / PR_USEC_PER_SEC + 3600)
+
+#define CACHED_URL "cache.com/"_ns
+#define NEG_CACHE_EXPIRED_URL "cache.negExpired.com/"_ns
+#define POS_CACHE_EXPIRED_URL "cache.posExpired.com/"_ns
+#define BOTH_CACHE_EXPIRED_URL "cache.negAndposExpired.com/"_ns
+
+static void SetupCacheEntry(LookupCacheV2* aLookupCache,
+ const nsCString& aCompletion,
+ bool aNegExpired = false,
+ bool aPosExpired = false) {
+ AddCompleteArray completes;
+ AddCompleteArray emptyCompletes;
+ MissPrefixArray misses;
+ MissPrefixArray emptyMisses;
+
+ AddComplete* add = completes.AppendElement(mozilla::fallible);
+ add->complete.FromPlaintext(aCompletion);
+
+ Prefix* prefix = misses.AppendElement(mozilla::fallible);
+ prefix->FromPlaintext(aCompletion);
+
+ // 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);
+}
+
+static void SetupCacheEntry(LookupCacheV4* aLookupCache,
+ const nsCString& aCompletion,
+ bool aNegExpired = false,
+ bool aPosExpired = false) {
+ FullHashResponseMap map;
+
+ Prefix prefix;
+ prefix.FromPlaintext(aCompletion);
+
+ CachedFullHashResponse* response = map.GetOrInsertNew(prefix.ToUint32());
+
+ response->negativeCacheExpirySec =
+ aNegExpired ? EXPIRED_TIME_SEC : NOTEXPIRED_TIME_SEC;
+ response->fullHashes.InsertOrUpdate(
+ CreatePrefixFromURL(aCompletion, COMPLETE_SIZE),
+ aPosExpired ? EXPIRED_TIME_SEC : NOTEXPIRED_TIME_SEC);
+
+ aLookupCache->AddFullHashResponseToCache(map);
+}
+
+template <typename T>
+static void TestCache(const Completion aCompletion, bool aExpectedHas,
+ bool aExpectedConfirmed, bool aExpectedInCache,
+ T* aCache = nullptr) {
+ bool has, inCache, confirmed;
+ uint32_t matchLength;
+
+ if (aCache) {
+ aCache->Has(aCompletion, &has, &matchLength, &confirmed);
+ inCache = aCache->IsInCache(aCompletion.ToUint32());
+ } else {
+ _PrefixArray array = {CreatePrefixFromURL("cache.notexpired.com/", 10),
+ CreatePrefixFromURL("cache.expired.com/", 8),
+ CreatePrefixFromURL("gound.com/", 5),
+ CreatePrefixFromURL("small.com/", 4)};
+
+ RefPtr<T> cache = SetupLookupCache<T>(array);
+
+ // Create an expired entry and a non-expired entry
+ SetupCacheEntry(cache, "cache.notexpired.com/"_ns);
+ SetupCacheEntry(cache, "cache.expired.com/"_ns, true, true);
+
+ cache->Has(aCompletion, &has, &matchLength, &confirmed);
+ inCache = cache->IsInCache(aCompletion.ToUint32());
+ }
+
+ EXPECT_EQ(has, aExpectedHas);
+ EXPECT_EQ(confirmed, aExpectedConfirmed);
+ EXPECT_EQ(inCache, aExpectedInCache);
+}
+
+template <typename T>
+static void TestCache(const nsCString& aURL, bool aExpectedHas,
+ bool aExpectedConfirmed, bool aExpectedInCache,
+ T* aCache = nullptr) {
+ Completion lookupHash;
+ lookupHash.FromPlaintext(aURL);
+
+ TestCache<T>(lookupHash, aExpectedHas, aExpectedConfirmed, aExpectedInCache,
+ aCache);
+}
+
+// This testcase check the returned result of |Has| API if fullhash cannot match
+// any prefix in the local database.
+TEST(UrlClassifierCaching, NotFound)
+{
+ TestCache<LookupCacheV2>("nomatch.com/"_ns, false, false, false);
+ TestCache<LookupCacheV4>("nomatch.com/"_ns, false, false, false);
+}
+
+// This testcase check the returned result of |Has| API if fullhash find a match
+// in the local database but not in the cache.
+TEST(UrlClassifierCaching, NotInCache)
+{
+ TestCache<LookupCacheV2>("gound.com/"_ns, true, false, false);
+ TestCache<LookupCacheV4>("gound.com/"_ns, true, false, false);
+}
+
+// This testcase check the returned result of |Has| API if fullhash matches
+// a cache entry in positive cache.
+TEST(UrlClassifierCaching, InPositiveCacheNotExpired)
+{
+ TestCache<LookupCacheV2>("cache.notexpired.com/"_ns, true, true, true);
+ TestCache<LookupCacheV4>("cache.notexpired.com/"_ns, true, true, true);
+}
+
+// This testcase check the returned result of |Has| API if fullhash matches
+// a cache entry in positive cache but that it is expired.
+TEST(UrlClassifierCaching, InPositiveCacheExpired)
+{
+ TestCache<LookupCacheV2>("cache.expired.com/"_ns, true, false, true);
+ TestCache<LookupCacheV4>("cache.expired.com/"_ns, true, false, true);
+}
+
+// This testcase check the returned result of |Has| API if fullhash matches
+// a cache entry in negative cache.
+TEST(UrlClassifierCaching, InNegativeCacheNotExpired)
+{
+ // Create a fullhash whose prefix matches the prefix in negative cache
+ // but completion doesn't match any fullhash in positive cache.
+
+ Completion prefix;
+ prefix.FromPlaintext("cache.notexpired.com/"_ns);
+
+ Completion fullhash;
+ fullhash.FromPlaintext("firefox.com/"_ns);
+
+ // Overwrite the 4-byte prefix of `fullhash` so that it conflicts with
+ // `prefix`. Since "cache.notexpired.com" is added to database in TestCache as
+ // a 10-byte prefix, we should copy more than 10 bytes to fullhash to ensure
+ // it can match the prefix in database.
+ memcpy(fullhash.buf, prefix.buf, 10);
+
+ TestCache<LookupCacheV2>(fullhash, false, false, true);
+ TestCache<LookupCacheV4>(fullhash, false, false, true);
+}
+
+// This testcase check the returned result of |Has| API if fullhash matches
+// a cache entry in negative cache but that entry is expired.
+TEST(UrlClassifierCaching, InNegativeCacheExpired)
+{
+ // Create a fullhash whose prefix is in the cache.
+
+ Completion prefix;
+ prefix.FromPlaintext("cache.expired.com/"_ns);
+
+ Completion fullhash;
+ fullhash.FromPlaintext("firefox.com/"_ns);
+
+ memcpy(fullhash.buf, prefix.buf, 10);
+
+ TestCache<LookupCacheV2>(fullhash, true, false, true);
+ TestCache<LookupCacheV4>(fullhash, true, false, true);
+}
+
+// This testcase create 4 cache entries.
+// 1. unexpired entry.
+// 2. an entry whose negative cache time is expired but whose positive cache
+// is not expired.
+// 3. an entry whose positive cache time is expired
+// 4. an entry whose negative cache time and positive cache time are expired
+// After calling |InvalidateExpiredCacheEntry| API, entries with expired
+// negative time should be removed from cache(2 & 4)
+template <typename T>
+void TestInvalidateExpiredCacheEntry() {
+ _PrefixArray array = {CreatePrefixFromURL(CACHED_URL, 10),
+ CreatePrefixFromURL(NEG_CACHE_EXPIRED_URL, 8),
+ CreatePrefixFromURL(POS_CACHE_EXPIRED_URL, 5),
+ CreatePrefixFromURL(BOTH_CACHE_EXPIRED_URL, 4)};
+ RefPtr<T> cache = SetupLookupCache<T>(array);
+
+ SetupCacheEntry(cache, CACHED_URL, false, false);
+ SetupCacheEntry(cache, NEG_CACHE_EXPIRED_URL, true, false);
+ SetupCacheEntry(cache, POS_CACHE_EXPIRED_URL, false, true);
+ SetupCacheEntry(cache, BOTH_CACHE_EXPIRED_URL, true, true);
+
+ // Before invalidate
+ TestCache<T>(CACHED_URL, true, true, true, cache.get());
+ TestCache<T>(NEG_CACHE_EXPIRED_URL, true, true, true, cache.get());
+ TestCache<T>(POS_CACHE_EXPIRED_URL, true, false, true, cache.get());
+ TestCache<T>(BOTH_CACHE_EXPIRED_URL, true, false, true, cache.get());
+
+ // Call InvalidateExpiredCacheEntry to remove cache entries whose negative
+ // cache time is expired
+ cache->InvalidateExpiredCacheEntries();
+
+ // After invalidate, NEG_CACHE_EXPIRED_URL & BOTH_CACHE_EXPIRED_URL should
+ // not be found in cache.
+ TestCache<T>(NEG_CACHE_EXPIRED_URL, true, false, false, cache.get());
+ TestCache<T>(BOTH_CACHE_EXPIRED_URL, true, false, false, cache.get());
+
+ // Other entries should remain the same result.
+ TestCache<T>(CACHED_URL, true, true, true, cache.get());
+ TestCache<T>(POS_CACHE_EXPIRED_URL, true, false, true, cache.get());
+}
+
+TEST(UrlClassifierCaching, InvalidateExpiredCacheEntryV2)
+{ TestInvalidateExpiredCacheEntry<LookupCacheV2>(); }
+
+TEST(UrlClassifierCaching, InvalidateExpiredCacheEntryV4)
+{ TestInvalidateExpiredCacheEntry<LookupCacheV4>(); }
+
+// This testcase check if an cache entry whose negative cache time is expired
+// and it doesn't have any postive cache entries in it, it should be removed
+// from cache after calling |Has|.
+TEST(UrlClassifierCaching, NegativeCacheExpireV2)
+{
+ _PrefixArray array = {CreatePrefixFromURL(NEG_CACHE_EXPIRED_URL, 8)};
+ RefPtr<LookupCacheV2> cache = SetupLookupCache<LookupCacheV2>(array);
+
+ nsCOMPtr<nsICryptoHash> cryptoHash =
+ do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID);
+
+ MissPrefixArray misses;
+ Prefix* prefix = misses.AppendElement(mozilla::fallible);
+ prefix->FromPlaintext(NEG_CACHE_EXPIRED_URL);
+
+ AddCompleteArray dummy;
+ cache->AddGethashResultToCache(dummy, misses, EXPIRED_TIME_SEC);
+
+ // Ensure it is in cache in the first place.
+ EXPECT_EQ(cache->IsInCache(prefix->ToUint32()), true);
+
+ // It should be removed after calling Has API.
+ TestCache<LookupCacheV2>(NEG_CACHE_EXPIRED_URL, true, false, false,
+ cache.get());
+}
+
+TEST(UrlClassifierCaching, NegativeCacheExpireV4)
+{
+ _PrefixArray array = {CreatePrefixFromURL(NEG_CACHE_EXPIRED_URL, 8)};
+ RefPtr<LookupCacheV4> cache = SetupLookupCache<LookupCacheV4>(array);
+
+ FullHashResponseMap map;
+ Prefix prefix;
+ nsCOMPtr<nsICryptoHash> cryptoHash =
+ do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID);
+ prefix.FromPlaintext(NEG_CACHE_EXPIRED_URL);
+ CachedFullHashResponse* response = map.GetOrInsertNew(prefix.ToUint32());
+
+ response->negativeCacheExpirySec = EXPIRED_TIME_SEC;
+
+ cache->AddFullHashResponseToCache(map);
+
+ // Ensure it is in cache in the first place.
+ EXPECT_EQ(cache->IsInCache(prefix.ToUint32()), true);
+
+ // It should be removed after calling Has API.
+ TestCache<LookupCacheV4>(NEG_CACHE_EXPIRED_URL, true, false, false,
+ cache.get());
+}
diff --git a/toolkit/components/url-classifier/tests/gtest/TestChunkSet.cpp b/toolkit/components/url-classifier/tests/gtest/TestChunkSet.cpp
new file mode 100644
index 0000000000..6835103b30
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/TestChunkSet.cpp
@@ -0,0 +1,281 @@
+/* -*- 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 <stdio.h>
+#include <stdlib.h>
+
+#include <set>
+
+#include "ChunkSet.h"
+#include "mozilla/ArrayUtils.h"
+
+#include "Common.h"
+
+TEST(UrlClassifierChunkSet, Empty)
+{
+ mozilla::safebrowsing::ChunkSet chunkSet;
+ mozilla::safebrowsing::ChunkSet removeSet;
+
+ removeSet.Set(0);
+
+ ASSERT_FALSE(chunkSet.Has(0));
+ ASSERT_FALSE(chunkSet.Has(1));
+ ASSERT_TRUE(chunkSet.Remove(removeSet) == NS_OK);
+ ASSERT_TRUE(chunkSet.Length() == 0);
+
+ chunkSet.Set(0);
+
+ ASSERT_TRUE(chunkSet.Has(0));
+ ASSERT_TRUE(chunkSet.Length() == 1);
+ ASSERT_TRUE(chunkSet.Remove(removeSet) == NS_OK);
+ ASSERT_FALSE(chunkSet.Has(0));
+ ASSERT_TRUE(chunkSet.Length() == 0);
+}
+
+TEST(UrlClassifierChunkSet, Main)
+{
+ static int testVals[] = {2, 1, 5, 6, 8, 7, 14, 10, 12, 13};
+
+ mozilla::safebrowsing::ChunkSet chunkSet;
+
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(testVals); i++) {
+ chunkSet.Set(testVals[i]);
+ }
+
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(testVals); i++) {
+ ASSERT_TRUE(chunkSet.Has(testVals[i]));
+ }
+
+ ASSERT_FALSE(chunkSet.Has(3));
+ ASSERT_FALSE(chunkSet.Has(4));
+ ASSERT_FALSE(chunkSet.Has(9));
+ ASSERT_FALSE(chunkSet.Has(11));
+
+ ASSERT_TRUE(chunkSet.Length() == MOZ_ARRAY_LENGTH(testVals));
+}
+
+TEST(UrlClassifierChunkSet, Merge)
+{
+ static int testVals[] = {2, 1, 5, 6, 8, 7, 14, 10, 12, 13};
+ static int mergeVals[] = {9, 3, 4, 20, 14, 16};
+
+ mozilla::safebrowsing::ChunkSet chunkSet;
+ mozilla::safebrowsing::ChunkSet mergeSet;
+
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(testVals); i++) {
+ chunkSet.Set(testVals[i]);
+ }
+
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(mergeVals); i++) {
+ mergeSet.Set(mergeVals[i]);
+ }
+
+ chunkSet.Merge(mergeSet);
+
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(testVals); i++) {
+ ASSERT_TRUE(chunkSet.Has(testVals[i]));
+ }
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(mergeVals); i++) {
+ ASSERT_TRUE(chunkSet.Has(mergeVals[i]));
+ }
+
+ // -1 because 14 is duplicated in both sets
+ ASSERT_TRUE(chunkSet.Length() ==
+ MOZ_ARRAY_LENGTH(testVals) + MOZ_ARRAY_LENGTH(mergeVals) - 1);
+
+ ASSERT_FALSE(chunkSet.Has(11));
+ ASSERT_FALSE(chunkSet.Has(15));
+ ASSERT_FALSE(chunkSet.Has(17));
+ ASSERT_FALSE(chunkSet.Has(18));
+ ASSERT_FALSE(chunkSet.Has(19));
+}
+
+TEST(UrlClassifierChunkSet, Merge2)
+{
+ static int testVals[] = {2, 1, 5, 6, 8, 7, 14, 10, 12, 13};
+ static int mergeVals[] = {9, 3, 4, 20, 14, 16};
+ static int mergeVals2[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
+
+ mozilla::safebrowsing::ChunkSet chunkSet;
+ mozilla::safebrowsing::ChunkSet mergeSet;
+ mozilla::safebrowsing::ChunkSet mergeSet2;
+
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(testVals); i++) {
+ chunkSet.Set(testVals[i]);
+ }
+
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(mergeVals); i++) {
+ mergeSet.Set(mergeVals[i]);
+ }
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(mergeVals2); i++) {
+ mergeSet2.Set(mergeVals2[i]);
+ }
+
+ chunkSet.Merge(mergeSet);
+ chunkSet.Merge(mergeSet2);
+
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(testVals); i++) {
+ ASSERT_TRUE(chunkSet.Has(testVals[i]));
+ }
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(mergeVals); i++) {
+ ASSERT_TRUE(chunkSet.Has(mergeVals[i]));
+ }
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(mergeVals2); i++) {
+ ASSERT_TRUE(chunkSet.Has(mergeVals2[i]));
+ }
+
+ ASSERT_FALSE(chunkSet.Has(15));
+ ASSERT_FALSE(chunkSet.Has(17));
+ ASSERT_FALSE(chunkSet.Has(18));
+ ASSERT_FALSE(chunkSet.Has(19));
+}
+
+TEST(UrlClassifierChunkSet, Stress)
+{
+ mozilla::safebrowsing::ChunkSet chunkSet;
+ mozilla::safebrowsing::ChunkSet mergeSet;
+ std::set<int> refSet;
+ std::set<int> refMergeSet;
+ static const int TEST_ITERS = 7000;
+ static const int REMOVE_ITERS = 3000;
+ static const int TEST_RANGE = 10000;
+
+ // Construction by Set
+ for (int i = 0; i < TEST_ITERS; i++) {
+ int chunk = rand() % TEST_RANGE;
+ chunkSet.Set(chunk);
+ refSet.insert(chunk);
+ }
+
+ // Same elements as reference set
+ for (auto it = refSet.begin(); it != refSet.end(); ++it) {
+ ASSERT_TRUE(chunkSet.Has(*it));
+ }
+
+ // Hole punching via Remove
+ for (int i = 0; i < REMOVE_ITERS; i++) {
+ int chunk = rand() % TEST_RANGE;
+ mozilla::safebrowsing::ChunkSet helpChunk;
+ helpChunk.Set(chunk);
+
+ chunkSet.Remove(helpChunk);
+ refSet.erase(chunk);
+
+ ASSERT_FALSE(chunkSet.Has(chunk));
+ }
+
+ // Should have chunks present in reference set
+ // Should not have chunks absent in reference set
+ for (int it = 0; it < TEST_RANGE; ++it) {
+ auto found = refSet.find(it);
+ if (chunkSet.Has(it)) {
+ ASSERT_FALSE(found == refSet.end());
+ } else {
+ ASSERT_TRUE(found == refSet.end());
+ }
+ }
+
+ // Construct set to merge with
+ for (int i = 0; i < TEST_ITERS; i++) {
+ int chunk = rand() % TEST_RANGE;
+ mergeSet.Set(chunk);
+ refMergeSet.insert(chunk);
+ }
+
+ // Merge set constructed correctly
+ for (auto it = refMergeSet.begin(); it != refMergeSet.end(); ++it) {
+ ASSERT_TRUE(mergeSet.Has(*it));
+ }
+
+ mozilla::safebrowsing::ChunkSet origSet;
+ origSet = chunkSet.InfallibleClone();
+
+ chunkSet.Merge(mergeSet);
+ refSet.insert(refMergeSet.begin(), refMergeSet.end());
+
+ // Check for presence of elements from both source
+ // Should not have chunks absent in reference set
+ for (int it = 0; it < TEST_RANGE; ++it) {
+ auto found = refSet.find(it);
+ if (chunkSet.Has(it)) {
+ ASSERT_FALSE(found == refSet.end());
+ } else {
+ ASSERT_TRUE(found == refSet.end());
+ }
+ }
+
+ // Unmerge
+ chunkSet.Remove(origSet);
+ for (int it = 0; it < TEST_RANGE; ++it) {
+ if (origSet.Has(it)) {
+ ASSERT_FALSE(chunkSet.Has(it));
+ } else if (mergeSet.Has(it)) {
+ ASSERT_TRUE(chunkSet.Has(it));
+ }
+ }
+}
+
+TEST(UrlClassifierChunkSet, RemoveClear)
+{
+ static int testVals[] = {2, 1, 5, 6, 8, 7, 14, 10, 12, 13};
+ static int mergeVals[] = {3, 4, 9, 16, 20};
+
+ mozilla::safebrowsing::ChunkSet chunkSet;
+ mozilla::safebrowsing::ChunkSet mergeSet;
+ mozilla::safebrowsing::ChunkSet removeSet;
+
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(testVals); i++) {
+ chunkSet.Set(testVals[i]);
+ removeSet.Set(testVals[i]);
+ }
+
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(mergeVals); i++) {
+ mergeSet.Set(mergeVals[i]);
+ }
+
+ ASSERT_TRUE(chunkSet.Merge(mergeSet) == NS_OK);
+ ASSERT_TRUE(chunkSet.Remove(removeSet) == NS_OK);
+
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(mergeVals); i++) {
+ ASSERT_TRUE(chunkSet.Has(mergeVals[i]));
+ }
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(testVals); i++) {
+ ASSERT_FALSE(chunkSet.Has(testVals[i]));
+ }
+
+ chunkSet.Clear();
+
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(mergeVals); i++) {
+ ASSERT_FALSE(chunkSet.Has(mergeVals[i]));
+ }
+}
+
+TEST(UrlClassifierChunkSet, Serialize)
+{
+ static int testVals[] = {2, 1, 5, 6, 8, 7, 14, 10, 12, 13};
+ static int mergeVals[] = {3, 4, 9, 16, 20};
+
+ mozilla::safebrowsing::ChunkSet chunkSet;
+ mozilla::safebrowsing::ChunkSet mergeSet;
+
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(testVals); i++) {
+ chunkSet.Set(testVals[i]);
+ }
+
+ for (size_t i = 0; i < MOZ_ARRAY_LENGTH(mergeVals); i++) {
+ mergeSet.Set(mergeVals[i]);
+ }
+
+ chunkSet.Merge(mergeSet);
+
+ nsAutoCString mergeResult;
+ chunkSet.Serialize(mergeResult);
+
+ printf("mergeResult: %s\n", mergeResult.get());
+
+ nsAutoCString expected("1-10,12-14,16,20"_ns);
+
+ ASSERT_TRUE(mergeResult.Equals(expected));
+}
diff --git a/toolkit/components/url-classifier/tests/gtest/TestClassifier.cpp b/toolkit/components/url-classifier/tests/gtest/TestClassifier.cpp
new file mode 100644
index 0000000000..4000d6b32e
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/TestClassifier.cpp
@@ -0,0 +1,83 @@
+/* -*- 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 "Classifier.h"
+#include "LookupCacheV4.h"
+
+#include "Common.h"
+
+static void TestReadNoiseEntries(RefPtr<Classifier> classifier,
+ const nsCString& aTable, const nsCString& aURL,
+ const _PrefixArray& aPrefixArray) {
+ Completion lookupHash;
+ lookupHash.FromPlaintext(aURL);
+ RefPtr<LookupResult> result = new LookupResult;
+ result->hash.complete = lookupHash;
+
+ PrefixArray noiseEntries;
+ uint32_t noiseCount = 3;
+ nsresult rv;
+ rv = classifier->ReadNoiseEntries(result->hash.fixedLengthPrefix, aTable,
+ noiseCount, noiseEntries);
+ ASSERT_TRUE(rv == NS_OK)
+ << "ReadNoiseEntries returns an error";
+ EXPECT_TRUE(noiseEntries.Length() > 0)
+ << "Number of noise entries is less than 0";
+
+ for (uint32_t i = 0; i < noiseEntries.Length(); i++) {
+ // Test the noise entry should not equal the "real" hash request
+ EXPECT_NE(noiseEntries[i], result->hash.fixedLengthPrefix)
+ << "Noise entry is the same as real hash request";
+ // Test the noise entry should exist in the cached prefix array
+ nsAutoCString partialHash;
+ partialHash.Assign(reinterpret_cast<char*>(&noiseEntries[i]), PREFIX_SIZE);
+ EXPECT_TRUE(aPrefixArray.Contains(partialHash))
+ << "Noise entry is not in the cached prefix array";
+ }
+}
+
+TEST(UrlClassifierClassifier, ReadNoiseEntriesV4)
+{
+ RefPtr<Classifier> classifier = GetClassifier();
+ _PrefixArray array = {
+ CreatePrefixFromURL("bravo.com/", 5),
+ CreatePrefixFromURL("browsing.com/", 9),
+ CreatePrefixFromURL("gound.com/", 4),
+ CreatePrefixFromURL("small.com/", 4),
+ CreatePrefixFromURL("gdfad.com/", 4),
+ CreatePrefixFromURL("afdfound.com/", 4),
+ CreatePrefixFromURL("dffa.com/", 4),
+ };
+
+ nsresult rv;
+ rv = BuildLookupCache(classifier, GTEST_TABLE_V4, array);
+ ASSERT_TRUE(rv == NS_OK)
+ << "Fail to build LookupCache";
+
+ TestReadNoiseEntries(classifier, GTEST_TABLE_V4, "gound.com/"_ns, array);
+}
+
+TEST(UrlClassifierClassifier, ReadNoiseEntriesV2)
+{
+ RefPtr<Classifier> classifier = GetClassifier();
+ _PrefixArray array = {
+ CreatePrefixFromURL("helloworld.com/", 4),
+ CreatePrefixFromURL("firefox.com/", 4),
+ CreatePrefixFromURL("chrome.com/", 4),
+ CreatePrefixFromURL("safebrowsing.com/", 4),
+ CreatePrefixFromURL("opera.com/", 4),
+ CreatePrefixFromURL("torbrowser.com/", 4),
+ CreatePrefixFromURL("gfaads.com/", 4),
+ CreatePrefixFromURL("qggdsas.com/", 4),
+ CreatePrefixFromURL("nqtewq.com/", 4),
+ };
+
+ nsresult rv;
+ rv = BuildLookupCache(classifier, GTEST_TABLE_V2, array);
+ ASSERT_TRUE(rv == NS_OK)
+ << "Fail to build LookupCache";
+
+ TestReadNoiseEntries(classifier, GTEST_TABLE_V2, "helloworld.com/"_ns, array);
+}
diff --git a/toolkit/components/url-classifier/tests/gtest/TestFailUpdate.cpp b/toolkit/components/url-classifier/tests/gtest/TestFailUpdate.cpp
new file mode 100644
index 0000000000..f94852a821
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/TestFailUpdate.cpp
@@ -0,0 +1,109 @@
+/* -*- 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 "HashStore.h"
+#include "mozilla/Unused.h"
+#include "nsPrintfCString.h"
+#include "string.h"
+
+#include "Common.h"
+
+static const char* kFilesInV2[] = {".vlpset", ".sbstore"};
+static const char* kFilesInV4[] = {".vlpset", ".metadata"};
+
+#define GTEST_MALWARE_TABLE_V4 "goog-malware-proto"_ns
+#define GTEST_PHISH_TABLE_V4 "goog-phish-proto"_ns
+
+#define ROOT_DIR u"safebrowsing"_ns
+#define SB_FILE(x, y) NS_ConvertUTF8toUTF16(nsPrintfCString("%s%s", x, y))
+
+template <typename T, size_t N>
+static void CheckFileExist(const nsCString& aTable, const T (&aFiles)[N],
+ bool aExpectExists, const char* aMsg = nullptr) {
+ for (uint32_t i = 0; i < N; i++) {
+ // This is just a quick way to know if this is v4 table
+ NS_ConvertUTF8toUTF16 SUB_DIR(strstr(aTable.get(), "-proto") ? "google4"
+ : "");
+ nsCOMPtr<nsIFile> file = GetFile(nsTArray<nsString>{
+ ROOT_DIR, SUB_DIR, SB_FILE(aTable.get(), aFiles[i])});
+
+ bool exists;
+ file->Exists(&exists);
+
+ if (aMsg) {
+ ASSERT_EQ(aExpectExists, exists)
+ << file->HumanReadablePath().get() << " " << aMsg;
+ } else {
+ ASSERT_EQ(aExpectExists, exists) << file->HumanReadablePath().get();
+ }
+ }
+}
+
+TEST(UrlClassifierFailUpdate, CheckTableReset)
+{
+ const bool FULL_UPDATE = true;
+ const bool PARTIAL_UPDATE = false;
+
+ // Apply V2 update
+ {
+ RefPtr<TableUpdateV2> update = new TableUpdateV2(GTEST_TABLE_V2);
+ mozilla::Unused << update->NewAddChunk(1);
+
+ ApplyUpdate(update);
+
+ // A successful V2 update should create .vlpset & .sbstore files
+ CheckFileExist(GTEST_TABLE_V2, kFilesInV2, true,
+ "V2 update doesn't create vlpset or sbstore");
+ }
+
+ // Helper function to generate table update data
+ auto func = [](RefPtr<TableUpdateV4> update, bool full, const char* str) {
+ update->SetFullUpdate(full);
+ nsCString prefix(str);
+ update->NewPrefixes(prefix.Length(), prefix);
+ };
+
+ // Apply V4 update for table1
+ {
+ RefPtr<TableUpdateV4> update = new TableUpdateV4(GTEST_MALWARE_TABLE_V4);
+ func(update, FULL_UPDATE, "test_prefix");
+
+ ApplyUpdate(update);
+
+ // A successful V4 update should create .vlpset & .metadata files
+ CheckFileExist(GTEST_MALWARE_TABLE_V4, kFilesInV4, true,
+ "v4 update doesn't create vlpset or metadata");
+ }
+
+ // Apply V4 update for table2
+ {
+ RefPtr<TableUpdateV4> update = new TableUpdateV4(GTEST_PHISH_TABLE_V4);
+ func(update, FULL_UPDATE, "test_prefix");
+
+ ApplyUpdate(update);
+
+ CheckFileExist(GTEST_PHISH_TABLE_V4, kFilesInV4, true,
+ "v4 update doesn't create vlpset or metadata");
+ }
+
+ // Apply V4 update with the same prefix in previous full udpate
+ // This should cause an update error.
+ {
+ RefPtr<TableUpdateV4> update = new TableUpdateV4(GTEST_MALWARE_TABLE_V4);
+ func(update, PARTIAL_UPDATE, "test_prefix");
+
+ ApplyUpdate(update);
+
+ // A fail update should remove files for that table
+ CheckFileExist(GTEST_MALWARE_TABLE_V4, kFilesInV4, false,
+ "a fail v4 update doesn't remove the tables");
+
+ // A fail update should NOT remove files for the other tables
+ CheckFileExist(GTEST_TABLE_V2, kFilesInV2, true,
+ "a fail v4 update removes a v2 table");
+ CheckFileExist(GTEST_PHISH_TABLE_V4, kFilesInV4, true,
+ "a fail v4 update removes the other v4 table");
+ }
+}
diff --git a/toolkit/components/url-classifier/tests/gtest/TestFindFullHash.cpp b/toolkit/components/url-classifier/tests/gtest/TestFindFullHash.cpp
new file mode 100644
index 0000000000..f0f5785017
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/TestFindFullHash.cpp
@@ -0,0 +1,216 @@
+/* -*- 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 "mozilla/Base64.h"
+#include "nsUrlClassifierUtils.h"
+#include "safebrowsing.pb.h"
+
+#include "Common.h"
+
+using namespace mozilla;
+
+template <size_t N>
+static void ToBase64EncodedStringArray(nsCString (&aInput)[N],
+ nsTArray<nsCString>& aEncodedArray) {
+ for (size_t i = 0; i < N; i++) {
+ nsCString encoded;
+ nsresult rv = mozilla::Base64Encode(aInput[i], encoded);
+ NS_ENSURE_SUCCESS_VOID(rv);
+ aEncodedArray.AppendElement(std::move(encoded));
+ }
+}
+
+TEST(UrlClassifierFindFullHash, Request)
+{
+ nsUrlClassifierUtils* urlUtil = nsUrlClassifierUtils::GetInstance();
+
+ nsTArray<nsCString> listNames;
+ listNames.AppendElement("moztest-phish-proto");
+ listNames.AppendElement("moztest-unwanted-proto");
+
+ nsCString listStates[] = {nsCString("sta\x00te1", 7),
+ nsCString("sta\x00te2", 7)};
+ nsTArray<nsCString> listStateArray;
+ ToBase64EncodedStringArray(listStates, listStateArray);
+
+ nsCString prefixes[] = {nsCString("\x00\x00\x00\x01", 4),
+ nsCString("\x00\x00\x00\x00\x01", 5),
+ nsCString("\x00\xFF\x00\x01", 4),
+ nsCString("\x00\xFF\x00\x01\x11\x23\xAA\xBC", 8),
+ nsCString("\x00\x00\x00\x01\x00\x01\x98", 7)};
+ nsTArray<nsCString> prefixArray;
+ ToBase64EncodedStringArray(prefixes, prefixArray);
+
+ nsCString requestBase64;
+ nsresult rv;
+ rv = urlUtil->MakeFindFullHashRequestV4(listNames, listStateArray,
+ prefixArray, requestBase64);
+ ASSERT_NS_SUCCEEDED(rv);
+
+ // Base64 URL decode first.
+ FallibleTArray<uint8_t> requestBinary;
+ rv = Base64URLDecode(requestBase64, Base64URLDecodePaddingPolicy::Require,
+ requestBinary);
+ ASSERT_NS_SUCCEEDED(rv);
+
+ // Parse the FindFullHash binary and compare with the expected values.
+ FindFullHashesRequest r;
+ ASSERT_TRUE(r.ParseFromArray(&requestBinary[0], requestBinary.Length()));
+
+ // Compare client states.
+ ASSERT_EQ(r.client_states_size(), (int)ArrayLength(listStates));
+ for (int i = 0; i < r.client_states_size(); i++) {
+ auto s = r.client_states(i);
+ ASSERT_TRUE(listStates[i].Equals(nsCString(s.c_str(), s.size())));
+ }
+
+ auto threatInfo = r.threat_info();
+
+ // Compare threat types.
+ ASSERT_EQ(threatInfo.threat_types_size(), (int)ArrayLength(listStates));
+ for (int i = 0; i < threatInfo.threat_types_size(); i++) {
+ uint32_t expectedThreatType;
+ rv =
+ urlUtil->ConvertListNameToThreatType(listNames[i], &expectedThreatType);
+ ASSERT_NS_SUCCEEDED(rv);
+ ASSERT_EQ(threatInfo.threat_types(i), (int)expectedThreatType);
+ }
+
+ // Compare prefixes.
+ ASSERT_EQ(threatInfo.threat_entries_size(), (int)ArrayLength(prefixes));
+ for (int i = 0; i < threatInfo.threat_entries_size(); i++) {
+ auto p = threatInfo.threat_entries(i).hash();
+ ASSERT_TRUE(prefixes[i].Equals(nsCString(p.c_str(), p.size())));
+ }
+}
+
+/////////////////////////////////////////////////////////////
+// Following is to test parsing the gethash response.
+
+namespace {
+
+// safebrowsing::Duration manipulation.
+struct MyDuration {
+ uint32_t mSecs;
+ uint32_t mNanos;
+};
+void PopulateDuration(Duration& aDest, const MyDuration& aSrc) {
+ aDest.set_seconds(aSrc.mSecs);
+ aDest.set_nanos(aSrc.mNanos);
+}
+
+// The expected match data.
+static MyDuration EXPECTED_MIN_WAIT_DURATION = {12, 10};
+static MyDuration EXPECTED_NEG_CACHE_DURATION = {120, 9};
+static const struct ExpectedMatch {
+ nsCString mCompleteHash;
+ ThreatType mThreatType;
+ MyDuration mPerHashCacheDuration;
+} EXPECTED_MATCH[] = {
+ {nsCString("01234567890123456789012345678901"),
+ SOCIAL_ENGINEERING_PUBLIC,
+ {8, 500}},
+ {nsCString("12345678901234567890123456789012"),
+ SOCIAL_ENGINEERING_PUBLIC,
+ {7, 100}},
+ {nsCString("23456789012345678901234567890123"),
+ SOCIAL_ENGINEERING_PUBLIC,
+ {1, 20}},
+};
+
+class MyParseCallback final : public nsIUrlClassifierParseFindFullHashCallback {
+ public:
+ NS_DECL_ISUPPORTS
+
+ explicit MyParseCallback(uint32_t& aCallbackCount)
+ : mCallbackCount(aCallbackCount) {}
+
+ NS_IMETHOD
+ OnCompleteHashFound(const nsACString& aCompleteHash,
+ const nsACString& aTableNames,
+ uint32_t aPerHashCacheDuration) override {
+ Verify(aCompleteHash, aTableNames, aPerHashCacheDuration);
+
+ return NS_OK;
+ }
+
+ NS_IMETHOD
+ OnResponseParsed(uint32_t aMinWaitDuration,
+ uint32_t aNegCacheDuration) override {
+ VerifyDuration(aMinWaitDuration / 1000, EXPECTED_MIN_WAIT_DURATION);
+ VerifyDuration(aNegCacheDuration, EXPECTED_NEG_CACHE_DURATION);
+
+ return NS_OK;
+ }
+
+ private:
+ void Verify(const nsACString& aCompleteHash, const nsACString& aTableNames,
+ uint32_t aPerHashCacheDuration) {
+ auto expected = EXPECTED_MATCH[mCallbackCount];
+
+ ASSERT_TRUE(aCompleteHash.Equals(expected.mCompleteHash));
+
+ // Verify aTableNames
+ nsUrlClassifierUtils* urlUtil = nsUrlClassifierUtils::GetInstance();
+
+ nsCString tableNames;
+ nsresult rv =
+ urlUtil->ConvertThreatTypeToListNames(expected.mThreatType, tableNames);
+ ASSERT_NS_SUCCEEDED(rv);
+ ASSERT_TRUE(aTableNames.Equals(tableNames));
+
+ VerifyDuration(aPerHashCacheDuration, expected.mPerHashCacheDuration);
+
+ mCallbackCount++;
+ }
+
+ void VerifyDuration(uint32_t aToVerify, const MyDuration& aExpected) {
+ ASSERT_TRUE(aToVerify == aExpected.mSecs);
+ }
+
+ ~MyParseCallback() = default;
+
+ uint32_t& mCallbackCount;
+};
+
+NS_IMPL_ISUPPORTS(MyParseCallback, nsIUrlClassifierParseFindFullHashCallback)
+
+} // end of unnamed namespace.
+
+TEST(UrlClassifierFindFullHash, ParseRequest)
+{
+ // Build response.
+ FindFullHashesResponse r;
+
+ // Init response-wise durations.
+ auto minWaitDuration = r.mutable_minimum_wait_duration();
+ PopulateDuration(*minWaitDuration, EXPECTED_MIN_WAIT_DURATION);
+ auto negCacheDuration = r.mutable_negative_cache_duration();
+ PopulateDuration(*negCacheDuration, EXPECTED_NEG_CACHE_DURATION);
+
+ // Init matches.
+ for (uint32_t i = 0; i < ArrayLength(EXPECTED_MATCH); i++) {
+ auto expected = EXPECTED_MATCH[i];
+ auto match = r.mutable_matches()->Add();
+ match->set_threat_type(expected.mThreatType);
+ match->mutable_threat()->set_hash(expected.mCompleteHash.BeginReading(),
+ expected.mCompleteHash.Length());
+ auto perHashCacheDuration = match->mutable_cache_duration();
+ PopulateDuration(*perHashCacheDuration, expected.mPerHashCacheDuration);
+ }
+ std::string s;
+ r.SerializeToString(&s);
+
+ uint32_t callbackCount = 0;
+ nsCOMPtr<nsIUrlClassifierParseFindFullHashCallback> callback =
+ new MyParseCallback(callbackCount);
+
+ nsUrlClassifierUtils* urlUtil = nsUrlClassifierUtils::GetInstance();
+ nsresult rv = urlUtil->ParseFindFullHashResponseV4(
+ nsCString(s.c_str(), s.size()), callback);
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ ASSERT_EQ(callbackCount, ArrayLength(EXPECTED_MATCH));
+}
diff --git a/toolkit/components/url-classifier/tests/gtest/TestLookupCacheV4.cpp b/toolkit/components/url-classifier/tests/gtest/TestLookupCacheV4.cpp
new file mode 100644
index 0000000000..e1482c1416
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/TestLookupCacheV4.cpp
@@ -0,0 +1,152 @@
+/* -*- 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 "Classifier.h"
+#include "LookupCacheV4.h"
+#include "nsAppDirectoryServiceDefs.h"
+
+#include "Common.h"
+
+#define GTEST_SAFEBROWSING_DIR "safebrowsing"_ns
+
+static void TestHasPrefix(const nsCString& aURL, bool aExpectedHas,
+ bool aExpectedComplete) {
+ _PrefixArray array = {CreatePrefixFromURL("bravo.com/", 32),
+ CreatePrefixFromURL("browsing.com/", 8),
+ CreatePrefixFromURL("gound.com/", 5),
+ CreatePrefixFromURL("small.com/", 4)};
+
+ nsCOMPtr<nsIFile> file;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
+ file->AppendNative(GTEST_SAFEBROWSING_DIR);
+
+ RunTestInNewThread([&]() -> void {
+ RefPtr<LookupCache> cache = SetupLookupCache<LookupCacheV4>(array, file);
+
+ Completion lookupHash;
+ lookupHash.FromPlaintext(aURL);
+
+ bool has, confirmed;
+ uint32_t matchLength;
+ // Freshness is not used in V4 so we just put dummy values here.
+ TableFreshnessMap dummy;
+ nsresult rv = cache->Has(lookupHash, &has, &matchLength, &confirmed);
+
+ EXPECT_EQ(rv, NS_OK);
+ EXPECT_EQ(has, aExpectedHas);
+ EXPECT_EQ(matchLength == COMPLETE_SIZE, aExpectedComplete);
+ EXPECT_EQ(confirmed, false);
+
+ cache->ClearAll();
+ });
+}
+
+TEST(UrlClassifierLookupCacheV4, HasComplete)
+{ TestHasPrefix("bravo.com/"_ns, true, true); }
+
+TEST(UrlClassifierLookupCacheV4, HasPrefix)
+{ TestHasPrefix("browsing.com/"_ns, true, false); }
+
+TEST(UrlClassifierLookupCacheV4, Nomatch)
+{ TestHasPrefix("nomatch.com/"_ns, false, false); }
+
+// Test an existing .pset should be removed after .vlpset is written
+TEST(UrlClassifierLookupCacheV4, RemoveOldPset)
+{
+ nsCOMPtr<nsIFile> oldPsetFile;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+ getter_AddRefs(oldPsetFile));
+ oldPsetFile->AppendNative("safebrowsing"_ns);
+ oldPsetFile->AppendNative(GTEST_TABLE_V4 + ".pset"_ns);
+
+ nsCOMPtr<nsIFile> newPsetFile;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+ getter_AddRefs(newPsetFile));
+ newPsetFile->AppendNative("safebrowsing"_ns);
+ newPsetFile->AppendNative(GTEST_TABLE_V4 + ".vlpset"_ns);
+
+ // Create the legacy .pset file
+ nsresult rv = oldPsetFile->Create(nsIFile::NORMAL_FILE_TYPE, 0666);
+ EXPECT_EQ(rv, NS_OK);
+
+ bool exists;
+ rv = oldPsetFile->Exists(&exists);
+ EXPECT_EQ(rv, NS_OK);
+ EXPECT_EQ(exists, true);
+
+ // Setup the data in lookup cache and write its data to disk
+ RefPtr<Classifier> classifier = GetClassifier();
+ _PrefixArray array = {CreatePrefixFromURL("entry.com/", 4)};
+ rv = BuildLookupCache(classifier, GTEST_TABLE_V4, array);
+ EXPECT_EQ(rv, NS_OK);
+
+ RefPtr<LookupCache> cache = classifier->GetLookupCache(GTEST_TABLE_V4, false);
+ rv = cache->WriteFile();
+ EXPECT_EQ(rv, NS_OK);
+
+ // .vlpset should exist while .pset should be removed
+ rv = newPsetFile->Exists(&exists);
+ EXPECT_EQ(rv, NS_OK);
+ EXPECT_EQ(exists, true);
+
+ rv = oldPsetFile->Exists(&exists);
+ EXPECT_EQ(rv, NS_OK);
+ EXPECT_EQ(exists, false);
+
+ newPsetFile->Remove(false);
+}
+
+// Test the legacy load
+TEST(UrlClassifierLookupCacheV4, LoadOldPset)
+{
+ nsCOMPtr<nsIFile> oldPsetFile;
+
+ _PrefixArray array = {CreatePrefixFromURL("entry.com/", 4)};
+ PrefixStringMap map;
+ PrefixArrayToPrefixStringMap(array, map);
+
+ // Prepare .pset file on disk
+ {
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+ getter_AddRefs(oldPsetFile));
+ oldPsetFile->AppendNative("safebrowsing"_ns);
+ oldPsetFile->AppendNative(GTEST_TABLE_V4 + ".pset"_ns);
+
+ RefPtr<VariableLengthPrefixSet> pset = new VariableLengthPrefixSet;
+ pset->SetPrefixes(map);
+
+ nsCOMPtr<nsIOutputStream> stream;
+ nsresult rv =
+ NS_NewLocalFileOutputStream(getter_AddRefs(stream), oldPsetFile);
+ EXPECT_EQ(rv, NS_OK);
+
+ rv = pset->WritePrefixes(stream);
+ EXPECT_EQ(rv, NS_OK);
+ }
+
+ // Load data from disk
+ RefPtr<Classifier> classifier = GetClassifier();
+ RefPtr<LookupCache> cache = classifier->GetLookupCache(GTEST_TABLE_V4, false);
+
+ RefPtr<LookupCacheV4> cacheV4 = LookupCache::Cast<LookupCacheV4>(cache);
+ CheckContent(cacheV4, array);
+
+ oldPsetFile->Remove(false);
+}
+
+TEST(UrlClassifierLookupCacheV4, BuildAPI)
+{
+ _PrefixArray init = {_Prefix("alph")};
+ RefPtr<LookupCacheV4> cache = SetupLookupCache<LookupCacheV4>(init);
+
+ _PrefixArray update = {_Prefix("beta")};
+ PrefixStringMap map;
+ PrefixArrayToPrefixStringMap(update, map);
+
+ cache->Build(map);
+ EXPECT_TRUE(map.IsEmpty());
+
+ CheckContent(cache, update);
+}
diff --git a/toolkit/components/url-classifier/tests/gtest/TestPerProviderDirectory.cpp b/toolkit/components/url-classifier/tests/gtest/TestPerProviderDirectory.cpp
new file mode 100644
index 0000000000..a79ab643f4
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/TestPerProviderDirectory.cpp
@@ -0,0 +1,127 @@
+/* -*- 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 "HashStore.h"
+#include "LookupCache.h"
+#include "LookupCacheV4.h"
+#include "nsAppDirectoryServiceDefs.h"
+
+#include "Common.h"
+
+namespace mozilla {
+namespace safebrowsing {
+
+class PerProviderDirectoryTestUtils {
+ public:
+ template <typename T>
+ static nsIFile* InspectStoreDirectory(const T& aT) {
+ return aT.mStoreDirectory;
+ }
+};
+
+} // end of namespace safebrowsing
+} // end of namespace mozilla
+
+template <typename T>
+static void VerifyPrivateStorePath(T* target, const nsCString& aTableName,
+ const nsCString& aProvider,
+ const nsCOMPtr<nsIFile>& aRootDir,
+ bool aUsePerProviderStore) {
+ nsString rootStorePath;
+ nsresult rv = aRootDir->GetPath(rootStorePath);
+ EXPECT_EQ(rv, NS_OK);
+
+ nsIFile* privateStoreDirectory =
+ PerProviderDirectoryTestUtils::InspectStoreDirectory(*target);
+
+ nsString privateStorePath;
+ rv = privateStoreDirectory->GetPath(privateStorePath);
+ ASSERT_EQ(rv, NS_OK);
+
+ nsString expectedPrivateStorePath = rootStorePath;
+
+ if (aUsePerProviderStore) {
+ // Use API to append "provider" to the root directoy path
+ nsCOMPtr<nsIFile> expectedPrivateStoreDir;
+ rv = aRootDir->Clone(getter_AddRefs(expectedPrivateStoreDir));
+ ASSERT_EQ(rv, NS_OK);
+
+ expectedPrivateStoreDir->AppendNative(aProvider);
+ rv = expectedPrivateStoreDir->GetPath(expectedPrivateStorePath);
+ ASSERT_EQ(rv, NS_OK);
+ }
+
+ printf("table: %s\nprovider: %s\nroot path: %s\nprivate path: %s\n\n",
+ aTableName.get(), aProvider.get(),
+ NS_ConvertUTF16toUTF8(rootStorePath).get(),
+ NS_ConvertUTF16toUTF8(privateStorePath).get());
+
+ ASSERT_TRUE(privateStorePath == expectedPrivateStorePath);
+}
+
+TEST(UrlClassifierPerProviderDirectory, LookupCache)
+{
+ nsCOMPtr<nsIFile> rootDir;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(rootDir));
+
+ RunTestInNewThread([&]() -> void {
+ // For V2 tables (NOT ending with '-proto'), root directory should be
+ // used as the private store.
+ {
+ nsAutoCString table("goog-phish-shavar");
+ nsAutoCString provider("google");
+ RefPtr<LookupCacheV2> lc = new LookupCacheV2(table, provider, rootDir);
+ VerifyPrivateStorePath<LookupCacheV2>(lc, table, provider, rootDir,
+ false);
+ }
+
+ // For V4 tables, if provider is found, use per-provider subdirectory;
+ // If not found, use root directory.
+ {
+ nsAutoCString table("goog-noprovider-proto");
+ nsAutoCString provider("");
+ RefPtr<LookupCacheV4> lc = new LookupCacheV4(table, provider, rootDir);
+ VerifyPrivateStorePath<LookupCacheV4>(lc, table, provider, rootDir,
+ false);
+ }
+ {
+ nsAutoCString table("goog-phish-proto");
+ nsAutoCString provider("google4");
+ RefPtr<LookupCacheV4> lc = new LookupCacheV4(table, provider, rootDir);
+ VerifyPrivateStorePath<LookupCacheV4>(lc, table, provider, rootDir, true);
+ }
+ });
+}
+
+TEST(UrlClassifierPerProviderDirectory, HashStore)
+{
+ nsCOMPtr<nsIFile> rootDir;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(rootDir));
+
+ RunTestInNewThread([&]() -> void {
+ // For V2 tables (NOT ending with '-proto'), root directory should be
+ // used as the private store.
+ {
+ nsAutoCString table("goog-phish-shavar");
+ nsAutoCString provider("google");
+ HashStore hs(table, provider, rootDir);
+ VerifyPrivateStorePath(&hs, table, provider, rootDir, false);
+ }
+ // For V4 tables, if provider is found, use per-provider subdirectory;
+ // If not found, use root directory.
+ {
+ nsAutoCString table("goog-noprovider-proto");
+ nsAutoCString provider("");
+ HashStore hs(table, provider, rootDir);
+ VerifyPrivateStorePath(&hs, table, provider, rootDir, false);
+ }
+ {
+ nsAutoCString table("goog-phish-proto");
+ nsAutoCString provider("google4");
+ HashStore hs(table, provider, rootDir);
+ VerifyPrivateStorePath(&hs, table, provider, rootDir, true);
+ }
+ });
+}
diff --git a/toolkit/components/url-classifier/tests/gtest/TestPrefixSet.cpp b/toolkit/components/url-classifier/tests/gtest/TestPrefixSet.cpp
new file mode 100644
index 0000000000..6d83cd02f6
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/TestPrefixSet.cpp
@@ -0,0 +1,87 @@
+/* -*- 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 "mozilla/Preferences.h"
+#include "nsString.h"
+#include "nsUrlClassifierPrefixSet.h"
+
+#include "Common.h"
+
+// This function generate N 4 byte prefixes.
+static void RandomPrefixes(uint32_t N, nsTArray<uint32_t>& array) {
+ array.Clear();
+ array.SetCapacity(N);
+
+ for (uint32_t i = 0; i < N; i++) {
+ bool added = false;
+
+ while (!added) {
+ nsAutoCString prefix;
+ char* dst = prefix.BeginWriting();
+ for (uint32_t j = 0; j < 4; j++) {
+ dst[j] = static_cast<char>(rand() % 256);
+ }
+
+ const char* src = prefix.BeginReading();
+ uint32_t data = 0;
+ memcpy(&data, src, sizeof(data));
+
+ if (!array.Contains(data)) {
+ array.AppendElement(data);
+ added = true;
+ }
+ }
+ }
+
+ struct Comparator {
+ bool LessThan(const uint32_t& aA, const uint32_t& aB) const {
+ return aA < aB;
+ }
+
+ bool Equals(const uint32_t& aA, const uint32_t& aB) const {
+ return aA == aB;
+ }
+ };
+
+ array.Sort(Comparator());
+}
+
+void RunTest(uint32_t aTestSize) {
+ RefPtr<nsUrlClassifierPrefixSet> prefixSet = new nsUrlClassifierPrefixSet();
+ nsTArray<uint32_t> array;
+
+ RandomPrefixes(aTestSize, array);
+
+ nsresult rv = prefixSet->SetPrefixes(array.Elements(), array.Length());
+ ASSERT_NS_SUCCEEDED(rv);
+
+ for (uint32_t i = 0; i < array.Length(); i++) {
+ uint32_t value = 0;
+ rv = prefixSet->GetPrefixByIndex(i, &value);
+ ASSERT_NS_SUCCEEDED(rv);
+
+ ASSERT_TRUE(value == array[i]);
+ }
+}
+
+TEST(URLClassifierPrefixSet, GetTargetPrefixWithLargeSet)
+{
+ // Make sure the delta algorithm will be used.
+ static const char prefKey[] = "browser.safebrowsing.prefixset_max_array_size";
+ mozilla::Preferences::SetUint(prefKey, 10000);
+
+ // Ideally, we should test more than 512 * 1024 entries. But, it will make the
+ // test too long. So, we test 100k entries instead.
+ RunTest(100000);
+}
+
+TEST(URLClassifierPrefixSet, GetTargetPrefixWithSmallSet)
+{
+ // Make sure the delta algorithm won't be used.
+ static const char prefKey[] = "browser.safebrowsing.prefixset_max_array_size";
+ mozilla::Preferences::SetUint(prefKey, 10000);
+
+ RunTest(1000);
+}
diff --git a/toolkit/components/url-classifier/tests/gtest/TestProtocolParser.cpp b/toolkit/components/url-classifier/tests/gtest/TestProtocolParser.cpp
new file mode 100644
index 0000000000..2155140b4d
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/TestProtocolParser.cpp
@@ -0,0 +1,140 @@
+/* -*- 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 "mozilla/EndianUtils.h"
+#include "ProtocolParser.h"
+
+#include "Common.h"
+
+typedef FetchThreatListUpdatesResponse_ListUpdateResponse ListUpdateResponse;
+
+static bool InitUpdateResponse(ListUpdateResponse* aUpdateResponse,
+ ThreatType aThreatType, const nsACString& aState,
+ const nsACString& aChecksum, bool isFullUpdate,
+ const nsTArray<uint32_t>& aFixedLengthPrefixes,
+ bool aDoPrefixEncoding) {
+ aUpdateResponse->set_threat_type(aThreatType);
+ aUpdateResponse->set_new_client_state(aState.BeginReading(), aState.Length());
+ aUpdateResponse->mutable_checksum()->set_sha256(aChecksum.BeginReading(),
+ aChecksum.Length());
+ aUpdateResponse->set_response_type(isFullUpdate
+ ? ListUpdateResponse::FULL_UPDATE
+ : ListUpdateResponse::PARTIAL_UPDATE);
+
+ auto additions = aUpdateResponse->mutable_additions()->Add();
+
+ if (!aDoPrefixEncoding) {
+ additions->set_compression_type(RAW);
+ auto rawHashes = additions->mutable_raw_hashes();
+ rawHashes->set_prefix_size(4);
+ auto prefixes = rawHashes->mutable_raw_hashes();
+ for (auto p : aFixedLengthPrefixes) {
+ char buffer[4];
+ mozilla::NativeEndian::copyAndSwapToBigEndian(buffer, &p, 1);
+ prefixes->append(buffer, 4);
+ }
+ return true;
+ }
+
+ if (1 != aFixedLengthPrefixes.Length()) {
+ printf("This function only supports single value encoding.\n");
+ return false;
+ }
+
+ uint32_t firstValue = aFixedLengthPrefixes[0];
+ additions->set_compression_type(RICE);
+ auto riceHashes = additions->mutable_rice_hashes();
+ riceHashes->set_first_value(firstValue);
+ riceHashes->set_num_entries(0);
+
+ return true;
+}
+
+static void DumpBinary(const nsACString& aBinary) {
+ nsCString s;
+ for (size_t i = 0; i < aBinary.Length(); i++) {
+ s.AppendPrintf("\\x%.2X", (uint8_t)aBinary[i]);
+ }
+ printf("%s\n", s.get());
+}
+
+TEST(UrlClassifierProtocolParser, UpdateWait)
+{
+ // Top level response which contains a list of update response
+ // for different lists.
+ FetchThreatListUpdatesResponse response;
+
+ auto r = response.mutable_list_update_responses()->Add();
+ InitUpdateResponse(r, SOCIAL_ENGINEERING_PUBLIC, nsCString("sta\x00te", 6),
+ nsCString("check\x0sum", 9), true, {0, 1, 2, 3},
+ false /* aDoPrefixEncoding */);
+
+ // Set min wait duration.
+ auto minWaitDuration = response.mutable_minimum_wait_duration();
+ minWaitDuration->set_seconds(8);
+ minWaitDuration->set_nanos(1 * 1000000000);
+
+ std::string s;
+ response.SerializeToString(&s);
+
+ DumpBinary(nsCString(s.c_str(), s.length()));
+
+ ProtocolParser* p = new ProtocolParserProtobuf();
+ p->AppendStream(nsCString(s.c_str(), s.length()));
+ p->End();
+ ASSERT_EQ(p->UpdateWaitSec(), 9u);
+ delete p;
+}
+
+TEST(UrlClassifierProtocolParser, SingleValueEncoding)
+{
+ // Top level response which contains a list of update response
+ // for different lists.
+ FetchThreatListUpdatesResponse response;
+
+ auto r = response.mutable_list_update_responses()->Add();
+
+ const char* expectedPrefix = "\x00\x01\x02\x00";
+ if (!InitUpdateResponse(r, SOCIAL_ENGINEERING_PUBLIC,
+ nsCString("sta\x00te", 6),
+ nsCString("check\x0sum", 9), true,
+ // As per spec, we should interpret the prefix as
+ // uint32 in little endian before encoding.
+ {mozilla::LittleEndian::readUint32(expectedPrefix)},
+ true /* aDoPrefixEncoding */)) {
+ printf("Failed to initialize update response.");
+ ASSERT_TRUE(false);
+ return;
+ }
+
+ // Set min wait duration.
+ auto minWaitDuration = response.mutable_minimum_wait_duration();
+ minWaitDuration->set_seconds(8);
+ minWaitDuration->set_nanos(1 * 1000000000);
+
+ std::string s;
+ response.SerializeToString(&s);
+
+ // Feed data to the protocol parser.
+ ProtocolParser* p = new ProtocolParserProtobuf();
+ p->SetRequestedTables({nsCString("googpub-phish-proto")});
+ p->AppendStream(nsCString(s.c_str(), s.length()));
+ p->End();
+
+ const TableUpdateArray& tus = p->GetTableUpdates();
+ RefPtr<const TableUpdateV4> tuv4 = TableUpdate::Cast<TableUpdateV4>(tus[0]);
+ auto& prefixMap = tuv4->Prefixes();
+ for (const auto& entry : prefixMap) {
+ // This prefix map should contain only a single 4-byte prefixe.
+ ASSERT_EQ(entry.GetKey(), 4u);
+
+ // The fixed-length prefix string from ProtocolParser should
+ // exactly match the expected prefix string.
+ nsCString* prefix = entry.GetWeak();
+ ASSERT_TRUE(prefix->Equals(nsCString(expectedPrefix, 4)));
+ }
+
+ delete p;
+}
diff --git a/toolkit/components/url-classifier/tests/gtest/TestRiceDeltaDecoder.cpp b/toolkit/components/url-classifier/tests/gtest/TestRiceDeltaDecoder.cpp
new file mode 100644
index 0000000000..9130cfe385
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/TestRiceDeltaDecoder.cpp
@@ -0,0 +1,173 @@
+/* -*- 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 "mozilla/ArrayUtils.h"
+#include "RiceDeltaDecoder.h"
+
+#include "Common.h"
+
+namespace {
+
+struct TestingData {
+ std::vector<uint32_t> mExpectedDecoded;
+ std::vector<uint8_t> mEncoded;
+ uint32_t mRiceParameter;
+};
+
+} // namespace
+
+static bool runOneTest(TestingData& aData) {
+ RiceDeltaDecoder decoder(&aData.mEncoded[0], aData.mEncoded.size());
+
+ std::vector<uint32_t> decoded(aData.mExpectedDecoded.size());
+
+ uint32_t firstValue = aData.mExpectedDecoded[0];
+ bool rv = decoder.Decode(
+ aData.mRiceParameter, firstValue,
+ decoded.size() - 1, // # of entries (first value not included).
+ &decoded[0]);
+
+ return rv && decoded == aData.mExpectedDecoded;
+}
+
+TEST(UrlClassifierRiceDeltaDecoder, SingleEncodedValue)
+{
+ TestingData td = {{99}, {99}, 0};
+
+ ASSERT_TRUE(runOneTest(td));
+}
+
+// In this batch of tests, the encoded data would be like
+// what we originally receive from the network. See comment
+// in |runOneTest| for more detail.
+TEST(UrlClassifierRiceDeltaDecoder, Empty)
+{
+ // The following structure and testing data is copied from Chromium source
+ // code:
+ //
+ // https://chromium.googlesource.com/chromium/src.git/+/950f9975599768b6a08c7146cb4befa161be87aa/components/safe_browsing_db/v4_rice_unittest.cc#75
+ //
+ // and will be translated to our own testing format.
+
+ struct RiceDecodingTestInfo {
+ uint32_t mRiceParameter;
+ std::vector<uint32_t> mDeltas;
+ std::string mEncoded;
+
+ RiceDecodingTestInfo(uint32_t aRiceParameter,
+ const std::vector<uint32_t>& aDeltas,
+ const std::string& aEncoded)
+ : mRiceParameter(aRiceParameter),
+ mDeltas(aDeltas),
+ mEncoded(aEncoded) {}
+ };
+
+ // Copyright 2016 The Chromium Authors. All rights reserved.
+ // Use of this source code is governed by a BSD-style license that can be
+ // found in the media/webrtc/trunk/webrtc/LICENSE.
+
+ // ----- Start of Chromium test code ----
+ const std::vector<RiceDecodingTestInfo> TESTING_DATA_CHROMIUM = {
+ RiceDecodingTestInfo(2, {15, 9}, "\xf7\x2"),
+ RiceDecodingTestInfo(
+ 28, {1777762129, 2093280223, 924369848},
+ "\xbf\xa8\x3f\xfb\xfc\xfb\x5e\x27\xe6\xc3\x1d\xc6\x38"),
+ RiceDecodingTestInfo(
+ 28, {62763050, 1046523781, 192522171, 1800511020, 4442775, 582142548},
+ "\x54\x60\x7b\xe7\x0a\x5f\xc1\xdc\xee\x69\xde"
+ "\xfe\x58\x3c\xa3\xd6\xa5\xf2\x10\x8c\x4a\x59"
+ "\x56\x00"),
+ RiceDecodingTestInfo(
+ 28,
+ {26067715, 344823336, 8420095, 399843890, 95029378, 731622412,
+ 35811335, 1047558127, 1117722715, 78698892},
+ "\x06\x86\x1b\x23\x14\xcb\x46\xf2\xaf\x07\x08\xc9\x88\x54\x1f\x41\x04"
+ "\xd5\x1a\x03\xeb\xe6\x3a\x80\x13\x91\x7b\xbf\x83\xf3\xb7\x85\xf1\x29"
+ "\x18\xb3\x61\x09"),
+ RiceDecodingTestInfo(
+ 27,
+ {225846818, 328287420, 166748623, 29117720, 552397365, 350353215,
+ 558267528, 4738273, 567093445, 28563065, 55077698, 73091685,
+ 339246010, 98242620, 38060941, 63917830, 206319759, 137700744},
+ "\x89\x98\xd8\x75\xbc\x44\x91\xeb\x39\x0c\x3e\x30\x9a\x78\xf3\x6a\xd4"
+ "\xd9\xb1\x9f\xfb\x70\x3e\x44\x3e\xa3\x08\x67\x42\xc2\x2b\x46\x69\x8e"
+ "\x3c\xeb\xd9\x10\x5a\x43\x9a\x32\xa5\x2d\x4e\x77\x0f\x87\x78\x20\xb6"
+ "\xab\x71\x98\x48\x0c\x9e\x9e\xd7\x23\x0c\x13\x43\x2c\xa9\x01"),
+ RiceDecodingTestInfo(
+ 28,
+ {339784008, 263128563, 63871877, 69723256, 826001074, 797300228,
+ 671166008, 207712688},
+ std::string("\x21\xc5\x02\x91\xf9\x82\xd7\x57\xb8\xe9\x3c\xf0\xc8\x4f"
+ "\xe8\x64\x8d\x77\x62\x04\xd6\x85\x3f\x1c\x97\x00\x04\x1b"
+ "\x17\xc6",
+ 30)),
+ RiceDecodingTestInfo(
+ 28,
+ {471820069, 196333855, 855579133, 122737976, 203433838, 85354544,
+ 1307949392, 165938578, 195134475, 553930435, 49231136},
+ "\x95\x9c\x7d\xb0\x8f\xe8\xd9\xbd\xfe\x8c\x7f\x81\x53\x0d\x75\xdc\x4e"
+ "\x40\x18\x0c\x9a\x45\x3d\xa8\xdc\xfa\x26\x59\x40\x9e\x16\x08\x43\x77"
+ "\xc3\x4e\x04\x01\xa4\xe6\x5d\x00"),
+ RiceDecodingTestInfo(
+ 27,
+ {87336845, 129291033, 30906211, 433549264, 30899891, 53207875,
+ 11959529, 354827862, 82919275, 489637251, 53561020, 336722992,
+ 408117728, 204506246, 188216092, 9047110, 479817359, 230317256},
+ "\x1a\x4f\x69\x2a\x63\x9a\xf6\xc6\x2e\xaf\x73\xd0\x6f\xd7\x31\xeb\x77"
+ "\x1d\x43\xe3\x2b\x93\xce\x67\x8b\x59\xf9\x98\xd4\xda\x4f\x3c\x6f\xb0"
+ "\xe8\xa5\x78\x8d\x62\x36\x18\xfe\x08\x1e\x78\xd8\x14\x32\x24\x84\x61"
+ "\x1c\xf3\x37\x63\xc4\xa0\x88\x7b\x74\xcb\x64\xc8\x5c\xba\x05"),
+ RiceDecodingTestInfo(
+ 28,
+ {297968956, 19709657, 259702329, 76998112, 1023176123, 29296013,
+ 1602741145, 393745181, 177326295, 55225536, 75194472},
+ "\xf1\x94\x0a\x87\x6c\x5f\x96\x90\xe3\xab\xf7\xc0\xcb\x2d\xe9\x76\xdb"
+ "\xf8\x59\x63\xc1\x6f\x7c\x99\xe3\x87\x5f\xc7\x04\xde\xb9\x46\x8e\x54"
+ "\xc0\xac\x4a\x03\x0d\x6c\x8f\x00"),
+ RiceDecodingTestInfo(
+ 28,
+ {532220688, 780594691, 436816483, 163436269, 573044456, 1069604,
+ 39629436, 211410997, 227714491, 381562898, 75610008, 196754597,
+ 40310339, 15204118, 99010842},
+ "\x41\x2c\xe4\xfe\x06\xdc\x0d\xbd\x31\xa5\x04\xd5\x6e\xdd\x9b\x43\xb7"
+ "\x3f\x11\x24\x52\x10\x80\x4f\x96\x4b\xd4\x80\x67\xb2\xdd\x52\xc9\x4e"
+ "\x02\xc6\xd7\x60\xde\x06\x92\x52\x1e\xdd\x35\x64\x71\x26\x2c\xfe\xcf"
+ "\x81\x46\xb2\x79\x01"),
+ RiceDecodingTestInfo(
+ 28,
+ {219354713, 389598618, 750263679, 554684211, 87381124, 4523497,
+ 287633354, 801308671, 424169435, 372520475, 277287849},
+ "\xb2\x2c\x26\x3a\xcd\x66\x9c\xdb\x5f\x07\x2e\x6f\xe6\xf9\x21\x10\x52"
+ "\xd5\x94\xf4\x82\x22\x48\xf9\x9d\x24\xf6\xff\x2f\xfc\x6d\x3f\x21\x65"
+ "\x1b\x36\x34\x56\xea\xc4\x21\x00"),
+ };
+
+ // ----- End of Chromium test code ----
+
+ for (auto tdc : TESTING_DATA_CHROMIUM) {
+ // Populate chromium testing data to our native testing data struct.
+ TestingData d;
+
+ d.mRiceParameter = tdc.mRiceParameter; // Populate rice parameter.
+
+ // Populate encoded data from std::string to vector<uint8>.
+ d.mEncoded.resize(tdc.mEncoded.size());
+ memcpy(&d.mEncoded[0], tdc.mEncoded.c_str(), tdc.mEncoded.size());
+
+ // Populate deltas to expected decoded data. The first value would be just
+ // set to an arbitrary value, say 7, to avoid any assumption to the
+ // first value in the implementation.
+ d.mExpectedDecoded.resize(tdc.mDeltas.size() + 1);
+ for (size_t i = 0; i < d.mExpectedDecoded.size(); i++) {
+ if (0 == i) {
+ d.mExpectedDecoded[i] = 7; // "7" is an arbitrary starting value
+ } else {
+ d.mExpectedDecoded[i] = d.mExpectedDecoded[i - 1] + tdc.mDeltas[i - 1];
+ }
+ }
+
+ ASSERT_TRUE(runOneTest(d));
+ }
+}
diff --git a/toolkit/components/url-classifier/tests/gtest/TestSafeBrowsingProtobuf.cpp b/toolkit/components/url-classifier/tests/gtest/TestSafeBrowsingProtobuf.cpp
new file mode 100644
index 0000000000..aa15344324
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/TestSafeBrowsingProtobuf.cpp
@@ -0,0 +1,30 @@
+/* -*- 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 "safebrowsing.pb.h"
+
+#include "Common.h"
+
+TEST(UrlClassifierProtobuf, Empty)
+{
+ using namespace mozilla::safebrowsing;
+
+ const std::string CLIENT_ID = "firefox";
+
+ // Construct a simple update request.
+ FetchThreatListUpdatesRequest r;
+ r.set_allocated_client(new ClientInfo());
+ r.mutable_client()->set_client_id(CLIENT_ID);
+
+ // Then serialize.
+ std::string s;
+ r.SerializeToString(&s);
+
+ // De-serialize.
+ FetchThreatListUpdatesRequest r2;
+ r2.ParseFromString(s);
+
+ ASSERT_EQ(r2.client().client_id(), CLIENT_ID);
+}
diff --git a/toolkit/components/url-classifier/tests/gtest/TestSafebrowsingHash.cpp b/toolkit/components/url-classifier/tests/gtest/TestSafebrowsingHash.cpp
new file mode 100644
index 0000000000..2fc4c793f7
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/TestSafebrowsingHash.cpp
@@ -0,0 +1,58 @@
+/* -*- 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 "mozilla/EndianUtils.h"
+
+#include "Common.h"
+
+TEST(UrlClassifierHash, ToFromUint32)
+{
+ using namespace mozilla::safebrowsing;
+
+ // typedef SafebrowsingHash<PREFIX_SIZE, PrefixComparator> Prefix;
+ // typedef nsTArray<Prefix> PrefixArray;
+
+ const char PREFIX_RAW[4] = {0x1, 0x2, 0x3, 0x4};
+ uint32_t PREFIX_UINT32;
+ memcpy(&PREFIX_UINT32, PREFIX_RAW, 4);
+
+ Prefix p;
+ p.Assign(nsCString(PREFIX_RAW, 4));
+ ASSERT_EQ(p.ToUint32(), PREFIX_UINT32);
+
+ p.FromUint32(PREFIX_UINT32);
+ ASSERT_EQ(memcmp(PREFIX_RAW, p.buf, 4), 0);
+}
+
+TEST(UrlClassifierHash, Compare)
+{
+ using namespace mozilla;
+ using namespace mozilla::safebrowsing;
+
+ Prefix p1, p2, p3;
+
+ // The order of p1,p2,p3 is "p1 == p3 < p2"
+#if MOZ_LITTLE_ENDIAN()
+ p1.Assign(nsCString("\x01\x00\x00\x00", 4));
+ p2.Assign(nsCString("\x00\x00\x00\x01", 4));
+ p3.Assign(nsCString("\x01\x00\x00\x00", 4));
+#else
+ p1.Assign(nsCString("\x00\x00\x00\x01", 4));
+ p2.Assign(nsCString("\x01\x00\x00\x00", 4));
+ p3.Assign(nsCString("\x00\x00\x00\x01", 4));
+#endif
+
+ // Make sure "p1 == p3 < p2" is true
+ // on both little and big endian machine.
+
+ ASSERT_EQ(p1.Compare(p2), -1);
+ ASSERT_EQ(p1.Compare(p1), 0);
+ ASSERT_EQ(p2.Compare(p1), 1);
+ ASSERT_EQ(p1.Compare(p3), 0);
+
+ ASSERT_TRUE(p1 < p2);
+ ASSERT_TRUE(p1 == p1);
+ ASSERT_TRUE(p1 == p3);
+}
diff --git a/toolkit/components/url-classifier/tests/gtest/TestTable.cpp b/toolkit/components/url-classifier/tests/gtest/TestTable.cpp
new file mode 100644
index 0000000000..796f40b265
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/TestTable.cpp
@@ -0,0 +1,59 @@
+/* -*- 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 "nsUrlClassifierDBService.h"
+
+#include "Common.h"
+
+static void TestResponseCode(const char* table, nsresult result) {
+ nsCString tableName(table);
+ ASSERT_EQ(TablesToResponse(tableName), result);
+}
+
+TEST(UrlClassifierTable, ResponseCode)
+{
+ // malware URIs.
+ TestResponseCode("goog-malware-shavar", NS_ERROR_MALWARE_URI);
+ TestResponseCode("test-malware-simple", NS_ERROR_MALWARE_URI);
+ TestResponseCode("goog-phish-shavar,test-malware-simple",
+ NS_ERROR_MALWARE_URI);
+ TestResponseCode(
+ "test-malware-simple,mozstd-track-digest256,mozplugin-block-digest256",
+ NS_ERROR_MALWARE_URI);
+
+ // phish URIs.
+ TestResponseCode("goog-phish-shavar", NS_ERROR_PHISHING_URI);
+ TestResponseCode("test-phish-simple", NS_ERROR_PHISHING_URI);
+ TestResponseCode("test-phish-simple,mozplugin-block-digest256",
+ NS_ERROR_PHISHING_URI);
+ TestResponseCode(
+ "mozstd-track-digest256,test-phish-simple,goog-unwanted-shavar",
+ NS_ERROR_PHISHING_URI);
+
+ // unwanted URIs.
+ TestResponseCode("goog-unwanted-shavar", NS_ERROR_UNWANTED_URI);
+ TestResponseCode("test-unwanted-simple", NS_ERROR_UNWANTED_URI);
+ TestResponseCode("mozplugin-unwanted-digest256,mozfull-track-digest256",
+ NS_ERROR_UNWANTED_URI);
+ TestResponseCode(
+ "test-block-simple,mozfull-track-digest256,test-unwanted-simple",
+ NS_ERROR_UNWANTED_URI);
+
+ // track URIs.
+ TestResponseCode("test-track-simple", NS_ERROR_TRACKING_URI);
+ TestResponseCode("mozstd-track-digest256", NS_ERROR_TRACKING_URI);
+ TestResponseCode("test-block-simple,mozstd-track-digest256",
+ NS_ERROR_TRACKING_URI);
+
+ // block URIs
+ TestResponseCode("test-block-simple", NS_ERROR_BLOCKED_URI);
+ TestResponseCode("mozplugin-block-digest256", NS_ERROR_BLOCKED_URI);
+ TestResponseCode("mozplugin2-block-digest256", NS_ERROR_BLOCKED_URI);
+
+ TestResponseCode("test-trackwhite-simple", NS_OK);
+ TestResponseCode("mozstd-trackwhite-digest256", NS_OK);
+ TestResponseCode("goog-badbinurl-shavar", NS_OK);
+ TestResponseCode("goog-downloadwhite-digest256", NS_OK);
+}
diff --git a/toolkit/components/url-classifier/tests/gtest/TestURLsAndHashing.cpp b/toolkit/components/url-classifier/tests/gtest/TestURLsAndHashing.cpp
new file mode 100644
index 0000000000..0563c6a776
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/TestURLsAndHashing.cpp
@@ -0,0 +1,71 @@
+/* -*- 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 "Common.h"
+
+static void VerifyFragments(const nsACString& aURL,
+ const nsTArray<nsCString>& aExpected) {
+ nsTArray<nsCString> fragments;
+ nsresult rv = LookupCache::GetLookupFragments(aURL, &fragments);
+ ASSERT_EQ(rv, NS_OK) << "GetLookupFragments should not fail";
+
+ ASSERT_EQ(aExpected.Length(), fragments.Length())
+ << "Fragments generated from " << aURL.BeginReading()
+ << " are not the same as expected";
+
+ for (const auto& fragment : fragments) {
+ ASSERT_TRUE(aExpected.Contains(fragment))
+ << "Fragments generated from " << aURL.BeginReading()
+ << " are not the same as expected";
+ }
+}
+
+// This testcase references SafeBrowsing spec:
+// https://developers.google.com/safe-browsing/v4/urls-hashing#suffixprefix-expressions
+TEST(URLsAndHashing, FragmentURLWithQuery)
+{
+ const nsLiteralCString url("a.b.c/1/2.html?param=1");
+ nsTArray<nsCString> expect = {
+ "a.b.c/1/2.html?param=1"_ns,
+ "a.b.c/1/2.html"_ns,
+ "a.b.c/"_ns,
+ "a.b.c/1/"_ns,
+ "b.c/1/2.html?param=1"_ns,
+ "b.c/1/2.html"_ns,
+ "b.c/"_ns,
+ "b.c/1/"_ns,
+ };
+
+ VerifyFragments(url, expect);
+}
+
+// This testcase references SafeBrowsing spec:
+// https://developers.google.com/safe-browsing/v4/urls-hashing#suffixprefix-expressions
+TEST(URLsAndHashing, FragmentURLWithoutQuery)
+{
+ const nsLiteralCString url("a.b.c.d.e.f.g/1.html");
+ nsTArray<nsCString> expect = {
+ "a.b.c.d.e.f.g/1.html"_ns, "a.b.c.d.e.f.g/"_ns,
+ "c.d.e.f.g/1.html"_ns, "c.d.e.f.g/"_ns,
+ "d.e.f.g/1.html"_ns, "d.e.f.g/"_ns,
+ "e.f.g/1.html"_ns, "e.f.g/"_ns,
+ "f.g/1.html"_ns, "f.g/"_ns,
+ };
+
+ VerifyFragments(url, expect);
+}
+
+TEST(URLsAndHashing, FragmentURLEndWithoutPath)
+{
+ const nsLiteralCString url("1.2.3.4/?query=string");
+ nsTArray<nsCString> expect = {
+ "1.2.3.4/?query=string"_ns,
+ "1.2.3.4/"_ns,
+ };
+
+ VerifyFragments(url, expect);
+}
diff --git a/toolkit/components/url-classifier/tests/gtest/TestUrlClassifierTableUpdateV4.cpp b/toolkit/components/url-classifier/tests/gtest/TestUrlClassifierTableUpdateV4.cpp
new file mode 100644
index 0000000000..d4dec867bd
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/TestUrlClassifierTableUpdateV4.cpp
@@ -0,0 +1,785 @@
+/* -*- 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 "Classifier.h"
+#include "HashStore.h"
+#include "mozilla/Components.h"
+#include "mozilla/Unused.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsIFile.h"
+#include "nsThreadUtils.h"
+#include "string.h"
+#include "LookupCacheV4.h"
+#include "nsUrlClassifierUtils.h"
+
+#include "Common.h"
+
+#define GTEST_SAFEBROWSING_DIR "safebrowsing"_ns
+#define GTEST_TABLE "gtest-malware-proto"_ns
+#define GTEST_PREFIXFILE "gtest-malware-proto.vlpset"_ns
+
+// This function removes common elements of inArray and outArray from
+// outArray. This is used by partial update testcase to ensure partial update
+// data won't contain prefixes we already have.
+static void RemoveIntersection(const _PrefixArray& inArray,
+ _PrefixArray& outArray) {
+ for (uint32_t i = 0; i < inArray.Length(); i++) {
+ int32_t idx = outArray.BinaryIndexOf(inArray[i]);
+ if (idx >= 0) {
+ outArray.RemoveElementAt(idx);
+ }
+ }
+}
+
+// This fucntion removes elements from outArray by index specified in
+// removal array.
+static void RemoveElements(const nsTArray<uint32_t>& removal,
+ _PrefixArray& outArray) {
+ for (int32_t i = removal.Length() - 1; i >= 0; i--) {
+ outArray.RemoveElementAt(removal[i]);
+ }
+}
+
+static void MergeAndSortArray(const _PrefixArray& array1,
+ const _PrefixArray& array2,
+ _PrefixArray& output) {
+ output.Clear();
+ output.AppendElements(array1);
+ output.AppendElements(array2);
+ output.Sort();
+}
+
+static void CalculateSHA256(_PrefixArray& prefixArray, nsCString& sha256) {
+ prefixArray.Sort();
+
+ nsresult rv;
+ nsCOMPtr<nsICryptoHash> cryptoHash =
+ do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
+
+ cryptoHash->Init(nsICryptoHash::SHA256);
+ for (uint32_t i = 0; i < prefixArray.Length(); i++) {
+ const _Prefix& prefix = prefixArray[i];
+ cryptoHash->Update(
+ reinterpret_cast<uint8_t*>(const_cast<char*>(prefix.get())),
+ prefix.Length());
+ }
+ cryptoHash->Finish(false, sha256);
+}
+
+// N: Number of prefixes, MIN/MAX: minimum/maximum prefix size
+// This function will append generated prefixes to outArray.
+static void CreateRandomSortedPrefixArray(uint32_t N, uint32_t MIN,
+ uint32_t MAX,
+ _PrefixArray& outArray) {
+ outArray.SetCapacity(outArray.Length() + N);
+
+ const uint32_t range = (MAX - MIN + 1);
+
+ for (uint32_t i = 0; i < N; i++) {
+ uint32_t prefixSize = (rand() % range) + MIN;
+ _Prefix prefix;
+ prefix.SetLength(prefixSize);
+
+ while (true) {
+ char* dst = prefix.BeginWriting();
+ for (uint32_t j = 0; j < prefixSize; j++) {
+ dst[j] = rand() % 256;
+ }
+
+ if (!outArray.Contains(prefix)) {
+ outArray.AppendElement(prefix);
+ break;
+ }
+ }
+ }
+
+ outArray.Sort();
+}
+
+// N: Number of removal indices, MAX: maximum index
+static void CreateRandomRemovalIndices(uint32_t N, uint32_t MAX,
+ nsTArray<uint32_t>& outArray) {
+ for (uint32_t i = 0; i < N; i++) {
+ uint32_t idx = rand() % MAX;
+ if (!outArray.Contains(idx)) {
+ outArray.InsertElementSorted(idx);
+ }
+ }
+}
+
+// Function to generate TableUpdateV4.
+static void GenerateUpdateData(bool fullUpdate, PrefixStringMap& add,
+ nsTArray<uint32_t>* removal, nsCString* sha256,
+ TableUpdateArray& tableUpdates) {
+ RefPtr<TableUpdateV4> tableUpdate = new TableUpdateV4(GTEST_TABLE);
+ tableUpdate->SetFullUpdate(fullUpdate);
+
+ for (const auto& entry : add) {
+ nsCString* pstring = entry.GetWeak();
+ tableUpdate->NewPrefixes(entry.GetKey(), *pstring);
+ }
+
+ if (removal) {
+ tableUpdate->NewRemovalIndices(removal->Elements(), removal->Length());
+ }
+
+ if (sha256) {
+ std::string stdSHA256;
+ stdSHA256.assign(const_cast<char*>(sha256->BeginReading()),
+ sha256->Length());
+
+ tableUpdate->SetSHA256(stdSHA256);
+ }
+
+ tableUpdates.AppendElement(tableUpdate);
+}
+
+static void VerifyPrefixSet(PrefixStringMap& expected) {
+ // Verify the prefix set is written to disk.
+ nsCOMPtr<nsIFile> file;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
+ file->AppendNative(GTEST_SAFEBROWSING_DIR);
+
+ RefPtr<LookupCacheV4> lookup =
+ new LookupCacheV4(GTEST_TABLE, "test"_ns, file);
+ lookup->Init();
+
+ file->AppendNative(GTEST_PREFIXFILE);
+ lookup->LoadFromFile(file);
+
+ PrefixStringMap prefixesInFile;
+ lookup->GetPrefixes(prefixesInFile);
+
+ for (const auto& entry : expected) {
+ nsCString* expectedPrefix = entry.GetWeak();
+ nsCString* resultPrefix = prefixesInFile.Get(entry.GetKey());
+
+ ASSERT_TRUE(*resultPrefix == *expectedPrefix);
+ }
+}
+
+static void Clear() {
+ nsCOMPtr<nsIFile> file;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
+
+ RefPtr<Classifier> classifier = new Classifier();
+ classifier->Open(*file);
+ classifier->Reset();
+}
+
+static void testUpdateFail(TableUpdateArray& tableUpdates) {
+ nsresult rv = SyncApplyUpdates(tableUpdates);
+ ASSERT_NS_FAILED(rv);
+}
+
+static void testUpdate(TableUpdateArray& tableUpdates,
+ PrefixStringMap& expected) {
+ // Force nsUrlClassifierUtils loading on main thread
+ // because nsIUrlClassifierDBService will not run in advance
+ // in gtest.
+ nsUrlClassifierUtils::GetInstance();
+
+ nsresult rv = SyncApplyUpdates(tableUpdates);
+ ASSERT_TRUE(rv == NS_OK);
+ VerifyPrefixSet(expected);
+}
+
+static void testFullUpdate(PrefixStringMap& add, nsCString* sha256) {
+ TableUpdateArray tableUpdates;
+
+ GenerateUpdateData(true, add, nullptr, sha256, tableUpdates);
+
+ testUpdate(tableUpdates, add);
+}
+
+static void testPartialUpdate(PrefixStringMap& add, nsTArray<uint32_t>* removal,
+ nsCString* sha256, PrefixStringMap& expected) {
+ TableUpdateArray tableUpdates;
+ GenerateUpdateData(false, add, removal, sha256, tableUpdates);
+
+ testUpdate(tableUpdates, expected);
+}
+
+static void testOpenLookupCache() {
+ nsCOMPtr<nsIFile> file;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
+ file->AppendNative(GTEST_SAFEBROWSING_DIR);
+
+ RunTestInNewThread([&]() -> void {
+ RefPtr<LookupCacheV4> cache =
+ new LookupCacheV4(nsCString(GTEST_TABLE), ""_ns, file);
+ nsresult rv = cache->Init();
+ ASSERT_EQ(rv, NS_OK);
+
+ rv = cache->Open();
+ ASSERT_EQ(rv, NS_OK);
+ });
+}
+
+// Tests start from here.
+TEST(UrlClassifierTableUpdateV4, FixLengthPSetFullUpdate)
+{
+ srand(time(NULL));
+
+ _PrefixArray array;
+ PrefixStringMap map;
+ nsCString sha256;
+
+ CreateRandomSortedPrefixArray(5000, 4, 4, array);
+ PrefixArrayToPrefixStringMap(array, map);
+ CalculateSHA256(array, sha256);
+
+ testFullUpdate(map, &sha256);
+
+ Clear();
+}
+
+TEST(UrlClassifierTableUpdateV4, VariableLengthPSetFullUpdate)
+{
+ _PrefixArray array;
+ PrefixStringMap map;
+ nsCString sha256;
+
+ CreateRandomSortedPrefixArray(5000, 5, 32, array);
+ PrefixArrayToPrefixStringMap(array, map);
+ CalculateSHA256(array, sha256);
+
+ testFullUpdate(map, &sha256);
+
+ Clear();
+}
+
+// This test contain both variable length prefix set and fixed-length prefix set
+TEST(UrlClassifierTableUpdateV4, MixedPSetFullUpdate)
+{
+ _PrefixArray array;
+ PrefixStringMap map;
+ nsCString sha256;
+
+ CreateRandomSortedPrefixArray(5000, 4, 4, array);
+ CreateRandomSortedPrefixArray(1000, 5, 32, array);
+ PrefixArrayToPrefixStringMap(array, map);
+ CalculateSHA256(array, sha256);
+
+ testFullUpdate(map, &sha256);
+
+ Clear();
+}
+
+TEST(UrlClassifierTableUpdateV4, PartialUpdateWithRemoval)
+{
+ _PrefixArray fArray;
+
+ // Apply a full update first.
+ {
+ PrefixStringMap fMap;
+ nsCString sha256;
+
+ CreateRandomSortedPrefixArray(10000, 4, 4, fArray);
+ CreateRandomSortedPrefixArray(2000, 5, 32, fArray);
+ PrefixArrayToPrefixStringMap(fArray, fMap);
+ CalculateSHA256(fArray, sha256);
+
+ testFullUpdate(fMap, &sha256);
+ }
+
+ // Apply a partial update with removal.
+ {
+ _PrefixArray pArray, mergedArray;
+ PrefixStringMap pMap, mergedMap;
+ nsCString sha256;
+
+ CreateRandomSortedPrefixArray(5000, 4, 4, pArray);
+ CreateRandomSortedPrefixArray(1000, 5, 32, pArray);
+ RemoveIntersection(fArray, pArray);
+ PrefixArrayToPrefixStringMap(pArray, pMap);
+
+ // Remove 1/5 of elements of original prefix set.
+ nsTArray<uint32_t> removal;
+ CreateRandomRemovalIndices(fArray.Length() / 5, fArray.Length(), removal);
+ RemoveElements(removal, fArray);
+
+ // Calculate the expected prefix map.
+ MergeAndSortArray(fArray, pArray, mergedArray);
+ PrefixArrayToPrefixStringMap(mergedArray, mergedMap);
+ CalculateSHA256(mergedArray, sha256);
+
+ testPartialUpdate(pMap, &removal, &sha256, mergedMap);
+ }
+
+ Clear();
+}
+
+TEST(UrlClassifierTableUpdateV4, PartialUpdateWithoutRemoval)
+{
+ _PrefixArray fArray;
+
+ // Apply a full update first.
+ {
+ PrefixStringMap fMap;
+ nsCString sha256;
+
+ CreateRandomSortedPrefixArray(10000, 4, 4, fArray);
+ CreateRandomSortedPrefixArray(2000, 5, 32, fArray);
+ PrefixArrayToPrefixStringMap(fArray, fMap);
+ CalculateSHA256(fArray, sha256);
+
+ testFullUpdate(fMap, &sha256);
+ }
+
+ // Apply a partial update without removal
+ {
+ _PrefixArray pArray, mergedArray;
+ PrefixStringMap pMap, mergedMap;
+ nsCString sha256;
+
+ CreateRandomSortedPrefixArray(5000, 4, 4, pArray);
+ CreateRandomSortedPrefixArray(1000, 5, 32, pArray);
+ RemoveIntersection(fArray, pArray);
+ PrefixArrayToPrefixStringMap(pArray, pMap);
+
+ // Calculate the expected prefix map.
+ MergeAndSortArray(fArray, pArray, mergedArray);
+ PrefixArrayToPrefixStringMap(mergedArray, mergedMap);
+ CalculateSHA256(mergedArray, sha256);
+
+ testPartialUpdate(pMap, nullptr, &sha256, mergedMap);
+ }
+
+ Clear();
+}
+
+// Expect failure because partial update contains prefix already
+// in old prefix set.
+TEST(UrlClassifierTableUpdateV4, PartialUpdatePrefixAlreadyExist)
+{
+ _PrefixArray fArray;
+
+ // Apply a full update fist.
+ {
+ PrefixStringMap fMap;
+ nsCString sha256;
+
+ CreateRandomSortedPrefixArray(1000, 4, 32, fArray);
+ PrefixArrayToPrefixStringMap(fArray, fMap);
+ CalculateSHA256(fArray, sha256);
+
+ testFullUpdate(fMap, &sha256);
+ }
+
+ // Apply a partial update which contains a prefix in previous full update.
+ // This should cause an update error.
+ {
+ _PrefixArray pArray;
+ PrefixStringMap pMap;
+ TableUpdateArray tableUpdates;
+
+ // Pick one prefix from full update prefix and add it to partial update.
+ // This should result a failure when call ApplyUpdates.
+ pArray.AppendElement(fArray[rand() % fArray.Length()]);
+ CreateRandomSortedPrefixArray(200, 4, 32, pArray);
+ PrefixArrayToPrefixStringMap(pArray, pMap);
+
+ GenerateUpdateData(false, pMap, nullptr, nullptr, tableUpdates);
+ testUpdateFail(tableUpdates);
+ }
+
+ Clear();
+}
+
+// Test apply partial update directly without applying an full update first.
+TEST(UrlClassifierTableUpdateV4, OnlyPartialUpdate)
+{
+ _PrefixArray pArray;
+ PrefixStringMap pMap;
+ nsCString sha256;
+
+ CreateRandomSortedPrefixArray(5000, 4, 4, pArray);
+ CreateRandomSortedPrefixArray(1000, 5, 32, pArray);
+ PrefixArrayToPrefixStringMap(pArray, pMap);
+ CalculateSHA256(pArray, sha256);
+
+ testPartialUpdate(pMap, nullptr, &sha256, pMap);
+
+ Clear();
+}
+
+// Test partial update without any ADD prefixes, only removalIndices.
+TEST(UrlClassifierTableUpdateV4, PartialUpdateOnlyRemoval)
+{
+ _PrefixArray fArray;
+
+ // Apply a full update first.
+ {
+ PrefixStringMap fMap;
+ nsCString sha256;
+
+ CreateRandomSortedPrefixArray(5000, 4, 4, fArray);
+ CreateRandomSortedPrefixArray(1000, 5, 32, fArray);
+ PrefixArrayToPrefixStringMap(fArray, fMap);
+ CalculateSHA256(fArray, sha256);
+
+ testFullUpdate(fMap, &sha256);
+ }
+
+ // Apply a partial update without add prefix, only contain removal indices.
+ {
+ _PrefixArray pArray;
+ PrefixStringMap pMap, mergedMap;
+ nsCString sha256;
+
+ // Remove 1/5 of elements of original prefix set.
+ nsTArray<uint32_t> removal;
+ CreateRandomRemovalIndices(fArray.Length() / 5, fArray.Length(), removal);
+ RemoveElements(removal, fArray);
+
+ PrefixArrayToPrefixStringMap(fArray, mergedMap);
+ CalculateSHA256(fArray, sha256);
+
+ testPartialUpdate(pMap, &removal, &sha256, mergedMap);
+ }
+
+ Clear();
+}
+
+// Test one tableupdate array contains full update and multiple partial updates.
+TEST(UrlClassifierTableUpdateV4, MultipleTableUpdates)
+{
+ _PrefixArray fArray, pArray, mergedArray;
+ PrefixStringMap fMap, pMap, mergedMap;
+ nsCString sha256;
+
+ TableUpdateArray tableUpdates;
+
+ // Generate first full udpate
+ CreateRandomSortedPrefixArray(10000, 4, 4, fArray);
+ CreateRandomSortedPrefixArray(2000, 5, 32, fArray);
+ PrefixArrayToPrefixStringMap(fArray, fMap);
+ CalculateSHA256(fArray, sha256);
+
+ GenerateUpdateData(true, fMap, nullptr, &sha256, tableUpdates);
+
+ // Generate second partial update
+ CreateRandomSortedPrefixArray(3000, 4, 4, pArray);
+ CreateRandomSortedPrefixArray(1000, 5, 32, pArray);
+ RemoveIntersection(fArray, pArray);
+ PrefixArrayToPrefixStringMap(pArray, pMap);
+
+ MergeAndSortArray(fArray, pArray, mergedArray);
+ CalculateSHA256(mergedArray, sha256);
+
+ GenerateUpdateData(false, pMap, nullptr, &sha256, tableUpdates);
+
+ // Generate thrid partial update
+ fArray.AppendElements(pArray);
+ fArray.Sort();
+ pArray.Clear();
+ CreateRandomSortedPrefixArray(3000, 4, 4, pArray);
+ CreateRandomSortedPrefixArray(1000, 5, 32, pArray);
+ RemoveIntersection(fArray, pArray);
+ PrefixArrayToPrefixStringMap(pArray, pMap);
+
+ // Remove 1/5 of elements of original prefix set.
+ nsTArray<uint32_t> removal;
+ CreateRandomRemovalIndices(fArray.Length() / 5, fArray.Length(), removal);
+ RemoveElements(removal, fArray);
+
+ MergeAndSortArray(fArray, pArray, mergedArray);
+ PrefixArrayToPrefixStringMap(mergedArray, mergedMap);
+ CalculateSHA256(mergedArray, sha256);
+
+ GenerateUpdateData(false, pMap, &removal, &sha256, tableUpdates);
+
+ testUpdate(tableUpdates, mergedMap);
+
+ Clear();
+}
+
+// Test apply full update first, and then apply multiple partial updates
+// in one tableupdate array.
+TEST(UrlClassifierTableUpdateV4, MultiplePartialUpdateTableUpdates)
+{
+ _PrefixArray fArray;
+
+ // Apply a full update first
+ {
+ PrefixStringMap fMap;
+ nsCString sha256;
+
+ // Generate first full udpate
+ CreateRandomSortedPrefixArray(10000, 4, 4, fArray);
+ CreateRandomSortedPrefixArray(3000, 5, 32, fArray);
+ PrefixArrayToPrefixStringMap(fArray, fMap);
+ CalculateSHA256(fArray, sha256);
+
+ testFullUpdate(fMap, &sha256);
+ }
+
+ // Apply multiple partial updates in one table update
+ {
+ _PrefixArray pArray, mergedArray;
+ PrefixStringMap pMap, mergedMap;
+ nsCString sha256;
+ nsTArray<uint32_t> removal;
+ TableUpdateArray tableUpdates;
+
+ // Generate first partial update
+ CreateRandomSortedPrefixArray(3000, 4, 4, pArray);
+ CreateRandomSortedPrefixArray(1000, 5, 32, pArray);
+ RemoveIntersection(fArray, pArray);
+ PrefixArrayToPrefixStringMap(pArray, pMap);
+
+ // Remove 1/5 of elements of original prefix set.
+ CreateRandomRemovalIndices(fArray.Length() / 5, fArray.Length(), removal);
+ RemoveElements(removal, fArray);
+
+ MergeAndSortArray(fArray, pArray, mergedArray);
+ CalculateSHA256(mergedArray, sha256);
+
+ GenerateUpdateData(false, pMap, &removal, &sha256, tableUpdates);
+
+ fArray.AppendElements(pArray);
+ fArray.Sort();
+ pArray.Clear();
+ removal.Clear();
+
+ // Generate second partial update.
+ CreateRandomSortedPrefixArray(2000, 4, 4, pArray);
+ CreateRandomSortedPrefixArray(1000, 5, 32, pArray);
+ RemoveIntersection(fArray, pArray);
+ PrefixArrayToPrefixStringMap(pArray, pMap);
+
+ // Remove 1/5 of elements of original prefix set.
+ CreateRandomRemovalIndices(fArray.Length() / 5, fArray.Length(), removal);
+ RemoveElements(removal, fArray);
+
+ MergeAndSortArray(fArray, pArray, mergedArray);
+ PrefixArrayToPrefixStringMap(mergedArray, mergedMap);
+ CalculateSHA256(mergedArray, sha256);
+
+ GenerateUpdateData(false, pMap, &removal, &sha256, tableUpdates);
+
+ testUpdate(tableUpdates, mergedMap);
+ }
+
+ Clear();
+}
+
+// Test removal indices are larger than the original prefix set.
+TEST(UrlClassifierTableUpdateV4, RemovalIndexTooLarge)
+{
+ _PrefixArray fArray;
+
+ // Apply a full update first
+ {
+ PrefixStringMap fMap;
+ nsCString sha256;
+
+ CreateRandomSortedPrefixArray(1000, 4, 32, fArray);
+ PrefixArrayToPrefixStringMap(fArray, fMap);
+ CalculateSHA256(fArray, sha256);
+
+ testFullUpdate(fMap, &sha256);
+ }
+
+ // Apply a partial update with removal indice array larger than
+ // old prefix set(fArray). This should cause an error.
+ {
+ _PrefixArray pArray;
+ PrefixStringMap pMap;
+ nsTArray<uint32_t> removal;
+ TableUpdateArray tableUpdates;
+
+ CreateRandomSortedPrefixArray(200, 4, 32, pArray);
+ RemoveIntersection(fArray, pArray);
+ PrefixArrayToPrefixStringMap(pArray, pMap);
+
+ for (uint32_t i = 0; i < fArray.Length() + 1; i++) {
+ removal.AppendElement(i);
+ }
+
+ GenerateUpdateData(false, pMap, &removal, nullptr, tableUpdates);
+ testUpdateFail(tableUpdates);
+ }
+
+ Clear();
+}
+
+TEST(UrlClassifierTableUpdateV4, ChecksumMismatch)
+{
+ // Apply a full update first
+ {
+ _PrefixArray fArray;
+ PrefixStringMap fMap;
+ nsCString sha256;
+
+ CreateRandomSortedPrefixArray(1000, 4, 32, fArray);
+ PrefixArrayToPrefixStringMap(fArray, fMap);
+ CalculateSHA256(fArray, sha256);
+
+ testFullUpdate(fMap, &sha256);
+ }
+
+ // Apply a partial update with incorrect sha256
+ {
+ _PrefixArray pArray;
+ PrefixStringMap pMap;
+ nsCString sha256;
+ TableUpdateArray tableUpdates;
+
+ CreateRandomSortedPrefixArray(200, 4, 32, pArray);
+ PrefixArrayToPrefixStringMap(pArray, pMap);
+
+ // sha256 should be calculated with both old prefix set and add prefix
+ // set, here we only calculate sha256 with add prefix set to check if
+ // applyUpdate will return failure.
+ CalculateSHA256(pArray, sha256);
+
+ GenerateUpdateData(false, pMap, nullptr, &sha256, tableUpdates);
+ testUpdateFail(tableUpdates);
+ }
+
+ Clear();
+}
+
+TEST(UrlClassifierTableUpdateV4, ApplyUpdateThenLoad)
+{
+ // Apply update with sha256
+ {
+ _PrefixArray fArray;
+ PrefixStringMap fMap;
+ nsCString sha256;
+
+ CreateRandomSortedPrefixArray(1000, 4, 32, fArray);
+ PrefixArrayToPrefixStringMap(fArray, fMap);
+ CalculateSHA256(fArray, sha256);
+
+ testFullUpdate(fMap, &sha256);
+
+ // Open lookup cache will load prefix set and verify the sha256
+ testOpenLookupCache();
+ }
+
+ Clear();
+
+ // Apply update without sha256
+ {
+ _PrefixArray fArray;
+ PrefixStringMap fMap;
+
+ CreateRandomSortedPrefixArray(1000, 4, 32, fArray);
+ PrefixArrayToPrefixStringMap(fArray, fMap);
+
+ testFullUpdate(fMap, nullptr);
+
+ testOpenLookupCache();
+ }
+
+ Clear();
+}
+
+// This test is used to avoid an eror from nsICryptoHash
+TEST(UrlClassifierTableUpdateV4, ApplyUpdateWithFixedChecksum)
+{
+ _PrefixArray fArray = {_Prefix("enus"),
+ _Prefix("apollo"),
+ _Prefix("mars"),
+ _Prefix("Hecatonchires cyclopes"),
+ _Prefix("vesta"),
+ _Prefix("neptunus"),
+ _Prefix("jupiter"),
+ _Prefix("diana"),
+ _Prefix("minerva"),
+ _Prefix("ceres"),
+ _Prefix("Aidos,Adephagia,Adikia,Aletheia"),
+ _Prefix("hecatonchires"),
+ _Prefix("alcyoneus"),
+ _Prefix("hades"),
+ _Prefix("vulcanus"),
+ _Prefix("juno"),
+ _Prefix("mercury"),
+ _Prefix("Stheno, Euryale and Medusa")};
+ fArray.Sort();
+
+ PrefixStringMap fMap;
+ PrefixArrayToPrefixStringMap(fArray, fMap);
+
+ nsCString sha256(
+ "\xae\x18\x94\xd7\xd0\x83\x5f\xc1"
+ "\x58\x59\x5c\x2c\x72\xb9\x6e\x5e"
+ "\xf4\xe8\x0a\x6b\xff\x5e\x6b\x81"
+ "\x65\x34\x06\x16\x06\x59\xa0\x67");
+
+ testFullUpdate(fMap, &sha256);
+
+ // Open lookup cache will load prefix set and verify the sha256
+ testOpenLookupCache();
+
+ Clear();
+}
+
+// This test ensure that an empty update works correctly. Empty update
+// should be skipped by CheckValidUpdate in Classifier::UpdateTableV4.
+TEST(UrlClassifierTableUpdateV4, EmptyUpdate)
+{
+ PrefixStringMap emptyAddition;
+ nsTArray<uint32_t> emptyRemoval;
+
+ _PrefixArray array;
+ PrefixStringMap map;
+ nsCString sha256;
+
+ CalculateSHA256(array, sha256);
+
+ // Test apply empty full/partial update before we already
+ // have data in DB.
+ testFullUpdate(emptyAddition, &sha256);
+ testPartialUpdate(emptyAddition, &emptyRemoval, &sha256, map);
+
+ // Apply an full update.
+ CreateRandomSortedPrefixArray(100, 4, 4, array);
+ CreateRandomSortedPrefixArray(10, 5, 32, array);
+ PrefixArrayToPrefixStringMap(array, map);
+ CalculateSHA256(array, sha256);
+
+ testFullUpdate(map, &sha256);
+
+ // Test apply empty full/partial update when we already
+ // have data in DB
+ testPartialUpdate(emptyAddition, &emptyRemoval, &sha256, map);
+ testFullUpdate(emptyAddition, &sha256);
+
+ Clear();
+}
+
+// This test ensure applying an empty update directly through update algorithm
+// should be correct.
+TEST(UrlClassifierTableUpdateV4, EmptyUpdate2)
+{
+ // Setup LookupCache with initial data
+ _PrefixArray array;
+ CreateRandomSortedPrefixArray(100, 4, 4, array);
+ CreateRandomSortedPrefixArray(10, 5, 32, array);
+ RefPtr<LookupCacheV4> cache = SetupLookupCache<LookupCacheV4>(array);
+
+ // Setup TableUpdate object with only sha256 from previous update(initial
+ // data).
+ nsCString sha256;
+ CalculateSHA256(array, sha256);
+ std::string stdSHA256;
+ stdSHA256.assign(const_cast<char*>(sha256.BeginReading()), sha256.Length());
+
+ RefPtr<TableUpdateV4> tableUpdate = new TableUpdateV4(GTEST_TABLE);
+ tableUpdate->SetSHA256(stdSHA256);
+
+ // Apply update directly through LookupCache interface
+ PrefixStringMap input, output;
+ PrefixArrayToPrefixStringMap(array, input);
+ nsresult rv = cache->ApplyUpdate(tableUpdate.get(), input, output);
+
+ ASSERT_TRUE(rv == NS_OK);
+
+ Clear();
+}
diff --git a/toolkit/components/url-classifier/tests/gtest/TestUrlClassifierUtils.cpp b/toolkit/components/url-classifier/tests/gtest/TestUrlClassifierUtils.cpp
new file mode 100644
index 0000000000..354a47f07d
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/TestUrlClassifierUtils.cpp
@@ -0,0 +1,254 @@
+/* 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 <ctype.h>
+#include <stdio.h>
+
+#include <mozilla/RefPtr.h>
+#include "nsEscape.h"
+#include "nsString.h"
+#include "nsUrlClassifierUtils.h"
+#include "stdlib.h"
+
+#include "Common.h"
+
+static char int_to_hex_digit(int32_t i) {
+ NS_ASSERTION((i >= 0) && (i <= 15), "int too big in int_to_hex_digit");
+ return static_cast<char>(((i < 10) ? (i + '0') : ((i - 10) + 'A')));
+}
+
+static void CheckEquals(nsCString& expected, nsCString& actual) {
+ ASSERT_TRUE((expected).Equals((actual)))
+ << "Expected:" << expected.get() << ", Actual:" << actual.get();
+}
+
+static void TestUnescapeHelper(const char* in, const char* expected) {
+ nsCString out, strIn(in), strExp(expected);
+
+ NS_UnescapeURL(strIn.get(), strIn.Length(), esc_AlwaysCopy, out);
+ CheckEquals(strExp, out);
+}
+
+static void TestEncodeHelper(const char* in, const char* expected) {
+ nsCString out, strIn(in), strExp(expected);
+ nsUrlClassifierUtils::GetInstance()->SpecialEncode(strIn, true, out);
+ CheckEquals(strExp, out);
+}
+
+static void TestCanonicalizeHelper(const char* in, const char* expected) {
+ nsCString out, strIn(in), strExp(expected);
+ nsUrlClassifierUtils::GetInstance()->CanonicalizePath(strIn, out);
+ CheckEquals(strExp, out);
+}
+
+static void TestCanonicalNumHelper(const char* in, uint32_t bytes,
+ bool allowOctal, const char* expected) {
+ nsCString out, strIn(in), strExp(expected);
+ nsUrlClassifierUtils::GetInstance()->CanonicalNum(strIn, bytes, allowOctal,
+ out);
+ CheckEquals(strExp, out);
+}
+
+void TestHostnameHelper(const char* in, const char* expected) {
+ nsCString out, strIn(in), strExp(expected);
+ nsUrlClassifierUtils::GetInstance()->CanonicalizeHostname(strIn, out);
+ CheckEquals(strExp, out);
+}
+
+// Make sure Unescape from nsEncode.h's unescape does what the server does.
+TEST(UrlClassifierUtils, Unescape)
+{
+ // test empty string
+ TestUnescapeHelper("\0", "\0");
+
+ // Test docoding of all characters.
+ nsCString allCharsEncoded, allCharsEncodedLowercase, allCharsAsString;
+ for (int32_t i = 1; i < 256; ++i) {
+ allCharsEncoded.Append('%');
+ allCharsEncoded.Append(int_to_hex_digit(i / 16));
+ allCharsEncoded.Append((int_to_hex_digit(i % 16)));
+
+ allCharsEncodedLowercase.Append('%');
+ allCharsEncodedLowercase.Append(tolower(int_to_hex_digit(i / 16)));
+ allCharsEncodedLowercase.Append(tolower(int_to_hex_digit(i % 16)));
+
+ allCharsAsString.Append(static_cast<char>(i));
+ }
+
+ nsCString out;
+ NS_UnescapeURL(allCharsEncoded.get(), allCharsEncoded.Length(),
+ esc_AlwaysCopy, out);
+
+ CheckEquals(allCharsAsString, out);
+
+ out.Truncate();
+ NS_UnescapeURL(allCharsEncodedLowercase.get(),
+ allCharsEncodedLowercase.Length(), esc_AlwaysCopy, out);
+ CheckEquals(allCharsAsString, out);
+
+ // Test %-related edge cases
+ TestUnescapeHelper("%", "%");
+ TestUnescapeHelper("%xx", "%xx");
+ TestUnescapeHelper("%%", "%%");
+ TestUnescapeHelper("%%%", "%%%");
+ TestUnescapeHelper("%%%%", "%%%%");
+ TestUnescapeHelper("%1", "%1");
+ TestUnescapeHelper("%1z", "%1z");
+ TestUnescapeHelper("a%1z", "a%1z");
+ TestUnescapeHelper("abc%d%e%fg%hij%klmno%", "abc%d%e%fg%hij%klmno%");
+
+ // A few more tests
+ TestUnescapeHelper("%25", "%");
+ TestUnescapeHelper("%25%32%35", "%25");
+}
+
+TEST(UrlClassifierUtils, Enc)
+{
+ // Test empty string
+ TestEncodeHelper("", "");
+
+ // Test that all characters we shouldn't encode ([33-34],36,[38,126]) are not.
+ nsCString noenc;
+ for (int32_t i = 33; i < 127; i++) {
+ if (i != 35 && i != 37) { // skip %
+ noenc.Append(static_cast<char>(i));
+ }
+ }
+ nsCString out;
+ nsUrlClassifierUtils::GetInstance()->SpecialEncode(noenc, false, out);
+ CheckEquals(noenc, out);
+
+ // Test that all the chars that we should encode [0,32],35, 37,[127,255] are
+ nsCString yesAsString, yesExpectedString;
+ for (int32_t i = 1; i < 256; i++) {
+ if (i < 33 || i == 35 || i == 37 || i > 126) {
+ yesAsString.Append(static_cast<char>(i));
+ yesExpectedString.Append('%');
+ yesExpectedString.Append(int_to_hex_digit(i / 16));
+ yesExpectedString.Append(int_to_hex_digit(i % 16));
+ }
+ }
+
+ out.Truncate();
+ nsUrlClassifierUtils::GetInstance()->SpecialEncode(yesAsString, false, out);
+ CheckEquals(yesExpectedString, out);
+
+ TestEncodeHelper("blah//blah", "blah/blah");
+}
+
+TEST(UrlClassifierUtils, Canonicalize)
+{
+ // Test repeated %-decoding. Note: %25 --> %, %32 --> 2, %35 --> 5
+ TestCanonicalizeHelper("%25", "%25");
+ TestCanonicalizeHelper("%25%32%35", "%25");
+ TestCanonicalizeHelper("asdf%25%32%35asd", "asdf%25asd");
+ TestCanonicalizeHelper("%%%25%32%35asd%%", "%25%25%25asd%25%25");
+ TestCanonicalizeHelper("%25%32%35%25%32%35%25%32%35", "%25%25%25");
+ TestCanonicalizeHelper("%25", "%25");
+ TestCanonicalizeHelper(
+ "%257Ea%2521b%2540c%2523d%2524e%25f%255E00%252611%252A22%252833%252944_"
+ "55%252B",
+ "~a!b@c%23d$e%25f^00&11*22(33)44_55+");
+
+ TestCanonicalizeHelper("", "");
+ TestCanonicalizeHelper(
+ "%31%36%38%2e%31%38%38%2e%39%39%2e%32%36/%2E%73%65%63%75%72%65/"
+ "%77%77%77%2E%65%62%61%79%2E%63%6F%6D/",
+ "168.188.99.26/.secure/www.ebay.com/");
+ TestCanonicalizeHelper(
+ "195.127.0.11/uploads/%20%20%20%20/.verify/"
+ ".eBaysecure=updateuserdataxplimnbqmn-xplmvalidateinfoswqpcmlx=hgplmcx/",
+ "195.127.0.11/uploads/%20%20%20%20/.verify/"
+ ".eBaysecure=updateuserdataxplimnbqmn-xplmvalidateinfoswqpcmlx=hgplmcx/");
+ // Added in bug 489455. %00 should no longer be changed to %01.
+ TestCanonicalizeHelper("%00", "%00");
+}
+
+void TestParseIPAddressHelper(const char* in, const char* expected) {
+ nsCString out, strIn(in), strExp(expected);
+ nsUrlClassifierUtils::GetInstance()->ParseIPAddress(strIn, out);
+ CheckEquals(strExp, out);
+}
+
+TEST(UrlClassifierUtils, ParseIPAddress)
+{
+ TestParseIPAddressHelper("123.123.0.0.1", "");
+ TestParseIPAddressHelper("255.0.0.1", "255.0.0.1");
+ TestParseIPAddressHelper("12.0x12.01234", "12.18.2.156");
+ TestParseIPAddressHelper("276.2.3", "20.2.0.3");
+ TestParseIPAddressHelper("012.034.01.055", "10.28.1.45");
+ TestParseIPAddressHelper("0x12.0x43.0x44.0x01", "18.67.68.1");
+ TestParseIPAddressHelper("167838211", "10.1.2.3");
+ TestParseIPAddressHelper("3279880203", "195.127.0.11");
+ TestParseIPAddressHelper("0x12434401", "18.67.68.1");
+ TestParseIPAddressHelper("413960661", "24.172.137.213");
+ TestParseIPAddressHelper("03053104725", "24.172.137.213");
+ TestParseIPAddressHelper("030.0254.0x89d5", "24.172.137.213");
+ TestParseIPAddressHelper("1.234.4.0377", "1.234.4.255");
+ TestParseIPAddressHelper("1.2.3.00x0", "");
+ TestParseIPAddressHelper("10.192.95.89 xy", "10.192.95.89");
+ TestParseIPAddressHelper("10.192.95.89 xyz", "");
+ TestParseIPAddressHelper("1.2.3.0x0", "1.2.3.0");
+ TestParseIPAddressHelper("1.2.3.4", "1.2.3.4");
+}
+
+TEST(UrlClassifierUtils, CanonicalNum)
+{
+ TestCanonicalNumHelper("", 1, true, "");
+ TestCanonicalNumHelper("10", 0, true, "");
+ TestCanonicalNumHelper("45", 1, true, "45");
+ TestCanonicalNumHelper("0x10", 1, true, "16");
+ TestCanonicalNumHelper("367", 2, true, "1.111");
+ TestCanonicalNumHelper("012345", 3, true, "0.20.229");
+ TestCanonicalNumHelper("0173", 1, true, "123");
+ TestCanonicalNumHelper("09", 1, false, "9");
+ TestCanonicalNumHelper("0x120x34", 2, true, "");
+ TestCanonicalNumHelper("0x12fc", 2, true, "18.252");
+ TestCanonicalNumHelper("3279880203", 4, true, "195.127.0.11");
+ TestCanonicalNumHelper("0x0000059", 1, true, "89");
+ TestCanonicalNumHelper("0x00000059", 1, true, "89");
+ TestCanonicalNumHelper("0x0000067", 1, true, "103");
+}
+
+TEST(UrlClassifierUtils, Hostname)
+{
+ TestHostnameHelper("abcd123;[]", "abcd123;[]");
+ TestHostnameHelper("abc.123", "abc.123");
+ TestHostnameHelper("abc..123", "abc.123");
+ TestHostnameHelper("trailing.", "trailing");
+ TestHostnameHelper("i love trailing dots....", "i%20love%20trailing%20dots");
+ TestHostnameHelper(".leading", "leading");
+ TestHostnameHelper("..leading", "leading");
+ TestHostnameHelper(".dots.", "dots");
+ TestHostnameHelper(".both.", "both");
+ TestHostnameHelper(".both..", "both");
+ TestHostnameHelper("..both.", "both");
+ TestHostnameHelper("..both..", "both");
+ TestHostnameHelper("..a.b.c.d..", "a.b.c.d");
+ TestHostnameHelper("..127.0.0.1..", "127.0.0.1");
+ TestHostnameHelper("AB CD 12354", "ab%20cd%2012354");
+ TestHostnameHelper("\1\2\3\4\112\177", "%01%02%03%04j%7F");
+ TestHostnameHelper("<>.AS/-+", "<>.as/-+");
+ // Added in bug 489455. %00 should no longer be changed to %01.
+ TestHostnameHelper("%00", "%00");
+}
+
+TEST(UrlClassifierUtils, LongHostname)
+{
+ static const int kTestSize = 1024 * 150;
+ char* str = static_cast<char*>(malloc(kTestSize + 1));
+ memset(str, 'x', kTestSize);
+ str[kTestSize] = '\0';
+
+ nsAutoCString out;
+ nsDependentCString in(str);
+ PRIntervalTime clockStart = PR_IntervalNow();
+ nsUrlClassifierUtils::GetInstance()->CanonicalizeHostname(in, out);
+ PRIntervalTime clockEnd = PR_IntervalNow();
+
+ CheckEquals(in, out);
+
+ printf("CanonicalizeHostname on long string (%dms)\n",
+ PR_IntervalToMilliseconds(clockEnd - clockStart));
+}
diff --git a/toolkit/components/url-classifier/tests/gtest/TestVariableLengthPrefixSet.cpp b/toolkit/components/url-classifier/tests/gtest/TestVariableLengthPrefixSet.cpp
new file mode 100644
index 0000000000..6eb36a12d2
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/TestVariableLengthPrefixSet.cpp
@@ -0,0 +1,486 @@
+/* -*- 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 "LookupCacheV4.h"
+#include "mozilla/Preferences.h"
+#include <mozilla/RefPtr.h>
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsClassHashtable.h"
+#include "nsString.h"
+#include "VariableLengthPrefixSet.h"
+
+#include "Common.h"
+
+// Create fullhash by appending random characters.
+static nsCString CreateFullHash(const nsACString& in) {
+ nsCString out(in);
+ out.SetLength(32);
+ for (size_t i = in.Length(); i < 32; i++) {
+ out.SetCharAt(char(rand() % 256), i);
+ }
+
+ return out;
+}
+
+// This function generate N prefixes with size between MIN and MAX.
+// The output array will not be cleared, random result will append to it
+static void RandomPrefixes(uint32_t N, uint32_t MIN, uint32_t MAX,
+ _PrefixArray& array) {
+ array.SetCapacity(array.Length() + N);
+
+ uint32_t range = (MAX - MIN + 1);
+
+ for (uint32_t i = 0; i < N; i++) {
+ uint32_t prefixSize = (rand() % range) + MIN;
+ _Prefix prefix;
+ prefix.SetLength(prefixSize);
+
+ bool added = false;
+ while (!added) {
+ char* dst = prefix.BeginWriting();
+ for (uint32_t j = 0; j < prefixSize; j++) {
+ dst[j] = rand() % 256;
+ }
+
+ if (!array.Contains(prefix)) {
+ array.AppendElement(prefix);
+ added = true;
+ }
+ }
+ }
+}
+
+// This test loops through all the prefixes and converts each prefix to
+// fullhash by appending random characters, each converted fullhash
+// should at least match its original length in the prefixSet.
+static void DoExpectedLookup(LookupCacheV4* cache, _PrefixArray& array) {
+ uint32_t matchLength = 0;
+ for (uint32_t i = 0; i < array.Length(); i++) {
+ const nsCString& prefix = array[i];
+ Completion complete;
+ complete.Assign(CreateFullHash(prefix));
+
+ // Find match for prefix-generated full hash
+ bool has, confirmed;
+ cache->Has(complete, &has, &matchLength, &confirmed);
+ MOZ_ASSERT(matchLength != 0);
+
+ if (matchLength != prefix.Length()) {
+ // Return match size is not the same as prefix size.
+ // In this case it could be because the generated fullhash match other
+ // prefixes, check if this prefix exist.
+ bool found = false;
+
+ for (uint32_t j = 0; j < array.Length(); j++) {
+ if (array[j].Length() != matchLength) {
+ continue;
+ }
+
+ if (0 == memcmp(complete.buf, array[j].BeginReading(), matchLength)) {
+ found = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(found);
+ }
+ }
+}
+
+static void DoRandomLookup(LookupCacheV4* cache, uint32_t N,
+ _PrefixArray& array) {
+ for (uint32_t i = 0; i < N; i++) {
+ // Random 32-bytes test fullhash
+ char buf[32];
+ for (uint32_t j = 0; j < 32; j++) {
+ buf[j] = (char)(rand() % 256);
+ }
+
+ // Get the expected result.
+ nsTArray<uint32_t> expected;
+ for (uint32_t j = 0; j < array.Length(); j++) {
+ const nsACString& str = array[j];
+ if (0 == memcmp(buf, str.BeginReading(), str.Length())) {
+ expected.AppendElement(str.Length());
+ }
+ }
+
+ Completion complete;
+ complete.Assign(nsDependentCSubstring(buf, 32));
+ bool has, confirmed;
+ uint32_t matchLength = 0;
+ cache->Has(complete, &has, &matchLength, &confirmed);
+
+ ASSERT_TRUE(expected.IsEmpty() ? !matchLength
+ : expected.Contains(matchLength));
+ }
+}
+
+static already_AddRefed<LookupCacheV4> SetupLookupCache(
+ const nsACString& aName) {
+ nsCOMPtr<nsIFile> rootDir;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(rootDir));
+
+ nsAutoCString provider("test");
+ RefPtr<LookupCacheV4> lookup = new LookupCacheV4(aName, provider, rootDir);
+ lookup->Init();
+
+ return lookup.forget();
+}
+
+class UrlClassifierPrefixSetTest : public ::testing::TestWithParam<uint32_t> {
+ protected:
+ void SetUp() override {
+ // max_array_size to 0 means we are testing delta algorithm here.
+ static const char prefKey[] =
+ "browser.safebrowsing.prefixset.max_array_size";
+ mozilla::Preferences::SetUint(prefKey, GetParam());
+
+ mCache = SetupLookupCache("test"_ns);
+ }
+
+ void TearDown() override {
+ mCache = nullptr;
+ mArray.Clear();
+ mMap.Clear();
+ }
+
+ nsresult SetupPrefixes(_PrefixArray&& aArray) {
+ mArray = std::move(aArray);
+ PrefixArrayToPrefixStringMap(mArray, mMap);
+ return mCache->Build(mMap);
+ }
+
+ void SetupPrefixesAndVerify(_PrefixArray& aArray) {
+ mArray = aArray.Clone();
+ PrefixArrayToPrefixStringMap(mArray, mMap);
+
+ ASSERT_NS_SUCCEEDED(mCache->Build(mMap));
+ Verify();
+ }
+
+ void SetupPrefixesAndVerify(_PrefixArray&& aArray) {
+ nsresult rv = SetupPrefixes(std::move(aArray));
+ ASSERT_NS_SUCCEEDED(rv);
+ Verify();
+ }
+
+ void SetupRandomPrefixesAndVerify(uint32_t N, uint32_t MIN, uint32_t MAX) {
+ srand(time(nullptr));
+ RandomPrefixes(N, MIN, MAX, mArray);
+ PrefixArrayToPrefixStringMap(mArray, mMap);
+
+ ASSERT_NS_SUCCEEDED(mCache->Build(mMap));
+ Verify();
+ }
+
+ void Verify() {
+ DoExpectedLookup(mCache, mArray);
+ DoRandomLookup(mCache, 1000, mArray);
+ CheckContent(mCache, mArray);
+ }
+
+ RefPtr<LookupCacheV4> mCache;
+ _PrefixArray mArray;
+ PrefixStringMap mMap;
+};
+
+// Test setting prefix set with only 4-bytes prefixes
+TEST_P(UrlClassifierPrefixSetTest, FixedLengthSet) {
+ SetupPrefixesAndVerify({
+ _Prefix("alph"),
+ _Prefix("brav"),
+ _Prefix("char"),
+ _Prefix("delt"),
+ _Prefix("echo"),
+ _Prefix("foxt"),
+ });
+}
+
+TEST_P(UrlClassifierPrefixSetTest, FixedLengthRandomSet) {
+ SetupRandomPrefixesAndVerify(1500, 4, 4);
+}
+
+TEST_P(UrlClassifierPrefixSetTest, FixedLengthRandomLargeSet) {
+ SetupRandomPrefixesAndVerify(15000, 4, 4);
+}
+
+TEST_P(UrlClassifierPrefixSetTest, FixedLengthTinySet) {
+ SetupPrefixesAndVerify({
+ _Prefix("tiny"),
+ });
+}
+
+// Test setting prefix set with only 5~32 bytes prefixes
+TEST_P(UrlClassifierPrefixSetTest, VariableLengthSet) {
+ SetupPrefixesAndVerify(
+ {_Prefix("bravo"), _Prefix("charlie"), _Prefix("delta"),
+ _Prefix("EchoEchoEchoEchoEcho"), _Prefix("foxtrot"),
+ _Prefix("GolfGolfGolfGolfGolfGolfGolfGolf"), _Prefix("hotel"),
+ _Prefix("november"), _Prefix("oscar"), _Prefix("quebec"),
+ _Prefix("romeo"), _Prefix("sierrasierrasierrasierrasierra"),
+ _Prefix("Tango"), _Prefix("whiskey"), _Prefix("yankee"),
+ _Prefix("ZuluZuluZuluZulu")});
+}
+
+TEST_P(UrlClassifierPrefixSetTest, VariableLengthRandomSet) {
+ SetupRandomPrefixesAndVerify(1500, 5, 32);
+}
+
+// Test setting prefix set with both 4-bytes prefixes and 5~32 bytes prefixes
+TEST_P(UrlClassifierPrefixSetTest, MixedPrefixSet) {
+ SetupPrefixesAndVerify(
+ {_Prefix("enus"), _Prefix("apollo"), _Prefix("mars"),
+ _Prefix("Hecatonchires cyclopes"), _Prefix("vesta"), _Prefix("neptunus"),
+ _Prefix("jupiter"), _Prefix("diana"), _Prefix("minerva"),
+ _Prefix("ceres"), _Prefix("Aidos,Adephagia,Adikia,Aletheia"),
+ _Prefix("hecatonchires"), _Prefix("alcyoneus"), _Prefix("hades"),
+ _Prefix("vulcanus"), _Prefix("juno"), _Prefix("mercury"),
+ _Prefix("Stheno, Euryale and Medusa")});
+}
+
+TEST_P(UrlClassifierPrefixSetTest, MixedRandomPrefixSet) {
+ SetupRandomPrefixesAndVerify(1500, 4, 32);
+}
+
+// Test resetting prefix set
+TEST_P(UrlClassifierPrefixSetTest, ResetPrefix) {
+ // Base prefix set
+ _PrefixArray oldArray = {
+ _Prefix("Iceland"), _Prefix("Peru"), _Prefix("Mexico"),
+ _Prefix("Australia"), _Prefix("Japan"), _Prefix("Egypt"),
+ _Prefix("America"), _Prefix("Finland"), _Prefix("Germany"),
+ _Prefix("Italy"), _Prefix("France"), _Prefix("Taiwan"),
+ };
+ SetupPrefixesAndVerify(oldArray);
+
+ // New prefix set
+ _PrefixArray newArray = {
+ _Prefix("Pikachu"), _Prefix("Bulbasaur"), _Prefix("Charmander"),
+ _Prefix("Blastoise"), _Prefix("Pidgey"), _Prefix("Mewtwo"),
+ _Prefix("Jigglypuff"), _Prefix("Persian"), _Prefix("Tentacool"),
+ _Prefix("Onix"), _Prefix("Eevee"), _Prefix("Jynx"),
+ };
+ SetupPrefixesAndVerify(newArray);
+
+ // Should not match any of the first prefix set
+ uint32_t matchLength = 0;
+ for (uint32_t i = 0; i < oldArray.Length(); i++) {
+ Completion complete;
+ complete.Assign(CreateFullHash(oldArray[i]));
+
+ // Find match for prefix-generated full hash
+ bool has, confirmed;
+ mCache->Has(complete, &has, &matchLength, &confirmed);
+
+ ASSERT_TRUE(matchLength == 0);
+ }
+}
+
+// Test only set one 4-bytes prefix and one full-length prefix
+TEST_P(UrlClassifierPrefixSetTest, TinyPrefixSet) {
+ SetupPrefixesAndVerify({
+ _Prefix("AAAA"),
+ _Prefix("11112222333344445555666677778888"),
+ });
+}
+
+// Test empty prefix set and IsEmpty function
+TEST_P(UrlClassifierPrefixSetTest, EmptyFixedPrefixSet) {
+ ASSERT_TRUE(mCache->IsEmpty());
+
+ SetupPrefixesAndVerify({});
+
+ // Insert an 4-bytes prefix, then IsEmpty should return false
+ SetupPrefixesAndVerify({_Prefix("test")});
+
+ ASSERT_TRUE(!mCache->IsEmpty());
+}
+
+TEST_P(UrlClassifierPrefixSetTest, EmptyVariableLengthPrefixSet) {
+ ASSERT_TRUE(mCache->IsEmpty());
+
+ SetupPrefixesAndVerify({});
+
+ // Insert an 5~32 bytes prefix, then IsEmpty should return false
+ SetupPrefixesAndVerify({_Prefix("test variable length")});
+
+ ASSERT_TRUE(!mCache->IsEmpty());
+}
+
+// Test prefix size should only between 4~32 bytes
+TEST_P(UrlClassifierPrefixSetTest, MinMaxPrefixSet) {
+ // Test prefix set between 4-32 bytes, should success
+ SetupPrefixesAndVerify({_Prefix("1234"), _Prefix("ABCDEFGHIJKKMNOP"),
+ _Prefix("1aaa2bbb3ccc4ddd5eee6fff7ggg8hhh")});
+
+ // Prefix size less than 4-bytes should fail
+ nsresult rv = SetupPrefixes({_Prefix("123")});
+ ASSERT_NS_FAILED(rv);
+
+ // Prefix size greater than 32-bytes should fail
+ rv = SetupPrefixes({_Prefix("1aaa2bbb3ccc4ddd5eee6fff7ggg8hhh9")});
+ ASSERT_NS_FAILED(rv);
+}
+
+// Test save then load prefix set with only 4-bytes prefixes
+TEST_P(UrlClassifierPrefixSetTest, LoadSaveFixedLengthPrefixSet) {
+ nsCOMPtr<nsIFile> file;
+ _PrefixArray array;
+ PrefixStringMap map;
+
+ // Save
+ {
+ RefPtr<LookupCacheV4> save = SetupLookupCache("test-save"_ns);
+
+ RandomPrefixes(10000, 4, 4, array);
+
+ PrefixArrayToPrefixStringMap(array, map);
+ save->Build(map);
+
+ DoExpectedLookup(save, array);
+ DoRandomLookup(save, 1000, array);
+ CheckContent(save, array);
+
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
+ file->Append(u"test.vlpset"_ns);
+ save->StoreToFile(file);
+ }
+
+ // Load
+ {
+ RefPtr<LookupCacheV4> load = SetupLookupCache("test-load"_ns);
+ load->LoadFromFile(file);
+
+ DoExpectedLookup(load, array);
+ DoRandomLookup(load, 1000, array);
+ CheckContent(load, array);
+ }
+
+ file->Remove(false);
+}
+
+// Test save then load prefix set with only 5~32 bytes prefixes
+TEST_P(UrlClassifierPrefixSetTest, LoadSaveVariableLengthPrefixSet) {
+ nsCOMPtr<nsIFile> file;
+ _PrefixArray array;
+ PrefixStringMap map;
+
+ // Save
+ {
+ RefPtr<LookupCacheV4> save = SetupLookupCache("test-save"_ns);
+
+ RandomPrefixes(10000, 5, 32, array);
+
+ PrefixArrayToPrefixStringMap(array, map);
+ save->Build(map);
+
+ DoExpectedLookup(save, array);
+ DoRandomLookup(save, 1000, array);
+ CheckContent(save, array);
+
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
+ file->Append(u"test.vlpset"_ns);
+ save->StoreToFile(file);
+ }
+
+ // Load
+ {
+ RefPtr<LookupCacheV4> load = SetupLookupCache("test-load"_ns);
+ load->LoadFromFile(file);
+
+ DoExpectedLookup(load, array);
+ DoRandomLookup(load, 1000, array);
+ CheckContent(load, array);
+ }
+
+ file->Remove(false);
+}
+
+// Test save then load prefix with both 4 bytes prefixes and 5~32 bytes prefixes
+TEST_P(UrlClassifierPrefixSetTest, LoadSavePrefixSet) {
+ nsCOMPtr<nsIFile> file;
+ _PrefixArray array;
+ PrefixStringMap map;
+
+ // Save
+ {
+ RefPtr<LookupCacheV4> save = SetupLookupCache("test-save"_ns);
+
+ // Try to simulate the real case that most prefixes are 4bytes
+ RandomPrefixes(20000, 4, 4, array);
+ RandomPrefixes(1000, 5, 32, array);
+
+ PrefixArrayToPrefixStringMap(array, map);
+ save->Build(map);
+
+ DoExpectedLookup(save, array);
+ DoRandomLookup(save, 1000, array);
+ CheckContent(save, array);
+
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
+ file->Append(u"test.vlpset"_ns);
+ save->StoreToFile(file);
+ }
+
+ // Load
+ {
+ RefPtr<LookupCacheV4> load = SetupLookupCache("test-load"_ns);
+ load->LoadFromFile(file);
+
+ DoExpectedLookup(load, array);
+ DoRandomLookup(load, 1000, array);
+ CheckContent(load, array);
+ }
+
+ file->Remove(false);
+}
+
+// This is for fixed-length prefixset
+TEST_P(UrlClassifierPrefixSetTest, LoadSaveNoDelta) {
+ nsCOMPtr<nsIFile> file;
+ _PrefixArray array;
+ PrefixStringMap map;
+
+ for (uint32_t i = 0; i < 100; i++) {
+ // construct a tree without deltas by making the distance
+ // between entries larger than 16 bits
+ uint32_t v = ((1 << 16) + 1) * i;
+ nsCString* ele = array.AppendElement();
+ ele->AppendASCII(reinterpret_cast<const char*>(&v), 4);
+ }
+
+ // Save
+ {
+ RefPtr<LookupCacheV4> save = SetupLookupCache("test-save"_ns);
+
+ PrefixArrayToPrefixStringMap(array, map);
+ save->Build(map);
+
+ DoExpectedLookup(save, array);
+ DoRandomLookup(save, 1000, array);
+ CheckContent(save, array);
+
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
+ file->Append(u"test.vlpset"_ns);
+ save->StoreToFile(file);
+ }
+
+ // Load
+ {
+ RefPtr<LookupCacheV4> load = SetupLookupCache("test-load"_ns);
+ load->LoadFromFile(file);
+
+ DoExpectedLookup(load, array);
+ DoRandomLookup(load, 1000, array);
+ CheckContent(load, array);
+ }
+
+ file->Remove(false);
+}
+
+// To run the same test for different configurations of
+// "browser_safebrowsing_prefixset_max_array_size"
+INSTANTIATE_TEST_SUITE_P(UrlClassifierPrefixSetTest, UrlClassifierPrefixSetTest,
+ ::testing::Values(0, UINT32_MAX));
diff --git a/toolkit/components/url-classifier/tests/gtest/moz.build b/toolkit/components/url-classifier/tests/gtest/moz.build
new file mode 100644
index 0000000000..070012de77
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/gtest/moz.build
@@ -0,0 +1,40 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+LOCAL_INCLUDES += [
+ "../..",
+]
+
+DEFINES["GOOGLE_PROTOBUF_NO_RTTI"] = True
+DEFINES["GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER"] = True
+
+UNIFIED_SOURCES += [
+ "Common.cpp",
+ "TestCaching.cpp",
+ "TestChunkSet.cpp",
+ "TestClassifier.cpp",
+ "TestFailUpdate.cpp",
+ "TestFindFullHash.cpp",
+ "TestLookupCacheV4.cpp",
+ "TestPerProviderDirectory.cpp",
+ "TestPrefixSet.cpp",
+ "TestProtocolParser.cpp",
+ "TestRiceDeltaDecoder.cpp",
+ "TestSafebrowsingHash.cpp",
+ "TestSafeBrowsingProtobuf.cpp",
+ "TestTable.cpp",
+ "TestUrlClassifierTableUpdateV4.cpp",
+ "TestUrlClassifierUtils.cpp",
+ "TestURLsAndHashing.cpp",
+ "TestVariableLengthPrefixSet.cpp",
+]
+
+# Required to have the same MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
+# as non-testing code.
+if CONFIG["NIGHTLY_BUILD"] or CONFIG["MOZ_DEBUG"]:
+ DEFINES["MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES"] = True
+
+FINAL_LIBRARY = "xul-gtest"
diff --git a/toolkit/components/url-classifier/tests/mochitest/allowlistAnnotatedFrame.html b/toolkit/components/url-classifier/tests/mochitest/allowlistAnnotatedFrame.html
new file mode 100644
index 0000000000..1096274260
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/allowlistAnnotatedFrame.html
@@ -0,0 +1,143 @@
+<html>
+<head>
+<title></title>
+
+<script type="text/javascript">
+
+// Modified by evil.js
+var scriptItem;
+
+var scriptItem1 = "untouched";
+var imageItem1 = "untouched";
+var frameItem1 = "untouched";
+var scriptItem2 = "untouched";
+var imageItem2 = "untouched";
+var frameItem2 = "untouched";
+var xhrItem = "untouched";
+var fetchItem = "untouched";
+var mediaItem1 = "untouched";
+
+async function checkLoads() {
+ window.parent.is(scriptItem1, "spoiled", "Should not block tracking js 1");
+ window.parent.is(scriptItem2, "spoiled", "Should not block tracking js 2");
+ window.parent.is(imageItem1, "spoiled", "Should not block tracking img 1");
+ window.parent.is(imageItem2, "spoiled", "Should not block tracking img 2");
+ window.parent.is(frameItem1, "spoiled", "Should not block tracking iframe 1");
+ window.parent.is(frameItem2, "spoiled", "Should not block tracking iframe 2");
+ window.parent.is(mediaItem1, "loaded", "Should not block tracking video");
+ window.parent.is(xhrItem, "loaded", "Should not block tracking XHR");
+ window.parent.is(fetchItem, "loaded", "Should not block fetches from tracking domains");
+ window.parent.is(window.document.blockedNodeByClassifierCount, 0,
+ "No elements should be blocked");
+
+ // End (parent) test.
+ await window.parent.clearPermissions();
+ window.parent.SimpleTest.finish();
+}
+
+var onloadCalled = false;
+var xhrFinished = false;
+var fetchFinished = false;
+var videoLoaded = false;
+function loaded(type) {
+ if (type === "onload") {
+ onloadCalled = true;
+ } else if (type === "xhr") {
+ xhrFinished = true;
+ } else if (type === "fetch") {
+ fetchFinished = true;
+ } else if (type === "video") {
+ videoLoaded = true;
+ }
+
+ if (onloadCalled && xhrFinished && fetchFinished && videoLoaded) {
+ checkLoads();
+ }
+}
+</script>
+
+</head>
+
+<body onload="loaded('onload')">
+
+<!-- Try loading from a tracking script URI (1) -->
+<script id="badscript1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="scriptItem1 = 'spoiled';"></script>
+
+<!-- Try loading from a tracking image URI (1) -->
+<img id="badimage1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg" onload="imageItem1 = 'spoiled';"/>
+
+<!-- Try loading from a tracking frame URI (1) -->
+<iframe id="badframe1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/track.html" onload="frameItem1 = 'spoiled';"></iframe>
+
+<!-- Try loading from a tracking video URI -->
+<video id="badmedia1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/vp9.webm"></video>
+
+<script>
+var v = document.getElementById("badmedia1");
+v.addEventListener("loadedmetadata", function() {
+ mediaItem1 = "loaded";
+ loaded("video");
+}, true);
+v.addEventListener("error", function() {
+ mediaItem1 = "error";
+ loaded("video");
+}, true);
+
+// Try loading from a tracking script URI (2) - The loader may follow a
+// different path depending on whether the resource is loaded from JS or HTML.
+var newScript = document.createElement("script");
+newScript.id = "badscript2";
+newScript.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js";
+newScript.addEventListener("load", function onload() { scriptItem2 = "spoiled"; });
+document.body.appendChild(newScript);
+
+// / Try loading from a tracking image URI (2)
+var newImage = document.createElement("img");
+newImage.id = "badimage2";
+newImage.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg";
+newImage.addEventListener("load", function onload() { imageItem2 = "spoiled"; });
+document.body.appendChild(newImage);
+
+// Try loading from a tracking iframe URI (2)
+var newFrame = document.createElement("iframe");
+newFrame.id = "badframe2";
+newFrame.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/track.html";
+newFrame.addEventListener("load", function onload() { frameItem2 = "spoiled"; });
+document.body.appendChild(newFrame);
+
+// Try doing an XHR against a tracking domain (bug 1216793)
+function reqListener() {
+ xhrItem = "loaded";
+ loaded("xhr");
+}
+function transferFailed() {
+ xhrItem = "failed";
+ loaded("xhr");
+}
+function transferCanceled() {
+ xhrItem = "canceled";
+ loaded("xhr");
+}
+var oReq = new XMLHttpRequest();
+oReq.addEventListener("load", reqListener);
+oReq.addEventListener("error", transferFailed);
+oReq.addEventListener("abort", transferCanceled);
+oReq.open("GET", "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js");
+oReq.send();
+
+// Fetch from a tracking domain
+fetch("http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js").then(function(response) {
+ if (response.ok) {
+ fetchItem = "loaded";
+ loaded("fetch");
+ } else {
+ fetchItem = "badresponse";
+ loaded("fetch");
+ }
+ }).catch(function(error) {
+ fetchItem = "error";
+ loaded("fetch");
+});
+</script>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/bad.css b/toolkit/components/url-classifier/tests/mochitest/bad.css
new file mode 100644
index 0000000000..f57b36a778
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/bad.css
@@ -0,0 +1 @@
+#styleBad { visibility: hidden; }
diff --git a/toolkit/components/url-classifier/tests/mochitest/bad.css^headers^ b/toolkit/components/url-classifier/tests/mochitest/bad.css^headers^
new file mode 100644
index 0000000000..4030ea1d3d
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/bad.css^headers^
@@ -0,0 +1 @@
+Cache-Control: no-store
diff --git a/toolkit/components/url-classifier/tests/mochitest/basic.vtt b/toolkit/components/url-classifier/tests/mochitest/basic.vtt
new file mode 100644
index 0000000000..7781790d04
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/basic.vtt
@@ -0,0 +1,27 @@
+WEBVTT
+Region: id=testOne lines=2 width=30%
+Region: id=testTwo lines=4 width=20%
+
+1
+00:00.500 --> 00:00.700 region:testOne
+This
+
+2
+00:01.200 --> 00:02.400 region:testTwo
+Is
+
+2.5
+00:02.000 --> 00:03.500 region:testOne
+(Over here?!)
+
+3
+00:02.710 --> 00:02.910
+A
+
+4
+00:03.217 --> 00:03.989
+Test
+
+5
+00:03.217 --> 00:03.989
+And more!
diff --git a/toolkit/components/url-classifier/tests/mochitest/basic.vtt^headers^ b/toolkit/components/url-classifier/tests/mochitest/basic.vtt^headers^
new file mode 100644
index 0000000000..23de552c1a
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/basic.vtt^headers^
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: * \ No newline at end of file
diff --git a/toolkit/components/url-classifier/tests/mochitest/bug_1281083.html b/toolkit/components/url-classifier/tests/mochitest/bug_1281083.html
new file mode 100644
index 0000000000..80124da3cc
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/bug_1281083.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+<title></title>
+</head>
+<body>
+
+<!-- Try loading from a malware javascript URI -->
+<script id="badscript" data-touched="not sure" src="http://bug1281083.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"></script>
+
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/bug_1580416.html b/toolkit/components/url-classifier/tests/mochitest/bug_1580416.html
new file mode 100644
index 0000000000..ae305bbb47
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/bug_1580416.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+<title></title>
+</head>
+<body>
+
+<script id="goodscript" data-touched="not sure" src="http://mochitest.apps.fbsbx.com/tests/toolkit/components/url-classifier/tests/mochitest/good.js" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"></script>
+
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/cache.sjs b/toolkit/components/url-classifier/tests/mochitest/cache.sjs
new file mode 100644
index 0000000000..84fb0e9089
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/cache.sjs
@@ -0,0 +1,90 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const CC = Components.Constructor;
+const BinaryInputStream = CC(
+ "@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream"
+);
+
+function handleRequest(request, response) {
+ var query = {};
+ request.queryString.split("&").forEach(function (val) {
+ var idx = val.indexOf("=");
+ query[val.slice(0, idx)] = unescape(val.slice(idx + 1));
+ });
+
+ var responseBody;
+
+ // Store fullhash in the server side.
+ if ("list" in query && "fullhash" in query) {
+ // In the server side we will store:
+ // 1. All the full hashes for a given list
+ // 2. All the lists we have right now
+ // data is separate by '\n'
+ let list = query.list;
+ let hashes = getState(list);
+
+ let hash = atob(query.fullhash);
+ hashes += hash + "\n";
+ setState(list, hashes);
+
+ let lists = getState("lists");
+ if (!lists.includes(list)) {
+ lists += list + "\n";
+ setState("lists", lists);
+ }
+
+ return;
+ // gethash count return how many gethash request received.
+ // This is used by client to know if a gethash request is triggered by gecko
+ } else if ("gethashcount" == request.queryString) {
+ let counter = getState("counter");
+ responseBody = counter == "" ? "0" : counter;
+ } else {
+ let body = new BinaryInputStream(request.bodyInputStream);
+ let avail;
+ let bytes = [];
+
+ while ((avail = body.available()) > 0) {
+ Array.prototype.push.apply(bytes, body.readByteArray(avail));
+ }
+
+ let counter = getState("counter");
+ counter = counter == "" ? "1" : (parseInt(counter) + 1).toString();
+ setState("counter", counter);
+
+ responseBody = parseV2Request(bytes);
+ }
+
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write(responseBody);
+}
+
+function parseV2Request(bytes) {
+ var request = String.fromCharCode.apply(this, bytes);
+ var [HEADER, PREFIXES] = request.split("\n");
+ var [PREFIXSIZE, LENGTH] = HEADER.split(":").map(val => {
+ return parseInt(val);
+ });
+
+ var ret = "";
+ for (var start = 0; start < LENGTH; start += PREFIXSIZE) {
+ getState("lists")
+ .split("\n")
+ .forEach(function (list) {
+ var completions = getState(list).split("\n");
+
+ for (var completion of completions) {
+ if (completion.indexOf(PREFIXES.substr(start, PREFIXSIZE)) == 0) {
+ ret += list + ":1:32\n";
+ ret += completion;
+ }
+ }
+ });
+ }
+
+ return ret;
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/chrome.toml b/toolkit/components/url-classifier/tests/mochitest/chrome.toml
new file mode 100644
index 0000000000..cbd0e475e3
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/chrome.toml
@@ -0,0 +1,83 @@
+[DEFAULT]
+skip-if = ["os == 'android'"]
+support-files = [
+ "allowlistAnnotatedFrame.html",
+ "classifiedAnnotatedFrame.html",
+ "classifiedAnnotatedPBFrame.html",
+ "trackingRequest.html",
+ "bug_1281083.html",
+ "bug_1580416.html",
+ "report.sjs",
+ "gethash.sjs",
+ "classifierCommon.js",
+ "classifierHelper.js",
+ "head.js",
+ "threathit.sjs",
+ "redirect_tracker.sjs",
+ "!/toolkit/components/url-classifier/tests/mochitest/classifierFrame.html",
+ "!/toolkit/components/url-classifier/tests/mochitest/cleanWorker.js",
+ "!/toolkit/components/url-classifier/tests/mochitest/good.js",
+ "!/toolkit/components/url-classifier/tests/mochitest/evil.css",
+ "!/toolkit/components/url-classifier/tests/mochitest/evil.css^headers^",
+ "!/toolkit/components/url-classifier/tests/mochitest/evil.js",
+ "!/toolkit/components/url-classifier/tests/mochitest/evil.js^headers^",
+ "!/toolkit/components/url-classifier/tests/mochitest/evilWorker.js",
+ "!/toolkit/components/url-classifier/tests/mochitest/import.css",
+ "!/toolkit/components/url-classifier/tests/mochitest/raptor.jpg",
+ "!/toolkit/components/url-classifier/tests/mochitest/track.html",
+ "!/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js",
+ "!/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js^headers^",
+ "!/toolkit/components/url-classifier/tests/mochitest/unwantedWorker.js",
+ "!/toolkit/components/url-classifier/tests/mochitest/vp9.webm",
+ "!/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html",
+ "!/toolkit/components/url-classifier/tests/mochitest/workerFrame.html",
+ "!/toolkit/components/url-classifier/tests/mochitest/ping.sjs",
+ "!/toolkit/components/url-classifier/tests/mochitest/basic.vtt",
+ "!/toolkit/components/url-classifier/tests/mochitest/basic.vtt^headers^",
+ "!/toolkit/components/url-classifier/tests/mochitest/dnt.html",
+ "!/toolkit/components/url-classifier/tests/mochitest/dnt.sjs",
+ "!/toolkit/components/url-classifier/tests/mochitest/update.sjs",
+ "!/toolkit/components/url-classifier/tests/mochitest/bad.css",
+ "!/toolkit/components/url-classifier/tests/mochitest/bad.css^headers^",
+ "!/toolkit/components/url-classifier/tests/mochitest/gethashFrame.html",
+ "!/toolkit/components/url-classifier/tests/mochitest/seek.webm",
+ "!/toolkit/components/url-classifier/tests/mochitest/cache.sjs",
+]
+
+["test_advisory_link.html"]
+
+["test_allowlisted_annotations.html"]
+tags = "trackingprotection"
+
+["test_classified_annotations.html"]
+tags = "trackingprotection"
+skip-if = ["os == 'linux' && asan"] # Bug 1202548
+
+["test_classifier_changetablepref.html"]
+skip-if = ["verify"]
+
+["test_classifier_changetablepref_bug1395411.html"]
+
+["test_donottrack.html"]
+
+["test_privatebrowsing_trackingprotection.html"]
+tags = "trackingprotection"
+
+["test_reporturl.html"]
+skip-if = ["verify"]
+
+["test_safebrowsing_bug1272239.html"]
+
+["test_threathit_report.html"]
+skip-if = ["verify"]
+
+["test_trackingprotection_bug1157081.html"]
+tags = "trackingprotection"
+
+["test_trackingprotection_bug1312515.html"]
+
+["test_trackingprotection_bug1580416.html"]
+tags = "trackingprotection"
+
+["test_trackingprotection_whitelist.html"]
+tags = "trackingprotection"
diff --git a/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedFrame.html b/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedFrame.html
new file mode 100644
index 0000000000..9b12f529cb
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedFrame.html
@@ -0,0 +1,154 @@
+<html>
+<head>
+<title></title>
+
+<script type="text/javascript">
+"use strict";
+
+var scriptItem = "untouched";
+var scriptItem1 = "untouched";
+var scriptItem2 = "untouched";
+var imageItem1 = "untouched";
+var imageItem2 = "untouched";
+var frameItem1 = "untouched";
+var frameItem2 = "untouched";
+var xhrItem = "untouched";
+var fetchItem = "untouched";
+var mediaItem1 = "untouched";
+
+var badids = [
+ "badscript1",
+ "badscript2",
+ "badimage1",
+ "badimage2",
+ "badframe1",
+ "badframe2",
+ "badmedia1",
+ "badcss",
+];
+
+var onloadCalled = false;
+var xhrFinished = false;
+var fetchFinished = false;
+var videoLoaded = false;
+function loaded(type) {
+ if (type === "onload") {
+ onloadCalled = true;
+ } else if (type === "xhr") {
+ xhrFinished = true;
+ } else if (type === "fetch") {
+ fetchFinished = true;
+ } else if (type === "video") {
+ videoLoaded = true;
+ }
+ if (onloadCalled && xhrFinished && fetchFinished && videoLoaded) {
+ var msg = new window.CustomEvent("OnLoadComplete", {
+ detail: JSON.stringify({
+ scriptItem,
+ scriptItem1,
+ scriptItem2,
+ imageItem1,
+ imageItem2,
+ frameItem1,
+ frameItem2,
+ xhrItem,
+ fetchItem,
+ mediaItem1,
+ }),
+ });
+ window.dispatchEvent(msg);
+ }
+}
+</script>
+
+<!-- Try loading from a tracking CSS URI -->
+<link id="badcss" rel="stylesheet" type="text/css" href="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.css"></link>
+
+</head>
+
+<body onload="loaded('onload')">
+
+<!-- Try loading from a tracking script URI (1): evil.js onload will have updated the scriptItem variable -->
+<script id="badscript1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="scriptItem1 = scriptItem;"></script>
+
+<!-- Try loading from a tracking image URI (1) -->
+<img id="badimage1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?reload=true" onload="imageItem1 = 'spoiled';"/>
+
+<!-- Try loading from a tracking frame URI (1) -->
+<iframe id="badframe1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/track.html" onload="frameItem1 = 'spoiled';"></iframe>
+
+<!-- Try loading from a tracking video URI -->
+<video id="badmedia1" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/vp9.webm?reload=true"></video>
+
+<script>
+var v = document.getElementById("badmedia1");
+v.addEventListener("loadedmetadata", function() {
+ mediaItem1 = "loaded";
+ loaded("video");
+}, true);
+v.addEventListener("error", function() {
+ mediaItem1 = "error";
+ loaded("video");
+}, true);
+
+// Try loading from a tracking script URI (2) - The loader may follow a different path depending on whether the resource is loaded from JS or HTML.
+var newScript = document.createElement("script");
+newScript.id = "badscript2";
+newScript.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js";
+newScript.addEventListener("load", function() { scriptItem2 = scriptItem; });
+document.body.appendChild(newScript);
+
+// Try loading from a tracking image URI (2)
+var newImage = document.createElement("img");
+newImage.id = "badimage2";
+newImage.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?reload=true";
+newImage.addEventListener("load", function() { imageItem2 = "spoiled"; });
+document.body.appendChild(newImage);
+
+// Try loading from a tracking iframe URI (2)
+var newFrame = document.createElement("iframe");
+newFrame.id = "badframe2";
+newFrame.src = "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/track.html";
+newFrame.addEventListener("load", function() { frameItem2 = "spoiled"; });
+document.body.appendChild(newFrame);
+
+// Try doing an XHR against a tracking domain (bug 1216793)
+function reqListener() {
+ xhrItem = "loaded";
+ loaded("xhr");
+}
+function transferFailed() {
+ xhrItem = "failed";
+ loaded("xhr");
+}
+function transferCanceled() {
+ xhrItem = "canceled";
+ loaded("xhr");
+}
+var oReq = new XMLHttpRequest();
+oReq.addEventListener("load", reqListener);
+oReq.addEventListener("error", transferFailed);
+oReq.addEventListener("abort", transferCanceled);
+oReq.open("GET", "http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js");
+oReq.send();
+
+// Fetch from a tracking domain
+fetch("http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js").then(function(response) {
+ if (response.ok) {
+ fetchItem = "loaded";
+ loaded("fetch");
+ } else {
+ fetchItem = "badresponse";
+ loaded("fetch");
+ }
+ }).catch(function(error) {
+ fetchItem = "error";
+ loaded("fetch");
+});
+</script>
+
+The following should not be hidden:
+<div id="styleCheck">STYLE TEST</div>
+
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html b/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html
new file mode 100644
index 0000000000..ecd89e91d6
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+<title></title>
+
+<link id="badcss" rel="stylesheet" type="text/css" href="https://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.css"></link>
+
+</head>
+<body>
+
+<script id="badscript" data-touched="not sure" src="https://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"></script>
+
+<script id="goodscript" data-touched="not sure" src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/good.js" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"></script>
+
+<!-- The image cache can cache JS handlers, so make sure we use a different URL for raptor.jpg each time -->
+<img id="badimage" data-touched="not sure" src="https://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?pbmode=test" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"/>
+
+<img id="goodimage" data-touched="not sure" src="https://tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?pbmode=test2" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"/>
+
+The following should not be hidden:
+<div id="styleCheck">STYLE TEST</div>
+
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/classifierCommon.js b/toolkit/components/url-classifier/tests/mochitest/classifierCommon.js
new file mode 100644
index 0000000000..ca288986e3
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/classifierCommon.js
@@ -0,0 +1,108 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/chrome-script */
+
+var dbService = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(
+ Ci.nsIUrlClassifierDBService
+);
+var listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"].getService(
+ Ci.nsIUrlListManager
+);
+
+var timer;
+function setTimeout(callback, delay) {
+ timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ timer.initWithCallback(
+ { notify: callback },
+ delay,
+ Ci.nsITimer.TYPE_ONE_SHOT
+ );
+}
+
+function doUpdate(update) {
+ let listener = {
+ QueryInterface: ChromeUtils.generateQI(["nsIUrlClassifierUpdateObserver"]),
+ updateUrlRequested(url) {},
+ streamFinished(status) {},
+ updateError(errorCode) {
+ sendAsyncMessage("updateError", errorCode);
+ },
+ updateSuccess(requestedTimeout) {
+ sendAsyncMessage("updateSuccess");
+ },
+ };
+
+ try {
+ dbService.beginUpdate(
+ listener,
+ "test-malware-simple,test-unwanted-simple",
+ ""
+ );
+ dbService.beginStream("", "");
+ dbService.updateStream(update);
+ dbService.finishStream();
+ dbService.finishUpdate();
+ } catch (e) {
+ // beginUpdate may fail if there's an existing update in progress
+ // retry until success or testcase timeout.
+ setTimeout(() => {
+ doUpdate(update);
+ }, 1000);
+ }
+}
+
+function doReload() {
+ try {
+ dbService.reloadDatabase();
+ sendAsyncMessage("reloadSuccess");
+ } catch (e) {
+ setTimeout(() => {
+ doReload();
+ }, 1000);
+ }
+}
+
+// SafeBrowsing.jsm is initialized after mozEntries are added. Add observer
+// to receive "finished" event. For the case when this function is called
+// after the event had already been notified, we lookup entries to see if
+// they are already added to database.
+function waitForInit() {
+ if (listmanager.isRegistered()) {
+ sendAsyncMessage("safeBrowsingInited");
+ } else {
+ setTimeout(() => {
+ waitForInit();
+ }, 1000);
+ }
+}
+
+function doGetTables() {
+ const callback = tables => {
+ sendAsyncMessage("GetTableSuccess", tables);
+ };
+
+ try {
+ dbService.getTables(callback);
+ } catch (e) {
+ setTimeout(() => {
+ doGetTables();
+ }, 1000);
+ }
+}
+
+addMessageListener("doUpdate", ({ testUpdate }) => {
+ doUpdate(testUpdate);
+});
+
+addMessageListener("doReload", () => {
+ doReload();
+});
+
+addMessageListener("waitForInit", () => {
+ waitForInit();
+});
+
+addMessageListener("doGetTables", () => {
+ doGetTables();
+});
diff --git a/toolkit/components/url-classifier/tests/mochitest/classifierFrame.html b/toolkit/components/url-classifier/tests/mochitest/classifierFrame.html
new file mode 100644
index 0000000000..1e3617b9d8
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/classifierFrame.html
@@ -0,0 +1,57 @@
+<html>
+<head>
+<title></title>
+
+<script type="text/javascript">
+
+var scriptItem = "untouched";
+
+function checkLoads() {
+ // Make sure the javascript did not load.
+ window.parent.is(scriptItem, "untouched", "Should not load bad javascript");
+
+ // Make sure the css did not load.
+ var elt = document.getElementById("styleCheck");
+ var style = document.defaultView.getComputedStyle(elt);
+ window.parent.isnot(style.visibility, "hidden", "Should not load bad css");
+
+ elt = document.getElementById("styleBad");
+ style = document.defaultView.getComputedStyle(elt);
+ window.parent.isnot(style.visibility, "hidden", "Should not load bad css");
+
+ elt = document.getElementById("styleImport");
+ style = document.defaultView.getComputedStyle(elt);
+ window.parent.isnot(style.visibility, "visible", "Should import clean css");
+
+ // Call parent.loadTestFrame again to test classification metadata in HTTP
+ // cache entries.
+ if (window.parent.firstLoad) {
+ window.parent.info("Reloading from cache...");
+ window.parent.firstLoad = false;
+ window.parent.loadTestFrame();
+ return;
+ }
+
+ // End (parent) test.
+ window.parent.SimpleTest.finish();
+}
+
+</script>
+
+<!-- Try loading from a malware javascript URI -->
+<script type="text/javascript" src="http://malware.mochi.test/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"></script>
+
+<!-- Try loading from an uwanted software css URI -->
+<link rel="stylesheet" type="text/css" href="http://unwanted.mochi.test/tests/toolkit/components/url-classifier/tests/mochitest/evil.css"></link>
+
+<!-- Try loading a marked-as-malware css through an @import from a clean URI -->
+<link rel="stylesheet" type="text/css" href="import2.css"></link>
+</head>
+
+<body onload="checkLoads()">
+The following should not be hidden:
+<div id="styleCheck">STYLE TEST</div>
+<div id="styleBad">STYLE BAD</div>
+<div id="styleImport">STYLE IMPORT</div>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/classifierHelper.js b/toolkit/components/url-classifier/tests/mochitest/classifierHelper.js
new file mode 100644
index 0000000000..157e5d54f7
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/classifierHelper.js
@@ -0,0 +1,204 @@
+if (typeof classifierHelper == "undefined") {
+ var classifierHelper = {};
+}
+
+const CLASSIFIER_COMMON_URL = SimpleTest.getTestFileURL("classifierCommon.js");
+var gScript = SpecialPowers.loadChromeScript(CLASSIFIER_COMMON_URL);
+var gOriginalGetHashURL;
+
+const PREFS = {
+ PROVIDER_LISTS: "browser.safebrowsing.provider.mozilla.lists",
+ DISALLOW_COMPLETIONS: "urlclassifier.disallow_completions",
+ PROVIDER_GETHASHURL: "browser.safebrowsing.provider.mozilla.gethashURL",
+};
+
+classifierHelper._curAddChunkNum = 1;
+
+// addUrlToDB is asynchronous, queue the task to ensure
+// the callback follow correct order.
+classifierHelper._updates = [];
+
+// Keep urls added to database, those urls should be automatically
+// removed after test complete.
+classifierHelper._updatesToCleanup = [];
+
+classifierHelper._initsCB = [];
+
+// This function return a Promise, promise is resolved when SafeBrowsing.jsm
+// is initialized.
+classifierHelper.waitForInit = function () {
+ return new Promise(function (resolve, reject) {
+ classifierHelper._initsCB.push(resolve);
+ gScript.sendAsyncMessage("waitForInit");
+ });
+};
+
+// This function is used to allow completion for specific "list",
+// some lists like "test-malware-simple" is default disabled to ask for complete.
+// "list" is the db we would like to allow it
+// "url" is the completion server
+classifierHelper.allowCompletion = async function (lists, url) {
+ for (var list of lists) {
+ // Add test db to provider
+ var pref = await SpecialPowers.getParentCharPref(PREFS.PROVIDER_LISTS);
+ pref += "," + list;
+ await SpecialPowers.setCharPref(PREFS.PROVIDER_LISTS, pref);
+
+ // Rename test db so we will not disallow it from completions
+ pref = await SpecialPowers.getParentCharPref(PREFS.DISALLOW_COMPLETIONS);
+ pref = pref.replace(list, list + "-backup");
+ await SpecialPowers.setCharPref(PREFS.DISALLOW_COMPLETIONS, pref);
+ }
+
+ // Store the original get hash URL in order to reset it back during clean up.
+ gOriginalGetHashURL = SpecialPowers.getCharPref(PREFS.PROVIDER_GETHASHURL);
+
+ // Set get hash url
+ await SpecialPowers.setCharPref(PREFS.PROVIDER_GETHASHURL, url);
+};
+
+// Pass { url: ..., db: ... } to add url to database,
+// onsuccess/onerror will be called when update complete.
+classifierHelper.addUrlToDB = function (updateData) {
+ return new Promise(function (resolve, reject) {
+ var testUpdate = "";
+ for (var update of updateData) {
+ var LISTNAME = update.db;
+ var CHUNKDATA = update.url;
+ var CHUNKLEN = CHUNKDATA.length;
+ var HASHLEN = update.len ? update.len : 32;
+
+ update.addChunk = classifierHelper._curAddChunkNum;
+ classifierHelper._curAddChunkNum += 1;
+
+ classifierHelper._updatesToCleanup.push(update);
+ testUpdate +=
+ "n:1000\n" +
+ "i:" +
+ LISTNAME +
+ "\n" +
+ "ad:1\n" +
+ "a:" +
+ update.addChunk +
+ ":" +
+ HASHLEN +
+ ":" +
+ CHUNKLEN +
+ "\n" +
+ CHUNKDATA;
+ }
+
+ classifierHelper._update(testUpdate, resolve, reject);
+ });
+};
+
+// This API is used to expire all add/sub chunks we have updated
+// by using addUrlToDB.
+classifierHelper.resetDatabase = function () {
+ function removeDatabase() {
+ return new Promise(function (resolve, reject) {
+ var testUpdate = "";
+ for (var update of classifierHelper._updatesToCleanup) {
+ testUpdate +=
+ "n:1000\ni:" + update.db + "\nad:" + update.addChunk + "\n";
+ }
+
+ classifierHelper._update(testUpdate, resolve, reject);
+ });
+ }
+
+ // Remove and then reload will ensure both database and cache will
+ // be cleared.
+ return Promise.resolve()
+ .then(removeDatabase)
+ .then(classifierHelper.reloadDatabase);
+};
+
+classifierHelper.reloadDatabase = function () {
+ return new Promise(function (resolve, reject) {
+ gScript.addMessageListener("reloadSuccess", function handler() {
+ gScript.removeMessageListener("reloadSuccess", handler);
+ resolve();
+ });
+
+ gScript.sendAsyncMessage("doReload");
+ });
+};
+
+classifierHelper.getTables = function () {
+ return new Promise(resolve => {
+ gScript.addMessageListener("GetTableSuccess", function handler(tables) {
+ gScript.removeMessageListener("GetTableSuccess", handler);
+ resolve(tables);
+ });
+
+ gScript.sendAsyncMessage("doGetTables");
+ });
+};
+
+classifierHelper._update = function (testUpdate, onsuccess, onerror) {
+ // Queue the task if there is still an on-going update
+ classifierHelper._updates.push({
+ data: testUpdate,
+ onsuccess,
+ onerror,
+ });
+ if (classifierHelper._updates.length != 1) {
+ return;
+ }
+
+ gScript.sendAsyncMessage("doUpdate", { testUpdate });
+};
+
+classifierHelper._updateSuccess = function () {
+ var update = classifierHelper._updates.shift();
+ update.onsuccess();
+
+ if (classifierHelper._updates.length) {
+ var testUpdate = classifierHelper._updates[0].data;
+ gScript.sendAsyncMessage("doUpdate", { testUpdate });
+ }
+};
+
+classifierHelper._updateError = function (errorCode) {
+ var update = classifierHelper._updates.shift();
+ update.onerror(errorCode);
+
+ if (classifierHelper._updates.length) {
+ var testUpdate = classifierHelper._updates[0].data;
+ gScript.sendAsyncMessage("doUpdate", { testUpdate });
+ }
+};
+
+classifierHelper._inited = function () {
+ classifierHelper._initsCB.forEach(function (cb) {
+ cb();
+ });
+ classifierHelper._initsCB = [];
+};
+
+classifierHelper._setup = function () {
+ gScript.addMessageListener("updateSuccess", classifierHelper._updateSuccess);
+ gScript.addMessageListener("updateError", classifierHelper._updateError);
+ gScript.addMessageListener("safeBrowsingInited", classifierHelper._inited);
+
+ // cleanup will be called at end of each testcase to remove all the urls added to database.
+ SimpleTest.registerCleanupFunction(classifierHelper._cleanup);
+};
+
+classifierHelper._cleanup = function () {
+ // clean all the preferences may touch by helper
+ Object.values(PREFS).map(pref => SpecialPowers.clearUserPref(pref));
+
+ // Set the getHashURL back, the original value isn't the same as the default
+ // pref value.
+ SpecialPowers.setCharPref(PREFS.PROVIDER_GETHASHURL, gOriginalGetHashURL);
+
+ if (!classifierHelper._updatesToCleanup) {
+ return Promise.resolve();
+ }
+
+ return classifierHelper.resetDatabase();
+};
+
+classifierHelper._setup();
diff --git a/toolkit/components/url-classifier/tests/mochitest/cleanWorker.js b/toolkit/components/url-classifier/tests/mochitest/cleanWorker.js
new file mode 100644
index 0000000000..e7361119b6
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/cleanWorker.js
@@ -0,0 +1,12 @@
+/* eslint-env worker */
+
+onmessage = function () {
+ try {
+ importScripts("evilWorker.js");
+ } catch (ex) {
+ postMessage("success");
+ return;
+ }
+
+ postMessage("failure");
+};
diff --git a/toolkit/components/url-classifier/tests/mochitest/dnt.html b/toolkit/components/url-classifier/tests/mochitest/dnt.html
new file mode 100644
index 0000000000..2246263688
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/dnt.html
@@ -0,0 +1,31 @@
+<html>
+<head>
+<title></title>
+
+<script type="text/javascript">
+
+function makeXHR(url, callback) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", url, true);
+ xhr.onload = function() {
+ callback(xhr.response);
+ };
+ xhr.send();
+}
+
+function loaded(type) {
+ window.parent.postMessage("navigator.doNotTrack=" + navigator.doNotTrack, "*");
+
+ makeXHR("dnt.sjs", (res) => {
+ window.parent.postMessage("DNT=" + res, "*");
+ window.parent.postMessage("finish", "*");
+ });
+}
+
+</script>
+</head>
+
+<body onload="loaded('onload')">
+</body>
+
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/dnt.sjs b/toolkit/components/url-classifier/tests/mochitest/dnt.sjs
new file mode 100644
index 0000000000..bbb836482a
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/dnt.sjs
@@ -0,0 +1,9 @@
+function handleRequest(request, response) {
+ var dnt = "unspecified";
+ if (request.hasHeader("DNT")) {
+ dnt = "1";
+ }
+
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write(dnt);
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/evil.css b/toolkit/components/url-classifier/tests/mochitest/evil.css
new file mode 100644
index 0000000000..62d506c899
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/evil.css
@@ -0,0 +1 @@
+#styleCheck { visibility: hidden; }
diff --git a/toolkit/components/url-classifier/tests/mochitest/evil.css^headers^ b/toolkit/components/url-classifier/tests/mochitest/evil.css^headers^
new file mode 100644
index 0000000000..4030ea1d3d
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/evil.css^headers^
@@ -0,0 +1 @@
+Cache-Control: no-store
diff --git a/toolkit/components/url-classifier/tests/mochitest/evil.js b/toolkit/components/url-classifier/tests/mochitest/evil.js
new file mode 100644
index 0000000000..3e8b165587
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/evil.js
@@ -0,0 +1,3 @@
+/* global scriptItem:true */
+
+scriptItem = "loaded malware javascript!";
diff --git a/toolkit/components/url-classifier/tests/mochitest/evil.js^headers^ b/toolkit/components/url-classifier/tests/mochitest/evil.js^headers^
new file mode 100644
index 0000000000..3eced96143
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/evil.js^headers^
@@ -0,0 +1,2 @@
+Access-Control-Allow-Origin: *
+Cache-Control: no-store
diff --git a/toolkit/components/url-classifier/tests/mochitest/evilWorker.js b/toolkit/components/url-classifier/tests/mochitest/evilWorker.js
new file mode 100644
index 0000000000..b4e8a47602
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/evilWorker.js
@@ -0,0 +1,5 @@
+/* eslint-env worker */
+
+onmessage = function () {
+ postMessage("loaded bad file");
+};
diff --git a/toolkit/components/url-classifier/tests/mochitest/features.js b/toolkit/components/url-classifier/tests/mochitest/features.js
new file mode 100644
index 0000000000..0004693d81
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/features.js
@@ -0,0 +1,291 @@
+var tests = [
+ // Config is an array with 4 elements:
+ // - annotation blocklist
+ // - annotation entitylist
+ // - tracking blocklist
+ // - tracking entitylist
+
+ // All disabled.
+ {
+ config: [false, false, false, false],
+ loadExpected: true,
+ annotationExpected: false,
+ },
+
+ // Just entitylisted.
+ {
+ config: [false, false, false, true],
+ loadExpected: true,
+ annotationExpected: false,
+ },
+
+ // Just blocklisted.
+ {
+ config: [false, false, true, false],
+ loadExpected: false,
+ annotationExpected: false,
+ },
+
+ // entitylist + blocklist => entitylist wins
+ {
+ config: [false, false, true, true],
+ loadExpected: true,
+ annotationExpected: false,
+ },
+
+ // just annotated in entitylist.
+ {
+ config: [false, true, false, false],
+ loadExpected: true,
+ annotationExpected: false,
+ },
+
+ // TP and annotation entitylisted.
+ {
+ config: [false, true, false, true],
+ loadExpected: true,
+ annotationExpected: false,
+ },
+
+ // Annotation entitylisted, but TP blocklisted.
+ {
+ config: [false, true, true, false],
+ loadExpected: false,
+ annotationExpected: false,
+ },
+
+ // Annotation entitylisted. TP blocklisted and entitylisted: entitylist wins.
+ {
+ config: [false, true, true, true],
+ loadExpected: true,
+ annotationExpected: false,
+ },
+
+ // Just blocklist annotated.
+ {
+ config: [true, false, false, false],
+ loadExpected: true,
+ annotationExpected: true,
+ },
+
+ // annotated but TP entitylisted.
+ {
+ config: [true, false, false, true],
+ loadExpected: true,
+ annotationExpected: true,
+ },
+
+ // annotated and blocklisted.
+ {
+ config: [true, false, true, false],
+ loadExpected: false,
+ annotationExpected: false,
+ },
+
+ // annotated, TP blocklisted and entitylisted: entitylist wins.
+ {
+ config: [true, false, true, true],
+ loadExpected: true,
+ annotationExpected: true,
+ },
+
+ // annotated in white and blocklist.
+ {
+ config: [true, true, false, false],
+ loadExpected: true,
+ annotationExpected: false,
+ },
+
+ // annotated in white and blocklist. TP Whiteslited
+ {
+ config: [true, true, false, true],
+ loadExpected: true,
+ annotationExpected: false,
+ },
+
+ // everywhere. TP entitylist wins.
+ {
+ config: [true, true, true, true],
+ loadExpected: true,
+ annotationExpected: false,
+ },
+];
+
+function prefBlacklistValue(value) {
+ return value ? "example.com" : "";
+}
+
+function prefWhitelistValue(value) {
+ return value ? "mochi.test,mochi.xorigin-test" : "";
+}
+
+async function runTest(test, expectedFlag, expectedTrackingResource, prefs) {
+ let config = [
+ [
+ "urlclassifier.trackingAnnotationTable.testEntries",
+ prefBlacklistValue(test.config[0]),
+ ],
+ [
+ "urlclassifier.features.fingerprinting.annotate.blacklistHosts",
+ prefBlacklistValue(test.config[0]),
+ ],
+ [
+ "urlclassifier.features.cryptomining.annotate.blacklistHosts",
+ prefBlacklistValue(test.config[0]),
+ ],
+ [
+ "urlclassifier.features.socialtracking.annotate.blacklistHosts",
+ prefBlacklistValue(test.config[0]),
+ ],
+
+ [
+ "urlclassifier.trackingAnnotationWhitelistTable.testEntries",
+ prefWhitelistValue(test.config[1]),
+ ],
+ [
+ "urlclassifier.features.fingerprinting.annotate.whitelistHosts",
+ prefWhitelistValue(test.config[1]),
+ ],
+ [
+ "urlclassifier.features.cryptomining.annotate.whitelistHosts",
+ prefWhitelistValue(test.config[1]),
+ ],
+ [
+ "urlclassifier.features.socialtracking.annotate.whitelistHosts",
+ prefWhitelistValue(test.config[1]),
+ ],
+
+ [
+ "urlclassifier.trackingTable.testEntries",
+ prefBlacklistValue(test.config[2]),
+ ],
+ [
+ "urlclassifier.features.fingerprinting.blacklistHosts",
+ prefBlacklistValue(test.config[2]),
+ ],
+ [
+ "urlclassifier.features.cryptomining.blacklistHosts",
+ prefBlacklistValue(test.config[2]),
+ ],
+ [
+ "urlclassifier.features.socialtracking.blacklistHosts",
+ prefBlacklistValue(test.config[2]),
+ ],
+
+ [
+ "urlclassifier.trackingWhitelistTable.testEntries",
+ prefWhitelistValue(test.config[3]),
+ ],
+ [
+ "urlclassifier.features.fingerprinting.whitelistHosts",
+ prefWhitelistValue(test.config[3]),
+ ],
+ [
+ "urlclassifier.features.cryptomining.whitelistHosts",
+ prefWhitelistValue(test.config[3]),
+ ],
+ [
+ "urlclassifier.features.socialtracking.whitelistHosts",
+ prefWhitelistValue(test.config[3]),
+ ],
+ ];
+
+ info("Testing: " + JSON.stringify(config) + "\n");
+
+ await SpecialPowers.pushPrefEnv({ set: config.concat(prefs) });
+
+ // This promise will be resolved when the chromeScript knows if the channel
+ // is annotated or not.
+ let annotationPromise;
+ if (test.loadExpected) {
+ info("We want to have annotation information");
+ annotationPromise = new Promise(resolve => {
+ chromeScript.addMessageListener(
+ "last-channel-flags",
+ data => resolve(data),
+ { once: true }
+ );
+ });
+ }
+
+ // Let's load a script with a random query string to avoid network cache.
+ // Using a script as the fingerprinting feature does not block display content
+ let result = await new Promise(resolve => {
+ let script = document.createElement("script");
+ script.setAttribute(
+ "src",
+ "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js?" +
+ Math.random()
+ );
+ script.onload = _ => resolve(true);
+ script.onerror = _ => resolve(false);
+ document.body.appendChild(script);
+ });
+
+ is(result, test.loadExpected, "The loading happened correctly");
+
+ if (annotationPromise) {
+ let data = await annotationPromise;
+ is(
+ !!data.classificationFlags,
+ test.annotationExpected,
+ "The annotation happened correctly"
+ );
+ if (test.annotationExpected) {
+ is(data.classificationFlags, expectedFlag, "Correct flag");
+ is(
+ data.isThirdPartyTrackingResource,
+ expectedTrackingResource,
+ "Tracking resource flag matches"
+ );
+ }
+ }
+}
+
+var chromeScript;
+
+function runTests(flag, prefs, trackingResource) {
+ chromeScript = SpecialPowers.loadChromeScript(_ => {
+ /* eslint-env mozilla/chrome-script */
+ function onExamResp(subject, topic, data) {
+ let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+ let classifiedChannel = subject.QueryInterface(Ci.nsIClassifiedChannel);
+ if (
+ !channel ||
+ !classifiedChannel ||
+ !channel.URI.spec.startsWith(
+ "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"
+ )
+ ) {
+ return;
+ }
+
+ sendAsyncMessage("last-channel-flags", {
+ classificationFlags: classifiedChannel.classificationFlags,
+ isThirdPartyTrackingResource:
+ classifiedChannel.isThirdPartyTrackingResource(),
+ });
+ }
+
+ addMessageListener("done", __ => {
+ Services.obs.removeObserver(onExamResp, "http-on-examine-response");
+ });
+
+ Services.obs.addObserver(onExamResp, "http-on-examine-response");
+
+ sendAsyncMessage("start-test");
+ });
+
+ chromeScript.addMessageListener(
+ "start-test",
+ async _ => {
+ for (let test in tests) {
+ await runTest(tests[test], flag, trackingResource, prefs);
+ }
+
+ chromeScript.sendAsyncMessage("done");
+ SimpleTest.finish();
+ },
+ { once: true }
+ );
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/gethash.sjs b/toolkit/components/url-classifier/tests/mochitest/gethash.sjs
new file mode 100644
index 0000000000..12e8c11835
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/gethash.sjs
@@ -0,0 +1,86 @@
+const CC = Components.Constructor;
+const BinaryInputStream = CC(
+ "@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream"
+);
+
+function handleRequest(request, response) {
+ var query = {};
+ request.queryString.split("&").forEach(function (val) {
+ var idx = val.indexOf("=");
+ query[val.slice(0, idx)] = unescape(val.slice(idx + 1));
+ });
+
+ var responseBody;
+
+ // Store fullhash in the server side.
+ if ("list" in query && "fullhash" in query) {
+ // In the server side we will store:
+ // 1. All the full hashes for a given list
+ // 2. All the lists we have right now
+ // data is separate by '\n'
+ let list = query.list;
+ let hashes = getState(list);
+
+ let hash = atob(query.fullhash);
+ hashes += hash + "\n";
+ setState(list, hashes);
+
+ let lists = getState("lists");
+ if (!lists.includes(list)) {
+ lists += list + "\n";
+ setState("lists", lists);
+ }
+
+ return;
+ // gethash count return how many gethash request received.
+ // This is used by client to know if a gethash request is triggered by gecko
+ } else if ("gethashcount" == request.queryString) {
+ let counter = getState("counter");
+ responseBody = counter == "" ? "0" : counter;
+ } else {
+ let body = new BinaryInputStream(request.bodyInputStream);
+ let avail;
+ let bytes = [];
+
+ while ((avail = body.available()) > 0) {
+ Array.prototype.push.apply(bytes, body.readByteArray(avail));
+ }
+
+ let counter = getState("counter");
+ counter = counter == "" ? "1" : (parseInt(counter) + 1).toString();
+ setState("counter", counter);
+
+ responseBody = parseV2Request(bytes);
+ }
+
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write(responseBody);
+}
+
+function parseV2Request(bytes) {
+ var request = String.fromCharCode.apply(this, bytes);
+ var [HEADER, PREFIXES] = request.split("\n");
+ var [PREFIXSIZE, LENGTH] = HEADER.split(":").map(val => {
+ return parseInt(val);
+ });
+
+ var ret = "";
+ for (var start = 0; start < LENGTH; start += PREFIXSIZE) {
+ getState("lists")
+ .split("\n")
+ .forEach(function (list) {
+ var completions = getState(list).split("\n");
+
+ for (var completion of completions) {
+ if (completion.indexOf(PREFIXES.substr(start, PREFIXSIZE)) == 0) {
+ ret += list + ":1:32\n";
+ ret += completion;
+ }
+ }
+ });
+ }
+
+ return ret;
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/gethashFrame.html b/toolkit/components/url-classifier/tests/mochitest/gethashFrame.html
new file mode 100644
index 0000000000..4f518dabe0
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/gethashFrame.html
@@ -0,0 +1,61 @@
+<html>
+<head>
+<title></title>
+
+<script type="text/javascript">
+
+var scriptItem = "untouched";
+
+function checkLoads() {
+ var title = document.getElementById("title");
+ title.textContent = window.parent.shouldLoad ?
+ "The following should be hidden:" :
+ "The following should not be hidden:";
+
+ if (window.parent.shouldLoad) {
+ window.parent.is(scriptItem, "loaded malware javascript!", "Should load bad javascript");
+ } else {
+ window.parent.is(scriptItem, "untouched", "Should not load bad javascript");
+ }
+
+ var elt = document.getElementById("styleImport");
+ var style = document.defaultView.getComputedStyle(elt);
+ window.parent.isnot(style.visibility, "visible", "Should load clean css");
+
+ // Make sure the css did not load.
+ elt = document.getElementById("styleCheck");
+ style = document.defaultView.getComputedStyle(elt);
+ if (window.parent.shouldLoad) {
+ window.parent.isnot(style.visibility, "visible", "Should load bad css");
+ } else {
+ window.parent.isnot(style.visibility, "hidden", "Should not load bad css");
+ }
+
+ elt = document.getElementById("styleBad");
+ style = document.defaultView.getComputedStyle(elt);
+ if (window.parent.shouldLoad) {
+ window.parent.isnot(style.visibility, "visible", "Should import bad css");
+ } else {
+ window.parent.isnot(style.visibility, "hidden", "Should not import bad css");
+ }
+}
+
+</script>
+
+<!-- Try loading from a malware javascript URI -->
+<script type="text/javascript" src="http://malware.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"></script>
+
+<!-- Try loading from an uwanted software css URI -->
+<link rel="stylesheet" type="text/css" href="http://unwanted.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.css"></link>
+
+<!-- Try loading a marked-as-malware css through an @import from a clean URI -->
+<link rel="stylesheet" type="text/css" href="import.css"></link>
+</head>
+
+<body onload="checkLoads()">
+<div id="title"></div>
+<div id="styleCheck">STYLE EVIL</div>
+<div id="styleBad">STYLE BAD</div>
+<div id="styleImport">STYLE IMPORT</div>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/good.js b/toolkit/components/url-classifier/tests/mochitest/good.js
new file mode 100644
index 0000000000..a3c8fcb0c8
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/good.js
@@ -0,0 +1,3 @@
+/* global scriptItem:true */
+
+scriptItem = "loaded whitelisted javascript!";
diff --git a/toolkit/components/url-classifier/tests/mochitest/head.js b/toolkit/components/url-classifier/tests/mochitest/head.js
new file mode 100644
index 0000000000..dca6abe279
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/head.js
@@ -0,0 +1,40 @@
+// calculate the fullhash and send it to gethash server
+function addCompletionToServer(list, url, mochitestUrl) {
+ return new Promise(function (resolve, reject) {
+ var listParam = "list=" + list;
+ var fullhashParam = "fullhash=" + hash(url);
+
+ var xhr = new XMLHttpRequest();
+ xhr.open("PUT", mochitestUrl + "?" + listParam + "&" + fullhashParam, true);
+ xhr.setRequestHeader("Content-Type", "text/plain");
+ xhr.onreadystatechange = function () {
+ if (this.readyState == this.DONE) {
+ resolve();
+ }
+ };
+ xhr.send();
+ });
+}
+
+function hash(str) {
+ var hasher = SpecialPowers.Cc["@mozilla.org/security/hash;1"].createInstance(
+ SpecialPowers.Ci.nsICryptoHash
+ );
+
+ var data = new TextEncoder().encode(str);
+ hasher.init(hasher.SHA256);
+ hasher.update(data, data.length);
+
+ return hasher.finish(true);
+}
+
+var pushPrefs = (...p) => SpecialPowers.pushPrefEnv({ set: p });
+
+function whenDelayedStartupFinished(aWindow, aCallback) {
+ Services.obs.addObserver(function observer(aSubject, aTopic) {
+ if (aWindow == aSubject) {
+ Services.obs.removeObserver(observer, aTopic);
+ setTimeout(aCallback, 0);
+ }
+ }, "browser-delayed-startup-finished");
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/import.css b/toolkit/components/url-classifier/tests/mochitest/import.css
new file mode 100644
index 0000000000..9b86c82169
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/import.css
@@ -0,0 +1,3 @@
+/* malware.example.com is in the malware database. */
+@import url("http://malware.example.com/tests/toolkit/components/url-classifier/tests/mochitest/bad.css");
+#styleImport { visibility: hidden; }
diff --git a/toolkit/components/url-classifier/tests/mochitest/import2.css b/toolkit/components/url-classifier/tests/mochitest/import2.css
new file mode 100644
index 0000000000..55de698e0c
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/import2.css
@@ -0,0 +1,3 @@
+/* malware.mochi.test is in the malware database. */
+@import url("http://malware.mochi.test/tests/toolkit/components/url-classifier/tests/mochitest/bad.css");
+#styleImport { visibility: hidden; }
diff --git a/toolkit/components/url-classifier/tests/mochitest/mochitest.toml b/toolkit/components/url-classifier/tests/mochitest/mochitest.toml
new file mode 100644
index 0000000000..20ebb3bf34
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/mochitest.toml
@@ -0,0 +1,91 @@
+[DEFAULT]
+tags = "condprof"
+support-files = [
+ "classifierCommon.js",
+ "classifierFrame.html",
+ "classifierHelper.js",
+ "cleanWorker.js",
+ "good.js",
+ "head.js",
+ "evil.css",
+ "evil.css^headers^",
+ "evil.js",
+ "evil.js^headers^",
+ "evilWorker.js",
+ "import.css",
+ "import2.css",
+ "raptor.jpg",
+ "track.html",
+ "trackerFrame.html",
+ "trackerFrame.sjs",
+ "trackingRequest.js",
+ "trackingRequest.js^headers^",
+ "unwantedWorker.js",
+ "vp9.webm",
+ "whitelistFrame.html",
+ "workerFrame.html",
+ "ping.sjs",
+ "basic.vtt",
+ "basic.vtt^headers^",
+ "dnt.html",
+ "dnt.sjs",
+ "update.sjs",
+ "bad.css",
+ "bad.css^headers^",
+ "gethash.sjs",
+ "gethashFrame.html",
+ "seek.webm",
+ "cache.sjs",
+ "features.js",
+ "sw_register.html",
+ "sw_unregister.html",
+ "sw_worker.js",
+ "sandboxed.html",
+ "sandboxed.html^headers^",
+]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_annotation_vs_TP.html"]
+
+["test_bug1254766.html"]
+
+["test_cachemiss.html"]
+skip-if = ["verify"]
+
+["test_classifier.html"]
+
+["test_classifier_match.html"]
+
+["test_classifier_worker.html"]
+
+["test_classify_by_default.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_classify_ping.html"]
+skip-if = ["verify && debug && (os == 'win' || os == 'mac')"]
+
+["test_classify_top_sandboxed.html"]
+
+["test_classify_track.html"]
+
+["test_cryptomining.html"]
+
+["test_cryptomining_annotate.html"]
+
+["test_emailtracking.html"]
+
+["test_fingerprinting.html"]
+
+["test_fingerprinting_annotate.html"]
+
+["test_gethash.html"]
+
+["test_socialtracking.html"]
+
+["test_socialtracking_annotate.html"]
diff --git a/toolkit/components/url-classifier/tests/mochitest/ping.sjs b/toolkit/components/url-classifier/tests/mochitest/ping.sjs
new file mode 100644
index 0000000000..09a5f17356
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/ping.sjs
@@ -0,0 +1,15 @@
+function handleRequest(request, response) {
+ var query = {};
+ request.queryString.split("&").forEach(function (val) {
+ var [name, value] = val.split("=");
+ query[name] = unescape(value);
+ });
+
+ if (request.method == "POST") {
+ setState(query.id, "ping");
+ } else {
+ var value = getState(query.id);
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write(value);
+ }
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/raptor.jpg b/toolkit/components/url-classifier/tests/mochitest/raptor.jpg
new file mode 100644
index 0000000000..243ba9e2d4
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/raptor.jpg
Binary files differ
diff --git a/toolkit/components/url-classifier/tests/mochitest/redirect_tracker.sjs b/toolkit/components/url-classifier/tests/mochitest/redirect_tracker.sjs
new file mode 100644
index 0000000000..563abae5cb
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/redirect_tracker.sjs
@@ -0,0 +1,7 @@
+const gURL =
+ "http://trackertest.org/tests/toolkit/components/url-classifier/tests/mochitest/evil.js";
+
+function handleRequest(request, response) {
+ response.setStatusLine("1.1", 302, "found");
+ response.setHeader("Location", gURL, false);
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/report.sjs b/toolkit/components/url-classifier/tests/mochitest/report.sjs
new file mode 100644
index 0000000000..b5844eb7a4
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/report.sjs
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const SJS = "report.sjs?";
+const REDIRECT =
+ "mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/" +
+ SJS;
+
+function createBlockedIframePage() {
+ return `<!DOCTYPE HTML>
+ <html>
+ <head>
+ <title></title>
+ </head>
+ <body>
+ <iframe id="phishingFrame" ></iframe>
+ </body>
+ </html>`;
+}
+
+function createPage() {
+ return `<!DOCTYPE HTML>
+ <html>
+ <head>
+ <title>Hello World</title>
+ </head>
+ <body>
+ <script></script>
+ </body>
+ </html>`;
+}
+
+function handleRequest(request, response) {
+ var params = new URLSearchParams(request.queryString);
+ var action = params.get("action");
+
+ if (action === "create-blocked-iframe") {
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/html; charset=utf-8", false);
+ response.write(createBlockedIframePage());
+ return;
+ }
+
+ if (action === "redirect") {
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/html; charset=utf-8", false);
+ response.write(createPage());
+ return;
+ }
+
+ if (action === "reporturl") {
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/html; charset=utf-8", false);
+ response.write(createPage());
+ return;
+ }
+
+ if (action === "create-blocked-redirect") {
+ params.delete("action");
+ params.append("action", "redirect");
+ response.setStatusLine("1.1", 302, "found");
+ response.setHeader(
+ "Location",
+ "https://" + REDIRECT + params.toString(),
+ false
+ );
+ return;
+ }
+
+ response.write("I don't know action " + action);
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/sandboxed.html b/toolkit/components/url-classifier/tests/mochitest/sandboxed.html
new file mode 100644
index 0000000000..3eb8870edd
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/sandboxed.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<body>
+<script src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/evil.js">
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/sandboxed.html^headers^ b/toolkit/components/url-classifier/tests/mochitest/sandboxed.html^headers^
new file mode 100644
index 0000000000..4705ce9ded
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/sandboxed.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: sandbox allow-scripts;
diff --git a/toolkit/components/url-classifier/tests/mochitest/seek.webm b/toolkit/components/url-classifier/tests/mochitest/seek.webm
new file mode 100644
index 0000000000..72b0297233
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/seek.webm
Binary files differ
diff --git a/toolkit/components/url-classifier/tests/mochitest/sw_register.html b/toolkit/components/url-classifier/tests/mochitest/sw_register.html
new file mode 100644
index 0000000000..2a536128fa
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/sw_register.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<script>
+function waitForState(worker, state, context) {
+ return new Promise(resolve => {
+ function onStateChange() {
+ if (worker.state === state) {
+ worker.removeEventListener("statechange", onStateChange);
+ resolve(context);
+ }
+ }
+
+ // First add an event listener, so we won't miss any change that happens
+ // before we check the current state.
+ worker.addEventListener("statechange", onStateChange);
+
+ // Now check if the worker is already in the desired state.
+ onStateChange();
+ });
+}
+
+(async function () {
+ dump("[Dimi]register sw...\n");
+ let reg = await navigator.serviceWorker.register("sw_worker.js", {scope: "."});
+ await waitForState(reg.installing, "activated", reg);
+ window.parent.postMessage({status: "registrationdone"}, "*");
+})();
+
+</script>
+</head>
+<html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/sw_unregister.html b/toolkit/components/url-classifier/tests/mochitest/sw_unregister.html
new file mode 100644
index 0000000000..b94fa0baac
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/sw_unregister.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.opener.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
diff --git a/toolkit/components/url-classifier/tests/mochitest/sw_worker.js b/toolkit/components/url-classifier/tests/mochitest/sw_worker.js
new file mode 100644
index 0000000000..bad16608b4
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/sw_worker.js
@@ -0,0 +1,10 @@
+self.addEventListener("fetch", function (event) {
+ let sep = "synth.html?";
+ let idx = event.request.url.indexOf(sep);
+ if (idx > 0) {
+ let url = event.request.url.substring(idx + sep.length);
+ event.respondWith(fetch(url, { credentials: "include" }));
+ } else {
+ event.respondWith(fetch(event.request));
+ }
+});
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_advisory_link.html b/toolkit/components/url-classifier/tests/mochitest/test_advisory_link.html
new file mode 100644
index 0000000000..5fa49f628e
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_advisory_link.html
@@ -0,0 +1,138 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Test advisory link (Bug #1366384)</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+const {TestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/TestUtils.sys.mjs"
+);
+const {BrowserTestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/BrowserTestUtils.sys.mjs"
+);
+
+var mainWindow = window.browsingContext.topChromeWindow;
+
+var testDatas = [
+ { url: "itisaphishingsite.org/phishing.html",
+ list: "mochi1-phish-simple",
+ provider: "google",
+ },
+
+ { url: "fakeitisaphishingsite.org/phishing.html",
+ list: "fake1-phish-simple",
+ provider: "mozilla",
+ },
+
+ { url: "phishing.example.com/test.html",
+ list: "mochi2-phish-simple",
+ provider: "google4",
+ },
+];
+
+let pushPrefs = (...p) => SpecialPowers.pushPrefEnv({set: p});
+
+function addUrlToDB(list, url) {
+ let testData = [{ db: list, url}];
+
+ return classifierHelper.addUrlToDB(testData)
+ .catch(function(err) {
+ ok(false, "Couldn't update classifier. Error code: " + err);
+ // Abort test.
+ SimpleTest.finish();
+ });
+}
+
+function setupTestData(data) {
+ let promises = [];
+ let providerList = "browser.safebrowsing.provider." + data.provider + ".lists";
+ promises.push(pushPrefs([providerList, data.list]));
+
+ let activeTablePref = "urlclassifier.phishTable";
+ let activeTable = SpecialPowers.getCharPref(activeTablePref);
+ activeTable += "," + data.list;
+ promises.push(pushPrefs([activeTablePref, activeTable]));
+
+ promises.push(addUrlToDB(data.list, data.url));
+ return Promise.all(promises);
+}
+
+function testOnWindow(aTestData) {
+ return new Promise(resolve => {
+ let win = mainWindow.OpenBrowserWindow();
+
+ (async function() {
+ await TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win);
+
+ let browser = win.gBrowser.selectedBrowser;
+ BrowserTestUtils.startLoadingURIString(browser, aTestData.url);
+ await BrowserTestUtils.waitForContentEvent(browser, "DOMContentLoaded");
+
+ let doc = win.gBrowser.contentDocument;
+
+ // This test works on a document which uses Fluent.
+ // Fluent localization may finish later than DOMContentLoaded,
+ // so let's wait for `document.l10n.ready` promise to resolve.
+ await doc.l10n.ready;
+ let advisoryEl = doc.getElementById("advisory_provider");
+ if (aTestData.provider != "google" && aTestData.provider != "google4") {
+ ok(!advisoryEl, "Advisory should not be shown");
+ } else {
+ ok(advisoryEl, "Advisory element should exist");
+ let expectedUrl =
+ SpecialPowers.getCharPref("browser.safebrowsing.provider." +
+ aTestData.provider +
+ ".advisoryURL");
+ is(advisoryEl.href, expectedUrl, "Correct advisory url");
+ let expectedText =
+ SpecialPowers.getCharPref("browser.safebrowsing.provider." +
+ aTestData.provider +
+ ".advisoryName");
+ is(advisoryEl.text, expectedText, "correct advisory text");
+ }
+
+ win.close();
+ resolve();
+ })();
+ });
+}
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["browser.safebrowsing.phishing.enabled", true]]},
+ test);
+
+function test() {
+ (async function() {
+ await classifierHelper.waitForInit();
+
+ for (let testData of testDatas) {
+ await setupTestData(testData);
+ await testOnWindow(testData);
+ await classifierHelper._cleanup();
+ }
+
+ SimpleTest.finish();
+ })();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_allowlisted_annotations.html b/toolkit/components/url-classifier/tests/mochitest/test_allowlisted_annotations.html
new file mode 100644
index 0000000000..bb512e7cdc
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_allowlisted_annotations.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the URI Classifier</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+
+var Cc = SpecialPowers.Cc;
+var Ci = SpecialPowers.Ci;
+
+const {UrlClassifierTestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/UrlClassifierTestUtils.sys.mjs"
+);
+
+// Add https://allowlisted.example.com to the permissions manager
+SpecialPowers.addPermission("trackingprotection",
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ { url: "https://allowlisted.example.com" });
+
+async function clearPermissions() {
+ await SpecialPowers.removePermission("trackingprotection",
+ { url: "https://allowlisted.example.com" });
+ ok(!await SpecialPowers.testPermission("trackingprotection",
+ Ci.nsIPermissionManager.ALLOW_ACTION,
+ { url: "https://allowlisted.example.com" }));
+}
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["urlclassifier.trackingTable", "moztest-track-simple"],
+ ["privacy.trackingprotection.enabled", true],
+ ["privacy.trackingprotection.testing.report_blocked_node", true],
+ ["channelclassifier.allowlist_example", true],
+ ["dom.security.skip_remote_script_assertion_in_system_priv_context", true]]},
+ test);
+
+function test() {
+ SimpleTest.registerCleanupFunction(UrlClassifierTestUtils.cleanupTestTrackers);
+ UrlClassifierTestUtils.addTestTrackers().then(() => {
+ document.getElementById("testFrame").src = "allowlistAnnotatedFrame.html";
+ });
+}
+
+// Expected finish() call is in "allowlistedAnnotatedFrame.html".
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_annotation_vs_TP.html b/toolkit/components/url-classifier/tests/mochitest/test_annotation_vs_TP.html
new file mode 100644
index 0000000000..ad8e98005b
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_annotation_vs_TP.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the relationship between annotation vs TP</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="features.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<script class="testbody" type="text/javascript">
+
+runTests(SpecialPowers.Ci.nsIClassifiedChannel.CLASSIFIED_TRACKING,
+ [
+ ["privacy.trackingprotection.enabled", true],
+ ["privacy.trackingprotection.annotate_channels", true],
+ ["urlclassifier.features.fingerprinting.annotate.blacklistTables", ""],
+ ["urlclassifier.features.fingerprinting.annotate.blacklistHosts", ""],
+ ["privacy.trackingprotection.fingerprinting.enabled", false],
+ ["urlclassifier.features.cryptomining.annotate.blacklistTables", ""],
+ ["urlclassifier.features.cryptomining.annotate.blacklistHosts", ""],
+ ["privacy.trackingprotection.cryptomining.enabled", false],
+ ["urlclassifier.features.socialtracking.annotate.blacklistTables", ""],
+ ["urlclassifier.features.socialtracking.annotate.blacklistHosts", ""],
+ ],
+ true /* a tracking resource */);
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_bug1254766.html b/toolkit/components/url-classifier/tests/mochitest/test_bug1254766.html
new file mode 100644
index 0000000000..2e528a7242
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_bug1254766.html
@@ -0,0 +1,265 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1272239 - Test gethash.</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script src="head.js"></script>
+<script class="testbody" type="text/javascript">
+const MALWARE_LIST = "mochi-malware-simple";
+const MALWARE_HOST1 = "malware.example.com/";
+const MALWARE_HOST2 = "test1.example.com/";
+
+const UNWANTED_LIST = "mochi-unwanted-simple";
+const UNWANTED_HOST1 = "unwanted.example.com/";
+const UNWANTED_HOST2 = "test2.example.com/";
+
+
+const UNUSED_MALWARE_HOST = "unused.malware.com/";
+const UNUSED_UNWANTED_HOST = "unused.unwanted.com/";
+
+const GETHASH_URL =
+ "http://mochi.test:8888/tests/toolkit/components/url-classifier/tests/mochitest/gethash.sjs";
+
+var gPreGethashCounter = 0;
+var gCurGethashCounter = 0;
+
+var expectLoad = false;
+
+function loadTestFrame() {
+ return new Promise(function(resolve, reject) {
+ var iframe = document.createElement("iframe");
+ iframe.setAttribute("src", "gethashFrame.html");
+ document.body.appendChild(iframe);
+
+ iframe.onload = function() {
+ document.body.removeChild(iframe);
+ resolve();
+ };
+ }).then(getGethashCounter);
+}
+
+function getGethashCounter() {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest;
+ xhr.open("PUT", GETHASH_URL + "?gethashcount");
+ xhr.setRequestHeader("Content-Type", "text/plain");
+ xhr.onreadystatechange = function() {
+ if (this.readyState == this.DONE) {
+ gPreGethashCounter = gCurGethashCounter;
+ gCurGethashCounter = parseInt(xhr.response);
+ resolve();
+ }
+ };
+ xhr.send();
+ });
+}
+
+// setup function allows classifier send gethash request for test database
+// also it calculate to fullhash for url and store those hashes in gethash sjs.
+async function setup() {
+ await classifierHelper.allowCompletion([MALWARE_LIST, UNWANTED_LIST], GETHASH_URL);
+
+ return Promise.all([
+ addCompletionToServer(MALWARE_LIST, MALWARE_HOST1, GETHASH_URL),
+ addCompletionToServer(MALWARE_LIST, MALWARE_HOST2, GETHASH_URL),
+ addCompletionToServer(UNWANTED_LIST, UNWANTED_HOST1, GETHASH_URL),
+ addCompletionToServer(UNWANTED_LIST, UNWANTED_HOST2, GETHASH_URL),
+ ]);
+}
+
+// Reset function in helper try to simulate the behavior we restart firefox
+function reset() {
+ return classifierHelper.resetDatabase()
+ .catch(err => {
+ ok(false, "Couldn't update classifier. Error code: " + err);
+ // Abort test.
+ SimpleTest.finish();
+ });
+}
+
+function updateUnusedUrl() {
+ var testData = [
+ { url: UNUSED_MALWARE_HOST, db: MALWARE_LIST },
+ { url: UNUSED_UNWANTED_HOST, db: UNWANTED_LIST },
+ ];
+
+ return classifierHelper.addUrlToDB(testData)
+ .catch(err => {
+ ok(false, "Couldn't update classifier. Error code: " + err);
+ // Abort test.
+ SimpleTest.finish();
+ });
+}
+
+function addPrefixToDB() {
+ return update(true);
+}
+
+function addCompletionToDB() {
+ return update(false);
+}
+
+function update(prefix = false) {
+ var length = prefix ? 4 : 32;
+ var testData = [
+ { url: MALWARE_HOST1, db: MALWARE_LIST, len: length },
+ { url: MALWARE_HOST2, db: MALWARE_LIST, len: length },
+ { url: UNWANTED_HOST1, db: UNWANTED_LIST, len: length },
+ { url: UNWANTED_HOST2, db: UNWANTED_LIST, len: length },
+ ];
+
+ return classifierHelper.addUrlToDB(testData)
+ .catch(err => {
+ ok(false, "Couldn't update classifier. Error code: " + err);
+ // Abort test.
+ SimpleTest.finish();
+ });
+}
+
+// This testcase is to make sure gethash works:
+// 1. Add prefixes to DB.
+// 2. Load test frame contains malware & unwanted url, those urls should be blocked.
+// 3. The second step should also trigger a gethash request since completions is not in
+// either cache or DB.
+// 4. Load test frame again, since completions is stored in cache now, no gethash
+// request should be triggered.
+function testGethash() {
+ return Promise.resolve()
+ .then(addPrefixToDB)
+ .then(loadTestFrame)
+ .then(() => {
+ ok(gCurGethashCounter > gPreGethashCounter, "Gethash request is triggered.");
+})
+ .then(loadTestFrame)
+ .then(() => {
+ ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is not triggered.");
+})
+ .then(reset);
+}
+
+// This testcae is to make sure completions in update works:
+// 1. Add completions to DB.
+// 2. Load test frame, since completions is stored in DB, gethash request should
+// not be triggered.
+function testUpdate() {
+ return Promise.resolve()
+ .then(addCompletionToDB)
+ .then(loadTestFrame)
+ .then(() => {
+ ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is not triggered.");
+})
+ .then(reset);
+}
+
+// This testcase is to make sure an update request will not clear completions in DB:
+// 1. Add completions to DB.
+// 2. Load test frame to make sure completions is stored in database, in this case, gethash
+// should not be triggered.
+// 3. Trigger an update, cache is cleared, but completions in DB should still remain.
+// 4. Load test frame again, since completions is in DB, gethash request should not be triggered.
+function testUpdateNotClearCompletions() {
+ return Promise.resolve()
+ .then(addCompletionToDB)
+ .then(loadTestFrame)
+ .then(() => {
+ ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is not triggered.");
+})
+ .then(updateUnusedUrl)
+ .then(loadTestFrame)
+ .then(() => {
+ ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is not triggered.");
+})
+ .then(reset);
+}
+
+// This testcase is to make sure completion store in DB will properly load after restarting.
+// 1. Add completions to DB.
+// 2. Simulate firefox restart by calling reloadDatabase.
+// 3. Load test frame, since completions should be loaded from DB, no gethash request should
+// be triggered.
+function testUpdateCompletionsAfterReload() {
+ return Promise.resolve()
+ .then(addCompletionToDB)
+ .then(classifierHelper.reloadDatabase)
+ // Call getTables to ensure the DB is fully reloaded before we load the test
+ // frame.
+ .then(classifierHelper.getTables)
+ .then(loadTestFrame)
+ .then(() => {
+ ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is not triggered.");
+})
+ .then(reset);
+}
+
+// This testcase is to make sure cache will be cleared after restarting
+// 1. Add prefixes to DB.
+// 2. Load test frame, this should trigger a gethash request and completions will be stored in
+// cache.
+// 3. Load test frame again, no gethash should be triggered because of cache.
+// 4. Simulate firefox restart by calling reloadDatabase.
+// 5. Load test frame again, since cache is cleared, gethash request should be triggered.
+function testGethashCompletionsAfterReload() {
+ return Promise.resolve()
+ .then(addPrefixToDB)
+ .then(loadTestFrame)
+ .then(() => {
+ ok(gCurGethashCounter > gPreGethashCounter, "Gethash request is triggered.");
+})
+ .then(loadTestFrame)
+ .then(() => {
+ ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is not triggered.");
+})
+ .then(classifierHelper.reloadDatabase)
+ // Call getTables to ensure the DB is fully reloaded before we load the test
+ // frame.
+ .then(classifierHelper.getTables)
+ .then(loadTestFrame)
+ .then(() => {
+ ok(gCurGethashCounter > gPreGethashCounter, "Gethash request is triggered.");
+})
+ .then(reset);
+}
+
+function runTest() {
+ Promise.resolve()
+ .then(classifierHelper.waitForInit)
+ .then(setup)
+ .then(testGethash)
+ .then(testUpdate)
+ .then(testUpdateNotClearCompletions)
+ .then(testUpdateCompletionsAfterReload)
+ .then(testGethashCompletionsAfterReload)
+ .then(function() {
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ SimpleTest.finish();
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+
+// 'network.predictor.enabled' is disabled because if other testcase load
+// evil.js, evil.css ...etc resources, it may cause we load them from cache
+// directly and bypass classifier check
+SpecialPowers.pushPrefEnv({"set": [
+ ["browser.safebrowsing.malware.enabled", true],
+ ["urlclassifier.malwareTable", "mochi-malware-simple,mochi-unwanted-simple"],
+ ["network.predictor.enabled", false],
+ ["urlclassifier.gethash.timeout_ms", 30000],
+]}, runTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_cachemiss.html b/toolkit/components/url-classifier/tests/mochitest/test_cachemiss.html
new file mode 100644
index 0000000000..2af0285884
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_cachemiss.html
@@ -0,0 +1,167 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1272239 - Test gethash.</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script src="head.js"></script>
+<script class="testbody" type="text/javascript">
+const MALWARE_LIST = "test-malware-simple";
+const MALWARE_HOST = "malware.example.com/";
+
+const UNWANTED_LIST = "test-unwanted-simple";
+const UNWANTED_HOST = "unwanted.example.com/";
+
+const GETHASH_URL = "http://mochi.test:8888/tests/toolkit/components/url-classifier/tests/mochitest/cache.sjs";
+
+var shouldLoad = false;
+
+var gPreGethashCounter = 0;
+var gCurGethashCounter = 0;
+
+function loadTestFrame() {
+ return new Promise(function(resolve, reject) {
+ var iframe = document.createElement("iframe");
+ iframe.setAttribute("src", "gethashFrame.html");
+ document.body.appendChild(iframe);
+
+ iframe.onload = function() {
+ document.body.removeChild(iframe);
+ resolve();
+ };
+ }).then(getGethashCounter);
+}
+
+function getGethashCounter() {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest;
+ xhr.open("PUT", GETHASH_URL + "?gethashcount");
+ xhr.setRequestHeader("Content-Type", "text/plain");
+ xhr.onreadystatechange = function() {
+ if (this.readyState == this.DONE) {
+ gPreGethashCounter = gCurGethashCounter;
+ gCurGethashCounter = parseInt(xhr.response);
+ resolve();
+ }
+ };
+ xhr.send();
+ });
+}
+
+// add 4-bytes prefixes to local database, so when we access the url,
+// it will trigger gethash request.
+function addPrefixToDB(list, url) {
+ var testData = [{ db: list, url, len: 4 }];
+
+ return classifierHelper.addUrlToDB(testData)
+ .catch(function(err) {
+ ok(false, "Couldn't update classifier. Error code: " + err);
+ // Abort test.
+ SimpleTest.finish();
+ });
+}
+
+// manually reset DB to make sure next test won't be affected by cache.
+function reset() {
+ return classifierHelper.resetDatabase();
+}
+
+// This test has to come before testPositiveCache to ensure gethash server doesn't
+// contain completions.
+function testNegativeCache() {
+ SpecialPowers.DOMWindowUtils.clearSharedStyleSheetCache();
+ shouldLoad = true;
+
+ async function setup() {
+ await classifierHelper.allowCompletion([MALWARE_LIST, UNWANTED_LIST], GETHASH_URL);
+
+ // Only add prefix to database. not server, so gethash will not return
+ // result.
+ return Promise.all([
+ addPrefixToDB(MALWARE_LIST, MALWARE_HOST),
+ addPrefixToDB(UNWANTED_LIST, UNWANTED_HOST),
+ ]);
+ }
+
+ return Promise.resolve()
+ .then(setup)
+ .then(() => loadTestFrame())
+ .then(() => {
+ ok(gCurGethashCounter > gPreGethashCounter, "Gethash request is triggered.");
+})
+ // Second load should not trigger gethash request because cache.
+ .then(() => loadTestFrame())
+ .then(() => {
+ ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is nottriggered.");
+})
+ .then(reset);
+}
+
+function testPositiveCache() {
+ SpecialPowers.DOMWindowUtils.clearSharedStyleSheetCache();
+ shouldLoad = false;
+
+ async function setup() {
+ await classifierHelper.allowCompletion([MALWARE_LIST, UNWANTED_LIST], GETHASH_URL);
+
+ return Promise.all([
+ addPrefixToDB(MALWARE_LIST, MALWARE_HOST),
+ addPrefixToDB(UNWANTED_LIST, UNWANTED_HOST),
+ addCompletionToServer(MALWARE_LIST, MALWARE_HOST, GETHASH_URL),
+ addCompletionToServer(UNWANTED_LIST, UNWANTED_HOST, GETHASH_URL),
+ ]);
+ }
+
+ return Promise.resolve()
+ .then(setup)
+ .then(() => loadTestFrame())
+ .then(() => {
+ ok(gCurGethashCounter > gPreGethashCounter, "Gethash request is triggered.");
+})
+ // Second load should not trigger gethash request because cache.
+ .then(() => loadTestFrame())
+ .then(() => {
+ ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is nottriggered.");
+})
+ .then(reset);
+}
+
+function runTest() {
+ Promise.resolve()
+ // This test resources get blocked when gethash returns successfully
+ .then(classifierHelper.waitForInit)
+ .then(testNegativeCache)
+ .then(testPositiveCache)
+ .then(function() {
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ SimpleTest.finish();
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+
+// 'network.predictor.enabled' is disabled because if other testcase load
+// evil.js, evil.css ...etc resources, it may cause we load them from cache
+// directly and bypass classifier check
+SpecialPowers.pushPrefEnv({"set": [
+ ["browser.safebrowsing.malware.enabled", true],
+ ["urlclassifier.malwareTable", "test-malware-simple,test-unwanted-simple"],
+ ["network.predictor.enabled", false],
+ ["urlclassifier.gethash.timeout_ms", 30000],
+]}, runTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classified_annotations.html b/toolkit/components/url-classifier/tests/mochitest/test_classified_annotations.html
new file mode 100644
index 0000000000..44a0c92549
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classified_annotations.html
@@ -0,0 +1,171 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the URI Classifier</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+
+var mainWindow = window.browsingContext.topChromeWindow;
+var contentPage = "http://mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedFrame.html";
+
+var Cc = SpecialPowers.Cc;
+var Ci = SpecialPowers.Ci;
+
+const {UrlClassifierTestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/UrlClassifierTestUtils.sys.mjs"
+);
+const {TestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/TestUtils.sys.mjs"
+);
+
+function testOnWindow() {
+ return new Promise((resolve, reject) => {
+ let win = mainWindow.OpenBrowserWindow();
+ win.addEventListener("load", function() {
+ TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win).then(() => {
+ win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+ if (win.content.location.href != contentPage) {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ return;
+ }
+ win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+
+ win.content.addEventListener("OnLoadComplete", function innerLoad2(e) {
+ win.content.removeEventListener("OnLoadComplete", innerLoad2);
+ SimpleTest.executeSoon(function() {
+ checkLoads(win, JSON.parse(e.detail));
+ resolve(win);
+ });
+ }, false, true);
+ }, true);
+ SimpleTest.executeSoon(function() {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ });
+ });
+ }, {capture: true, once: true});
+ });
+}
+
+var badids = [
+ "badscript1",
+ "badscript2",
+ "badimage1",
+ "badimage2",
+ "badframe1",
+ "badframe2",
+ "badmedia1",
+ "badcss",
+];
+
+function checkLoads(aWindow, aData) {
+ var win = aWindow.content;
+
+ is(aData.scriptItem1, "untouched", "Should not load tracking javascript");
+ is(aData.scriptItem2, "untouched", "Should not load tracking javascript (2)");
+
+ is(aData.imageItem1, "untouched", "Should not load tracking images");
+ is(aData.imageItem2, "untouched", "Should not load tracking images (2)");
+
+ is(aData.frameItem1, "untouched", "Should not load tracking iframes");
+ is(aData.frameItem2, "untouched", "Should not load tracking iframes (2)");
+ is(aData.mediaItem1, "error", "Should not load tracking videos");
+ is(aData.xhrItem, "failed", "Should not load tracking XHRs");
+ is(aData.fetchItem, "error", "Should not fetch from tracking URLs");
+
+ var elt = win.document.getElementById("styleCheck");
+ var style = win.document.defaultView.getComputedStyle(elt);
+ isnot(style.visibility, "hidden", "Should not load tracking css");
+
+ is(win.document.blockedNodeByClassifierCount, badids.length,
+ "Should identify all tracking elements");
+
+ var blockedNodes = win.document.blockedNodesByClassifier;
+
+ // Make sure that every node in blockedNodes exists in the tree
+ // (that may not always be the case but do not expect any nodes to disappear
+ // from the tree here)
+ var allNodeMatch = true;
+ for (let i = 0; i < blockedNodes.length; i++) {
+ let nodeMatch = false;
+ for (let j = 0; j < badids.length && !nodeMatch; j++) {
+ nodeMatch = nodeMatch ||
+ (blockedNodes[i] == win.document.getElementById(badids[j]));
+ }
+
+ allNodeMatch = allNodeMatch && nodeMatch;
+ }
+ ok(allNodeMatch, "All annotated nodes are expected in the tree");
+
+ // Make sure that every node with a badid (see badids) is found in the
+ // blockedNodes. This tells us if we are neglecting to annotate
+ // some nodes
+ allNodeMatch = true;
+ for (let j = 0; j < badids.length; j++) {
+ let nodeMatch = false;
+ for (let i = 0; i < blockedNodes.length && !nodeMatch; i++) {
+ nodeMatch = nodeMatch ||
+ (blockedNodes[i] == win.document.getElementById(badids[j]));
+ }
+
+ if (!nodeMatch) {
+ console.log(badids[j] + " was not found in blockedNodes");
+ }
+ allNodeMatch = allNodeMatch && nodeMatch;
+ }
+ ok(allNodeMatch, "All tracking nodes are expected to be annotated as such");
+
+ // End (parent) test.
+}
+
+function cleanup() {
+ SpecialPowers.clearUserPref("privacy.trackingprotection.enabled");
+ SpecialPowers.clearUserPref("channelclassifier.allowlist_example");
+ SpecialPowers.clearUserPref("privacy.trackingprotection.testing.report_blocked_node");
+
+ UrlClassifierTestUtils.cleanupTestTrackers();
+}
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["urlclassifier.trackingTable", "moztest-track-simple"]]},
+ test);
+
+async function test() {
+ SimpleTest.registerCleanupFunction(cleanup);
+ await UrlClassifierTestUtils.addTestTrackers();
+
+ await SpecialPowers.setBoolPref("privacy.trackingprotection.enabled", true);
+ // Make sure chrome:// URIs are processed. This does not white-list
+ // any URIs unless 'https://allowlisted.example.com' is added in the
+ // permission manager (see test_allowlisted_annotations.html)
+ await SpecialPowers.setBoolPref("channelclassifier.allowlist_example", true);
+ await SpecialPowers.setBoolPref("privacy.trackingprotection.testing.report_blocked_node", true);
+
+ await testOnWindow().then(function(aWindow) {
+ aWindow.close();
+ });
+
+ SimpleTest.finish();
+}
+
+// Expected finish() call is in "classifiedAnnotatedFrame.html".
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classifier.html b/toolkit/components/url-classifier/tests/mochitest/test_classifier.html
new file mode 100644
index 0000000000..787b70798a
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classifier.html
@@ -0,0 +1,141 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the URI Classifier</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+var firstLoad = true;
+
+// Add some URLs to the malware database.
+var testData = [
+ { url: "malware.mochi.test/",
+ db: "test-malware-simple",
+ },
+ { url: "unwanted.mochi.test/",
+ db: "test-unwanted-simple",
+ },
+ { url: "blocked.mochi.test/",
+ db: "test-block-simple",
+ },
+ { url: "harmful.mochi.test/",
+ db: "test-harmful-simple",
+ },
+ { url: "phish.mochi.test/firefox/its-a-trap.html",
+ db: "test-phish-simple",
+ },
+];
+
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+const Cr = SpecialPowers.Cr;
+
+var testURLs = [
+ { url: "http://mochi.test",
+ table: "",
+ result: Cr.NS_OK,
+ },
+ { url: "http://malware.mochi.test",
+ table: "test-malware-simple",
+ result: Cr.NS_ERROR_MALWARE_URI,
+ },
+ { url: "http://unwanted.mochi.test",
+ table: "test-unwanted-simple",
+ result: Cr.NS_ERROR_UNWANTED_URI,
+ },
+ { url: "http://phish.mochi.test/firefox/its-a-trap.html",
+ table: "test-phish-simple",
+ result: Cr.NS_ERROR_PHISHING_URI,
+ },
+ { url: "http://harmful.mochi.test",
+ table: "test-harmful-simple",
+ result: Cr.NS_ERROR_HARMFUL_URI,
+ },
+ { url: "http://blocked.mochi.test",
+ table: "test-block-simple",
+ result: Cr.NS_ERROR_BLOCKED_URI,
+ },
+];
+
+function loadTestFrame() {
+ document.getElementById("testFrame").src = "classifierFrame.html";
+}
+
+// Expected finish() call is in "classifierFrame.html".
+SimpleTest.waitForExplicitFinish();
+
+function updateSuccess() {
+ return SpecialPowers.pushPrefEnv(
+ {"set": [["browser.safebrowsing.malware.enabled", true]]});
+}
+
+function updateError(errorCode) {
+ ok(false, "Couldn't update classifier. Error code: " + errorCode);
+ // Abort test.
+ SimpleTest.finish();
+}
+
+function testService() {
+ return new Promise(resolve => {
+ function runNextTest() {
+ if (!testURLs.length) {
+ resolve();
+ return;
+ }
+ let test = testURLs.shift();
+ let tables = "test-malware-simple,test-unwanted-simple,test-phish-simple,test-block-simple,test-harmful-simple";
+ let uri = SpecialPowers.Services.io.newURI(test.url);
+ let prin = SpecialPowers.Services.scriptSecurityManager.createContentPrincipal(uri, {});
+ SpecialPowers.doUrlClassify(prin, function(errorCode) {
+ is(errorCode, test.result,
+ `Successful asynchronous classification of ${test.url}`);
+ SpecialPowers.doUrlClassifyLocal(uri, tables, function(results) {
+ if (!results.length) {
+ is(test.table, "",
+ `Successful asynchronous local classification of ${test.url}`);
+ } else {
+ let result = results[0].QueryInterface(Ci.nsIUrlClassifierFeatureResult);
+ is(result.list, test.table,
+ `Successful asynchronous local classification of ${test.url}`);
+ }
+ runNextTest();
+ });
+ });
+ }
+ runNextTest(resolve);
+ });
+}
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["urlclassifier.malwareTable", "test-malware-simple,test-unwanted-simple,test-harmful-simple"],
+ ["urlclassifier.blockedTable", "test-block-simple"],
+ ["urlclassifier.phishTable", "test-phish-simple"],
+ ["browser.safebrowsing.phishing.enabled", true],
+ ["browser.safebrowsing.malware.enabled", true],
+ ["browser.safebrowsing.blockedURIs.enabled", true],
+ ["browser.safebrowsing.debug", true]]},
+ function() {
+ classifierHelper.waitForInit()
+ .then(() => classifierHelper.addUrlToDB(testData))
+ .then(updateSuccess)
+ .catch(err => {
+ updateError(err);
+ })
+ .then(testService)
+ .then(loadTestFrame);
+ });
+
+</script>
+
+</pre>
+<iframe id="testFrame" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref.html b/toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref.html
new file mode 100644
index 0000000000..ea16c14e74
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref.html
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1281083 - Changing the urlclassifier.*Table prefs doesn't take effect before the next browser restart.</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script src="head.js"></script>
+<script class="testbody" type="text/javascript">
+
+var mainWindow = window.browsingContext.topChromeWindow;
+var contentPage = "http://mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/bug_1281083.html";
+
+const testTable = "moz-track-digest256";
+const UPDATE_URL = "http://mochi.test:8888/tests/toolkit/components/url-classifier/tests/mochitest/update.sjs";
+
+var Cc = SpecialPowers.Cc;
+var Ci = SpecialPowers.Ci;
+
+const {TestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/TestUtils.sys.mjs"
+);
+
+var timer = Cc["@mozilla.org/timer;1"]
+ .createInstance(Ci.nsITimer);
+
+// If default preference contain the table we want to test,
+// We should change test table to a different one.
+var trackingTables = SpecialPowers.getCharPref("urlclassifier.trackingTable").split(",");
+ok(!trackingTables.includes(testTable), "test table should not be in the preference");
+
+var listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"].
+ getService(Ci.nsIUrlListManager);
+
+is(listmanager.getGethashUrl(testTable), "",
+ "gethash url for test table should be empty before setting to preference");
+
+function checkLoads(aWindow, aBlocked) {
+ var win = aWindow.content;
+ is(win.document.getElementById("badscript").dataset.touched, aBlocked ? "no" : "yes", "Should not load tracking javascript");
+}
+
+function testOnWindow() {
+ return new Promise((resolve, reject) => {
+ let win = mainWindow.OpenBrowserWindow();
+ win.addEventListener("load", function() {
+ TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win).then(() => {
+ win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+ if (win.content.location.href != contentPage) {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ return;
+ }
+ win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+
+ win.content.addEventListener("load", function innerLoad2(e) {
+ win.content.removeEventListener("load", innerLoad2);
+ SimpleTest.executeSoon(function() {
+ resolve(win);
+ });
+ }, false, true);
+ }, true);
+ SimpleTest.executeSoon(function() {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ });
+ });
+ }, {capture: true, once: true});
+ });
+}
+
+function setup() {
+ return new Promise(function(resolve, reject) {
+ // gethash url of test table "moz-track-digest256" should be updated
+ // after setting preference.
+ var url = listmanager.getGethashUrl(testTable);
+ var expected = SpecialPowers.getCharPref("browser.safebrowsing.provider.mozilla.gethashURL");
+
+ is(url, expected, testTable + " matches its gethash url");
+
+ // Trigger update
+ listmanager.disableUpdate(testTable);
+ listmanager.enableUpdate(testTable);
+ listmanager.maybeToggleUpdateChecking();
+
+ // We wait until "nextupdattime" was set as a signal that update is complete.
+ waitForUpdateSuccess(function() {
+ resolve();
+ });
+ });
+}
+
+function waitForUpdateSuccess(callback) {
+ let nextupdatetime =
+ SpecialPowers.getCharPref("browser.safebrowsing.provider.mozilla.nextupdatetime");
+
+ if (nextupdatetime !== "1") {
+ callback();
+ return;
+ }
+
+ timer.initWithCallback(function() {
+ waitForUpdateSuccess(callback);
+ }, 10, Ci.nsITimer.TYPE_ONE_SHOT);
+}
+
+async function runTest() {
+ // To make sure url is not blocked by an already blocked url.
+ // Here we use non-tracking.example.com as a tracked url.
+ // Since this table is only used in this bug, so it won't affect other testcases.
+ await addCompletionToServer(testTable, "bug1281083.example.com/", UPDATE_URL);
+
+ /**
+ * In this test we try to modify only urlclassifier.*Table preference to see if
+ * url specified in the table will be blocked after update.
+ */
+ await SpecialPowers.pushPrefEnv(
+ {"set": [["urlclassifier.trackingTable", testTable]]});
+
+ await setup();
+
+ await testOnWindow().then(function(aWindow) {
+ checkLoads(aWindow, true);
+ aWindow.close();
+ });
+
+ SimpleTest.finish();
+}
+
+// Set nextupdatetime to 1 to trigger an update
+SpecialPowers.pushPrefEnv(
+ {"set": [["privacy.trackingprotection.enabled", true],
+ ["channelclassifier.allowlist_example", true],
+ ["browser.safebrowsing.provider.mozilla.nextupdatetime", "1"],
+ ["browser.safebrowsing.provider.mozilla.lists", testTable],
+ ["browser.safebrowsing.provider.mozilla.updateURL", UPDATE_URL]]},
+ runTest);
+
+// Expected finish() call is in "bug_1281083.html".
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref_bug1395411.html b/toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref_bug1395411.html
new file mode 100644
index 0000000000..df3ac79d8a
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classifier_changetablepref_bug1395411.html
@@ -0,0 +1,72 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Bug 1395411 - Changing the urlclassifier.*Table prefs doesn't remove them from the update checker.</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+var Cc = SpecialPowers.Cc;
+var Ci = SpecialPowers.Ci;
+
+const testTableV2 = "mochi1-phish-simple";
+const testTableV4 = "mochi1-phish-proto";
+const UPDATE_URL_V2 = "http://mochi.test:8888/tests/toolkit/components/url-classifier/dummyV2";
+const UPDATE_URL_V4 = "http://mochi.test:8888/tests/toolkit/components/url-classifier/dummyV4";
+
+let listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"].
+ getService(Ci.nsIUrlListManager);
+
+let pushPrefs = (...p) => SpecialPowers.pushPrefEnv({set: p});
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["browser.safebrowsing.phishing.enabled", true],
+ ["browser.safebrowsing.provider.mozilla.lists", testTableV2],
+ ["browser.safebrowsing.provider.mozilla4.lists", testTableV4],
+ ["browser.safebrowsing.provider.mozilla4.updateURL", UPDATE_URL_V4],
+ ["browser.safebrowsing.provider.mozilla.updateURL", UPDATE_URL_V2]]},
+ runTest);
+
+function runTest() {
+ (async function() {
+ await classifierHelper.waitForInit();
+
+ await pushPrefs(["urlclassifier.phishTable", testTableV2 + "," + testTableV4]);
+ is(listmanager.getUpdateUrl(testTableV4), UPDATE_URL_V4, "Correct update url v4");
+ is(listmanager.getUpdateUrl(testTableV2), UPDATE_URL_V2, "Correct update url v2");
+
+ await pushPrefs(["urlclassifier.phishTable", testTableV2]);
+ is(listmanager.getUpdateUrl(testTableV4), "", "Correct empty update url v4");
+ is(listmanager.getUpdateUrl(testTableV2), UPDATE_URL_V2, "Correct update url v2");
+
+ await pushPrefs(["urlclassifier.phishTable", testTableV4]);
+ is(listmanager.getUpdateUrl(testTableV4), UPDATE_URL_V4, "Correct update url v4");
+ is(listmanager.getUpdateUrl(testTableV2), "", "Correct empty update url v2");
+
+ await pushPrefs(["urlclassifier.phishTable", ""]);
+ is(listmanager.getUpdateUrl(testTableV4), "", "Correct empty update url v4");
+ is(listmanager.getUpdateUrl(testTableV2), "", "Correct empty update url v2");
+
+ await classifierHelper._cleanup();
+
+ SimpleTest.finish();
+ })();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classifier_match.html b/toolkit/components/url-classifier/tests/mochitest/test_classifier_match.html
new file mode 100644
index 0000000000..f6ea460be1
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classifier_match.html
@@ -0,0 +1,179 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the URI Classifier Matched Info (bug 1288633) </title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="application/javascript">
+var Cc = SpecialPowers.Cc;
+var Ci = SpecialPowers.Ci;
+var Cr = SpecialPowers.Cr;
+
+var inputDatas = [
+ { url: "malware.mochi.test/",
+ db: "mochi-block-simple",
+ },
+ { url: "malware1.mochi.test/",
+ db: "mochi1-block-simple",
+ },
+ { url: "malware1.mochi.test/",
+ db: "mochi1-malware-simple",
+ provider: "mozilla",
+ },
+ { url: "malware2.mochi.test/",
+ db: "mochi2-unwanted-simple",
+ provider: "mozilla",
+ },
+ { url: "malware2.mochi.test/",
+ db: "mochi2-malware-simple",
+ provider: "mozilla",
+ },
+ { url: "malware3.mochi.test/",
+ db: "mochig3-malware-simple",
+ provider: "google",
+ },
+ { url: "malware3.mochi.test/",
+ db: "mochim3-malware-simple",
+ provider: "mozilla",
+ },
+];
+
+function hash(str) {
+ let hasher = Cc["@mozilla.org/security/hash;1"]
+ .createInstance(Ci.nsICryptoHash);
+
+ let data = new TextEncoder().encode(str);
+ hasher.init(hasher.SHA256);
+ hasher.update(data, data.length);
+
+ return hasher.finish(false);
+}
+
+var testDatas = [
+ // Match empty provider
+ { url: "http://malware.mochi.test",
+ expect: { error: Cr.NS_ERROR_BLOCKED_URI,
+ table: "mochi-block-simple",
+ provider: "",
+ fullhash: (function() {
+ return hash("malware.mochi.test/");
+ })(),
+ },
+ },
+ // Match multiple tables, only one has valid provider
+ { url: "http://malware1.mochi.test",
+ expect: { error: Cr.NS_ERROR_MALWARE_URI,
+ table: "mochi1-malware-simple",
+ provider: "mozilla",
+ fullhash: (function() {
+ return hash("malware1.mochi.test/");
+ })(),
+ },
+ },
+ // Match multiple tables, handle order
+ { url: "http://malware2.mochi.test",
+ expect: { error: Cr.NS_ERROR_MALWARE_URI,
+ table: "mochi2-malware-simple",
+ provider: "mozilla",
+ fullhash: (function() {
+ return hash("malware2.mochi.test/");
+ })(),
+ },
+ },
+ // Match multiple tables, handle order
+ { url: "http://malware3.mochi.test",
+ expect: { error: Cr.NS_ERROR_MALWARE_URI,
+ table: "mochig3-malware-simple",
+ provider: "google",
+ fullhash: (function() {
+ return hash("malware3.mochi.test/");
+ })(),
+ },
+ },
+
+];
+
+SimpleTest.waitForExplicitFinish();
+
+function setupTestData(datas) {
+ let prefValues = {};
+ for (let data of datas) {
+ if (!data.provider) {
+ continue;
+ }
+ let providerPref = "browser.safebrowsing.provider." + data.provider + ".lists";
+ let prefValue;
+ if (!prefValues[providerPref]) {
+ prefValue = data.db;
+ } else {
+ prefValue = prefValues[providerPref] + "," + data.db;
+ }
+ prefValues[providerPref] = prefValue;
+ }
+
+ // Convert map to array
+ let prefArray = [];
+ for (var pref in prefValues) {
+ prefArray.push([pref, prefValues[pref]]);
+ }
+
+ let activeTablePref = "urlclassifier.malwareTable";
+ let activeTable = SpecialPowers.getCharPref(activeTablePref);
+ for (let data of datas) {
+ activeTable += "," + data.db;
+ }
+ prefArray.push([activeTablePref, activeTable]);
+
+ return SpecialPowers.pushPrefEnv({set: prefArray});
+}
+
+function runTest() {
+ return new Promise(resolve => {
+ function runNextTest() {
+ if (!testDatas.length) {
+ resolve();
+ return;
+ }
+ let test = testDatas.shift();
+ let uri = SpecialPowers.Services.io.newURI(test.url);
+ let prin = SpecialPowers.Services.scriptSecurityManager.createContentPrincipal(uri, {});
+ SpecialPowers.doUrlClassify(prin, function(errorCode, table, provider, fullhash) {
+ is(errorCode, test.expect.error, `Test url ${test.url} correct error`);
+ is(table, test.expect.table, `Test url ${test.url} correct table`);
+ is(provider, test.expect.provider, `Test url ${test.url} correct provider`);
+ is(fullhash, btoa(test.expect.fullhash), `Test url ${test.url} correct full hash`);
+ runNextTest();
+ });
+ }
+ runNextTest();
+ });
+}
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["browser.safebrowsing.malware.enabled", true]]},
+ function() {
+ classifierHelper.waitForInit()
+ .then(() => setupTestData(inputDatas))
+ .then(() => classifierHelper.addUrlToDB(inputDatas))
+ .then(runTest)
+ .then(function() {
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some tests failed with error " + e);
+ SimpleTest.finish();
+ });
+ });
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classifier_worker.html b/toolkit/components/url-classifier/tests/mochitest/test_classifier_worker.html
new file mode 100644
index 0000000000..0e984c2a64
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classifier_worker.html
@@ -0,0 +1,73 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the URI Classifier</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+// Add some URLs to the malware database.
+var testData = [
+ { url: "example.com/tests/toolkit/components/url-classifier/tests/mochitest/evilWorker.js",
+ db: "test-malware-simple",
+ },
+ { url: "example.com/tests/toolkit/components/url-classifier/tests/mochitest/unwantedWorker.js",
+ db: "test-unwanted-simple",
+ },
+];
+
+function loadTestFrame() {
+ document.getElementById("testFrame").src =
+ "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/workerFrame.html";
+}
+
+function onmessage(event) {
+ var pieces = event.data.split(":");
+ if (pieces[0] == "finish") {
+ SimpleTest.finish();
+ return;
+ }
+
+ is(pieces[0], "success", pieces[1]);
+}
+
+function updateSuccess() {
+ SpecialPowers.pushPrefEnv(
+ {"set": [["browser.safebrowsing.malware.enabled", true]]},
+ loadTestFrame);
+}
+
+function updateError(errorCode) {
+ ok(false, "Couldn't update classifier. Error code: " + errorCode);
+ // Abort test.
+ SimpleTest.finish();
+}
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["urlclassifier.malwareTable", "test-malware-simple,test-unwanted-simple"]]},
+ function() {
+ classifierHelper.waitForInit()
+ .then(() => classifierHelper.addUrlToDB(testData))
+ .then(updateSuccess)
+ .catch(err => {
+ updateError(err);
+ });
+ });
+
+window.addEventListener("message", onmessage);
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+<iframe id="testFrame" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classify_by_default.html b/toolkit/components/url-classifier/tests/mochitest/test_classify_by_default.html
new file mode 100644
index 0000000000..c10a0c62f9
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classify_by_default.html
@@ -0,0 +1,156 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1442496</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1442496">Mozilla Bug 1442496</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script type="text/javascript" src="classifierHelper.js"></script>
+<script class="testbody" type="text/javascript">
+
+// To add a request to test, add the request in trackerFrame.html
+// and the id of query string "?id=xxx" here.
+const trackersAll = [
+ "img-src",
+ "object-data",
+ "script-src",
+ "iframe-src",
+ "link-rel-stylesheet",
+ "video-src",
+ "track-src",
+ "ping",
+ "fetch",
+ "xmlhttprequest",
+ "send-beacon",
+ "fetch-in-sw",
+];
+
+const TRACKER_DOMAIN = "itisatracker.org";
+const TEST_TOP_DOMAIN = "example.com";
+
+const TEST_TOP_PAGE = "trackerFrame.html";
+const TRACKER_SERVER = "trackerFrame.sjs";
+
+const TEST_PATH = "/tests/toolkit/components/url-classifier/tests/mochitest/";
+
+const TEST_TOP_SITE = "https:///" + TEST_TOP_DOMAIN + TEST_PATH;
+const TRACKER_SITE = "https://" + TRACKER_DOMAIN + TEST_PATH;
+const TRACKER_SJS = "https://" + TRACKER_DOMAIN + TEST_PATH + TRACKER_SERVER;
+
+// This function ask the server to set the cookie
+async function setupAndRun(hasCookie, topLevelSite = TEST_TOP_SITE) {
+ // In order to apply the correct cookieBehavior, we need to first open a new
+ // window to setup cookies. So, the new window will use the correct
+ // cookieBehavior. Otherwise, it will use the default cookieBehavior.
+ let setup_win = window.open();
+ await setup_win.fetch(TRACKER_SJS + "?init=" + trackersAll.length, {
+ credentials: "include",
+ });
+ setup_win.close();
+
+ return new Promise(resolve => {
+ let win;
+ let query = hasCookie ? "with-cookie" : "without-cookie";
+ fetch(TRACKER_SJS + "?callback=" + query).then(r => {
+ r.text().then((body) => {
+ let trackers_found = body.split(",");
+ for (let tracker of trackersAll) {
+ let description = "Tracker request " + tracker + "received " +
+ (hasCookie ? "with" : "without") + " cookie";
+ ok(trackers_found.includes(tracker), description);
+ }
+ win.close();
+ resolve();
+ });
+ });
+
+ win = window.open(topLevelSite + TEST_TOP_PAGE);
+ });
+}
+
+async function cleanup(topLevelSite = TEST_TOP_SITE) {
+ function clearServiceWorker() {
+ return new Promise(resolve => {
+ let w;
+ window.onmessage = function(e) {
+ if (e.data.status == "unregistrationdone") {
+ w.close();
+ resolve();
+ }
+ }
+ w = window.open(TEST_TOP_SITE + "sw_unregister.html");
+ });
+ }
+
+ // Ensure we clear the stylesheet cache so that we re-classify.
+ SpecialPowers.DOMWindowUtils.clearSharedStyleSheetCache();
+
+ await clearServiceWorker();
+}
+
+async function runTests() {
+ await SpecialPowers.pushPrefEnv({set: [
+ ["urlclassifier.trackingAnnotationTable.testEntries", TRACKER_DOMAIN],
+ ["urlclassifier.trackingAnnotationWhitelistTable", "test-trackwhite-simple"],
+ ["network.cookie.cookieBehavior", 4],
+ ["privacy.trackingprotection.enabled", false ],
+ ["privacy.trackingprotection.annotate_channels", false],
+ ["browser.send_pings", true],
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]});
+
+ await classifierHelper.waitForInit();
+
+ let hasCookie;
+
+ info("Test all requests should be sent with cookies when ETP is off");
+ hasCookie = true;
+ await setupAndRun(hasCookie);
+ await cleanup();
+
+ info("Test all requests should be sent without cookies when ETP is on");
+ await SpecialPowers.pushPrefEnv({set: [
+ [ "privacy.trackingprotection.annotate_channels", true],
+ ]});
+
+ hasCookie = false;
+ await setupAndRun(hasCookie);
+ await cleanup();
+
+ info("Test all requests should be sent with cookies when top-level is in the whitelist");
+ await classifierHelper.addUrlToDB([{
+ url: TEST_TOP_DOMAIN + "/?resource=" + TRACKER_DOMAIN,
+ db: "test-trackwhite-simple",
+ }]);
+
+ hasCookie = true;
+ await setupAndRun(hasCookie);
+ await cleanup();
+
+ info("Test all requests should be sent with cookies when the tracker is a first-party");
+ hasCookie = true;
+ await setupAndRun(hasCookie, TRACKER_SITE);
+ await cleanup(TRACKER_SITE);
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+runTests();
+
+</script>
+
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classify_ping.html b/toolkit/components/url-classifier/tests/mochitest/test_classify_ping.html
new file mode 100644
index 0000000000..6a8a189ed2
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classify_ping.html
@@ -0,0 +1,131 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1233914 - ping doesn't honor the TP list here.</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+ SimpleTest.requestFlakyTimeout("Delay to make sure ping is made prior than XHR");
+
+ const timeout = 200;
+ const host_nottrack = "http://not-tracking.example.com/";
+ const host_track = "http://trackertest.org/";
+ const path_ping = "tests/toolkit/components/url-classifier/tests/mochitest/ping.sjs";
+ const TP_ENABLE_PREF = "privacy.trackingprotection.enabled";
+ const RETRY_TIMEOUT_MS = 200;
+
+ async function testPingNonBlocklist() {
+ await SpecialPowers.setBoolPref(TP_ENABLE_PREF, true);
+
+ var msg = "ping should reach page not in blacklist";
+ var expectPing = true;
+ var id = "1111";
+ ping(id, host_nottrack);
+
+ return new Promise(function(resolve, reject) {
+ // Retry at most 30 seconds.
+ isPingedWithRetry(id, expectPing, msg, resolve, 30 * 1000 / RETRY_TIMEOUT_MS);
+ });
+ }
+
+ async function testPingBlocklistSafebrowsingOff() {
+ await SpecialPowers.setBoolPref(TP_ENABLE_PREF, false);
+
+ var msg = "ping should reach page in blacklist when tracking protection is off";
+ var expectPing = true;
+ var id = "2222";
+ ping(id, host_track);
+
+ return new Promise(function(resolve, reject) {
+ // Retry at most 30 seconds.
+ isPingedWithRetry(id, expectPing, msg, resolve, 30 * 1000 / RETRY_TIMEOUT_MS);
+ });
+ }
+
+ async function testPingBlocklistSafebrowsingOn() {
+ await SpecialPowers.setBoolPref(TP_ENABLE_PREF, true);
+
+ var msg = "ping should not reach page in blacklist when tracking protection is on";
+ var expectPing = false;
+ var id = "3333";
+ ping(id, host_track);
+
+ return new Promise(function(resolve, reject) {
+ setTimeout(function() {
+ isPinged(id, expectPing, msg, resolve);
+ }, timeout);
+ });
+ }
+
+ function ping(id, host) {
+ var elm = document.createElement("a");
+ elm.setAttribute("ping", host + path_ping + "?id=" + id);
+ elm.setAttribute("href", "#");
+ document.body.appendChild(elm);
+
+ // Trigger ping.
+ elm.click();
+
+ document.body.removeChild(elm);
+ }
+
+ function isPingedWithRetry(id, expected, msg, callback, retryCnt) {
+ var url = "http://mochi.test:8888/" + path_ping;
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", url + "?id=" + id);
+ xhr.onload = function() {
+ let pinged = xhr.response === "ping";
+ let success = pinged === expected;
+ if (success || 0 === retryCnt) {
+ is(expected, pinged, msg);
+ callback();
+ return;
+ }
+ // Retry on failure.
+ setTimeout(() => {
+ isPingedWithRetry(id, expected, msg, callback, retryCnt - 1);
+ }, RETRY_TIMEOUT_MS);
+ };
+ xhr.send();
+ }
+
+ function isPinged(id, expected, msg, callback) {
+ isPingedWithRetry(id, expected, msg, callback, 0);
+ }
+
+ function cleanup() {
+ SpecialPowers.clearUserPref(TP_ENABLE_PREF);
+ }
+
+ function runTest() {
+ classifierHelper.waitForInit()
+ .then(testPingNonBlocklist)
+ .then(testPingBlocklistSafebrowsingOff)
+ .then(testPingBlocklistSafebrowsingOn)
+ .then(function() {
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.registerCleanupFunction(cleanup);
+ SpecialPowers.pushPrefEnv({"set": [
+ ["browser.send_pings", true],
+ ]}, runTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classify_top_sandboxed.html b/toolkit/components/url-classifier/tests/mochitest/test_classify_top_sandboxed.html
new file mode 100644
index 0000000000..7e3ae97751
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classify_top_sandboxed.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Bug 1647681</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+
+async function runTests() {
+ await SpecialPowers.pushPrefEnv({set: [
+ ["privacy.trackingprotection.annotate_channels", true],
+ ]});
+
+ var chromeScript;
+ chromeScript = SpecialPowers.loadChromeScript(_ => {
+ /* eslint-env mozilla/chrome-script */
+ function onExamResp(subject, topic, data) {
+ let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+ let classifiedChannel = subject.QueryInterface(Ci.nsIClassifiedChannel);
+ if (
+ !channel ||
+ !classifiedChannel ||
+ !channel.URI.spec.startsWith(
+ "https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"
+ )
+ ) {
+ return;
+ }
+
+ sendAsyncMessage("last-channel-flags", {
+ classified: Ci.nsIClassifiedChannel.CLASSIFIED_TRACKING == classifiedChannel.classificationFlags,
+ });
+ }
+
+ addMessageListener("done", __ => {
+ Services.obs.removeObserver(onExamResp, "http-on-examine-response");
+ });
+
+ Services.obs.addObserver(onExamResp, "http-on-examine-response");
+
+ sendAsyncMessage("start-test");
+ });
+
+ chromeScript.addMessageListener(
+ "start-test",
+ async _ => {
+ window.open("https://example.org/tests/toolkit/components/url-classifier/tests/mochitest/sandboxed.html")
+ },
+ { once: true }
+ );
+
+ chromeScript.addMessageListener(
+ "last-channel-flags",
+ data => {
+ ok(data.classified, "tracker script should be classified when the top-level is sandboxed");
+ chromeScript.sendAsyncMessage("done");
+ SimpleTest.finish();
+ },
+ { once: true }
+ );
+}
+
+SimpleTest.waitForExplicitFinish();
+runTests();
+
+ </script>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_classify_track.html b/toolkit/components/url-classifier/tests/mochitest/test_classify_track.html
new file mode 100644
index 0000000000..cbb6e67c78
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classify_track.html
@@ -0,0 +1,163 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1262406 - Track element doesn't use the URL classifier.</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+ const PREF = "browser.safebrowsing.malware.enabled";
+ const track_path = "tests/toolkit/components/url-classifier/tests/mochitest/basic.vtt";
+ const malware_url = "http://malware.example.com/" + track_path;
+ const validtrack_url = "http://mochi.test:8888/" + track_path;
+
+ var video = document.createElement("video");
+ video.src = "seek.webm";
+ video.crossOrigin = "anonymous";
+
+ document.body.appendChild(video);
+
+ function testValidTrack() {
+ SpecialPowers.setBoolPref(PREF, true);
+
+ return new Promise(function(resolve, reject) {
+ var track = document.createElement("track");
+ track.track.mode = "hidden";
+ track.src = validtrack_url;
+ video.appendChild(track);
+
+ function onload() {
+ ok(true, "Track should be loaded when url is not in blocklist");
+ finish();
+ }
+
+ function onerror() {
+ ok(false, "Error while loading track");
+ finish();
+ }
+
+ function finish() {
+ track.removeEventListener("load", onload);
+ track.removeEventListener("error", onerror);
+ resolve();
+ }
+
+ track.addEventListener("load", onload);
+ track.addEventListener("error", onerror);
+ });
+ }
+
+ function testBlocklistTrackSafebrowsingOff() {
+ SpecialPowers.setBoolPref(PREF, false);
+
+ return new Promise(function(resolve, reject) {
+ var track = document.createElement("track");
+ track.track.mode = "hidden";
+ track.src = malware_url;
+ video.appendChild(track);
+
+ function onload() {
+ ok(true, "Track should be loaded when url is in blocklist and safebrowsing is off");
+ finish();
+ }
+
+ function onerror() {
+ ok(false, "Error while loading track");
+ finish();
+ }
+
+ function finish() {
+ track.removeEventListener("load", onload);
+ track.removeEventListener("error", onerror);
+ resolve();
+ }
+
+ track.addEventListener("load", onload);
+ track.addEventListener("error", onerror);
+ });
+ }
+
+ function testBlocklistTrackSafebrowsingOn() {
+ SpecialPowers.setBoolPref(PREF, true);
+
+ return new Promise(function(resolve, reject) {
+ var track = document.createElement("track");
+ track.track.mode = "hidden";
+ // Add a query string parameter here to avoid url classifier bypass classify
+ // because of cache.
+ track.src = malware_url + "?testsbon";
+ video.appendChild(track);
+
+ function onload() {
+ ok(false, "Unexpected result while loading track in blocklist");
+ finish();
+ }
+
+ function onerror() {
+ ok(true, "Track should not be loaded when url is in blocklist and safebrowsing is on");
+ finish();
+ }
+
+ function finish() {
+ track.removeEventListener("load", onload);
+ track.removeEventListener("error", onerror);
+ resolve();
+ }
+
+ track.addEventListener("load", onload);
+ track.addEventListener("error", onerror);
+ });
+ }
+
+ function cleanup() {
+ SpecialPowers.clearUserPref(PREF);
+ }
+
+ function setup() {
+ var testData = [
+ { url: "malware.example.com/",
+ db: "test-malware-simple",
+ },
+ ];
+
+ return classifierHelper.addUrlToDB(testData)
+ .catch(function(err) {
+ ok(false, "Couldn't update classifier. Error code: " + err);
+ // Abort test.
+ SimpleTest.finish();
+ });
+ }
+
+ function runTest() {
+ Promise.resolve()
+ .then(classifierHelper.waitForInit)
+ .then(setup)
+ .then(testValidTrack)
+ .then(testBlocklistTrackSafebrowsingOff)
+ .then(testBlocklistTrackSafebrowsingOn)
+ .then(function() {
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.registerCleanupFunction(cleanup);
+ SpecialPowers.pushPrefEnv({"set": [
+ ["urlclassifier.malwareTable", "test-malware-simple"],
+ ]}, runTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_cryptomining.html b/toolkit/components/url-classifier/tests/mochitest/test_cryptomining.html
new file mode 100644
index 0000000000..0098d6c489
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_cryptomining.html
@@ -0,0 +1,100 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the cryptomining classifier</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<script class="testbody" type="text/javascript">
+
+var tests = [
+ // All disabled.
+ { config: [ false, false ], loadExpected: true },
+
+ // Just entitylisted.
+ { config: [ false, true ], loadExpected: true },
+
+ // Just blocklisted.
+ { config: [ true, false ], loadExpected: false },
+
+ // entitylist + blocklist: entitylist wins
+ { config: [ true, true ], loadExpected: true },
+];
+
+function prefValue(value, what) {
+ return value ? what : "";
+}
+
+async function runTest(test) {
+ await SpecialPowers.pushPrefEnv({set: [
+ [ "urlclassifier.features.cryptomining.blacklistHosts", prefValue(test.config[0], "example.com") ],
+ [ "urlclassifier.features.cryptomining.whitelistHosts", prefValue(test.config[1], "mochi.test,mochi.xorigin-test") ],
+ [ "urlclassifier.features.cryptomining.blacklistTables", prefValue(test.config[0], "mochitest1-track-simple") ],
+ [ "urlclassifier.features.cryptomining.whitelistTables", "" ],
+ [ "privacy.trackingprotection.enabled", false ],
+ [ "privacy.trackingprotection.annotate_channels", false ],
+ [ "privacy.trackingprotection.cryptomining.enabled", true ],
+ [ "privacy.trackingprotection.emailtracking.enabled", false ],
+ [ "privacy.trackingprotection.fingerprinting.enabled", false ],
+ [ "privacy.trackingprotection.socialtracking.enabled", false ],
+ ]});
+
+ info("Testing: " + JSON.stringify(test.config) + "\n");
+
+ // Let's load an image with a random query string, just to avoid network cache.
+ let result = await new Promise(resolve => {
+ let image = new Image();
+ image.src = "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random();
+ image.onload = _ => resolve(true);
+ image.onerror = _ => resolve(false);
+ });
+
+ is(result, test.loadExpected, "The loading happened correctly");
+
+ // Let's load an image with a random query string, just to avoid network cache.
+ result = await new Promise(resolve => {
+ let image = new Image();
+ image.src = "http://tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random();
+ image.onload = _ => resolve(true);
+ image.onerror = _ => resolve(false);
+ });
+
+ is(result, test.loadExpected, "The loading happened correctly (by table)");
+}
+
+async function runTests() {
+ let chromeScript = SpecialPowers.loadChromeScript(_ => {
+ /* eslint-env mozilla/chrome-script */
+ const {UrlClassifierTestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/UrlClassifierTestUtils.sys.mjs"
+ );
+
+ addMessageListener("loadTrackers", __ => {
+ return UrlClassifierTestUtils.addTestTrackers();
+ });
+
+ addMessageListener("unloadTrackers", __ => {
+ UrlClassifierTestUtils.cleanupTestTrackers();
+ });
+ });
+
+ await chromeScript.sendQuery("loadTrackers");
+
+ for (let test in tests) {
+ await runTest(tests[test]);
+ }
+
+ await chromeScript.sendQuery("unloadTrackers");
+
+ chromeScript.destroy();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+runTests();
+
+</script>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_cryptomining_annotate.html b/toolkit/components/url-classifier/tests/mochitest/test_cryptomining_annotate.html
new file mode 100644
index 0000000000..ecf5cd02a4
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_cryptomining_annotate.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the relationship between annotation vs blocking - cryptomining</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="features.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<script class="testbody" type="text/javascript">
+
+runTests(SpecialPowers.Ci.nsIClassifiedChannel.CLASSIFIED_CRYPTOMINING,
+ [
+ ["privacy.trackingprotection.enabled", false],
+ ["privacy.trackingprotection.annotate_channels", false],
+ ["urlclassifier.features.fingerprinting.annotate.blacklistHosts", ""],
+ ["urlclassifier.features.fingerprinting.annotate.blacklistTables", ""],
+ ["privacy.trackingprotection.fingerprinting.enabled", false],
+ ["privacy.trackingprotection.cryptomining.enabled", true],
+ ["urlclassifier.features.socialtracking.annotate.blacklistHosts", ""],
+ ["urlclassifier.features.socialtracking.annotate.blacklistTables", ""],
+ ["privacy.trackingprotection.socialtracking.enabled", false],
+ ["privacy.trackingprotection.emailtracking.enabled", false],
+ ],
+ false /* a tracking resource */);
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_donottrack.html b/toolkit/components/url-classifier/tests/mochitest/test_donottrack.html
new file mode 100644
index 0000000000..96278ae49d
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_donottrack.html
@@ -0,0 +1,141 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1258033 - Fix the DNT loophole for tracking protection</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+
+var mainWindow = window.browsingContext.topChromeWindow;
+
+const tests = [
+ // DNT turned on and TP turned off, DNT signal sent in both private browsing
+ // and normal mode.
+ {
+ setting: {dntPref: true, tpPref: false, tppbPref: false, pbMode: true},
+ expected: {dnt: "1"},
+ },
+ {
+ setting: {dntPref: true, tpPref: false, tppbPref: false, pbMode: false},
+ expected: {dnt: "1"},
+ },
+ // DNT turned off and TP turned on globally, DNT signal sent in both private
+ // browsing and normal mode.
+ {
+ setting: {dntPref: false, tpPref: true, tppbPref: false, pbMode: true},
+ expected: {dnt: "1"},
+ },
+ {
+ setting: {dntPref: false, tpPref: true, tppbPref: false, pbMode: false},
+ expected: {dnt: "1"},
+ },
+ // DNT turned off and TP in Private Browsing only, DNT signal sent in private
+ // browsing mode only.
+ {
+ setting: {dntPref: false, tpPref: false, tppbPref: true, pbMode: true},
+ expected: {dnt: "1"},
+ },
+ {
+ setting: {dntPref: false, tpPref: false, tppbPref: true, pbMode: false},
+ expected: {dnt: "unspecified"},
+ },
+ // DNT turned off and TP turned off, DNT signal is never sent.
+ {
+ setting: {dntPref: false, tpPref: false, tppbPref: false, pbMode: true},
+ expected: {dnt: "unspecified"},
+ },
+ {
+ setting: {dntPref: false, tpPref: false, tppbPref: false, pbMode: false},
+ expected: {dnt: "unspecified"},
+ },
+];
+
+const DNT_PREF = "privacy.donottrackheader.enabled";
+const TP_PREF = "privacy.trackingprotection.enabled";
+const TP_PB_PREF = "privacy.trackingprotection.pbmode.enabled";
+
+const contentPage =
+ "http://mochi.test:8888/tests/toolkit/components/url-classifier/tests/mochitest/dnt.html";
+
+const {TestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/TestUtils.sys.mjs"
+);
+
+function executeTest(test) {
+ SpecialPowers.pushPrefEnv({"set": [
+ [DNT_PREF, test.setting.dntPref],
+ [TP_PREF, test.setting.tpPref],
+ [TP_PB_PREF, test.setting.tppbPref],
+ ]});
+
+ var win = mainWindow.OpenBrowserWindow({private: test.setting.pbMode});
+
+ return new Promise(function(resolve, reject) {
+ win.addEventListener("load", function() {
+ TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win).then(() => {
+ win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+ if (win.content.location.href != contentPage) {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ return;
+ }
+
+ win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+
+ win.content.addEventListener("message", function(event) {
+ let [key, value] = event.data.split("=");
+ if (key == "finish") {
+ win.close();
+ resolve();
+ } else if (key == "navigator.doNotTrack") {
+ is(value, test.expected.dnt, "navigator.doNotTrack should be " + test.expected.dnt);
+ } else if (key == "DNT") {
+ let msg = test.expected.dnt == "1" ? "" : "not ";
+ is(value, test.expected.dnt, "DNT header should " + msg + "be sent");
+ } else {
+ ok(false, "unexpected message");
+ }
+ });
+ }, true);
+ SimpleTest.executeSoon(function() {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ });
+ });
+ }, {capture: true, once: true});
+ });
+}
+
+let loop = function loop(index) {
+ if (index >= tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ let test = tests[index];
+ let next = function next() {
+ loop(index + 1);
+ };
+ let result = executeTest(test);
+ result.then(next, next);
+};
+
+SimpleTest.waitForExplicitFinish();
+loop(0);
+
+</script>
+
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_emailtracking.html b/toolkit/components/url-classifier/tests/mochitest/test_emailtracking.html
new file mode 100644
index 0000000000..fd2996997c
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_emailtracking.html
@@ -0,0 +1,130 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the email tracking classifier</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<script class="testbody" type="text/javascript">
+
+var tests = [
+ // All disabled.
+ { config: [ false, false ], loadExpected: true },
+
+ // Just entitylisted.
+ { config: [ false, true ], loadExpected: true },
+
+ // Just blocklisted.
+ { config: [ true, false ], loadExpected: false },
+
+ // entitylist + blocklist: entitylist wins
+ { config: [ true, true ], loadExpected: true },
+];
+
+function prefValue(value, what) {
+ return value ? what : "";
+}
+
+async function runTest(test) {
+ await SpecialPowers.pushPrefEnv({set: [
+ [ "urlclassifier.features.emailtracking.blocklistHosts", prefValue(test.config[0], "example.com") ],
+ [ "urlclassifier.features.emailtracking.allowlistHosts", prefValue(test.config[1], "mochi.test,mochi.xorigin-test") ],
+ [ "urlclassifier.features.emailtracking.blocklistTables", prefValue(test.config[0], "mochitest5-track-simple") ],
+ [ "urlclassifier.features.emailtracking.allowlistTables", "" ],
+ [ "privacy.trackingprotection.enabled", false ],
+ [ "privacy.trackingprotection.annotate_channels", false ],
+ [ "privacy.trackingprotection.cryptomining.enabled", false ],
+ [ "privacy.trackingprotection.emailtracking.enabled", true ],
+ [ "privacy.trackingprotection.fingerprinting.enabled", false ],
+ [ "privacy.trackingprotection.socialtracking.enabled", false ],
+ ]});
+
+ info("Testing: " + JSON.stringify(test.config) + "\n");
+
+ // Let's load an image with a random query string to avoid network cache.
+ let result = await new Promise(resolve => {
+ let image = new Image();
+ image.src = "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random();
+ image.onload = _ => resolve(true);
+ image.onerror = _ => resolve(false);
+ });
+
+ is(result, test.loadExpected, "Image loading happened correctly");
+
+ // Let's load an image with a random query string to avoid network cache.
+ result = await new Promise(resolve => {
+ let image = new Image();
+ image.src = "http://email-tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random();
+ image.onload = _ => resolve(true);
+ image.onerror = _ => resolve(false);
+ });
+
+ is(result, test.loadExpected, "Image loading happened correctly (by table)");
+
+ // Let's load a script with a random query string to avoid network cache.
+ result = await new Promise(resolve => {
+ let script = document.createElement("script");
+ script.setAttribute(
+ "src",
+ "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js?" +
+ Math.random()
+ );
+ script.onload = _ => resolve(true);
+ script.onerror = _ => resolve(false);
+ document.body.appendChild(script);
+ });
+
+ is(result, test.loadExpected, "Script loading happened correctly");
+
+ // Let's load a script with a random query string to avoid network cache.
+ result = await new Promise(resolve => {
+ let script = document.createElement("script");
+ script.setAttribute(
+ "src",
+ "http://email-tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/evil.js?" +
+ Math.random()
+ );
+ script.onload = _ => resolve(true);
+ script.onerror = _ => resolve(false);
+ document.body.appendChild(script);
+ });
+
+ is(result, test.loadExpected, "Script loading happened correctly (by table)");
+}
+
+async function runTests() {
+ let chromeScript = SpecialPowers.loadChromeScript(_ => {
+ /* eslint-env mozilla/chrome-script */
+ const {UrlClassifierTestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/UrlClassifierTestUtils.sys.mjs"
+ );
+
+ addMessageListener("loadTrackers", __ => {
+ return UrlClassifierTestUtils.addTestTrackers();
+ });
+
+ addMessageListener("unloadTrackers", __ => {
+ UrlClassifierTestUtils.cleanupTestTrackers();
+ });
+ });
+
+ await chromeScript.sendQuery("loadTrackers");
+
+ for (let test in tests) {
+ await runTest(tests[test]);
+ }
+
+ await chromeScript.sendQuery("unloadTrackers");
+
+ chromeScript.destroy();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+runTests();
+
+</script>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting.html b/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting.html
new file mode 100644
index 0000000000..81e6f9a466
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting.html
@@ -0,0 +1,130 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the fingerprinting classifier</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<script class="testbody" type="text/javascript">
+
+var tests = [
+ // All disabled.
+ { config: [ false, false ], imgLoadExpected: true, scriptLoadExpected: true },
+
+ // Just entitylisted.
+ { config: [ false, true ], imgLoadExpected: true, scriptLoadExpected: true },
+
+ // Just blocklisted.
+ { config: [ true, false ], imgLoadExpected: true, scriptLoadExpected: false },
+
+ // entitylist + blocklist: entitylist wins
+ { config: [ true, true ], imgLoadExpected: true, scriptLoadExpected: true },
+];
+
+function prefValue(value, what) {
+ return value ? what : "";
+}
+
+async function runTest(test) {
+ await SpecialPowers.pushPrefEnv({set: [
+ [ "urlclassifier.features.fingerprinting.blacklistHosts", prefValue(test.config[0], "example.com") ],
+ [ "urlclassifier.features.fingerprinting.whitelistHosts", prefValue(test.config[1], "mochi.test,mochi.xorigin-test") ],
+ [ "urlclassifier.features.fingerprinting.blacklistTables", prefValue(test.config[0], "mochitest1-track-simple") ],
+ [ "urlclassifier.features.fingerprinting.whitelistTables", "" ],
+ [ "privacy.trackingprotection.enabled", false ],
+ [ "privacy.trackingprotection.annotate_channels", false ],
+ [ "privacy.trackingprotection.cryptomining.enabled", false ],
+ [ "privacy.trackingprotection.emailtracking.enabled", false ],
+ [ "privacy.trackingprotection.fingerprinting.enabled", true ],
+ [ "privacy.trackingprotection.socialtracking.enabled", false ],
+ ]});
+
+ info("Testing: " + JSON.stringify(test.config) + "\n");
+
+ // Let's load an image with a random query string to avoid network cache.
+ let result = await new Promise(resolve => {
+ let image = new Image();
+ image.src = "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random();
+ image.onload = _ => resolve(true);
+ image.onerror = _ => resolve(false);
+ });
+
+ is(result, test.imgLoadExpected, "Image loading happened correctly");
+
+ // Let's load an image with a random query string to avoid network cache.
+ result = await new Promise(resolve => {
+ let image = new Image();
+ image.src = "http://tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random();
+ image.onload = _ => resolve(true);
+ image.onerror = _ => resolve(false);
+ });
+
+ is(result, test.imgLoadExpected, "Image loading happened correctly (by table)");
+
+ // Let's load a script with a random query string to avoid network cache.
+ result = await new Promise(resolve => {
+ let script = document.createElement("script");
+ script.setAttribute(
+ "src",
+ "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js?" +
+ Math.random()
+ );
+ script.onload = _ => resolve(true);
+ script.onerror = _ => resolve(false);
+ document.body.appendChild(script);
+ });
+
+ is(result, test.scriptLoadExpected, "Script loading happened correctly");
+
+ // Let's load a script with a random query string to avoid network cache.
+ result = await new Promise(resolve => {
+ let script = document.createElement("script");
+ script.setAttribute(
+ "src",
+ "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js?" +
+ Math.random()
+ );
+ script.onload = _ => resolve(true);
+ script.onerror = _ => resolve(false);
+ document.body.appendChild(script);
+ });
+
+ is(result, test.scriptLoadExpected, "Script loading happened correctly");
+}
+
+async function runTests() {
+ let chromeScript = SpecialPowers.loadChromeScript(_ => {
+ /* eslint-env mozilla/chrome-script */
+ const {UrlClassifierTestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/UrlClassifierTestUtils.sys.mjs"
+ );
+
+ addMessageListener("loadTrackers", __ => {
+ return UrlClassifierTestUtils.addTestTrackers();
+ });
+
+ addMessageListener("unloadTrackers", __ => {
+ UrlClassifierTestUtils.cleanupTestTrackers();
+ });
+ });
+
+ await chromeScript.sendQuery("loadTrackers");
+
+ for (let test in tests) {
+ await runTest(tests[test]);
+ }
+
+ await chromeScript.sendQuery("unloadTrackers");
+
+ chromeScript.destroy();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+runTests();
+
+</script>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting_annotate.html b/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting_annotate.html
new file mode 100644
index 0000000000..0bc01645b1
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_fingerprinting_annotate.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the relationship between annotation vs blocking - fingerprinting</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="features.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<script class="testbody" type="text/javascript">
+
+runTests(SpecialPowers.Ci.nsIClassifiedChannel.CLASSIFIED_FINGERPRINTING,
+ [
+ ["privacy.trackingprotection.enabled", false],
+ ["privacy.trackingprotection.annotate_channels", false],
+ ["privacy.trackingprotection.fingerprinting.enabled", true],
+ ["urlclassifier.features.cryptomining.annotate.blacklistHosts", ""],
+ ["urlclassifier.features.cryptomining.annotate.blacklistTables", ""],
+ ["privacy.trackingprotection.cryptomining.enabled", false],
+ ["urlclassifier.features.socialtracking.annotate.blacklistHosts", ""],
+ ["urlclassifier.features.socialtracking.annotate.blacklistTables", ""],
+ ["privacy.trackingprotection.socialtracking.enabled", false],
+ ["privacy.trackingprotection.emailtracking.enabled", false],
+ ],
+ true /* a tracking resource */);
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_gethash.html b/toolkit/components/url-classifier/tests/mochitest/test_gethash.html
new file mode 100644
index 0000000000..b9d3a2fd44
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_gethash.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1272239 - Test gethash.</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<iframe id="testFrame1" onload=""></iframe>
+<iframe id="testFrame2" onload=""></iframe>
+
+<script src="head.js"></script>
+<script class="testbody" type="text/javascript">
+const MALWARE_LIST = "test-malware-simple";
+const MALWARE_HOST = "malware.example.com/";
+
+const UNWANTED_LIST = "test-unwanted-simple";
+const UNWANTED_HOST = "unwanted.example.com/";
+
+const GETHASH_URL = "http://mochi.test:8888/tests/toolkit/components/url-classifier/tests/mochitest/gethash.sjs";
+const NOTEXIST_URL = "http://mochi.test:8888/tests/toolkit/components/url-classifier/tests/mochitest/nonexistserver.sjs";
+
+var shouldLoad = false;
+
+// In this testcase we store prefixes to localdb and send the fullhash to gethash server.
+// When access the test page gecko should trigger gethash request to server and
+// get the completion response.
+function loadTestFrame(id) {
+ return new Promise(function(resolve, reject) {
+ var iframe = document.getElementById(id);
+ iframe.setAttribute("src", "gethashFrame.html");
+
+ iframe.onload = function() {
+ resolve();
+ };
+ });
+}
+
+// add 4-bytes prefixes to local database, so when we access the url,
+// it will trigger gethash request.
+function addPrefixToDB(list, url) {
+ var testData = [{ db: list, url, len: 4 }];
+
+ return classifierHelper.addUrlToDB(testData)
+ .catch(function(err) {
+ ok(false, "Couldn't update classifier. Error code: " + err);
+ // Abort test.
+ SimpleTest.finish();
+ });
+}
+
+function setup404() {
+ shouldLoad = true;
+
+ return Promise.all([
+ classifierHelper.allowCompletion(
+ [MALWARE_LIST, UNWANTED_LIST], NOTEXIST_URL),
+ addPrefixToDB(MALWARE_LIST, MALWARE_HOST),
+ addPrefixToDB(UNWANTED_LIST, UNWANTED_HOST),
+ ]);
+}
+
+function setup() {
+ return Promise.all([
+ classifierHelper.allowCompletion(
+ [MALWARE_LIST, UNWANTED_LIST], GETHASH_URL),
+ addPrefixToDB(MALWARE_LIST, MALWARE_HOST),
+ addPrefixToDB(UNWANTED_LIST, UNWANTED_HOST),
+ addCompletionToServer(MALWARE_LIST, MALWARE_HOST, GETHASH_URL),
+ addCompletionToServer(UNWANTED_LIST, UNWANTED_HOST, GETHASH_URL),
+ ]);
+}
+
+// manually reset DB to make sure next test won't be affected by cache.
+function reset() {
+ return classifierHelper.resetDatabase();
+}
+
+function runTest() {
+ Promise.resolve()
+ // This test resources get blocked when gethash returns successfully
+ .then(classifierHelper.waitForInit)
+ .then(setup)
+ .then(() => loadTestFrame("testFrame1"))
+ .then(reset)
+ // This test resources are not blocked when gethash returns an error
+ .then(setup404)
+ .then(() => loadTestFrame("testFrame2"))
+ .then(function() {
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ SimpleTest.finish();
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+
+// 'network.predictor.enabled' is disabled because if other testcase load
+// evil.js, evil.css ...etc resources, it may cause we load them from cache
+// directly and bypass classifier check
+SpecialPowers.pushPrefEnv({"set": [
+ ["browser.safebrowsing.malware.enabled", true],
+ ["urlclassifier.malwareTable", "test-malware-simple,test-unwanted-simple"],
+ ["network.predictor.enabled", false],
+ ["urlclassifier.gethash.timeout_ms", 30000],
+]}, runTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_privatebrowsing_trackingprotection.html b/toolkit/components/url-classifier/tests/mochitest/test_privatebrowsing_trackingprotection.html
new file mode 100644
index 0000000000..0959ecf42e
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_privatebrowsing_trackingprotection.html
@@ -0,0 +1,153 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Test Tracking Protection in Private Browsing mode</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+
+var mainWindow = window.browsingContext.topChromeWindow;
+var contentPage = "https://www.itisatrap.org/chrome/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html";
+
+const {UrlClassifierTestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/UrlClassifierTestUtils.sys.mjs"
+);
+const {TestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/TestUtils.sys.mjs"
+);
+
+function testOnWindow(aPrivate) {
+ return new Promise((resolve, reject) => {
+ let win = mainWindow.OpenBrowserWindow({private: aPrivate});
+ win.addEventListener("load", function() {
+ TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win).then(() => {
+ win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+ if (win.content.location.href != contentPage) {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ return;
+ }
+ win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+
+ win.content.addEventListener("load", function innerLoad2() {
+ win.content.removeEventListener("load", innerLoad2);
+ SimpleTest.executeSoon(function() {
+ resolve(win);
+ });
+ }, false, true);
+ }, true);
+ SimpleTest.executeSoon(function() {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ });
+ });
+ }, {capture: true, once: true});
+ });
+}
+
+var badids = [
+ "badscript",
+ "badimage",
+ "badcss",
+];
+
+function checkLoads(aWindow, aBlocked) {
+ var win = aWindow.content;
+ is(win.document.getElementById("badscript").dataset.touched, aBlocked ? "no" : "yes", "Should not load tracking javascript");
+ is(win.document.getElementById("badimage").dataset.touched, aBlocked ? "no" : "yes", "Should not load tracking images");
+ is(win.document.getElementById("goodscript").dataset.touched, "yes", "Should load entitylisted tracking javascript");
+ is(win.document.getElementById("goodimage").dataset.touched, "yes", "Should load non-blocklisted image");
+
+ var elt = win.document.getElementById("styleCheck");
+ var style = win.document.defaultView.getComputedStyle(elt);
+ isnot(style.visibility, aBlocked ? "hidden" : "", "Should not load tracking css");
+
+ is(win.document.blockedNodeByClassifierCount, aBlocked ? badids.length : 0, "Should identify all tracking elements");
+
+ var blockedNodes = win.document.blockedNodesByClassifier;
+
+ // Make sure that every node in blockedNodes exists in the tree
+ // (that may not always be the case but do not expect any nodes to disappear
+ // from the tree here)
+ var allNodeMatch = true;
+ for (let i = 0; i < blockedNodes.length; i++) {
+ let nodeMatch = false;
+ for (let j = 0; j < badids.length && !nodeMatch; j++) {
+ nodeMatch = nodeMatch ||
+ (blockedNodes[i] == win.document.getElementById(badids[j]));
+ }
+
+ allNodeMatch = allNodeMatch && nodeMatch;
+ }
+ is(allNodeMatch, true, "All annotated nodes are expected in the tree");
+
+ // Make sure that every node with a badid (see badids) is found in the
+ // blockedNodes. This tells us if we are neglecting to annotate
+ // some nodes
+ allNodeMatch = true;
+ for (let j = 0; j < badids.length; j++) {
+ let nodeMatch = false;
+ for (let i = 0; i < blockedNodes.length && !nodeMatch; i++) {
+ nodeMatch = nodeMatch ||
+ (blockedNodes[i] == win.document.getElementById(badids[j]));
+ }
+
+ allNodeMatch = allNodeMatch && nodeMatch;
+ }
+ is(allNodeMatch, aBlocked, "All tracking nodes are expected to be annotated as such");
+}
+
+SpecialPowers.pushPrefEnv(
+ {"set": [
+ ["privacy.trackingprotection.enabled", false],
+ ["privacy.trackingprotection.pbmode.enabled", true],
+ ["privacy.trackingprotection.testing.report_blocked_node", true],
+ ]}, test);
+
+async function test() {
+ SimpleTest.registerCleanupFunction(UrlClassifierTestUtils.cleanupTestTrackers);
+ await UrlClassifierTestUtils.addTestTrackers();
+
+ // Normal mode, with the pref (trackers should be loaded)
+ await testOnWindow(false).then(function(aWindow) {
+ checkLoads(aWindow, false);
+ aWindow.close();
+ });
+
+ // Private Browsing, with the pref (trackers should be blocked)
+ await testOnWindow(true).then(function(aWindow) {
+ checkLoads(aWindow, true);
+ aWindow.close();
+ });
+
+ // Private Browsing, without the pref (trackers should be loaded)
+ await SpecialPowers.setBoolPref("privacy.trackingprotection.pbmode.enabled", false);
+ await testOnWindow(true).then(function(aWindow) {
+ checkLoads(aWindow, false);
+ aWindow.close();
+ });
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_reporturl.html b/toolkit/components/url-classifier/tests/mochitest/test_reporturl.html
new file mode 100644
index 0000000000..36d128cdf7
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_reporturl.html
@@ -0,0 +1,217 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Test report matched URL info (Bug #1288633)</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+const {BrowserTestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/BrowserTestUtils.sys.mjs"
+);
+;
+const {TestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/TestUtils.sys.mjs"
+);
+
+var mainWindow = window.browsingContext.topChromeWindow;
+const SJS = "mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs";
+const BASE_URL = "http://" + SJS + "?";
+
+var pushPrefs = (...p) => SpecialPowers.pushPrefEnv({set: p});
+
+function addUrlToDB(list, url) {
+ let testData = [{ db: list, url}];
+
+ return classifierHelper.addUrlToDB(testData)
+ .catch(function(err) {
+ ok(false, "Couldn't update classifier. Error code: " + err);
+ // Abort test.
+ SimpleTest.finish();
+ });
+}
+
+function setupTestData(data) {
+ let promises = [];
+ let providerList = "browser.safebrowsing.provider." + data.provider + ".lists";
+ if (!Services.prefs.prefHasUserValue(providerList)) {
+ promises.push(pushPrefs([providerList, data.list]));
+ } else {
+ let pref = SpecialPowers.getCharPref(providerList);
+ pref += "," + data.list;
+ promises.push(pushPrefs([providerList, pref]));
+ }
+
+ let activeTablePref = "urlclassifier.phishTable";
+ let activeTable = SpecialPowers.getCharPref(activeTablePref);
+ activeTable += "," + data.list;
+ promises.push(pushPrefs([activeTablePref, activeTable]));
+
+ promises.push(addUrlToDB(data.list, data.testUrl));
+ return Promise.all(promises);
+}
+
+function testOnWindow(aTestData, aCallback, aTestCreater) {
+ return new Promise(resolve => {
+ let win = mainWindow.OpenBrowserWindow();
+
+ (async function() {
+ await TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win);
+
+ let browser = win.gBrowser.selectedBrowser;
+ aTestCreater(win, browser, aTestData.topUrl, aTestData.testUrl);
+
+ let notification = await BrowserTestUtils.waitForNotificationBar(win.gBrowser, browser, "blocked-badware-page");
+ ok(notification, "Notification box should be displayed");
+
+ let buttons = notification.buttonContainer.getElementsByTagName("button");
+ let button = buttons[1];
+ if (aTestData.provider != "google" && aTestData.provider != "google4") {
+ is(button, undefined, "Report button should not be showed");
+ win.close();
+ resolve();
+ return;
+ }
+
+ button.click();
+
+ let newTabBrowser = win.gBrowser.selectedTab.linkedBrowser;
+ await BrowserTestUtils.browserLoaded(newTabBrowser);
+
+ aCallback(newTabBrowser);
+ win.close();
+ resolve();
+ })();
+ });
+}
+
+var createBlockedIframe = function(aWindow, aBrowser, aTopUrl, aUrl) {
+ (async function() {
+ BrowserTestUtils.startLoadingURIString(aBrowser, aTopUrl);
+ await BrowserTestUtils.browserLoaded(aBrowser);
+
+ await SpecialPowers.spawn(aBrowser, [aUrl], async function(url) {
+ return new Promise(resolve => {
+ let listener = e => {
+ docShell.chromeEventHandler.removeEventListener("AboutBlockedLoaded", listener, false, true);
+ resolve();
+ };
+ docShell.chromeEventHandler.addEventListener("AboutBlockedLoaded", listener, false, true);
+ let frame = content.document.getElementById("phishingFrame");
+ frame.setAttribute("src", "http://" + url);
+ });
+ });
+
+ let doc = aWindow.gBrowser.contentDocument.getElementsByTagName("iframe")[0].contentDocument;
+ let ignoreWarningLink = doc.getElementById("ignore_warning_link");
+ ok(ignoreWarningLink, "Ignore warning link should exist");
+ ignoreWarningLink.click();
+ })();
+};
+
+var createBlockedPage = function(aWindow, aBrowser, aTopUrl, aUrl) {
+ (async function() {
+ BrowserTestUtils.startLoadingURIString(aBrowser, aTopUrl);
+ await BrowserTestUtils.waitForContentEvent(aBrowser, "DOMContentLoaded");
+
+ let doc = aWindow.gBrowser.contentDocument;
+ let ignoreWarningLink = doc.getElementById("ignore_warning_link");
+ ok(ignoreWarningLink, "Ignore warning link should exist");
+ ignoreWarningLink.click();
+ })();
+};
+
+function checkReportURL(aReportBrowser, aUrl) {
+ let expectedReportUrl = BASE_URL + "action=reporturl&reporturl=" + encodeURIComponent(aUrl);
+ is(aReportBrowser.contentDocument.location.href, expectedReportUrl, "Correct report URL");
+}
+
+var testDatas = [
+ { topUrl: "http://itisaphishingsite.org/phishing.html",
+ testUrl: "itisaphishingsite.org/phishing.html",
+ list: "mochi1-phish-simple",
+ provider: "google",
+ blockCreater: createBlockedPage,
+ expectedReportUri: "http://itisaphishingsite.org/phishing.html",
+ },
+
+ // Non-google provider, no report button is showed.
+ // Test provider needs a valid update URL (mozilla for example) otherwise
+ // the updates inserting the test data will fail.
+ { topUrl: "http://fakeitisaphishingsite.org/phishing.html",
+ testUrl: "fakeitisaphishingsite.org/phishing.html",
+ list: "fake-phish-simple",
+ provider: "mozilla",
+ blockCreater: createBlockedPage,
+ },
+
+ // Iframe case:
+ // A top level page at
+ // http://mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs?action=create-blocked-iframe
+ // contains an iframe to http://phishing.example.com/test.html (blocked).
+
+ { topUrl: "http://mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs?action=create-blocked-iframe",
+ testUrl: "phishing.example.com/test.html",
+ list: "mochi2-phish-simple",
+ provider: "google4",
+ blockCreater: createBlockedIframe,
+ expectedReportUri: "http://phishing.example.com/test.html",
+ },
+
+ // Redirect case:
+ // A top level page at
+ // http://prefixexample.com/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs?action=create-blocked-redirect (blocked)
+ // will get redirected to
+ // https://mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs?action=create-blocked-redirect.
+ { topUrl: "http://prefixexample.com/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs?action=create-blocked-redirect",
+ testUrl: "prefixexample.com/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs?action=create-blocked-redirect",
+ list: "mochi3-phish-simple",
+ provider: "google4",
+ blockCreater: createBlockedPage,
+ expectedReportUri: "http://prefixexample.com/chrome/toolkit/components/url-classifier/tests/mochitest/report.sjs",
+ },
+
+];
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["browser.safebrowsing.provider.google.reportPhishMistakeURL", BASE_URL + "action=reporturl&reporturl="],
+ ["browser.safebrowsing.provider.google4.reportPhishMistakeURL", BASE_URL + "action=reporturl&reporturl="],
+ ["browser.safebrowsing.phishing.enabled", true]]},
+ test);
+
+function test() {
+ (async function() {
+ await classifierHelper.waitForInit();
+
+ for (let testData of testDatas) {
+ await setupTestData(testData);
+ await testOnWindow(testData, function(browser) {
+ checkReportURL(browser, testData.expectedReportUri);
+ }, testData.blockCreater);
+
+ await classifierHelper._cleanup();
+ }
+
+ SimpleTest.finish();
+ })();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_safebrowsing_bug1272239.html b/toolkit/components/url-classifier/tests/mochitest/test_safebrowsing_bug1272239.html
new file mode 100644
index 0000000000..475145591d
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_safebrowsing_bug1272239.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1272239 - Only tables with provider could register gethash url in listmanager.</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+
+var Cc = SpecialPowers.Cc;
+var Ci = SpecialPowers.Ci;
+
+// List all the tables
+const prefs = [
+ "urlclassifier.phishTable",
+ "urlclassifier.malwareTable",
+ "urlclassifier.downloadBlockTable",
+ "urlclassifier.downloadAllowTable",
+ "urlclassifier.trackingTable",
+ "urlclassifier.trackingWhitelistTable",
+ "urlclassifier.blockedTable",
+];
+
+// Get providers
+var providers = {};
+
+var branch = SpecialPowers.Services.prefs.getBranch("browser.safebrowsing.provider.");
+var children = branch.getChildList("");
+
+for (var child of children) {
+ var prefComponents = child.split(".");
+ var providerName = prefComponents[0];
+ providers[providerName] = {};
+}
+
+// Get lists from |browser.safebrowsing.provider.PROVIDER_NAME.lists| preference.
+var listsWithProvider = [];
+var listsToProvider = [];
+for (let provider in providers) {
+ let pref = "browser.safebrowsing.provider." + provider + ".lists";
+ let list = SpecialPowers.getCharPref(pref).split(",");
+
+ listsToProvider = listsToProvider.concat(list.map( () => { return provider; }));
+ listsWithProvider = listsWithProvider.concat(list);
+}
+
+// Get all the lists
+var lists = [];
+for (let pref of prefs) {
+ lists = lists.concat(SpecialPowers.getCharPref(pref).split(","));
+}
+
+var listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"].
+ getService(Ci.nsIUrlListManager);
+
+let googleKey = SpecialPowers.Services.urlFormatter.formatURL("%GOOGLE_SAFEBROWSING_API_KEY%").trim();
+
+for (let list of lists) {
+ if (!list)
+ continue;
+
+ // For lists having a provider, it should have a correct gethash url
+ // For lists without a provider, for example, moztest-malware-simple, it should not
+ // have a gethash url.
+ var url = listmanager.getGethashUrl(list);
+ var index = listsWithProvider.indexOf(list);
+ if (index >= 0) {
+ let provider = listsToProvider[index];
+ let pref = "browser.safebrowsing.provider." + provider + ".gethashURL";
+ if ((provider == "google" || provider == "google4") &&
+ (!googleKey || googleKey == "no-google-safebrowsing-api-key")) {
+ is(url, "", "getHash url of " + list + " should be empty");
+ } else {
+ is(url, SpecialPowers.getCharPref(pref), list + " matches its gethash url");
+ }
+ } else {
+ is(url, "", list + " should not have a gethash url");
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_socialtracking.html b/toolkit/components/url-classifier/tests/mochitest/test_socialtracking.html
new file mode 100644
index 0000000000..ef1114686f
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_socialtracking.html
@@ -0,0 +1,109 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the socialtracking classifier</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<script class="testbody" type="text/javascript">
+
+var tests = [
+ // All disabled.
+ { config: [ false, false ], loadExpected: true },
+
+ // Just entitylisted.
+ { config: [ false, true ], loadExpected: true },
+
+ // Just blocklisted.
+ { config: [ true, false ], loadExpected: false },
+
+ // entitylist + blocklist: entitylist wins
+ { config: [ true, true ], loadExpected: true },
+];
+
+function prefValue(value, what) {
+ return value ? what : "";
+}
+
+async function runTest(test) {
+ await SpecialPowers.pushPrefEnv({set: [
+ [ "urlclassifier.features.socialtracking.blacklistHosts", prefValue(test.config[0], "example.com") ],
+ [ "urlclassifier.features.socialtracking.whitelistHosts", prefValue(test.config[1], "mochi.test,mochi.xorigin-test") ],
+ [ "urlclassifier.features.socialtracking.blacklistTables", prefValue(test.config[0], "mochitest1-track-simple") ],
+ [ "urlclassifier.features.socialtracking.whitelistTables", "" ],
+ [ "privacy.trackingprotection.enabled", false ],
+ [ "privacy.trackingprotection.annotate_channels", false ],
+ [ "privacy.trackingprotection.cryptomining.enabled", false ],
+ [ "privacy.trackingprotection.emailtracking.enabled", false ],
+ [ "privacy.trackingprotection.fingerprinting.enabled", false ],
+ [ "privacy.trackingprotection.socialtracking.enabled", true ],
+ ]});
+
+ info("Testing: " + JSON.stringify(test.config) + "\n");
+
+ // Let's load an image with a random query string, just to avoid network cache.
+ let result = await new Promise(resolve => {
+ let image = new Image();
+ image.src = "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random();
+ image.onload = _ => resolve(true);
+ image.onerror = _ => resolve(false);
+ });
+
+ is(result, test.loadExpected, "The loading happened correctly");
+
+ // Let's load an image with a random query string, just to avoid network cache.
+ result = await new Promise(resolve => {
+ let image = new Image();
+ image.src = "http://tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random();
+ image.onload = _ => resolve(true);
+ image.onerror = _ => resolve(false);
+ });
+
+ is(result, test.loadExpected, "The loading happened correctly (by table)");
+}
+
+async function runTests() {
+ let chromeScript = SpecialPowers.loadChromeScript(_ => {
+ /* eslint-env mozilla/chrome-script */
+ const {UrlClassifierTestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/UrlClassifierTestUtils.sys.mjs"
+ );
+
+ addMessageListener("loadTrackers", __ => {
+ UrlClassifierTestUtils.addTestTrackers().then(___ => {
+ sendAsyncMessage("trackersLoaded");
+ });
+ });
+
+ addMessageListener("unloadTrackers", __ => {
+ UrlClassifierTestUtils.cleanupTestTrackers();
+ sendAsyncMessage("trackersUnloaded");
+ });
+ });
+
+ await new Promise(resolve => {
+ chromeScript.addMessageListener("trackersLoaded", resolve);
+ chromeScript.sendAsyncMessage("loadTrackers");
+ });
+
+ for (let test in tests) {
+ await runTest(tests[test]);
+ }
+
+ await new Promise(resolve => {
+ chromeScript.addMessageListener("trackersUnloaded", resolve);
+ chromeScript.sendAsyncMessage("unloadTrackers");
+ });
+
+ chromeScript.destroy();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+runTests();
+
+</script>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_socialtracking_annotate.html b/toolkit/components/url-classifier/tests/mochitest/test_socialtracking_annotate.html
new file mode 100644
index 0000000000..f12dcf11ec
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_socialtracking_annotate.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the relationship between annotation vs blocking - socialtracking</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="features.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<script class="testbody" type="text/javascript">
+
+runTests(SpecialPowers.Ci.nsIClassifiedChannel.CLASSIFIED_SOCIALTRACKING,
+ [
+ ["privacy.socialtracking.block_cookies.enabled", false],
+ ["privacy.trackingprotection.enabled", false],
+ ["privacy.trackingprotection.annotate_channels", false],
+ ["urlclassifier.features.fingerprinting.annotate.blacklistHosts", ""],
+ ["urlclassifier.features.fingerprinting.annotate.blacklistTables", ""],
+ ["privacy.trackingprotection.fingerprinting.enabled", false],
+ ["urlclassifier.features.cryptomining.annotate.blacklistHosts", ""],
+ ["urlclassifier.features.cryptomining.annotate.blacklistTables", ""],
+ ["privacy.trackingprotection.cryptomining.enabled", false],
+ ["privacy.trackingprotection.socialtracking.enabled", true],
+ ["privacy.trackingprotection.emailtracking.enabled", false],
+ ],
+ false /* a tracking resource */);
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html b/toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html
new file mode 100644
index 0000000000..341d26fa3f
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_threathit_report.html
@@ -0,0 +1,235 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Test threathit repoty </title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script src="head.js"></script>
+<script class="testbody" type="text/javascript">
+const {BrowserTestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/BrowserTestUtils.sys.mjs"
+);
+
+var mainWindow = window.browsingContext.topChromeWindow;
+
+var listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"].
+ getService(Ci.nsIUrlListManager);
+const SJS = "mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/threathit.sjs";
+
+function hash(str) {
+ let hasher = Cc["@mozilla.org/security/hash;1"]
+ .createInstance(Ci.nsICryptoHash);
+
+ let data = new TextEncoder().encode(str);
+ hasher.init(hasher.SHA256);
+ hasher.update(data, data.length);
+
+ return hasher.finish(false);
+}
+
+var testDatas = [
+ { url: "itisaphishingsite1.org/phishing.html",
+ list: "test-phish-proto",
+ provider: "test",
+ // The base64 of binary protobuf representation of response:
+ //
+ // [
+ // {
+ // 'threat_type': 2, // SOCIAL_ENGINEERING_PUBLIC
+ // 'response_type': 2, // FULL_UPDATE
+ // 'new_client_state': 'sta\x0te', // NEW_CLIENT_STATE
+ // 'additions': { 'compression_type': RAW,
+ // 'prefix_size': 1,
+ // 'raw_hashes': "xxxx"} // hash prefix of url itisaphishingsite.org/phishing.html
+ // 'minimumWaitDuration': "8.1s",
+ // }
+ // ]
+ //
+ updateProtobuf: "ChoIAiACKgwIARIICAQSBM9UdYs6BnN0YQB0ZRIECAwQCg==",
+ // The base64 of binary protobuf representation of response:
+ // {
+ // "matches": [
+ // {
+ // "threat_type": 2, // SOCIAL_ENGINEERING_PUBLIC
+ // "threat": {
+ // "hash": string,
+ // },
+ // "cacheDuration": "8.1",
+ // }
+ // ],
+ // "minimumWaitDuration": 12.0..1,
+ // "negativeCacheDuration": 12.0..1,
+ // }
+ fullhashProtobuf: "CiwIAhoiCiDPVHWLptJSc/UYiabk1/wo5OkJqbggiylVKISK28bfeSoECAwQChIECAwQChoECAwQCg==",
+ },
+];
+
+function addDataV4ToServer(list, type, data) {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest;
+ let params = new URLSearchParams();
+ params.append("action", "store");
+ params.append("list", list);
+ params.append("type", type);
+ params.append("data", data);
+
+ xhr.open("PUT", "http://" + SJS + "?" + params.toString(), true);
+ xhr.setRequestHeader("Content-Type", "text/plain");
+ xhr.onreadystatechange = function() {
+ if (this.readyState == this.DONE) {
+ resolve();
+ }
+ };
+ xhr.send();
+ });
+}
+/**
+ * Grabs the results via XHR
+ */
+function checkResults(aTestdata, aExpected) {
+ let xhr = new XMLHttpRequest();
+ xhr.responseType = "text";
+ xhr.onload = function() {
+ is(aExpected, xhr.response, "Correct report request");
+ SimpleTest.finish();
+ };
+ xhr.onerror = function() {
+ ok(false, "Can't get results from server.");
+ SimpleTest.finish();
+ };
+ let params = new URLSearchParams();
+ params.append("action", "getreport");
+ params.append("list", aTestdata.list);
+ let url = "http://" + SJS + "?" + params.toString();
+
+ xhr.open("GET", url, true);
+ xhr.setRequestHeader("Content-Type", "text/plain");
+ xhr.send();
+}
+
+function waitForUpdate(data) {
+ listmanager.checkForUpdates(data.updateUrl);
+ return new Promise(resolve => {
+ Services.obs.addObserver(function observer(aSubject, aTopic) {
+ Services.obs.removeObserver(observer, aTopic);
+ resolve();
+ }, "safebrowsing-update-finished");
+ });
+}
+
+function addUpdateDataV4ToServer(list, data) {
+ return addDataV4ToServer(list, "update", data);
+}
+
+function addFullhashV4DataToServer(list, data) {
+ return addDataV4ToServer(list, "fullhash", data);
+}
+
+function setupTestData(data) {
+ let updateParams = new URLSearchParams();
+ updateParams.append("action", "get");
+ updateParams.append("list", data.list);
+ updateParams.append("type", "update");
+ data.updateUrl = "http://" + SJS + "?" + updateParams.toString();
+
+ let gethashParams = new URLSearchParams();
+ gethashParams.append("action", "get");
+ gethashParams.append("list", data.list);
+ gethashParams.append("type", "fullhash");
+ data.gethashUrl = "http://" + SJS + "?" + gethashParams.toString();
+
+ listmanager.registerTable(data.list,
+ data.provider,
+ data.updateUrl,
+ data.gethashUrl);
+
+ let promises = [];
+ let activeTablePref = "urlclassifier.phishTable";
+ let activeTable = SpecialPowers.getCharPref(activeTablePref);
+ activeTable += "," + data.list;
+
+ let reportPref = "browser.safebrowsing.provider." + data.provider + ".dataSharingURL";
+ let reportParams = new URLSearchParams();
+ reportParams.append("action", "report");
+ reportParams.append("list", data.list);
+ data.reportUrl = "http://" + SJS + "?" + reportParams.toString();
+
+ let reportEnabledPref = "browser.safebrowsing.provider." + data.provider + ".dataSharing.enabled";
+
+ promises.push(pushPrefs([reportPref, data.reportUrl]));
+ promises.push(pushPrefs([reportEnabledPref, true]));
+ promises.push(pushPrefs([activeTablePref, activeTable]));
+ promises.push(addUpdateDataV4ToServer(data.list, data.updateProtobuf));
+ promises.push(addFullhashV4DataToServer(data.list, data.fullhashProtobuf));
+ return Promise.all(promises);
+}
+
+function testOnWindow(aTestData) {
+ return new Promise(resolve => {
+ let win = mainWindow.OpenBrowserWindow();
+
+ (async function() {
+ await new Promise(rs => whenDelayedStartupFinished(win, rs));
+
+ let expected;
+ let browser = win.gBrowser.selectedBrowser;
+ let progressListener = {
+ onContentBlockingEvent(aWebProgress, aRequest, aEvent) {
+ expected = aTestData.reportUrl;
+ },
+ QueryInterface: ChromeUtils.generateQI(["nsISupportsWeakReference"]),
+ };
+ win.gBrowser.addProgressListener(progressListener, Ci.nsIWebProgress.NOTIFY_CONTENT_BLOCKING);
+
+ BrowserTestUtils.startLoadingURIString(browser, aTestData.url);
+ await BrowserTestUtils.browserLoaded(
+ browser,
+ false,
+ `http://${aTestData.url}`,
+ true
+ );
+ checkResults(aTestData, expected);
+ win.close();
+ resolve();
+ })();
+ });
+}
+SpecialPowers.pushPrefEnv(
+ {"set": [
+ ["browser.safebrowsing.phishing.enabled", true],
+ ["dom.testing.sync-content-blocking-notifications", true],
+ ]},
+ test);
+
+function test() {
+ (async function() {
+ await classifierHelper.waitForInit();
+
+ for (let testData of testDatas) {
+ await setupTestData(testData);
+ await waitForUpdate(testData);
+ await testOnWindow(testData);
+ await classifierHelper._cleanup();
+ }
+ })();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1157081.html b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1157081.html
new file mode 100644
index 0000000000..098d6098d9
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1157081.html
@@ -0,0 +1,100 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Test Tracking Protection with and without Safe Browsing (Bug #1157081)</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+
+var mainWindow = window.browsingContext.topChromeWindow;
+var contentPage = "http://mochi.test:8888/chrome/toolkit/components/url-classifier/tests/mochitest/classifiedAnnotatedPBFrame.html";
+
+const {UrlClassifierTestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/UrlClassifierTestUtils.sys.mjs"
+);
+const {TestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/TestUtils.sys.mjs"
+);
+
+function testOnWindow(aCallback) {
+ var win = mainWindow.OpenBrowserWindow();
+ win.addEventListener("load", function() {
+ TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win).then(() => {
+ win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+ if (win.content.location.href != contentPage) {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ return;
+ }
+ win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+
+ win.content.addEventListener("load", function innerLoad2() {
+ win.content.removeEventListener("load", innerLoad2);
+ SimpleTest.executeSoon(function() { aCallback(win); });
+ }, false, true);
+ }, true);
+ SimpleTest.executeSoon(function() {
+ win.gBrowser.loadURI(Services.io.newURI(contentPage), {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ });
+ });
+ });
+ }, {capture: true, once: true});
+}
+
+var badids = [
+ "badscript",
+];
+
+function checkLoads(aWindow, aBlocked) {
+ var win = aWindow.content;
+ is(win.document.getElementById("badscript").dataset.touched, aBlocked ? "no" : "yes", "Should not load tracking javascript");
+}
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["urlclassifier.trackingTable", "moztest-track-simple"],
+ ["privacy.trackingprotection.enabled", true],
+ ["browser.safebrowsing.malware.enabled", false],
+ ["browser.safebrowsing.phishing.enabled", false],
+ ["channelclassifier.allowlist_example", true]]},
+ test);
+
+function test() {
+ SimpleTest.registerCleanupFunction(UrlClassifierTestUtils.cleanupTestTrackers);
+ UrlClassifierTestUtils.addTestTrackers().then(() => {
+ // Safe Browsing turned OFF, tracking protection should work nevertheless
+ testOnWindow(function(aWindow) {
+ checkLoads(aWindow, true);
+ aWindow.close();
+
+ // Safe Browsing turned ON, tracking protection should still work
+ SpecialPowers.setBoolPref("browser.safebrowsing.phishing.enabled", true);
+ testOnWindow(function(aWindow1) {
+ checkLoads(aWindow1, true);
+ aWindow1.close();
+ SimpleTest.finish();
+ });
+ });
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1312515.html b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1312515.html
new file mode 100644
index 0000000000..31c69ac293
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1312515.html
@@ -0,0 +1,164 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Test Bug 1312515</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+
+<p><b>To see more of what is happening: <code>export MOZ_LOG=nsChannelClassifier:3</code></b></p>
+
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+
+var mainWindow = window.browsingContext.topChromeWindow;
+var contentPage = "http://www.itisatrap.org/chrome/toolkit/components/url-classifier/tests/mochitest/trackingRequest.html";
+
+const {UrlClassifierTestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/UrlClassifierTestUtils.sys.mjs"
+);
+const {BrowserTestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/BrowserTestUtils.sys.mjs"
+);
+const {TestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/TestUtils.sys.mjs"
+);
+
+function testOnWindow(aPrivate, aCallback) {
+ var win = mainWindow.OpenBrowserWindow({private: aPrivate});
+ win.addEventListener("load", function() {
+ TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win).then(() => {
+ win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+ if (win.content.location.href != contentPage) {
+ BrowserTestUtils.startLoadingURIString(win.gBrowser, contentPage);
+ return;
+ }
+ win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+
+ win.content.addEventListener("load", function innerLoad2() {
+ win.content.removeEventListener("load", innerLoad2);
+ SimpleTest.executeSoon(function() { aCallback(win); });
+ }, false, true);
+ }, true);
+ SimpleTest.executeSoon(function () {
+ BrowserTestUtils.startLoadingURIString(win.gBrowser, contentPage);
+ });
+ });
+ }, {capture: true, once: true});
+}
+
+const topic = "http-on-before-connect";
+var testUrl;
+var testWindow;
+var resolve;
+
+function checkLowestPriority(aSubject) {
+ checkPriority(aSubject, checkLowestPriority, Ci.nsISupportsPriority.PRIORITY_LOWEST, "Priority should be lowest.");
+}
+
+function checkNormalPriority(aSubject) {
+ checkPriority(aSubject, checkNormalPriority, Ci.nsISupportsPriority.PRIORITY_NORMAL, "Priority should be normal.");
+}
+
+function checkPriority(aSubject, aCallback, aPriority, aMessage) {
+ var channel = aSubject.QueryInterface(Ci.nsIChannel);
+ info("Channel classified: " + channel.name);
+ if (channel.name !== testUrl) {
+ return;
+ }
+
+ SpecialPowers.removeObserver(aCallback, topic);
+
+ var p = aSubject.QueryInterface(Ci.nsISupportsPriority);
+ is(p.priority, aPriority, aMessage);
+
+ info("Resolving promise for " + channel.name);
+ resolve();
+}
+
+function testXHR1() {
+ return new Promise(function(aResolve, aReject) {
+ testUrl = "http://tracking.example.com/";
+ info("Not blocklisted: " + testUrl);
+ resolve = aResolve;
+ SpecialPowers.addObserver(checkNormalPriority, topic);
+ testWindow.content.postMessage({type: "doXHR", url: testUrl}, "*");
+ });
+}
+
+function testXHR2() {
+ return new Promise(function(aResolve, aReject) {
+ testUrl = "http://trackertest.org/";
+ info("Blocklisted and not entitylisted: " + testUrl);
+ resolve = aResolve;
+ SpecialPowers.addObserver(checkLowestPriority, topic);
+ testWindow.content.postMessage({type: "doXHR", url: testUrl}, "*");
+ });
+}
+
+function testFetch1() {
+ return new Promise(function(aResolve, aReject) {
+ testUrl = "http://itisatracker.org/"; // only entitylisted in TP, not for annotations
+ info("Blocklisted and not entitylisted: " + testUrl);
+ resolve = aResolve;
+ SpecialPowers.addObserver(checkLowestPriority, topic);
+ testWindow.content.postMessage({type: "doFetch", url: testUrl}, "*");
+ });
+}
+
+function testFetch2() {
+ return new Promise(function(aResolve, aReject) {
+ testUrl = "http://tracking.example.org/"; // only entitylisted for annotations, not in TP
+ info("Blocklisted but also entitylisted: " + testUrl);
+ resolve = aResolve;
+ SpecialPowers.addObserver(checkNormalPriority, topic);
+ testWindow.content.postMessage({type: "doFetch", url: testUrl}, "*");
+ });
+}
+
+function endTest() {
+ info("Finishing up...");
+ testWindow.close();
+ testWindow = null;
+ SimpleTest.finish();
+}
+
+async function test() {
+ await SpecialPowers.pushPrefEnv(
+ {"set": [["network.http.tailing.enabled", false],
+ ["privacy.trackingprotection.enabled", false],
+ ["privacy.trackingprotection.annotate_channels", true],
+ ["privacy.trackingprotection.lower_network_priority", true],
+ ["dom.security.https_first", false]]});
+ await UrlClassifierTestUtils.addTestTrackers();
+ testOnWindow(false, async function(aWindow) {
+ testWindow = aWindow;
+ await testXHR1();
+ await testXHR2();
+ await testFetch1();
+ await testFetch2();
+ await endTest();
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.registerCleanupFunction(function() {
+ info("Cleaning up prefs...");
+ UrlClassifierTestUtils.cleanupTestTrackers();
+});
+test();
+
+</script>
+
+</pre>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1580416.html b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1580416.html
new file mode 100644
index 0000000000..75d69b2596
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_bug1580416.html
@@ -0,0 +1,102 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Test Tracking Protection in Private Browsing mode</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="classifierHelper.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+
+var mainWindow = window.window.browsingContext.topChromeWindow;
+var contentPage1 = "http://www.example.com/chrome/toolkit/components/url-classifier/tests/mochitest/bug_1580416.html";
+
+const {BrowserTestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/BrowserTestUtils.sys.mjs"
+);
+const {TestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/TestUtils.sys.mjs"
+);
+
+function testOnWindow(contentPage) {
+ return new Promise((resolve, reject) => {
+ var win = mainWindow.OpenBrowserWindow();
+ win.addEventListener("load", function() {
+ TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win).then(() => {
+ win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+ if (win.content.location.href != contentPage) {
+ BrowserTestUtils.startLoadingURIString(win.gBrowser, contentPage);
+ return;
+ }
+ win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+
+ win.content.addEventListener("load", function innerLoad2() {
+ win.content.removeEventListener("load", innerLoad2);
+ SimpleTest.executeSoon(function() {
+ resolve(win);
+ });
+ }, false, true);
+ }, true);
+ SimpleTest.executeSoon(function() {
+ BrowserTestUtils.startLoadingURIString(win.gBrowser, contentPage);
+ });
+ });
+ }, {capture: true, once: true});
+ });
+}
+
+var testData = [
+ { url: "apps.fbsbx.com/",
+ db: "test-track-simple",
+ },
+ { url: "www.example.com/?resource=apps.fbsbx.com",
+ db: "test-trackwhite-simple",
+ },
+];
+
+function checkLoads(aWindow, aWhitelisted) {
+ var win = aWindow.content;
+
+ is(win.document.getElementById("goodscript").dataset.touched, aWhitelisted ? "yes" : "no", "Should load whitelisted tracking javascript");
+}
+
+SpecialPowers.pushPrefEnv(
+ // Disable STS preloadlist because apps.fbsbx.com is in the list.
+ {"set": [["privacy.trackingprotection.enabled", true],
+ ["urlclassifier.trackingTable", "test-track-simple"],
+ ["urlclassifier.trackingWhitelistTable", "test-trackwhite-simple"],
+ ["dom.security.https_first", false],
+ ["network.stricttransportsecurity.preloadlist", false]]},
+ test);
+
+async function test() {
+ await classifierHelper.waitForInit();
+ await classifierHelper.addUrlToDB(testData);
+
+ // Load the test from a URL on the whitelist
+ await testOnWindow(contentPage1).then(function(aWindow) {
+ checkLoads(aWindow, true);
+ aWindow.close();
+ });
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_whitelist.html b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_whitelist.html
new file mode 100644
index 0000000000..69ca337c33
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/test_trackingprotection_whitelist.html
@@ -0,0 +1,165 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+ <title>Test Tracking Protection in Private Browsing mode</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+
+<script class="testbody" type="text/javascript">
+
+var mainWindow = window.browsingContext.topChromeWindow;
+var contentPage1 = "http://www.itisatrap.org/tests/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html";
+var contentPage2 = "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html";
+
+const {UrlClassifierTestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/UrlClassifierTestUtils.sys.mjs"
+);
+const {BrowserTestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/BrowserTestUtils.sys.mjs"
+);
+const {TestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/TestUtils.sys.mjs"
+);
+
+function testOnWindow(contentPage) {
+ return new Promise((resolve, reject) => {
+ var win = mainWindow.OpenBrowserWindow();
+ win.addEventListener("load", function() {
+ TestUtils.topicObserved("browser-delayed-startup-finished",
+ subject => subject == win).then(() => {
+ win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+ if (win.content.location.href != contentPage) {
+ BrowserTestUtils.startLoadingURIString(win.gBrowser, contentPage);
+ return;
+ }
+ win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+
+ win.content.addEventListener("load", function innerLoad2() {
+ win.content.removeEventListener("load", innerLoad2);
+ SimpleTest.executeSoon(function() {
+ resolve(win);
+ });
+ }, false, true);
+ }, true);
+ SimpleTest.executeSoon(function() {
+ BrowserTestUtils.startLoadingURIString(win.gBrowser, contentPage);
+ });
+ });
+ }, {capture: true, once: true});
+ });
+}
+
+var alwaysbadids = [
+ "badscript",
+];
+
+function checkLoads(aWindow, aWhitelisted, tpEnabled) {
+ var win = aWindow.content;
+ if (!tpEnabled) {
+ is(win.document.getElementById("badscript").dataset.touched, "yes", "Should load tracking javascript");
+ is(win.document.blockedNodeByClassifierCount, 0, "Should not identify any tracking elements");
+ return;
+ }
+
+ is(win.document.getElementById("badscript").dataset.touched, "no", "Should not load tracking javascript");
+ is(win.document.getElementById("goodscript").dataset.touched, aWhitelisted ? "yes" : "no", "Should load whitelisted tracking javascript");
+
+ var badids = alwaysbadids.slice();
+ if (!aWhitelisted) {
+ badids.push("goodscript");
+ }
+ is(win.document.blockedNodeByClassifierCount, badids.length, "Should identify all tracking elements");
+
+ var blockedNodes = win.document.blockedNodesByClassifier;
+
+ // Make sure that every node in blockedNodes exists in the tree
+ // (that may not always be the case but do not expect any nodes to disappear
+ // from the tree here)
+ var allNodeMatch = true;
+ for (let i = 0; i < blockedNodes.length; i++) {
+ let nodeMatch = false;
+ for (let j = 0; j < badids.length && !nodeMatch; j++) {
+ nodeMatch = nodeMatch ||
+ (blockedNodes[i] == win.document.getElementById(badids[j]));
+ }
+
+ allNodeMatch = allNodeMatch && nodeMatch;
+ }
+ is(allNodeMatch, true, "All annotated nodes are expected in the tree");
+
+ // Make sure that every node with a badid (see badids) is found in the
+ // blockedNodes. This tells us if we are neglecting to annotate
+ // some nodes
+ allNodeMatch = true;
+ for (let j = 0; j < badids.length; j++) {
+ let nodeMatch = false;
+ for (let i = 0; i < blockedNodes.length && !nodeMatch; i++) {
+ nodeMatch = nodeMatch ||
+ (blockedNodes[i] == win.document.getElementById(badids[j]));
+ }
+
+ allNodeMatch = allNodeMatch && nodeMatch;
+ }
+ is(allNodeMatch, true, "All tracking nodes are expected to be annotated as such");
+}
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["privacy.trackingprotection.enabled", true],
+ ["privacy.trackingprotection.testing.report_blocked_node", true],
+ ["dom.security.https_first", false],
+ ["channelclassifier.allowlist_example", true]]},
+ test);
+
+async function test() {
+ SimpleTest.registerCleanupFunction(UrlClassifierTestUtils.cleanupTestTrackers);
+ await UrlClassifierTestUtils.addTestTrackers();
+
+ // Load the test from a URL that's NOT on the whitelist with tracking protection disabled
+ await SpecialPowers.setBoolPref("privacy.trackingprotection.enabled", false);
+ await testOnWindow(contentPage2).then(function(aWindow) {
+ checkLoads(aWindow, false, false);
+ aWindow.close();
+ });
+ await SpecialPowers.setBoolPref("privacy.trackingprotection.enabled", true);
+
+ // Load the test from a URL that's NOT on the whitelist
+ await testOnWindow(contentPage2).then(function(aWindow) {
+ checkLoads(aWindow, false, true);
+ aWindow.close();
+ });
+
+ // Load the test from a URL on the whitelist
+ await testOnWindow(contentPage1).then(function(aWindow) {
+ checkLoads(aWindow, true, true);
+ aWindow.close();
+ });
+
+ // Load the test from a URL on the whitelist but without the whitelist
+ await SpecialPowers.setCharPref("urlclassifier.trackingWhitelistTable", "");
+ await testOnWindow(contentPage1).then(function(aWindow) {
+ checkLoads(aWindow, false, true);
+ aWindow.close();
+ });
+ await SpecialPowers.clearUserPref("urlclassifier.trackingWhitelistTable");
+ await SpecialPowers.clearUserPref("privacy.trackingprotection.enabled");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+
+</pre>
+<iframe id="testFrame" width="100%" height="100%" onload=""></iframe>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/threathit.sjs b/toolkit/components/url-classifier/tests/mochitest/threathit.sjs
new file mode 100644
index 0000000000..a083a2e247
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/threathit.sjs
@@ -0,0 +1,47 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const CC = Components.Constructor;
+const BinaryInputStream = CC(
+ "@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream"
+);
+
+function handleRequest(request, response) {
+ let params = new URLSearchParams(request.queryString);
+ var action = params.get("action");
+
+ var responseBody;
+
+ // Store data in the server side.
+ if (action == "store") {
+ // In the server side we will store:
+ // All the full hashes or update for a given list
+ let state = params.get("list") + params.get("type");
+ let dataStr = params.get("data");
+ setState(state, dataStr);
+ } else if (action == "get") {
+ let state = params.get("list") + params.get("type");
+ responseBody = atob(getState(state));
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.bodyOutputStream.write(responseBody, responseBody.length);
+ } else if (action == "report") {
+ let state = params.get("list") + "report";
+ let requestUrl =
+ request.scheme +
+ "://" +
+ request.host +
+ ":" +
+ request.port +
+ request.path +
+ "?" +
+ request.queryString;
+ setState(state, requestUrl);
+ } else if (action == "getreport") {
+ let state = params.get("list") + "report";
+ responseBody = getState(state);
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write(responseBody);
+ }
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/track.html b/toolkit/components/url-classifier/tests/mochitest/track.html
new file mode 100644
index 0000000000..8785e7c5b1
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/track.html
@@ -0,0 +1,7 @@
+<html>
+ <head>
+ </head>
+ <body>
+ <h1>Tracking Works!</h1>
+ </body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/trackerFrame.html b/toolkit/components/url-classifier/tests/mochitest/trackerFrame.html
new file mode 100644
index 0000000000..73409b5cda
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/trackerFrame.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+<meta charset="utf-8">
+<title></title>
+</head>
+<body>
+<div id="content" style="display: none">
+
+<img src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=img-src">
+
+<!--nsObjectLoadingContent::OpenChannel-->
+<object data="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=object-data"></object>
+
+<!--ScriptLoader::StartLoad-->
+<script src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=script-src"></script>
+
+<!--nsDocShell::DoURILoad-->
+<iframe src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=iframe-src"></iframe>
+
+<!--Loader::LoadSheet-->
+<link rel="stylesheet" href="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=link-rel-stylesheet" />
+
+<!--nsPrefetchNode::OpenChannel-->
+<!-- Temporarily disable this because it doesn't work in fission when the scheme is https -->
+<!--<link rel="prefetch" href="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=link-rel-prefetch" />-->
+
+<!--HTMLMediaElement::ChannelLoader::LoadInternal-->
+<video src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=video-src">
+</video>
+
+<video src="https://mochi.test:8888/basic.vtt", crossorigin=use-credentials>
+ <track default src="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=track-src" ></track>
+</video>
+
+<!--SendPing-->
+<a ping="https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=ping" id="a-ping" href="#"></a>
+<script>
+ (function() {
+ document.getElementById("a-ping").click();
+ })();
+</script>
+
+<script>
+
+// FetchDriver::HttpFetch
+(function() {
+ try {
+ fetch(new Request("https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=fetch"), {
+ credentials: "include",
+ });
+ } catch (err) {
+ console.log(err);
+ }
+})();
+
+// XMLHttpRequestMainThread::CreateChannel
+(function() {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", "https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=xmlhttprequest");
+ xhr.withCredentials = true;
+ xhr.send();
+})();
+
+// Navigator::SendBeaconInternal
+(function() {
+ navigator.sendBeacon("https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=send-beacon");
+})();
+
+</script>
+
+// Fetch inside service worker's script
+<iframe id="sw" src="https://example.com/tests/toolkit/components/url-classifier/tests/mochitest/sw_register.html"></iframe>
+<script>
+ let iframe = document.getElementById("sw");
+ window.onmessage = function(e) {
+ if (e.data.status == "registrationdone") {
+ iframe.remove();
+ iframe = document.createElement("iframe");
+ document.getElementById("content").appendChild(iframe);
+ iframe.src = "https://example.com/tests/toolkit/components/url-classifier/tests/mochitest/synth.html?" +
+ "https://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs?id=fetch-in-sw";
+ }
+ };
+</script>
+
+</div>
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs b/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs
new file mode 100644
index 0000000000..be31e666b0
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/trackerFrame.sjs
@@ -0,0 +1,80 @@
+/* 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/. */
+
+const stateTotalRequests = "total-request";
+const stateCallback = "callback-response";
+const stateTrackersWithCookie = "trackers-with-cookie";
+const stateTrackersWithoutCookie = "trackers-without-cookie";
+const stateReceivedTrackers = "received-trackers";
+const stateResponseType = "response-tracker-with-cookie";
+
+function reset() {
+ setState(stateCallback, "");
+ setState(stateTrackersWithCookie, "");
+ setState(stateTrackersWithoutCookie, "");
+ setState(stateReceivedTrackers, "");
+ setState(stateResponseType, "");
+}
+
+function handleRequest(aRequest, aResponse) {
+ let params = new URLSearchParams(aRequest.queryString);
+
+ // init the server and tell the server the total number requests to process
+ // server set the cookie
+ if (params.has("init")) {
+ setState(stateTotalRequests, params.get("init"));
+
+ aResponse.setStatusLine(aRequest.httpVersion, 200);
+ aResponse.setHeader("Content-Type", "text/plain", false);
+
+ // Prepare the cookie
+ aResponse.setHeader("Set-Cookie", "cookie=1234; SameSite=None; Secure");
+ aResponse.setHeader(
+ "Access-Control-Allow-Origin",
+ aRequest.getHeader("Origin"),
+ false
+ );
+ aResponse.setHeader("Access-Control-Allow-Credentials", "true", false);
+ aResponse.write("begin-test");
+ // register the callback response, the response will be fired after receiving
+ // all the request
+ } else if (params.has("callback")) {
+ aResponse.processAsync();
+ aResponse.setHeader("Content-Type", "text/plain", false);
+ aResponse.setHeader(
+ "Access-Control-Allow-Origin",
+ aRequest.getHeader("Origin"),
+ false
+ );
+ aResponse.setHeader("Access-Control-Allow-Credentials", "true", false);
+
+ setState(stateResponseType, params.get("callback"));
+ setObjectState(stateCallback, aResponse);
+ } else {
+ let count = parseInt(getState(stateReceivedTrackers) || 0) + 1;
+ setState(stateReceivedTrackers, count.toString());
+
+ let state = "";
+ if (aRequest.hasHeader("Cookie")) {
+ state = stateTrackersWithCookie;
+ } else {
+ state = stateTrackersWithoutCookie;
+ }
+
+ let ids = params.get("id").concat(",", getState(state));
+ setState(state, ids);
+
+ if (getState(stateTotalRequests) == getState(stateReceivedTrackers)) {
+ getObjectState(stateCallback, r => {
+ if (getState(stateResponseType) == "with-cookie") {
+ r.write(getState(stateTrackersWithCookie));
+ } else {
+ r.write(getState(stateTrackersWithoutCookie));
+ }
+ r.finish();
+ reset();
+ });
+ }
+ }
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/trackingRequest.html b/toolkit/components/url-classifier/tests/mochitest/trackingRequest.html
new file mode 100644
index 0000000000..ea0f92c481
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/trackingRequest.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+<title></title>
+
+</head>
+<body>
+
+<!--
+ This domain is not blocklisted for annotations but it is for tracking protection.
+ Therefore if tracking protection is accidentally enabled, this test will fail. On
+ the other hand, tracking.example.com will not be used in any of the same-origin
+ comparisons since we always look for the top window URI when there is one and
+ that's set to be www.itisatrap.org.
+-->
+<script id="badscript" data-touched="not sure" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js"></script>
+
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js b/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js
new file mode 100644
index 0000000000..2720578eed
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js
@@ -0,0 +1,9 @@
+window.addEventListener("message", function onMessage(evt) {
+ if (evt.data.type === "doXHR") {
+ var request = new XMLHttpRequest();
+ request.open("GET", evt.data.url, true);
+ request.send(null);
+ } else if (evt.data.type === "doFetch") {
+ fetch(evt.data.url);
+ }
+});
diff --git a/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js^headers^ b/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js^headers^
new file mode 100644
index 0000000000..3eced96143
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/trackingRequest.js^headers^
@@ -0,0 +1,2 @@
+Access-Control-Allow-Origin: *
+Cache-Control: no-store
diff --git a/toolkit/components/url-classifier/tests/mochitest/unwantedWorker.js b/toolkit/components/url-classifier/tests/mochitest/unwantedWorker.js
new file mode 100644
index 0000000000..b4e8a47602
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/unwantedWorker.js
@@ -0,0 +1,5 @@
+/* eslint-env worker */
+
+onmessage = function () {
+ postMessage("loaded bad file");
+};
diff --git a/toolkit/components/url-classifier/tests/mochitest/update.sjs b/toolkit/components/url-classifier/tests/mochitest/update.sjs
new file mode 100644
index 0000000000..f3984b2a6f
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/update.sjs
@@ -0,0 +1,71 @@
+const CC = Components.Constructor;
+const BinaryInputStream = CC(
+ "@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream"
+);
+
+function handleRequest(request, response) {
+ var query = {};
+ request.queryString.split("&").forEach(function (val) {
+ var idx = val.indexOf("=");
+ query[val.slice(0, idx)] = unescape(val.slice(idx + 1));
+ });
+
+ // Store fullhash in the server side.
+ if ("list" in query && "fullhash" in query) {
+ // In the server side we will store:
+ // 1. All the full hashes for a given list
+ // 2. All the lists we have right now
+ // data is separate by '\n'
+ let list = query.list;
+ let hashes = getState(list);
+
+ let hash = atob(query.fullhash);
+ hashes += hash + "\n";
+ setState(list, hashes);
+
+ let lists = getState("lists");
+ if (!lists.includes(list)) {
+ lists += list + "\n";
+ setState("lists", lists);
+ }
+
+ return;
+ }
+
+ var body = new BinaryInputStream(request.bodyInputStream);
+ var avail;
+ var bytes = [];
+
+ while ((avail = body.available()) > 0) {
+ Array.prototype.push.apply(bytes, body.readByteArray(avail));
+ }
+
+ var responseBody = parseV2Request(bytes);
+
+ response.setHeader("Content-Type", "text/plain", false);
+ response.write(responseBody);
+}
+
+function parseV2Request(bytes) {
+ var table = String.fromCharCode.apply(this, bytes).slice(0, -2);
+
+ var ret = "";
+ getState("lists")
+ .split("\n")
+ .forEach(function (list) {
+ if (list == table) {
+ var completions = getState(list).split("\n");
+ ret += "n:1000\n";
+ ret += "i:" + list + "\n";
+ ret += "a:1:32:" + 32 * (completions.length - 1) + "\n";
+
+ for (var completion of completions) {
+ ret += completion;
+ }
+ }
+ });
+
+ return ret;
+}
diff --git a/toolkit/components/url-classifier/tests/mochitest/vp9.webm b/toolkit/components/url-classifier/tests/mochitest/vp9.webm
new file mode 100644
index 0000000000..221877e303
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/vp9.webm
Binary files differ
diff --git a/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html b/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html
new file mode 100644
index 0000000000..620416fc74
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/whitelistFrame.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+<title></title>
+</head>
+<body>
+
+<script id="badscript" data-touched="not sure" src="http://trackertest.org/tests/toolkit/components/url-classifier/tests/mochitest/evil.js" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"></script>
+
+<script id="goodscript" data-touched="not sure" src="http://itisatracker.org/tests/toolkit/components/url-classifier/tests/mochitest/good.js" onload="this.dataset.touched = 'yes';" onerror="this.dataset.touched = 'no';"></script>
+
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/mochitest/workerFrame.html b/toolkit/components/url-classifier/tests/mochitest/workerFrame.html
new file mode 100644
index 0000000000..69e8dd0074
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/workerFrame.html
@@ -0,0 +1,65 @@
+<html>
+<head>
+<title></title>
+
+<script type="text/javascript">
+
+function startCleanWorker() {
+ var worker = new Worker("cleanWorker.js");
+
+ worker.onmessage = function(event) {
+ if (event.data == "success") {
+ window.parent.postMessage("success:blocked importScripts('evilWorker.js')", "*");
+ } else {
+ window.parent.postMessage("failure:failed to block importScripts('evilWorker.js')", "*");
+ }
+ window.parent.postMessage("finish", "*");
+ };
+
+ worker.onerror = function(event) {
+ window.parent.postmessage("failure:failed to load cleanWorker.js", "*");
+ window.parent.postMessage("finish", "*");
+ };
+
+ worker.postMessage("");
+}
+
+function startEvilWorker() {
+ var worker = new Worker("evilWorker.js");
+
+ worker.onmessage = function(event) {
+ window.parent.postMessage("failure:failed to block evilWorker.js", "*");
+ startUnwantedWorker();
+ };
+
+ worker.onerror = function(event) {
+ window.parent.postMessage("success:blocked evilWorker.js", "*");
+ startUnwantedWorker();
+ };
+
+ worker.postMessage("");
+}
+
+function startUnwantedWorker() {
+ var worker = new Worker("unwantedWorker.js");
+
+ worker.onmessage = function(event) {
+ window.parent.postMessage("failure:failed to block unwantedWorker.js", "*");
+ startCleanWorker();
+ };
+
+ worker.onerror = function(event) {
+ window.parent.postMessage("success:blocked unwantedWorker.js", "*");
+ startCleanWorker();
+ };
+
+ worker.postMessage("");
+}
+
+</script>
+
+</head>
+
+<body onload="startEvilWorker()">
+</body>
+</html>
diff --git a/toolkit/components/url-classifier/tests/moz.build b/toolkit/components/url-classifier/tests/moz.build
new file mode 100644
index 0000000000..79364db097
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+MOCHITEST_MANIFESTS += ["mochitest/mochitest.toml"]
+MOCHITEST_CHROME_MANIFESTS += ["mochitest/chrome.toml"]
+XPCSHELL_TESTS_MANIFESTS += ["unit/xpcshell.toml"]
+BROWSER_CHROME_MANIFESTS += ["browser/browser.toml"]
+
+TESTING_JS_MODULES += [
+ "UrlClassifierTestUtils.sys.mjs",
+]
+
+if CONFIG["ENABLE_TESTS"]:
+ DIRS += ["gtest"]
diff --git a/toolkit/components/url-classifier/tests/unit/data/content-fingerprinting-track-digest256 b/toolkit/components/url-classifier/tests/unit/data/content-fingerprinting-track-digest256
new file mode 100644
index 0000000000..cf95b25ac3
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/data/content-fingerprinting-track-digest256
Binary files differ
diff --git a/toolkit/components/url-classifier/tests/unit/data/digest1.chunk b/toolkit/components/url-classifier/tests/unit/data/digest1.chunk
new file mode 100644
index 0000000000..3850373c19
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/data/digest1.chunk
Binary files differ
diff --git a/toolkit/components/url-classifier/tests/unit/data/digest2.chunk b/toolkit/components/url-classifier/tests/unit/data/digest2.chunk
new file mode 100644
index 0000000000..738c96f6ba
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/data/digest2.chunk
@@ -0,0 +1,2 @@
+a:5:32:32
+“Ê_Há^˜aÍ7ÂÙ]´=#ÌnmåÃøún‹æo—ÌQ‰ \ No newline at end of file
diff --git a/toolkit/components/url-classifier/tests/unit/data/google-trackwhite-digest256 b/toolkit/components/url-classifier/tests/unit/data/google-trackwhite-digest256
new file mode 100644
index 0000000000..81d28053ff
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/data/google-trackwhite-digest256
Binary files differ
diff --git a/toolkit/components/url-classifier/tests/unit/data/invalid.chunk b/toolkit/components/url-classifier/tests/unit/data/invalid.chunk
new file mode 100644
index 0000000000..7911ca4963
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/data/invalid.chunk
@@ -0,0 +1,2 @@
+a:5:32
+“Ê_Há^˜aÃ7ÂÙ]´=#ÃŒnmåÃøún‹æo—ÌQ‰
diff --git a/toolkit/components/url-classifier/tests/unit/data/mozplugin-block-digest256 b/toolkit/components/url-classifier/tests/unit/data/mozplugin-block-digest256
new file mode 100644
index 0000000000..40f64f3cbf
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/data/mozplugin-block-digest256
Binary files differ
diff --git a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js
new file mode 100644
index 0000000000..0ed731b564
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js
@@ -0,0 +1,570 @@
+//* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- *
+function dumpn(s) {
+ dump(s + "\n");
+}
+
+const NS_APP_USER_PROFILE_50_DIR = "ProfD";
+const NS_APP_USER_PROFILE_LOCAL_50_DIR = "ProfLD";
+
+var {
+ HTTP_400,
+ HTTP_401,
+ HTTP_402,
+ HTTP_403,
+ HTTP_404,
+ HTTP_405,
+ HTTP_406,
+ HTTP_407,
+ HTTP_408,
+ HTTP_409,
+ HTTP_410,
+ HTTP_411,
+ HTTP_412,
+ HTTP_413,
+ HTTP_414,
+ HTTP_415,
+ HTTP_417,
+ HTTP_500,
+ HTTP_501,
+ HTTP_502,
+ HTTP_503,
+ HTTP_504,
+ HTTP_505,
+ HttpError,
+ HttpServer,
+} = ChromeUtils.importESModule("resource://testing-common/httpd.sys.mjs");
+
+do_get_profile();
+
+// Ensure PSM is initialized before the test
+Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
+
+// Disable hashcompleter noise for tests
+Services.prefs.setIntPref("urlclassifier.gethashnoise", 0);
+
+// Enable malware/phishing checking for tests
+Services.prefs.setBoolPref("browser.safebrowsing.malware.enabled", true);
+Services.prefs.setBoolPref("browser.safebrowsing.blockedURIs.enabled", true);
+Services.prefs.setBoolPref("browser.safebrowsing.phishing.enabled", true);
+Services.prefs.setBoolPref(
+ "browser.safebrowsing.provider.test.disableBackoff",
+ true
+);
+
+// Add testing tables, we don't use moztest-* here because it doesn't support update
+Services.prefs.setCharPref("urlclassifier.phishTable", "test-phish-simple");
+Services.prefs.setCharPref(
+ "urlclassifier.malwareTable",
+ "test-harmful-simple,test-malware-simple,test-unwanted-simple"
+);
+Services.prefs.setCharPref("urlclassifier.blockedTable", "test-block-simple");
+Services.prefs.setCharPref("urlclassifier.trackingTable", "test-track-simple");
+Services.prefs.setCharPref(
+ "urlclassifier.trackingWhitelistTable",
+ "test-trackwhite-simple"
+);
+
+// Enable all completions for tests
+Services.prefs.setCharPref("urlclassifier.disallow_completions", "");
+
+// Hash completion timeout
+Services.prefs.setIntPref("urlclassifier.gethash.timeout_ms", 5000);
+
+function delFile(name) {
+ try {
+ // Delete a previously created sqlite file
+ var file = Services.dirsvc.get("ProfLD", Ci.nsIFile);
+ file.append(name);
+ if (file.exists()) {
+ file.remove(false);
+ }
+ } catch (e) {}
+}
+
+function cleanUp() {
+ delFile("urlclassifier3.sqlite");
+ delFile("safebrowsing/classifier.hashkey");
+ delFile("safebrowsing/test-phish-simple.sbstore");
+ delFile("safebrowsing/test-malware-simple.sbstore");
+ delFile("safebrowsing/test-unwanted-simple.sbstore");
+ delFile("safebrowsing/test-block-simple.sbstore");
+ delFile("safebrowsing/test-harmful-simple.sbstore");
+ delFile("safebrowsing/test-track-simple.sbstore");
+ delFile("safebrowsing/test-trackwhite-simple.sbstore");
+ delFile("safebrowsing/test-phish-simple.pset");
+ delFile("safebrowsing/test-malware-simple.pset");
+ delFile("safebrowsing/test-unwanted-simple.pset");
+ delFile("safebrowsing/test-block-simple.pset");
+ delFile("safebrowsing/test-harmful-simple.pset");
+ delFile("safebrowsing/test-track-simple.pset");
+ delFile("safebrowsing/test-trackwhite-simple.pset");
+ delFile("safebrowsing/moz-phish-simple.sbstore");
+ delFile("safebrowsing/moz-phish-simple.pset");
+ delFile("testLarge.pset");
+ delFile("testNoDelta.pset");
+}
+
+// Update uses allTables by default
+var allTables =
+ "test-phish-simple,test-malware-simple,test-unwanted-simple,test-track-simple,test-trackwhite-simple,test-block-simple";
+var mozTables = "moz-phish-simple";
+
+var dbservice = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(
+ Ci.nsIUrlClassifierDBService
+);
+var streamUpdater = Cc[
+ "@mozilla.org/url-classifier/streamupdater;1"
+].getService(Ci.nsIUrlClassifierStreamUpdater);
+
+/*
+ * Builds an update from an object that looks like:
+ *{ "test-phish-simple" : [{
+ * "chunkType" : "a", // 'a' is assumed if not specified
+ * "chunkNum" : 1, // numerically-increasing chunk numbers are assumed
+ * // if not specified
+ * "urls" : [ "foo.com/a", "foo.com/b", "bar.com/" ]
+ * }
+ */
+
+function buildUpdate(update, hashSize) {
+ if (!hashSize) {
+ hashSize = 32;
+ }
+ var updateStr = "n:1000\n";
+
+ for (var tableName in update) {
+ if (tableName != "") {
+ updateStr += "i:" + tableName + "\n";
+ }
+ var chunks = update[tableName];
+ for (var j = 0; j < chunks.length; j++) {
+ var chunk = chunks[j];
+ var chunkType = chunk.chunkType ? chunk.chunkType : "a";
+ var chunkNum = chunk.chunkNum ? chunk.chunkNum : j;
+ updateStr += chunkType + ":" + chunkNum + ":" + hashSize;
+
+ if (chunk.urls) {
+ var chunkData = chunk.urls.join("\n");
+ updateStr += ":" + chunkData.length + "\n" + chunkData;
+ }
+
+ updateStr += "\n";
+ }
+ }
+
+ return updateStr;
+}
+
+function buildPhishingUpdate(chunks, hashSize) {
+ return buildUpdate({ "test-phish-simple": chunks }, hashSize);
+}
+
+function buildMalwareUpdate(chunks, hashSize) {
+ return buildUpdate({ "test-malware-simple": chunks }, hashSize);
+}
+
+function buildUnwantedUpdate(chunks, hashSize) {
+ return buildUpdate({ "test-unwanted-simple": chunks }, hashSize);
+}
+
+function buildBlockedUpdate(chunks, hashSize) {
+ return buildUpdate({ "test-block-simple": chunks }, hashSize);
+}
+
+function buildMozPhishingUpdate(chunks, hashSize) {
+ return buildUpdate({ "moz-phish-simple": chunks }, hashSize);
+}
+
+function buildBareUpdate(chunks, hashSize) {
+ return buildUpdate({ "": chunks }, hashSize);
+}
+
+/**
+ * Performs an update of the dbservice manually, bypassing the stream updater
+ */
+function doSimpleUpdate(updateText, success, failure) {
+ var listener = {
+ QueryInterface: ChromeUtils.generateQI(["nsIUrlClassifierUpdateObserver"]),
+
+ updateUrlRequested(url) {},
+ streamFinished(status) {},
+ updateError(errorCode) {
+ failure(errorCode);
+ },
+ updateSuccess(requestedTimeout) {
+ success(requestedTimeout);
+ },
+ };
+
+ dbservice.beginUpdate(listener, allTables);
+ dbservice.beginStream("", "");
+ dbservice.updateStream(updateText);
+ dbservice.finishStream();
+ dbservice.finishUpdate();
+}
+
+/**
+ * Simulates a failed database update.
+ */
+function doErrorUpdate(tables, success, failure) {
+ var listener = {
+ QueryInterface: ChromeUtils.generateQI(["nsIUrlClassifierUpdateObserver"]),
+
+ updateUrlRequested(url) {},
+ streamFinished(status) {},
+ updateError(errorCode) {
+ success(errorCode);
+ },
+ updateSuccess(requestedTimeout) {
+ failure(requestedTimeout);
+ },
+ };
+
+ dbservice.beginUpdate(listener, tables, null);
+ dbservice.beginStream("", "");
+ dbservice.cancelUpdate();
+}
+
+/**
+ * Performs an update of the dbservice using the stream updater and a
+ * data: uri
+ */
+function doStreamUpdate(updateText, success, failure, downloadFailure) {
+ var dataUpdate = "data:," + encodeURIComponent(updateText);
+
+ if (!downloadFailure) {
+ downloadFailure = failure;
+ }
+
+ streamUpdater.downloadUpdates(
+ allTables,
+ "",
+ true,
+ dataUpdate,
+ success,
+ failure,
+ downloadFailure
+ );
+}
+
+var gAssertions = {
+ tableData(expectedTables, cb) {
+ dbservice.getTables(function (tables) {
+ // rebuild the tables in a predictable order.
+ var parts = tables.split("\n");
+ while (parts[parts.length - 1] == "") {
+ parts.pop();
+ }
+ parts.sort();
+ tables = parts.join("\n");
+
+ Assert.equal(tables, expectedTables);
+ cb();
+ });
+ },
+
+ checkUrls(urls, expected, cb, useMoz = false) {
+ // work with a copy of the list.
+ urls = urls.slice(0);
+ var doLookup = function () {
+ if (urls.length) {
+ var tables = useMoz ? mozTables : allTables;
+ var fragment = urls.shift();
+ var principal = Services.scriptSecurityManager.createContentPrincipal(
+ Services.io.newURI("http://" + fragment),
+ {}
+ );
+ dbservice.lookup(
+ principal,
+ tables,
+ function (arg) {
+ Assert.equal(expected, arg);
+ doLookup();
+ },
+ true
+ );
+ } else {
+ cb();
+ }
+ };
+ doLookup();
+ },
+
+ checkTables(url, expected, cb) {
+ var principal = Services.scriptSecurityManager.createContentPrincipal(
+ Services.io.newURI("http://" + url),
+ {}
+ );
+ dbservice.lookup(
+ principal,
+ allTables,
+ function (tables) {
+ // Rebuild tables in a predictable order.
+ var parts = tables.split(",");
+ while (parts[parts.length - 1] == "") {
+ parts.pop();
+ }
+ parts.sort();
+ tables = parts.join(",");
+ Assert.equal(tables, expected);
+ cb();
+ },
+ true
+ );
+ },
+
+ urlsDontExist(urls, cb) {
+ this.checkUrls(urls, "", cb);
+ },
+
+ urlsExist(urls, cb) {
+ this.checkUrls(urls, "test-phish-simple", cb);
+ },
+
+ malwareUrlsExist(urls, cb) {
+ this.checkUrls(urls, "test-malware-simple", cb);
+ },
+
+ unwantedUrlsExist(urls, cb) {
+ this.checkUrls(urls, "test-unwanted-simple", cb);
+ },
+
+ blockedUrlsExist(urls, cb) {
+ this.checkUrls(urls, "test-block-simple", cb);
+ },
+
+ mozPhishingUrlsExist(urls, cb) {
+ this.checkUrls(urls, "moz-phish-simple", cb, true);
+ },
+
+ subsDontExist(urls, cb) {
+ // XXX: there's no interface for checking items in the subs table
+ cb();
+ },
+
+ subsExist(urls, cb) {
+ // XXX: there's no interface for checking items in the subs table
+ cb();
+ },
+
+ urlExistInMultipleTables(data, cb) {
+ this.checkTables(data.url, data.tables, cb);
+ },
+};
+
+/**
+ * Check a set of assertions against the gAssertions table.
+ */
+function checkAssertions(assertions, doneCallback) {
+ var checkAssertion = function () {
+ for (var i in assertions) {
+ var data = assertions[i];
+ delete assertions[i];
+ gAssertions[i](data, checkAssertion);
+ return;
+ }
+
+ doneCallback();
+ };
+
+ checkAssertion();
+}
+
+function updateError(arg) {
+ do_throw(arg);
+}
+
+/**
+ * Utility functions
+ */
+ChromeUtils.defineESModuleGetters(this, {
+ NetUtil: "resource://gre/modules/NetUtil.sys.mjs",
+});
+
+function readFileToString(aFilename) {
+ let f = do_get_file(aFilename);
+ let stream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
+ Ci.nsIFileInputStream
+ );
+ stream.init(f, -1, 0, 0);
+ let buf = NetUtil.readInputStreamToString(stream, stream.available());
+ return buf;
+}
+
+// Runs a set of updates, and then checks a set of assertions.
+function doUpdateTest(updates, assertions, successCallback, errorCallback) {
+ var errorUpdate = function () {
+ checkAssertions(assertions, errorCallback);
+ };
+
+ var runUpdate = function () {
+ if (updates.length) {
+ var update = updates.shift();
+ doStreamUpdate(update, runUpdate, errorUpdate, null);
+ } else {
+ checkAssertions(assertions, successCallback);
+ }
+ };
+
+ runUpdate();
+}
+
+var gTests;
+var gNextTest = 0;
+
+function runNextTest() {
+ if (gNextTest >= gTests.length) {
+ do_test_finished();
+ return;
+ }
+
+ dbservice.resetDatabase();
+ dbservice.setHashCompleter("test-phish-simple", null);
+
+ let test = gTests[gNextTest++];
+ dump("running " + test.name + "\n");
+ test();
+}
+
+function runTests(tests) {
+ gTests = tests;
+ runNextTest();
+}
+
+var timerArray = [];
+
+function Timer(delay, cb) {
+ this.cb = cb;
+ var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ timer.initWithCallback(this, delay, timer.TYPE_ONE_SHOT);
+ timerArray.push(timer);
+}
+
+Timer.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsITimerCallback"]),
+ notify(timer) {
+ this.cb();
+ },
+};
+
+// LFSRgenerator is a 32-bit linear feedback shift register random number
+// generator. It is highly predictable and is not intended to be used for
+// cryptography but rather to allow easier debugging than a test that uses
+// Math.random().
+function LFSRgenerator(seed) {
+ // Force |seed| to be a number.
+ seed = +seed;
+ // LFSR generators do not work with a value of 0.
+ if (seed == 0) {
+ seed = 1;
+ }
+
+ this._value = seed;
+}
+LFSRgenerator.prototype = {
+ // nextNum returns a random unsigned integer of in the range [0,2^|bits|].
+ nextNum(bits) {
+ if (!bits) {
+ bits = 32;
+ }
+
+ let val = this._value;
+ // Taps are 32, 22, 2 and 1.
+ let bit = ((val >>> 0) ^ (val >>> 10) ^ (val >>> 30) ^ (val >>> 31)) & 1;
+ val = (val >>> 1) | (bit << 31);
+ this._value = val;
+
+ return val >>> (32 - bits);
+ },
+};
+
+function waitUntilMetaDataSaved(expectedState, expectedChecksum, callback) {
+ let dbService = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(
+ Ci.nsIUrlClassifierDBService
+ );
+
+ dbService.getTables(metaData => {
+ info("metadata: " + metaData);
+ let didCallback = false;
+ metaData.split("\n").some(line => {
+ // Parse [tableName];[stateBase64]
+ let p = line.indexOf(";");
+ if (-1 === p) {
+ return false; // continue.
+ }
+ let tableName = line.substring(0, p);
+ let metadata = line.substring(p + 1).split(":");
+ let stateBase64 = metadata[0];
+ let checksumBase64 = metadata[1];
+
+ if (tableName !== "test-phish-proto") {
+ return false; // continue.
+ }
+
+ if (
+ stateBase64 === btoa(expectedState) &&
+ checksumBase64 === btoa(expectedChecksum)
+ ) {
+ info("State has been saved to disk!");
+
+ // We slightly defer the callback to see if the in-memory
+ // |getTables| caching works correctly.
+ dbService.getTables(cachedMetadata => {
+ equal(cachedMetadata, metaData);
+ callback();
+ });
+
+ // Even though we haven't done callback at this moment
+ // but we still claim "we have" in order to stop repeating
+ // a new timer.
+ didCallback = true;
+ }
+
+ return true; // break no matter whether the state is matching.
+ });
+
+ if (!didCallback) {
+ do_timeout(
+ 1000,
+ waitUntilMetaDataSaved.bind(
+ null,
+ expectedState,
+ expectedChecksum,
+ callback
+ )
+ );
+ }
+ });
+}
+
+var gUpdateFinishedObserverEnabled = false;
+var gUpdateFinishedObserver = function (aSubject, aTopic, aData) {
+ info("[" + aTopic + "] " + aData);
+ if (aData != "success") {
+ updateError(aData);
+ }
+};
+
+function throwOnUpdateErrors() {
+ Services.obs.addObserver(
+ gUpdateFinishedObserver,
+ "safebrowsing-update-finished"
+ );
+ gUpdateFinishedObserverEnabled = true;
+}
+
+function stopThrowingOnUpdateErrors() {
+ if (gUpdateFinishedObserverEnabled) {
+ Services.obs.removeObserver(
+ gUpdateFinishedObserver,
+ "safebrowsing-update-finished"
+ );
+ gUpdateFinishedObserverEnabled = false;
+ }
+}
+
+cleanUp();
+
+registerCleanupFunction(function () {
+ cleanUp();
+});
diff --git a/toolkit/components/url-classifier/tests/unit/test_addsub.js b/toolkit/components/url-classifier/tests/unit/test_addsub.js
new file mode 100644
index 0000000000..f58a02506f
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_addsub.js
@@ -0,0 +1,329 @@
+function doTest(updates, assertions) {
+ doUpdateTest(updates, assertions, runNextTest, updateError);
+}
+
+// Test an add of two urls to a fresh database
+function testSimpleAdds() {
+ var addUrls = ["foo.com/a", "foo.com/b", "bar.com/c"];
+ var update = buildPhishingUpdate([{ chunkNum: 1, urls: addUrls }]);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1",
+ urlsExist: addUrls,
+ };
+
+ doTest([update], assertions);
+}
+
+// Same as testSimpleAdds, but make the same-domain URLs come from different
+// chunks.
+function testMultipleAdds() {
+ var add1Urls = ["foo.com/a", "bar.com/c"];
+ var add2Urls = ["foo.com/b"];
+
+ var update = buildPhishingUpdate([
+ { chunkNum: 1, urls: add1Urls },
+ { chunkNum: 2, urls: add2Urls },
+ ]);
+ var assertions = {
+ tableData: "test-phish-simple;a:1-2",
+ urlsExist: add1Urls.concat(add2Urls),
+ };
+
+ doTest([update], assertions);
+}
+
+// Test that a sub will remove an existing add
+function testSimpleSub() {
+ var addUrls = ["foo.com/a", "bar.com/b"];
+ var subUrls = ["1:foo.com/a"];
+
+ var addUpdate = buildPhishingUpdate([
+ {
+ chunkNum: 1, // adds and subtracts don't share a chunk numbering space
+ urls: addUrls,
+ },
+ ]);
+
+ var subUpdate = buildPhishingUpdate([
+ { chunkNum: 50, chunkType: "s", urls: subUrls },
+ ]);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1:s:50",
+ urlsExist: ["bar.com/b"],
+ urlsDontExist: ["foo.com/a"],
+ subsDontExist: ["foo.com/a"],
+ };
+
+ doTest([addUpdate, subUpdate], assertions);
+}
+
+// Same as testSimpleSub(), but the sub comes in before the add.
+function testSubEmptiesAdd() {
+ var subUrls = ["1:foo.com/a"];
+ var addUrls = ["foo.com/a", "bar.com/b"];
+
+ var subUpdate = buildPhishingUpdate([
+ { chunkNum: 50, chunkType: "s", urls: subUrls },
+ ]);
+
+ var addUpdate = buildPhishingUpdate([{ chunkNum: 1, urls: addUrls }]);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1:s:50",
+ urlsExist: ["bar.com/b"],
+ urlsDontExist: ["foo.com/a"],
+ subsDontExist: ["foo.com/a"], // this sub was found, it shouldn't exist anymore
+ };
+
+ doTest([subUpdate, addUpdate], assertions);
+}
+
+// Very similar to testSubEmptiesAdd, except that the domain entry will
+// still have an item left over that needs to be synced.
+function testSubPartiallyEmptiesAdd() {
+ var subUrls = ["1:foo.com/a"];
+ var addUrls = ["foo.com/a", "foo.com/b", "bar.com/b"];
+
+ var subUpdate = buildPhishingUpdate([
+ { chunkNum: 1, chunkType: "s", urls: subUrls },
+ ]);
+
+ var addUpdate = buildPhishingUpdate([
+ {
+ chunkNum: 1, // adds and subtracts don't share a chunk numbering space
+ urls: addUrls,
+ },
+ ]);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1:s:1",
+ urlsExist: ["foo.com/b", "bar.com/b"],
+ urlsDontExist: ["foo.com/a"],
+ subsDontExist: ["foo.com/a"], // this sub was found, it shouldn't exist anymore
+ };
+
+ doTest([subUpdate, addUpdate], assertions);
+}
+
+// We SHOULD be testing that pending subs are removed using
+// subsDontExist assertions. Since we don't have a good interface for getting
+// at sub entries, we'll verify it by side-effect. Subbing a url once
+// then adding it twice should leave the url intact.
+function testPendingSubRemoved() {
+ var subUrls = ["1:foo.com/a", "2:foo.com/b"];
+ var addUrls = ["foo.com/a", "foo.com/b"];
+
+ var subUpdate = buildPhishingUpdate([
+ { chunkNum: 1, chunkType: "s", urls: subUrls },
+ ]);
+
+ var addUpdate1 = buildPhishingUpdate([
+ {
+ chunkNum: 1, // adds and subtracts don't share a chunk numbering space
+ urls: addUrls,
+ },
+ ]);
+
+ var addUpdate2 = buildPhishingUpdate([{ chunkNum: 2, urls: addUrls }]);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1-2:s:1",
+ urlsExist: ["foo.com/a", "foo.com/b"],
+ subsDontExist: ["foo.com/a", "foo.com/b"], // this sub was found, it shouldn't exist anymore
+ };
+
+ doTest([subUpdate, addUpdate1, addUpdate2], assertions);
+}
+
+// Make sure that a saved sub is removed when the sub chunk is expired.
+function testPendingSubExpire() {
+ var subUrls = ["1:foo.com/a", "1:foo.com/b"];
+ var addUrls = ["foo.com/a", "foo.com/b"];
+
+ var subUpdate = buildPhishingUpdate([
+ { chunkNum: 1, chunkType: "s", urls: subUrls },
+ ]);
+
+ var expireUpdate = buildPhishingUpdate([{ chunkNum: 1, chunkType: "sd" }]);
+
+ var addUpdate = buildPhishingUpdate([
+ {
+ chunkNum: 1, // adds and subtracts don't share a chunk numbering space
+ urls: addUrls,
+ },
+ ]);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1",
+ urlsExist: ["foo.com/a", "foo.com/b"],
+ subsDontExist: ["foo.com/a", "foo.com/b"], // this sub was expired
+ };
+
+ doTest([subUpdate, expireUpdate, addUpdate], assertions);
+}
+
+// Make sure that the sub url removes from only the chunk that it specifies
+function testDuplicateAdds() {
+ var urls = ["foo.com/a"];
+
+ var addUpdate1 = buildPhishingUpdate([{ chunkNum: 1, urls }]);
+ var addUpdate2 = buildPhishingUpdate([{ chunkNum: 2, urls }]);
+ var subUpdate = buildPhishingUpdate([
+ { chunkNum: 3, chunkType: "s", urls: ["2:foo.com/a"] },
+ ]);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1-2:s:3",
+ urlsExist: ["foo.com/a"],
+ subsDontExist: ["foo.com/a"],
+ };
+
+ doTest([addUpdate1, addUpdate2, subUpdate], assertions);
+}
+
+// Tests a sub which matches some existing adds but leaves others.
+function testSubPartiallyMatches() {
+ var addUrls = ["1:foo.com/a", "2:foo.com/b"];
+
+ var addUpdate = buildPhishingUpdate([{ chunkNum: 1, urls: addUrls }]);
+
+ var subUpdate = buildPhishingUpdate([
+ { chunkNum: 1, chunkType: "s", urls: addUrls },
+ ]);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1:s:1",
+ urlsDontExist: ["foo.com/a"],
+ subsDontExist: ["foo.com/a"],
+ subsExist: ["foo.com/b"],
+ };
+
+ doTest([addUpdate, subUpdate], assertions);
+}
+
+// XXX: because subsExist isn't actually implemented, this is the same
+// test as above but with a second add chunk that should fail to be added
+// because of a pending sub chunk.
+function testSubPartiallyMatches2() {
+ var addUrls = ["foo.com/a"];
+ var subUrls = ["1:foo.com/a", "2:foo.com/b"];
+ var addUrls2 = ["foo.com/b"];
+
+ var addUpdate = buildPhishingUpdate([{ chunkNum: 1, urls: addUrls }]);
+
+ var subUpdate = buildPhishingUpdate([
+ { chunkNum: 1, chunkType: "s", urls: subUrls },
+ ]);
+
+ var addUpdate2 = buildPhishingUpdate([{ chunkNum: 2, urls: addUrls2 }]);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1-2:s:1",
+ urlsDontExist: ["foo.com/a", "foo.com/b"],
+ subsDontExist: ["foo.com/a", "foo.com/b"],
+ };
+
+ doTest([addUpdate, subUpdate, addUpdate2], assertions);
+}
+
+// Verify that two subs for the same domain but from different chunks
+// match (tests that existing sub entries are properly updated)
+function testSubsDifferentChunks() {
+ var subUrls1 = ["3:foo.com/a"];
+ var subUrls2 = ["3:foo.com/b"];
+
+ var addUrls = ["foo.com/a", "foo.com/b", "foo.com/c"];
+
+ var subUpdate1 = buildPhishingUpdate([
+ { chunkNum: 1, chunkType: "s", urls: subUrls1 },
+ ]);
+ var subUpdate2 = buildPhishingUpdate([
+ { chunkNum: 2, chunkType: "s", urls: subUrls2 },
+ ]);
+ var addUpdate = buildPhishingUpdate([{ chunkNum: 3, urls: addUrls }]);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:3:s:1-2",
+ urlsExist: ["foo.com/c"],
+ urlsDontExist: ["foo.com/a", "foo.com/b"],
+ subsDontExist: ["foo.com/a", "foo.com/b"],
+ };
+
+ doTest([subUpdate1, subUpdate2, addUpdate], assertions);
+}
+
+// for bug 534079
+function testSubsDifferentChunksSameHostId() {
+ var subUrls1 = ["1:foo.com/a"];
+ var subUrls2 = ["1:foo.com/b", "2:foo.com/c"];
+
+ var addUrls = ["foo.com/a", "foo.com/b"];
+ var addUrls2 = ["foo.com/c"];
+
+ var subUpdate1 = buildPhishingUpdate([
+ { chunkNum: 1, chunkType: "s", urls: subUrls1 },
+ ]);
+ var subUpdate2 = buildPhishingUpdate([
+ { chunkNum: 2, chunkType: "s", urls: subUrls2 },
+ ]);
+
+ var addUpdate = buildPhishingUpdate([{ chunkNum: 1, urls: addUrls }]);
+ var addUpdate2 = buildPhishingUpdate([{ chunkNum: 2, urls: addUrls2 }]);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1-2:s:1-2",
+ urlsDontExist: ["foo.com/c", "foo.com/b", "foo.com/a"],
+ };
+
+ doTest([addUpdate, addUpdate2, subUpdate1, subUpdate2], assertions);
+}
+
+// Test lists of expired chunks
+function testExpireLists() {
+ var addUpdate = buildPhishingUpdate([
+ { chunkNum: 1, urls: ["foo.com/a"] },
+ { chunkNum: 3, urls: ["bar.com/a"] },
+ { chunkNum: 4, urls: ["baz.com/a"] },
+ { chunkNum: 5, urls: ["blah.com/a"] },
+ ]);
+ var subUpdate = buildPhishingUpdate([
+ { chunkNum: 1, chunkType: "s", urls: ["50:foo.com/1"] },
+ { chunkNum: 2, chunkType: "s", urls: ["50:bar.com/1"] },
+ { chunkNum: 3, chunkType: "s", urls: ["50:baz.com/1"] },
+ { chunkNum: 5, chunkType: "s", urls: ["50:blah.com/1"] },
+ ]);
+
+ var expireUpdate = buildPhishingUpdate([
+ { chunkType: "ad:1,3-5" },
+ { chunkType: "sd:1-3,5" },
+ ]);
+
+ var assertions = {
+ // "tableData" : "test-phish-simple;"
+ tableData: "",
+ };
+
+ doTest([addUpdate, subUpdate, expireUpdate], assertions);
+}
+
+function run_test() {
+ runTests([
+ testSimpleAdds,
+ testMultipleAdds,
+ testSimpleSub,
+ testSubEmptiesAdd,
+ testSubPartiallyEmptiesAdd,
+ testPendingSubRemoved,
+ testPendingSubExpire,
+ testDuplicateAdds,
+ testSubPartiallyMatches,
+ testSubPartiallyMatches2,
+ testSubsDifferentChunks,
+ testSubsDifferentChunksSameHostId,
+ testExpireLists,
+ ]);
+}
+
+do_test_pending();
diff --git a/toolkit/components/url-classifier/tests/unit/test_backoff.js b/toolkit/components/url-classifier/tests/unit/test_backoff.js
new file mode 100644
index 0000000000..e78a3ee23c
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_backoff.js
@@ -0,0 +1,92 @@
+// Some unittests (e.g., paste into JS shell)
+var jslib =
+ Cc["@mozilla.org/url-classifier/jslib;1"].getService().wrappedJSObject;
+
+var jslibDate = Cu.getGlobalForObject(jslib).Date;
+
+var _Datenow = jslibDate.now;
+function setNow(time) {
+ jslibDate.now = function () {
+ return time;
+ };
+}
+
+function run_test() {
+ // 3 errors, 1ms retry period, max 3 requests per ten milliseconds,
+ // 5ms backoff interval, 19ms max delay
+ var rb = new jslib.RequestBackoff(3, 1, 3, 10, 5, 19, 0);
+ setNow(1);
+ rb.noteServerResponse(200);
+ Assert.ok(rb.canMakeRequest());
+ setNow(2);
+ Assert.ok(rb.canMakeRequest());
+
+ // First error should trigger a 1ms delay
+ rb.noteServerResponse(500);
+ Assert.ok(!rb.canMakeRequest());
+ Assert.equal(rb.nextRequestTime_, 3);
+ setNow(3);
+ Assert.ok(rb.canMakeRequest());
+
+ // Second error should also trigger a 1ms delay
+ rb.noteServerResponse(500);
+ Assert.ok(!rb.canMakeRequest());
+ Assert.equal(rb.nextRequestTime_, 4);
+ setNow(4);
+ Assert.ok(rb.canMakeRequest());
+
+ // Third error should trigger a 5ms backoff
+ rb.noteServerResponse(500);
+ Assert.ok(!rb.canMakeRequest());
+ Assert.equal(rb.nextRequestTime_, 9);
+ setNow(9);
+ Assert.ok(rb.canMakeRequest());
+
+ // Trigger backoff again
+ rb.noteServerResponse(503);
+ Assert.ok(!rb.canMakeRequest());
+ Assert.equal(rb.nextRequestTime_, 19);
+ setNow(19);
+ Assert.ok(rb.canMakeRequest());
+
+ // Trigger backoff a third time and hit max timeout
+ rb.noteServerResponse(302);
+ Assert.ok(!rb.canMakeRequest());
+ Assert.equal(rb.nextRequestTime_, 38);
+ setNow(38);
+ Assert.ok(rb.canMakeRequest());
+
+ // One more backoff, should still be at the max timeout
+ rb.noteServerResponse(400);
+ Assert.ok(!rb.canMakeRequest());
+ Assert.equal(rb.nextRequestTime_, 57);
+ setNow(57);
+ Assert.ok(rb.canMakeRequest());
+
+ // Request goes through
+ rb.noteServerResponse(200);
+ Assert.ok(rb.canMakeRequest());
+ Assert.equal(rb.nextRequestTime_, 0);
+ setNow(58);
+ rb.noteServerResponse(500);
+
+ // Another error, should trigger a 1ms backoff
+ Assert.ok(!rb.canMakeRequest());
+ Assert.equal(rb.nextRequestTime_, 59);
+
+ setNow(59);
+ Assert.ok(rb.canMakeRequest());
+
+ setNow(200);
+ rb.noteRequest();
+ setNow(201);
+ rb.noteRequest();
+ setNow(202);
+ Assert.ok(rb.canMakeRequest());
+ rb.noteRequest();
+ Assert.ok(!rb.canMakeRequest());
+ setNow(211);
+ Assert.ok(rb.canMakeRequest());
+
+ jslibDate.now = _Datenow;
+}
diff --git a/toolkit/components/url-classifier/tests/unit/test_bug1274685_unowned_list.js b/toolkit/components/url-classifier/tests/unit/test_bug1274685_unowned_list.js
new file mode 100644
index 0000000000..4668a901eb
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_bug1274685_unowned_list.js
@@ -0,0 +1,65 @@
+const { SafeBrowsing } = ChromeUtils.importESModule(
+ "resource://gre/modules/SafeBrowsing.sys.mjs"
+);
+const { updateAppInfo } = ChromeUtils.importESModule(
+ "resource://testing-common/AppInfo.sys.mjs"
+);
+
+add_setup(async () => {
+ // 'Cc["@mozilla.org/xre/app-info;1"]' for xpcshell has no nsIXULAppInfo
+ // so that we have to update it to make nsURLFormatter.js happy.
+ // (SafeBrowsing.init() will indirectly use nsURLFormatter.js)
+ updateAppInfo();
+
+ // This test should not actually try to create a connection to any real
+ // endpoint. But a background request could try that while the test is in
+ // progress before we've actually shut down networking, and would cause a
+ // crash due to connecting to a non-local IP.
+ Services.prefs.setCharPref(
+ "browser.safebrowsing.provider.mozilla.updateURL",
+ `http://localhost:4444/safebrowsing/update`
+ );
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref(
+ "browser.safebrowsing.provider.mozilla.updateURL"
+ );
+ Services.prefs.clearUserPref("browser.safebrowsing.provider.google.lists");
+ Services.prefs.clearUserPref("browser.safebrowsing.provider.google4.lists");
+ });
+});
+
+add_task(async function test() {
+ SafeBrowsing.init();
+
+ let origListV2 = Services.prefs.getCharPref(
+ "browser.safebrowsing.provider.google.lists"
+ );
+ let origListV4 = Services.prefs.getCharPref(
+ "browser.safebrowsing.provider.google4.lists"
+ );
+
+ // Ensure there's a list missing in both Safe Browsing V2 and V4.
+ let trimmedListV2 = origListV2.replace("goog-malware-shavar,", "");
+ Services.prefs.setCharPref(
+ "browser.safebrowsing.provider.google.lists",
+ trimmedListV2
+ );
+ let trimmedListV4 = origListV4.replace("goog-malware-proto,", "");
+ Services.prefs.setCharPref(
+ "browser.safebrowsing.provider.google4.lists",
+ trimmedListV4
+ );
+
+ try {
+ // Bug 1274685 - Unowned Safe Browsing tables break list updates
+ //
+ // If SafeBrowsing.registerTableWithURLs() doesn't check if
+ // a provider is found before registering table, an exception
+ // will be thrown while accessing a null object.
+ //
+ SafeBrowsing.registerTables();
+ ok(true, "SafeBrowsing.registerTables() did not throw.");
+ } catch (e) {
+ ok(false, "Exception thrown due to " + e.toString());
+ }
+});
diff --git a/toolkit/components/url-classifier/tests/unit/test_canonicalization.js b/toolkit/components/url-classifier/tests/unit/test_canonicalization.js
new file mode 100644
index 0000000000..e26bb5d84a
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_canonicalization.js
@@ -0,0 +1,83 @@
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function canonicalize(url) {
+ let urlUtils = Cc["@mozilla.org/url-classifier/utils;1"].getService(
+ Ci.nsIUrlClassifierUtils
+ );
+
+ let uri = Services.io.newURI(url);
+ return uri.scheme + "://" + urlUtils.getKeyForURI(uri);
+}
+
+function run_test() {
+ // These testcases are from
+ // https://developers.google.com/safe-browsing/v4/urls-hashing
+ equal(canonicalize("http://host/%25%32%35"), "http://host/%25");
+ equal(canonicalize("http://host/%25%32%35%25%32%35"), "http://host/%25%25");
+ equal(canonicalize("http://host/%2525252525252525"), "http://host/%25");
+ equal(canonicalize("http://host/asdf%25%32%35asd"), "http://host/asdf%25asd");
+ equal(
+ canonicalize("http://host/%%%25%32%35asd%%"),
+ "http://host/%25%25%25asd%25%25"
+ );
+ equal(canonicalize("http://www.google.com/"), "http://www.google.com/");
+ equal(
+ canonicalize(
+ "http://%31%36%38%2e%31%38%38%2e%39%39%2e%32%36/%2E%73%65%63%75%72%65/%77%77%77%2E%65%62%61%79%2E%63%6F%6D/"
+ ),
+ "http://168.188.99.26/.secure/www.ebay.com/"
+ );
+ equal(
+ canonicalize(
+ "http://195.127.0.11/uploads/%20%20%20%20/.verify/.eBaysecure=updateuserdataxplimnbqmn-xplmvalidateinfoswqpcmlx=hgplmcx/"
+ ),
+ "http://195.127.0.11/uploads/%20%20%20%20/.verify/.eBaysecure=updateuserdataxplimnbqmn-xplmvalidateinfoswqpcmlx=hgplmcx/"
+ );
+ equal(canonicalize("http://3279880203/blah"), "http://195.127.0.11/blah");
+ equal(
+ canonicalize("http://www.google.com/blah/.."),
+ "http://www.google.com/"
+ );
+ equal(
+ canonicalize("http://www.evil.com/blah#frag"),
+ "http://www.evil.com/blah"
+ );
+ equal(canonicalize("http://www.GOOgle.com/"), "http://www.google.com/");
+ equal(canonicalize("http://www.google.com.../"), "http://www.google.com/");
+ equal(
+ canonicalize("http://www.google.com/foo\tbar\rbaz\n2"),
+ "http://www.google.com/foobarbaz2"
+ );
+ equal(canonicalize("http://www.google.com/q?"), "http://www.google.com/q?");
+ equal(
+ canonicalize("http://www.google.com/q?r?"),
+ "http://www.google.com/q?r?"
+ );
+ equal(
+ canonicalize("http://www.google.com/q?r?s"),
+ "http://www.google.com/q?r?s"
+ );
+ equal(canonicalize("http://evil.com/foo#bar#baz"), "http://evil.com/foo");
+ equal(canonicalize("http://evil.com/foo;"), "http://evil.com/foo;");
+ equal(canonicalize("http://evil.com/foo?bar;"), "http://evil.com/foo?bar;");
+ equal(
+ canonicalize("http://notrailingslash.com"),
+ "http://notrailingslash.com/"
+ );
+ equal(
+ canonicalize("http://www.gotaport.com:1234/"),
+ "http://www.gotaport.com/"
+ );
+ equal(
+ canonicalize("https://www.securesite.com/"),
+ "https://www.securesite.com/"
+ );
+ equal(canonicalize("http://host.com/ab%23cd"), "http://host.com/ab%23cd");
+ equal(
+ canonicalize("http://host.com//twoslashes?more//slashes"),
+ "http://host.com/twoslashes?more//slashes"
+ );
+}
diff --git a/toolkit/components/url-classifier/tests/unit/test_channelClassifierService.js b/toolkit/components/url-classifier/tests/unit/test_channelClassifierService.js
new file mode 100644
index 0000000000..9d5f5edb65
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_channelClassifierService.js
@@ -0,0 +1,225 @@
+/* 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/. */
+
+"use strict";
+
+/* Unit tests for the nsIUrlClassifierSkipListService implementation. */
+
+var httpserver = new HttpServer();
+
+const { NetUtil } = ChromeUtils.importESModule(
+ "resource://gre/modules/NetUtil.sys.mjs"
+);
+const { UrlClassifierTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/UrlClassifierTestUtils.sys.mjs"
+);
+
+const FEATURE_STP_PREF = "privacy.trackingprotection.socialtracking.enabled";
+const TOP_LEVEL_DOMAIN = "http://www.example.com/";
+const TRACKER_DOMAIN = "http://social-tracking.example.org/";
+
+function setupChannel(uri, topUri = TOP_LEVEL_DOMAIN) {
+ httpserver.registerPathHandler("/", null);
+ httpserver.start(-1);
+
+ let channel = NetUtil.newChannel({
+ uri: uri + ":" + httpserver.identity.primaryPort,
+ loadingPrincipal: Services.scriptSecurityManager.createContentPrincipal(
+ NetUtil.newURI(topUri),
+ {}
+ ),
+ securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
+ });
+
+ channel
+ .QueryInterface(Ci.nsIHttpChannelInternal)
+ .setTopWindowURIIfUnknown(Services.io.newURI(topUri));
+
+ return channel;
+}
+
+function waitForBeforeBlockEvent(expected, callback) {
+ return new Promise(function (resolve) {
+ let observer = function observe(aSubject, aTopic, aData) {
+ switch (aTopic) {
+ case "urlclassifier-before-block-channel":
+ let channel = aSubject.QueryInterface(
+ Ci.nsIUrlClassifierBlockedChannel
+ );
+ Assert.equal(
+ channel.reason,
+ expected.reason,
+ "verify blocked reason"
+ );
+ Assert.equal(
+ channel.url,
+ expected.url,
+ "verify url of blocked channel"
+ );
+
+ if (callback) {
+ callback(channel);
+ }
+
+ service.removeListener(observer);
+ resolve(channel);
+ break;
+ }
+ };
+
+ let service = Cc[
+ "@mozilla.org/url-classifier/channel-classifier-service;1"
+ ].getService(Ci.nsIChannelClassifierService);
+ service.addListener(observer);
+ });
+}
+
+add_task(async function test_block_channel() {
+ Services.prefs.setBoolPref(FEATURE_STP_PREF, true);
+ await UrlClassifierTestUtils.addTestTrackers();
+
+ let channel = setupChannel(TRACKER_DOMAIN);
+
+ let blockPromise = waitForBeforeBlockEvent(
+ {
+ reason: Ci.nsIUrlClassifierBlockedChannel.SOCIAL_TRACKING_PROTECTION,
+ url: channel.URI.spec,
+ },
+ null
+ );
+
+ let openPromise = new Promise((resolve, reject) => {
+ channel.asyncOpen({
+ onStartRequest: (request, context) => {},
+ onDataAvailable: (request, context, stream, offset, count) => {},
+ onStopRequest: (request, status) => {
+ dump("status = " + status + "\n");
+ if (status == 200) {
+ Assert.ok(false, "Should not successfully open the channel");
+ } else {
+ Assert.equal(
+ status,
+ Cr.NS_ERROR_SOCIALTRACKING_URI,
+ "Should fail to open the channel"
+ );
+ }
+ resolve();
+ },
+ });
+ });
+
+ // wait for block event from url-classifier
+ await blockPromise;
+
+ // wait for onStopRequest callback from AsyncOpen
+ await openPromise;
+
+ // clean up
+ UrlClassifierTestUtils.cleanupTestTrackers();
+ Services.prefs.clearUserPref(FEATURE_STP_PREF);
+ httpserver.stop();
+});
+
+add_task(async function test_unblock_channel() {
+ Services.prefs.setBoolPref(FEATURE_STP_PREF, true);
+ //Services.prefs.setBoolPref("network.dns.native-is-localhost", true);
+
+ await UrlClassifierTestUtils.addTestTrackers();
+
+ let channel = setupChannel(TRACKER_DOMAIN);
+
+ let blockPromise = waitForBeforeBlockEvent(
+ {
+ reason: Ci.nsIUrlClassifierBlockedChannel.SOCIAL_TRACKING_PROTECTION,
+ url: channel.URI.spec,
+ },
+ ch => {
+ ch.replace();
+ }
+ );
+
+ let openPromise = new Promise((resolve, reject) => {
+ channel.asyncOpen({
+ onStartRequest: (request, context) => {},
+ onDataAvailable: (request, context, stream, offset, count) => {},
+ onStopRequest: (request, status) => {
+ if (status == Cr.NS_ERROR_SOCIALTRACKING_URI) {
+ Assert.ok(false, "Classifier should not cancel this channel");
+ } else {
+ // This request is supposed to fail, but we need to ensure it
+ // is not canceled by url-classifier
+ Assert.equal(
+ status,
+ Cr.NS_ERROR_UNKNOWN_HOST,
+ "Not cancel by classifier"
+ );
+ }
+ resolve();
+ },
+ });
+ });
+
+ // wait for block event from url-classifier
+ await blockPromise;
+
+ // wait for onStopRequest callback from AsyncOpen
+ await openPromise;
+
+ // clean up
+ UrlClassifierTestUtils.cleanupTestTrackers();
+ Services.prefs.clearUserPref(FEATURE_STP_PREF);
+ httpserver.stop();
+});
+
+add_task(async function test_allow_channel() {
+ Services.prefs.setBoolPref(FEATURE_STP_PREF, true);
+ //Services.prefs.setBoolPref("network.dns.native-is-localhost", true);
+
+ await UrlClassifierTestUtils.addTestTrackers();
+
+ let channel = setupChannel(TRACKER_DOMAIN);
+
+ let blockPromise = waitForBeforeBlockEvent(
+ {
+ reason: Ci.nsIUrlClassifierBlockedChannel.SOCIAL_TRACKING_PROTECTION,
+ url: channel.URI.spec,
+ },
+ ch => {
+ ch.allow();
+ }
+ );
+
+ let openPromise = new Promise((resolve, reject) => {
+ channel.asyncOpen({
+ onStartRequest: (request, context) => {},
+ onDataAvailable: (request, context, stream, offset, count) => {},
+ onStopRequest: (request, status) => {
+ if (status == Cr.NS_ERROR_SOCIALTRACKING_URI) {
+ Assert.ok(false, "Classifier should not cancel this channel");
+ } else {
+ // This request is supposed to fail, but we need to ensure it
+ // is not canceled by url-classifier
+ Assert.equal(
+ status,
+ Cr.NS_ERROR_UNKNOWN_HOST,
+ "Not cancel by classifier"
+ );
+ }
+ resolve();
+ },
+ });
+ });
+
+ // wait for block event from url-classifier
+ await blockPromise;
+
+ // wait for onStopRequest callback from AsyncOpen
+ await openPromise;
+
+ // clean up
+ UrlClassifierTestUtils.cleanupTestTrackers();
+ Services.prefs.clearUserPref(FEATURE_STP_PREF);
+ httpserver.stop();
+});
diff --git a/toolkit/components/url-classifier/tests/unit/test_dbservice.js b/toolkit/components/url-classifier/tests/unit/test_dbservice.js
new file mode 100644
index 0000000000..70ac02021a
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_dbservice.js
@@ -0,0 +1,329 @@
+var chunk1Urls = ["test.com/aba", "test.com/foo/bar", "foo.bar.com/a/b/c"];
+var chunk1 = chunk1Urls.join("\n");
+
+var chunk2Urls = [
+ "blah.com/a",
+ "baz.com/",
+ "255.255.0.1/",
+ "www.foo.com/test2?param=1",
+];
+var chunk2 = chunk2Urls.join("\n");
+
+var chunk3Urls = ["test.com/a", "foo.bar.com/a", "blah.com/a"];
+var chunk3 = chunk3Urls.join("\n");
+
+var chunk3SubUrls = ["1:test.com/a", "1:foo.bar.com/a", "2:blah.com/a"];
+var chunk3Sub = chunk3SubUrls.join("\n");
+
+var chunk4Urls = ["a.com/b", "b.com/c"];
+var chunk4 = chunk4Urls.join("\n");
+
+var chunk5Urls = ["d.com/e", "f.com/g"];
+var chunk5 = chunk5Urls.join("\n");
+
+var chunk6Urls = ["h.com/i", "j.com/k"];
+var chunk6 = chunk6Urls.join("\n");
+
+var chunk7Urls = ["l.com/m", "n.com/o"];
+var chunk7 = chunk7Urls.join("\n");
+
+// we are going to add chunks 1, 2, 4, 5, and 6 to phish-simple,
+// chunk 2 to malware-simple, and chunk 3 to unwanted-simple,
+// and chunk 7 to block-simple.
+// Then we'll remove the urls in chunk3 from phish-simple, then
+// expire chunk 1 and chunks 4-7 from phish-simple.
+var phishExpected = {};
+var phishUnexpected = {};
+var malwareExpected = {};
+var unwantedExpected = {};
+var blockedExpected = {};
+for (let i = 0; i < chunk2Urls.length; i++) {
+ phishExpected[chunk2Urls[i]] = true;
+ malwareExpected[chunk2Urls[i]] = true;
+}
+for (let i = 0; i < chunk3Urls.length; i++) {
+ unwantedExpected[chunk3Urls[i]] = true;
+ delete phishExpected[chunk3Urls[i]];
+ phishUnexpected[chunk3Urls[i]] = true;
+}
+for (let i = 0; i < chunk1Urls.length; i++) {
+ // chunk1 urls are expired
+ phishUnexpected[chunk1Urls[i]] = true;
+}
+for (let i = 0; i < chunk4Urls.length; i++) {
+ // chunk4 urls are expired
+ phishUnexpected[chunk4Urls[i]] = true;
+}
+for (let i = 0; i < chunk5Urls.length; i++) {
+ // chunk5 urls are expired
+ phishUnexpected[chunk5Urls[i]] = true;
+}
+for (let i = 0; i < chunk6Urls.length; i++) {
+ // chunk6 urls are expired
+ phishUnexpected[chunk6Urls[i]] = true;
+}
+for (let i = 0; i < chunk7Urls.length; i++) {
+ blockedExpected[chunk7Urls[i]] = true;
+ // chunk7 urls are expired
+ phishUnexpected[chunk7Urls[i]] = true;
+}
+
+// Check that the entries hit based on sub-parts
+phishExpected["baz.com/foo/bar"] = true;
+phishExpected["foo.bar.baz.com/foo"] = true;
+phishExpected["bar.baz.com/"] = true;
+
+var numExpecting;
+
+function testFailure(arg) {
+ do_throw(arg);
+}
+
+function checkNoHost() {
+ // Looking up a no-host uri such as a data: uri should throw an exception.
+ var exception;
+ try {
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ Services.io.newURI("data:text/html,<b>test</b>"),
+ {}
+ );
+ dbservice.lookup(principal, allTables);
+
+ exception = false;
+ } catch (e) {
+ exception = true;
+ }
+ Assert.ok(exception);
+
+ do_test_finished();
+}
+
+function tablesCallbackWithoutSub(tables) {
+ var parts = tables.split("\n");
+ parts.sort();
+
+ // there's a leading \n here because splitting left an empty string
+ // after the trailing newline, which will sort first
+ Assert.equal(
+ parts.join("\n"),
+ "\ntest-block-simple;a:1\ntest-malware-simple;a:1\ntest-phish-simple;a:2\ntest-unwanted-simple;a:1"
+ );
+
+ checkNoHost();
+}
+
+function expireSubSuccess(result) {
+ dbservice.getTables(tablesCallbackWithoutSub);
+}
+
+function tablesCallbackWithSub(tables) {
+ var parts = tables.split("\n");
+
+ let expectedChunks = [
+ "test-block-simple;a:1",
+ "test-malware-simple;a:1",
+ "test-phish-simple;a:2:s:3",
+ "test-unwanted-simple;a:1",
+ ];
+ for (let chunk of expectedChunks) {
+ Assert.ok(parts.includes(chunk));
+ }
+
+ // verify that expiring a sub chunk removes its name from the list
+ var data = "n:1000\ni:test-phish-simple\nsd:3\n";
+
+ doSimpleUpdate(data, expireSubSuccess, testFailure);
+}
+
+function checkChunksWithSub() {
+ dbservice.getTables(tablesCallbackWithSub);
+}
+
+function checkDone() {
+ if (--numExpecting == 0) {
+ checkChunksWithSub();
+ }
+}
+
+function phishExists(result) {
+ dumpn("phishExists: " + result);
+ try {
+ Assert.ok(result.includes("test-phish-simple"));
+ } finally {
+ checkDone();
+ }
+}
+
+function phishDoesntExist(result) {
+ dumpn("phishDoesntExist: " + result);
+ try {
+ Assert.ok(!result.includes("test-phish-simple"));
+ } finally {
+ checkDone();
+ }
+}
+
+function malwareExists(result) {
+ dumpn("malwareExists: " + result);
+
+ try {
+ Assert.ok(result.includes("test-malware-simple"));
+ } finally {
+ checkDone();
+ }
+}
+
+function unwantedExists(result) {
+ dumpn("unwantedExists: " + result);
+
+ try {
+ Assert.ok(result.includes("test-unwanted-simple"));
+ } finally {
+ checkDone();
+ }
+}
+
+function blockedExists(result) {
+ dumpn("blockedExists: " + result);
+
+ try {
+ Assert.ok(result.includes("test-block-simple"));
+ } finally {
+ checkDone();
+ }
+}
+
+function checkState() {
+ numExpecting = 0;
+
+ for (let key in phishExpected) {
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ Services.io.newURI("http://" + key),
+ {}
+ );
+ dbservice.lookup(principal, allTables, phishExists, true);
+ numExpecting++;
+ }
+
+ for (let key in phishUnexpected) {
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ Services.io.newURI("http://" + key),
+ {}
+ );
+ dbservice.lookup(principal, allTables, phishDoesntExist, true);
+ numExpecting++;
+ }
+
+ for (let key in malwareExpected) {
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ Services.io.newURI("http://" + key),
+ {}
+ );
+ dbservice.lookup(principal, allTables, malwareExists, true);
+ numExpecting++;
+ }
+
+ for (let key in unwantedExpected) {
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ Services.io.newURI("http://" + key),
+ {}
+ );
+ dbservice.lookup(principal, allTables, unwantedExists, true);
+ numExpecting++;
+ }
+
+ for (let key in blockedExpected) {
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ Services.io.newURI("http://" + key),
+ {}
+ );
+ dbservice.lookup(principal, allTables, blockedExists, true);
+ numExpecting++;
+ }
+}
+
+function testSubSuccess(result) {
+ Assert.equal(result, "1000");
+ checkState();
+}
+
+function do_subs() {
+ var data =
+ "n:1000\n" +
+ "i:test-phish-simple\n" +
+ "s:3:32:" +
+ chunk3Sub.length +
+ "\n" +
+ chunk3Sub +
+ "\n" +
+ "ad:1\n" +
+ "ad:4-6\n";
+
+ doSimpleUpdate(data, testSubSuccess, testFailure);
+}
+
+function testAddSuccess(arg) {
+ Assert.equal(arg, "1000");
+
+ do_subs();
+}
+
+function do_adds() {
+ // This test relies on the fact that only -regexp tables are ungzipped,
+ // and only -hash tables are assumed to be pre-md5'd. So we use
+ // a 'simple' table type to get simple hostname-per-line semantics.
+
+ var data =
+ "n:1000\n" +
+ "i:test-phish-simple\n" +
+ "a:1:32:" +
+ chunk1.length +
+ "\n" +
+ chunk1 +
+ "\n" +
+ "a:2:32:" +
+ chunk2.length +
+ "\n" +
+ chunk2 +
+ "\n" +
+ "a:4:32:" +
+ chunk4.length +
+ "\n" +
+ chunk4 +
+ "\n" +
+ "a:5:32:" +
+ chunk5.length +
+ "\n" +
+ chunk5 +
+ "\n" +
+ "a:6:32:" +
+ chunk6.length +
+ "\n" +
+ chunk6 +
+ "\n" +
+ "i:test-malware-simple\n" +
+ "a:1:32:" +
+ chunk2.length +
+ "\n" +
+ chunk2 +
+ "\n" +
+ "i:test-unwanted-simple\n" +
+ "a:1:32:" +
+ chunk3.length +
+ "\n" +
+ chunk3 +
+ "\n" +
+ "i:test-block-simple\n" +
+ "a:1:32:" +
+ chunk7.length +
+ "\n" +
+ chunk7 +
+ "\n";
+
+ doSimpleUpdate(data, testAddSuccess, testFailure);
+}
+
+function run_test() {
+ do_adds();
+ do_test_pending();
+}
diff --git a/toolkit/components/url-classifier/tests/unit/test_digest256.js b/toolkit/components/url-classifier/tests/unit/test_digest256.js
new file mode 100644
index 0000000000..f96f13f7d1
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_digest256.js
@@ -0,0 +1,143 @@
+// Global test server for serving safebrowsing updates.
+var gHttpServ = null;
+// Global nsIUrlClassifierDBService
+var gDbService = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(
+ Ci.nsIUrlClassifierDBService
+);
+
+// A map of tables to arrays of update redirect urls.
+var gTables = {};
+
+// Registers a table for which to serve update chunks. Returns a promise that
+// resolves when that chunk has been downloaded.
+function registerTableUpdate(aTable, aFilename) {
+ return new Promise(resolve => {
+ // If we haven't been given an update for this table yet, add it to the map
+ if (!(aTable in gTables)) {
+ gTables[aTable] = [];
+ }
+
+ // The number of chunks associated with this table.
+ let numChunks = gTables[aTable].length + 1;
+ let redirectPath = "/" + aTable + "-" + numChunks;
+ let redirectUrl = "localhost:4444" + redirectPath;
+
+ // Store redirect url for that table so we can return it later when we
+ // process an update request.
+ gTables[aTable].push(redirectUrl);
+
+ gHttpServ.registerPathHandler(redirectPath, function (request, response) {
+ info("Mock safebrowsing server handling request for " + redirectPath);
+ let contents = readFileToString(aFilename);
+ response.setHeader(
+ "Content-Type",
+ "application/vnd.google.safebrowsing-update",
+ false
+ );
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.bodyOutputStream.write(contents, contents.length);
+ resolve(contents);
+ });
+ });
+}
+
+// Construct a response with redirect urls.
+function processUpdateRequest() {
+ let response = "n:1000\n";
+ for (let table in gTables) {
+ response += "i:" + table + "\n";
+ for (let i = 0; i < gTables[table].length; ++i) {
+ response += "u:" + gTables[table][i] + "\n";
+ }
+ }
+ info("Returning update response: " + response);
+ return response;
+}
+
+// Set up our test server to handle update requests.
+function run_test() {
+ gHttpServ = new HttpServer();
+ gHttpServ.registerDirectory("/", do_get_cwd());
+
+ gHttpServ.registerPathHandler("/downloads", function (request, response) {
+ let blob = processUpdateRequest();
+ response.setHeader(
+ "Content-Type",
+ "application/vnd.google.safebrowsing-update",
+ false
+ );
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.bodyOutputStream.write(blob, blob.length);
+ });
+
+ gHttpServ.start(4444);
+ run_next_test();
+}
+
+// Just throw if we ever get an update or download error.
+function handleError(aEvent) {
+ do_throw("We didn't download or update correctly: " + aEvent);
+}
+
+add_test(function test_update() {
+ let streamUpdater = Cc[
+ "@mozilla.org/url-classifier/streamupdater;1"
+ ].getService(Ci.nsIUrlClassifierStreamUpdater);
+
+ // Load up some update chunks for the safebrowsing server to serve.
+ registerTableUpdate("goog-downloadwhite-digest256", "data/digest1.chunk");
+ registerTableUpdate("goog-downloadwhite-digest256", "data/digest2.chunk");
+
+ // Download some updates, and don't continue until the downloads are done.
+ function updateSuccess(aEvent) {
+ // Timeout of n:1000 is constructed in processUpdateRequest above and
+ // passed back in the callback in nsIUrlClassifierStreamUpdater on success.
+ Assert.equal("1000", aEvent);
+ info("All data processed");
+ run_next_test();
+ }
+ streamUpdater.downloadUpdates(
+ "goog-downloadwhite-digest256",
+ "goog-downloadwhite-digest256;\n",
+ true,
+ "http://localhost:4444/downloads",
+ updateSuccess,
+ handleError,
+ handleError
+ );
+});
+
+add_test(function test_url_not_whitelisted() {
+ let uri = Services.io.newURI("http://example.com");
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ uri,
+ {}
+ );
+ gDbService.lookup(
+ principal,
+ "goog-downloadwhite-digest256",
+ function handleEvent(aEvent) {
+ // This URI is not on any lists.
+ Assert.equal("", aEvent);
+ run_next_test();
+ }
+ );
+});
+
+add_test(function test_url_whitelisted() {
+ // Hash of "whitelisted.com/" (canonicalized URL) is:
+ // 93CA5F48E15E9861CD37C2D95DB43D23CC6E6DE5C3F8FA6E8BE66F97CC518907
+ let uri = Services.io.newURI("http://whitelisted.com");
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ uri,
+ {}
+ );
+ gDbService.lookup(
+ principal,
+ "goog-downloadwhite-digest256",
+ function handleEvent(aEvent) {
+ Assert.equal("goog-downloadwhite-digest256", aEvent);
+ run_next_test();
+ }
+ );
+});
diff --git a/toolkit/components/url-classifier/tests/unit/test_exceptionListService.js b/toolkit/components/url-classifier/tests/unit/test_exceptionListService.js
new file mode 100644
index 0000000000..218aec4934
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_exceptionListService.js
@@ -0,0 +1,285 @@
+/* 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/. */
+
+"use strict";
+
+/* Unit tests for the nsIUrlClassifierExceptionListService implementation. */
+
+const { RemoteSettings } = ChromeUtils.importESModule(
+ "resource://services-settings/remote-settings.sys.mjs"
+);
+
+const COLLECTION_NAME = "url-classifier-skip-urls";
+const FEATURE_TRACKING_NAME = "tracking-annotation-test";
+const FEATURE_TRACKING_PREF_NAME = "urlclassifier.tracking-annotation-test";
+const FEATURE_SOCIAL_NAME = "socialtracking-annotation-test";
+const FEATURE_SOCIAL_PREF_NAME = "urlclassifier.socialtracking-annotation-test";
+const FEATURE_FINGERPRINTING_NAME = "fingerprinting-annotation-test";
+const FEATURE_FINGERPRINTING_PREF_NAME =
+ "urlclassifier.fingerprinting-annotation-test";
+
+do_get_profile();
+
+class UpdateEvent extends EventTarget {}
+function waitForEvent(element, eventName) {
+ return new Promise(function (resolve) {
+ element.addEventListener(eventName, e => resolve(e.detail), { once: true });
+ });
+}
+
+add_task(async function test_list_changes() {
+ let exceptionListService = Cc[
+ "@mozilla.org/url-classifier/exception-list-service;1"
+ ].getService(Ci.nsIUrlClassifierExceptionListService);
+
+ // Make sure we have a pref initially, since the exception list service
+ // requires it.
+ Services.prefs.setStringPref(FEATURE_TRACKING_PREF_NAME, "");
+
+ let updateEvent = new UpdateEvent();
+ let obs = data => {
+ let event = new CustomEvent("update", { detail: data });
+ updateEvent.dispatchEvent(event);
+ };
+
+ let records = [
+ {
+ id: "1",
+ last_modified: 1000000000000001,
+ feature: FEATURE_TRACKING_NAME,
+ pattern: "example.com",
+ },
+ ];
+
+ // Add some initial data.
+ let db = RemoteSettings(COLLECTION_NAME).db;
+ await db.importChanges({}, Date.now(), records);
+ let promise = waitForEvent(updateEvent, "update");
+
+ exceptionListService.registerAndRunExceptionListObserver(
+ FEATURE_TRACKING_NAME,
+ FEATURE_TRACKING_PREF_NAME,
+ obs
+ );
+
+ Assert.equal(await promise, "", "No items in the list");
+
+ // Second event is from the RemoteSettings record.
+ let list = await waitForEvent(updateEvent, "update");
+ Assert.equal(list, "example.com", "Has one item in the list");
+
+ records.push(
+ {
+ id: "2",
+ last_modified: 1000000000000002,
+ feature: FEATURE_TRACKING_NAME,
+ pattern: "MOZILLA.ORG",
+ },
+ {
+ id: "3",
+ last_modified: 1000000000000003,
+ feature: "some-other-feature",
+ pattern: "noinclude.com",
+ },
+ {
+ last_modified: 1000000000000004,
+ feature: FEATURE_TRACKING_NAME,
+ pattern: "*.example.org",
+ }
+ );
+
+ promise = waitForEvent(updateEvent, "update");
+
+ await RemoteSettings(COLLECTION_NAME).emit("sync", {
+ data: { current: records },
+ });
+
+ list = await promise;
+
+ Assert.equal(
+ list,
+ "example.com,mozilla.org,*.example.org",
+ "Has several items in the list"
+ );
+
+ promise = waitForEvent(updateEvent, "update");
+
+ Services.prefs.setStringPref(FEATURE_TRACKING_PREF_NAME, "test.com");
+
+ list = await promise;
+
+ Assert.equal(
+ list,
+ "test.com,example.com,mozilla.org,*.example.org",
+ "Has several items in the list"
+ );
+
+ promise = waitForEvent(updateEvent, "update");
+
+ Services.prefs.setStringPref(
+ FEATURE_TRACKING_PREF_NAME,
+ "test.com,whatever.com,*.abc.com"
+ );
+
+ list = await promise;
+
+ Assert.equal(
+ list,
+ "test.com,whatever.com,*.abc.com,example.com,mozilla.org,*.example.org",
+ "Has several items in the list"
+ );
+
+ exceptionListService.unregisterExceptionListObserver(
+ FEATURE_TRACKING_NAME,
+ obs
+ );
+ exceptionListService.clear();
+
+ await db.clear();
+});
+
+/**
+ * This test make sure when a feature registers itself to exceptionlist service,
+ * it can get the correct initial data.
+ */
+add_task(async function test_list_init_data() {
+ let exceptionListService = Cc[
+ "@mozilla.org/url-classifier/exception-list-service;1"
+ ].getService(Ci.nsIUrlClassifierExceptionListService);
+
+ // Make sure we have a pref initially, since the exception list service
+ // requires it.
+ Services.prefs.setStringPref(FEATURE_TRACKING_PREF_NAME, "");
+
+ let updateEvent = new UpdateEvent();
+
+ let records = [
+ {
+ id: "1",
+ last_modified: 1000000000000001,
+ feature: FEATURE_TRACKING_NAME,
+ pattern: "tracking.example.com",
+ },
+ {
+ id: "2",
+ last_modified: 1000000000000002,
+ feature: FEATURE_SOCIAL_NAME,
+ pattern: "social.example.com",
+ },
+ {
+ id: "3",
+ last_modified: 1000000000000003,
+ feature: FEATURE_TRACKING_NAME,
+ pattern: "*.tracking.org",
+ },
+ {
+ id: "4",
+ last_modified: 1000000000000004,
+ feature: FEATURE_SOCIAL_NAME,
+ pattern: "MOZILLA.ORG",
+ },
+ ];
+
+ // Add some initial data.
+ let db = RemoteSettings(COLLECTION_NAME).db;
+ await db.importChanges({}, Date.now(), records);
+
+ // The first registered feature make ExceptionListService get the initial data
+ // from remote setting.
+ let promise = waitForEvent(updateEvent, "update");
+
+ let obs = data => {
+ let event = new CustomEvent("update", { detail: data });
+ updateEvent.dispatchEvent(event);
+ };
+ exceptionListService.registerAndRunExceptionListObserver(
+ FEATURE_TRACKING_NAME,
+ FEATURE_TRACKING_PREF_NAME,
+ obs
+ );
+
+ let list = await promise;
+ Assert.equal(list, "", "Empty list initially");
+
+ Assert.equal(
+ await waitForEvent(updateEvent, "update"),
+ "tracking.example.com,*.tracking.org",
+ "Has several items in the list"
+ );
+
+ // Register another feature after ExceptionListService got the initial data.
+ promise = waitForEvent(updateEvent, "update");
+
+ exceptionListService.registerAndRunExceptionListObserver(
+ FEATURE_SOCIAL_NAME,
+ FEATURE_SOCIAL_PREF_NAME,
+ obs
+ );
+
+ list = await promise;
+
+ Assert.equal(
+ list,
+ "social.example.com,mozilla.org",
+ "Has several items in the list"
+ );
+
+ // Test registering a feature after ExceptionListService recieved the synced data.
+ records.push(
+ {
+ id: "5",
+ last_modified: 1000000000000002,
+ feature: FEATURE_FINGERPRINTING_NAME,
+ pattern: "fingerprinting.example.com",
+ },
+ {
+ id: "6",
+ last_modified: 1000000000000002,
+ feature: "other-fature",
+ pattern: "not-a-fingerprinting.example.com",
+ },
+ {
+ id: "7",
+ last_modified: 1000000000000002,
+ feature: FEATURE_FINGERPRINTING_NAME,
+ pattern: "*.fingerprinting.org",
+ }
+ );
+
+ await RemoteSettings(COLLECTION_NAME).emit("sync", {
+ data: { current: records },
+ });
+
+ promise = waitForEvent(updateEvent, "update");
+
+ exceptionListService.registerAndRunExceptionListObserver(
+ FEATURE_FINGERPRINTING_NAME,
+ FEATURE_FINGERPRINTING_PREF_NAME,
+ obs
+ );
+
+ list = await promise;
+
+ Assert.equal(
+ list,
+ "fingerprinting.example.com,*.fingerprinting.org",
+ "Has several items in the list"
+ );
+
+ exceptionListService.unregisterExceptionListObserver(
+ FEATURE_TRACKING_NAME,
+ obs
+ );
+ exceptionListService.unregisterExceptionListObserver(
+ FEATURE_SOCIAL_NAME,
+ obs
+ );
+ exceptionListService.unregisterExceptionListObserver(
+ FEATURE_FINGERPRINTING_NAME,
+ obs
+ );
+ exceptionListService.clear();
+
+ await db.clear();
+});
diff --git a/toolkit/components/url-classifier/tests/unit/test_features.js b/toolkit/components/url-classifier/tests/unit/test_features.js
new file mode 100644
index 0000000000..07b22b1b21
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_features.js
@@ -0,0 +1,81 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+*/
+
+"use strict";
+
+add_test(async _ => {
+ ok(
+ Services.cookies,
+ "Force the cookie service to be initialized to avoid issues later. " +
+ "See https://bugzilla.mozilla.org/show_bug.cgi?id=1621759#c3"
+ );
+
+ let classifier = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(
+ Ci.nsIURIClassifier
+ );
+ ok(!!classifier, "We have the URI-Classifier");
+
+ var tests = [
+ { name: "a", expectedResult: false },
+ { name: "tracking-annotation", expectedResult: true },
+ { name: "tracking-protection", expectedResult: true },
+ ];
+
+ tests.forEach(test => {
+ let feature;
+ try {
+ feature = classifier.getFeatureByName(test.name);
+ } catch (e) {}
+
+ equal(
+ !!feature,
+ test.expectedResult,
+ "Exceptected result for: " + test.name
+ );
+ if (feature) {
+ equal(feature.name, test.name, "Feature name matches");
+ }
+ });
+
+ let uri = Services.io.newURI("https://example.com");
+
+ let feature = classifier.getFeatureByName("tracking-protection");
+
+ let results = await new Promise(resolve => {
+ classifier.asyncClassifyLocalWithFeatures(
+ uri,
+ [feature],
+ Ci.nsIUrlClassifierFeature.blocklist,
+ r => {
+ resolve(r);
+ }
+ );
+ });
+ equal(results.length, 0, "No tracker");
+
+ Services.prefs.setCharPref(
+ "urlclassifier.trackingTable.testEntries",
+ "example.com"
+ );
+
+ feature = classifier.getFeatureByName("tracking-protection");
+
+ results = await new Promise(resolve => {
+ classifier.asyncClassifyLocalWithFeatures(
+ uri,
+ [feature],
+ Ci.nsIUrlClassifierFeature.blocklist,
+ r => {
+ resolve(r);
+ }
+ );
+ });
+ equal(results.length, 1, "Tracker");
+ let result = results[0];
+ equal(result.feature.name, "tracking-protection", "Correct feature");
+ equal(result.list, "tracking-blocklist-pref", "Correct list");
+
+ Services.prefs.clearUserPref("browser.safebrowsing.password.enabled");
+ run_next_test();
+});
diff --git a/toolkit/components/url-classifier/tests/unit/test_hashcompleter.js b/toolkit/components/url-classifier/tests/unit/test_hashcompleter.js
new file mode 100644
index 0000000000..b8d6c7b128
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_hashcompleter.js
@@ -0,0 +1,438 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This test ensures that the nsIUrlClassifierHashCompleter works as expected
+// and simulates an HTTP server to provide completions.
+//
+// In order to test completions, each group of completions sent as one request
+// to the HTTP server is called a completion set. There is currently not
+// support for multiple requests being sent to the server at once, in this test.
+// This tests makes a request for each element of |completionSets|, waits for
+// a response and then moves to the next element.
+// Each element of |completionSets| is an array of completions, and each
+// completion is an object with the properties:
+// hash: complete hash for the completion. Automatically right-padded
+// to be COMPLETE_LENGTH.
+// expectCompletion: boolean indicating whether the server should respond
+// with a full hash.
+// forceServerError: boolean indicating whether the server should respond
+// with a 503.
+// table: name of the table that the hash corresponds to. Only needs to be set
+// if a completion is expected.
+// chunkId: positive integer corresponding to the chunk that the hash belongs
+// to. Only needs to be set if a completion is expected.
+// multipleCompletions: boolean indicating whether the server should respond
+// with more than one full hash. If this is set to true
+// then |expectCompletion| must also be set to true and
+// |hash| must have the same prefix as all |completions|.
+// completions: an array of completions (objects with a hash, table and
+// chunkId property as described above). This property is only
+// used when |multipleCompletions| is set to true.
+
+// Basic prefixes with 2/3 completions.
+var basicCompletionSet = [
+ {
+ hash: "abcdefgh",
+ expectCompletion: true,
+ table: "test",
+ chunkId: 1234,
+ },
+ {
+ hash: "1234",
+ expectCompletion: false,
+ },
+ {
+ hash: "\u0000\u0000\u000012312",
+ expectCompletion: true,
+ table: "test",
+ chunkId: 1234,
+ },
+];
+
+// 3 prefixes with 0 completions to test HashCompleter handling a 204 status.
+var falseCompletionSet = [
+ {
+ hash: "1234",
+ expectCompletion: false,
+ },
+ {
+ hash: "",
+ expectCompletion: false,
+ },
+ {
+ hash: "abc",
+ expectCompletion: false,
+ },
+];
+
+// The current implementation (as of Mar 2011) sometimes sends duplicate
+// entries to HashCompleter and even expects responses for duplicated entries.
+var dupedCompletionSet = [
+ {
+ hash: "1234",
+ expectCompletion: true,
+ table: "test",
+ chunkId: 1,
+ },
+ {
+ hash: "5678",
+ expectCompletion: false,
+ table: "test2",
+ chunkId: 2,
+ },
+ {
+ hash: "1234",
+ expectCompletion: true,
+ table: "test",
+ chunkId: 1,
+ },
+ {
+ hash: "5678",
+ expectCompletion: false,
+ table: "test2",
+ chunkId: 2,
+ },
+];
+
+// It is possible for a hash completion request to return with multiple
+// completions, the HashCompleter should return all of these.
+var multipleResponsesCompletionSet = [
+ {
+ hash: "1234",
+ expectCompletion: true,
+ multipleCompletions: true,
+ completions: [
+ {
+ hash: "123456",
+ table: "test1",
+ chunkId: 3,
+ },
+ {
+ hash: "123478",
+ table: "test2",
+ chunkId: 4,
+ },
+ ],
+ },
+];
+
+function buildCompletionRequest(aCompletionSet) {
+ let prefixes = [];
+ let prefixSet = new Set();
+ aCompletionSet.forEach(s => {
+ let prefix = s.hash.substring(0, 4);
+ if (prefixSet.has(prefix)) {
+ return;
+ }
+ prefixSet.add(prefix);
+ prefixes.push(prefix);
+ });
+ return 4 + ":" + 4 * prefixes.length + "\n" + prefixes.join("");
+}
+
+function parseCompletionRequest(aRequest) {
+ // Format: [partial_length]:[num_of_prefix * partial_length]\n[prefixes_data]
+
+ let tokens = /(\d):(\d+)/.exec(aRequest);
+ if (tokens.length < 3) {
+ dump("Request format error.");
+ return null;
+ }
+
+ let partialLength = parseInt(tokens[1]);
+
+ let payloadStart =
+ tokens[1].length + // partial length
+ 1 + // ':'
+ tokens[2].length + // payload length
+ 1; // '\n'
+
+ let prefixSet = [];
+ for (let i = payloadStart; i < aRequest.length; i += partialLength) {
+ let prefix = aRequest.substr(i, partialLength);
+ if (prefix.length !== partialLength) {
+ dump("Header info not correct: " + aRequest.substr(0, payloadStart));
+ return null;
+ }
+ prefixSet.push(prefix);
+ }
+ prefixSet.sort();
+
+ return prefixSet;
+}
+
+// Compare the requests in string format.
+function compareCompletionRequest(aRequest1, aRequest2) {
+ let prefixSet1 = parseCompletionRequest(aRequest1);
+ let prefixSet2 = parseCompletionRequest(aRequest2);
+
+ return equal(JSON.stringify(prefixSet1), JSON.stringify(prefixSet2));
+}
+
+// The fifth completion set is added at runtime by getRandomCompletionSet.
+// Each completion in the set only has one response and its purpose is to
+// provide an easy way to test the HashCompleter handling an arbitrarily large
+// completion set (determined by SIZE_OF_RANDOM_SET).
+const SIZE_OF_RANDOM_SET = 16;
+function getRandomCompletionSet(forceServerError) {
+ let completionSet = [];
+ let hashPrefixes = [];
+
+ let seed = Math.floor(Math.random() * Math.pow(2, 32));
+ dump("Using seed of " + seed + " for random completion set.\n");
+ let rand = new LFSRgenerator(seed);
+
+ for (let i = 0; i < SIZE_OF_RANDOM_SET; i++) {
+ let completion = {
+ expectCompletion: false,
+ forceServerError: false,
+ _finished: false,
+ };
+
+ // Generate a random 256 bit hash. First we get a random number and then
+ // convert it to a string.
+ let hash;
+ let prefix;
+ do {
+ hash = "";
+ let length = 1 + rand.nextNum(5);
+ for (let j = 0; j < length; j++) {
+ hash += String.fromCharCode(rand.nextNum(8));
+ }
+ prefix = hash.substring(0, 4);
+ } while (hashPrefixes.includes(prefix));
+
+ hashPrefixes.push(prefix);
+ completion.hash = hash;
+
+ if (!forceServerError) {
+ completion.expectCompletion = rand.nextNum(1) == 1;
+ } else {
+ completion.forceServerError = true;
+ }
+ if (completion.expectCompletion) {
+ // Generate a random alpha-numeric string of length start with "test" for the
+ // table name.
+ completion.table = "test" + rand.nextNum(31).toString(36);
+
+ completion.chunkId = rand.nextNum(16);
+ }
+ completionSet.push(completion);
+ }
+
+ return completionSet;
+}
+
+var completionSets = [
+ basicCompletionSet,
+ falseCompletionSet,
+ dupedCompletionSet,
+ multipleResponsesCompletionSet,
+];
+var currentCompletionSet = -1;
+var finishedCompletions = 0;
+
+const SERVER_PATH = "/hash-completer";
+var server;
+
+// Completion hashes are automatically right-padded with null chars to have a
+// length of COMPLETE_LENGTH.
+// Taken from nsUrlClassifierDBService.h
+const COMPLETE_LENGTH = 32;
+
+var completer = Cc["@mozilla.org/url-classifier/hashcompleter;1"].getService(
+ Ci.nsIUrlClassifierHashCompleter
+);
+
+var gethashUrl;
+
+// Expected highest completion set for which the server sends a response.
+var expectedMaxServerCompletionSet = 0;
+var maxServerCompletionSet = 0;
+
+function run_test() {
+ // This test case exercises the backoff functionality so we can't leave it disabled.
+ Services.prefs.setBoolPref(
+ "browser.safebrowsing.provider.test.disableBackoff",
+ false
+ );
+ // Generate a random completion set that return successful responses.
+ completionSets.push(getRandomCompletionSet(false));
+ // We backoff after receiving an error, so requests shouldn't reach the
+ // server after that.
+ expectedMaxServerCompletionSet = completionSets.length;
+ // Generate some completion sets that return 503s.
+ for (let j = 0; j < 10; ++j) {
+ completionSets.push(getRandomCompletionSet(true));
+ }
+
+ // Fix up the completions before running the test.
+ for (let completionSet of completionSets) {
+ for (let completion of completionSet) {
+ // Pad the right of each |hash| so that the length is COMPLETE_LENGTH.
+ if (completion.multipleCompletions) {
+ for (let responseCompletion of completion.completions) {
+ let numChars = COMPLETE_LENGTH - responseCompletion.hash.length;
+ responseCompletion.hash += new Array(numChars + 1).join("\u0000");
+ }
+ } else {
+ let numChars = COMPLETE_LENGTH - completion.hash.length;
+ completion.hash += new Array(numChars + 1).join("\u0000");
+ }
+ }
+ }
+ do_test_pending();
+
+ server = new HttpServer();
+ server.registerPathHandler(SERVER_PATH, hashCompleterServer);
+
+ server.start(-1);
+ const SERVER_PORT = server.identity.primaryPort;
+
+ gethashUrl = "http://localhost:" + SERVER_PORT + SERVER_PATH;
+
+ runNextCompletion();
+}
+
+function runNextCompletion() {
+ // The server relies on currentCompletionSet to send the correct response, so
+ // don't increment it until we start the new set of callbacks.
+ currentCompletionSet++;
+ if (currentCompletionSet >= completionSets.length) {
+ finish();
+ return;
+ }
+
+ dump(
+ "Now on completion set index " +
+ currentCompletionSet +
+ ", length " +
+ completionSets[currentCompletionSet].length +
+ "\n"
+ );
+ // Number of finished completions for this set.
+ finishedCompletions = 0;
+ for (let completion of completionSets[currentCompletionSet]) {
+ completer.complete(
+ completion.hash.substring(0, 4),
+ gethashUrl,
+ "test-phish-shavar", // Could be arbitrary v2 table name.
+ new callback(completion)
+ );
+ }
+}
+
+function hashCompleterServer(aRequest, aResponse) {
+ let stream = aRequest.bodyInputStream;
+ let wrapperStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(
+ Ci.nsIBinaryInputStream
+ );
+ wrapperStream.setInputStream(stream);
+
+ let len = stream.available();
+ let data = wrapperStream.readBytes(len);
+
+ // Check if we got the expected completion request.
+ let expectedRequest = buildCompletionRequest(
+ completionSets[currentCompletionSet]
+ );
+ compareCompletionRequest(data, expectedRequest);
+
+ // To avoid a response with duplicate hash completions, we keep track of all
+ // completed hash prefixes so far.
+ let completedHashes = [];
+ let responseText = "";
+
+ function responseForCompletion(x) {
+ return x.table + ":" + x.chunkId + ":" + x.hash.length + "\n" + x.hash;
+ }
+ // As per the spec, a server should response with a 204 if there are no
+ // full-length hashes that match the prefixes.
+ let httpStatus = 204;
+ for (let completion of completionSets[currentCompletionSet]) {
+ if (
+ completion.expectCompletion &&
+ !completedHashes.includes(completion.hash)
+ ) {
+ completedHashes.push(completion.hash);
+
+ if (completion.multipleCompletions) {
+ responseText += completion.completions
+ .map(responseForCompletion)
+ .join("");
+ } else {
+ responseText += responseForCompletion(completion);
+ }
+ }
+ if (completion.forceServerError) {
+ httpStatus = 503;
+ }
+ }
+
+ dump("Server sending response for " + currentCompletionSet + "\n");
+ maxServerCompletionSet = currentCompletionSet;
+ if (responseText && httpStatus != 503) {
+ aResponse.write(responseText);
+ } else {
+ aResponse.setStatusLine(null, httpStatus, null);
+ }
+}
+
+function callback(completion) {
+ this._completion = completion;
+}
+
+callback.prototype = {
+ completionV2: function completionV2(hash, table, chunkId, trusted) {
+ Assert.ok(this._completion.expectCompletion);
+ if (this._completion.multipleCompletions) {
+ for (let completion of this._completion.completions) {
+ if (completion.hash == hash) {
+ Assert.equal(JSON.stringify(hash), JSON.stringify(completion.hash));
+ Assert.equal(table, completion.table);
+ Assert.equal(chunkId, completion.chunkId);
+
+ completion._completed = true;
+
+ if (this._completion.completions.every(x => x._completed)) {
+ this._completed = true;
+ }
+
+ break;
+ }
+ }
+ } else {
+ // Hashes are not actually strings and can contain arbitrary data.
+ Assert.equal(JSON.stringify(hash), JSON.stringify(this._completion.hash));
+ Assert.equal(table, this._completion.table);
+ Assert.equal(chunkId, this._completion.chunkId);
+
+ this._completed = true;
+ }
+ },
+
+ completionFinished: function completionFinished(status) {
+ finishedCompletions++;
+ Assert.equal(!!this._completion.expectCompletion, !!this._completed);
+ this._completion._finished = true;
+
+ // currentCompletionSet can mutate before all of the callbacks are complete.
+ if (
+ currentCompletionSet < completionSets.length &&
+ finishedCompletions == completionSets[currentCompletionSet].length
+ ) {
+ runNextCompletion();
+ }
+ },
+};
+
+function finish() {
+ Services.prefs.clearUserPref(
+ "browser.safebrowsing.provider.test.disableBackoff"
+ );
+
+ Assert.equal(expectedMaxServerCompletionSet, maxServerCompletionSet);
+ server.stop(function () {
+ do_test_finished();
+ });
+}
diff --git a/toolkit/components/url-classifier/tests/unit/test_hashcompleter_v4.js b/toolkit/components/url-classifier/tests/unit/test_hashcompleter_v4.js
new file mode 100644
index 0000000000..5910acb0eb
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_hashcompleter_v4.js
@@ -0,0 +1,292 @@
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+// These tables have a different update URL (for v4).
+const TEST_TABLE_DATA_V4 = {
+ tableName: "test-phish-proto",
+ providerName: "google4",
+ updateUrl: "http://localhost:5555/safebrowsing/update?",
+ gethashUrl: "http://localhost:5555/safebrowsing/gethash-v4?",
+};
+
+const PREF_NEXTUPDATETIME_V4 =
+ "browser.safebrowsing.provider.google4.nextupdatetime";
+const GETHASH_PATH = "/safebrowsing/gethash-v4";
+
+// The protobuf binary represention of gethash response:
+// minimumWaitDuration : 12 secs 10 nanosecs
+// negativeCacheDuration : 120 secs 9 nanosecs
+//
+// { CompleteHash, ThreatType, CacheDuration { secs, nanos } };
+// { nsCString("01234567890123456789012345678901"), SOCIAL_ENGINEERING_PUBLIC, { 8, 500 } },
+// { nsCString("12345678901234567890123456789012"), SOCIAL_ENGINEERING_PUBLIC, { 7, 100} },
+// { nsCString("23456789012345678901234567890123"), SOCIAL_ENGINEERING_PUBLIC, { 1, 20 } },
+
+const GETHASH_RESPONSE_CONTENT =
+ "\x0A\x2D\x08\x02\x1A\x22\x0A\x20\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x2A\x05\x08\x08\x10\xF4\x03\x0A\x2C\x08\x02\x1A\x22\x0A\x20\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32\x2A\x04\x08\x07\x10\x64\x0A\x2C\x08\x02\x1A\x22\x0A\x20\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x30\x31\x32\x33\x2A\x04\x08\x01\x10\x14\x12\x04\x08\x0C\x10\x0A\x1A\x04\x08\x78\x10\x09";
+
+// The protobuf binary represention of update response:
+//
+// [
+// {
+// 'threat_type': 2, // SOCIAL_ENGINEERING_PUBLIC
+// 'response_type': 2, // FULL_UPDATE
+// 'new_client_state': 'sta\x00te', // NEW_CLIENT_STATE
+// 'checksum': { "sha256": CHECKSUM }, // CHECKSUM
+// 'additions': { 'compression_type': RAW,
+// 'prefix_size': 4,
+// 'raw_hashes': "00000001000000020000000300000004"}
+// }
+// ]
+//
+const UPDATE_RESPONSE_CONTENT =
+ "\x0A\x4A\x08\x02\x20\x02\x2A\x18\x08\x01\x12\x14\x08\x04\x12\x10\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x3A\x06\x73\x74\x61\x00\x74\x65\x42\x22\x0A\x20\x30\x67\xC7\x2C\x5E\x50\x1C\x31\xE3\xFE\xCA\x73\xF0\x47\xDC\x34\x1A\x95\x63\x99\xEC\x70\x5E\x0A\xEE\x9E\xFB\x17\xA1\x55\x35\x78\x12\x08\x08\x08\x10\x80\x94\xEB\xDC\x03";
+const UPDATE_PATH = "/safebrowsing/update";
+
+let gListManager = Cc["@mozilla.org/url-classifier/listmanager;1"].getService(
+ Ci.nsIUrlListManager
+);
+
+let gCompleter = Cc["@mozilla.org/url-classifier/hashcompleter;1"].getService(
+ Ci.nsIUrlClassifierHashCompleter
+);
+
+XPCOMUtils.defineLazyServiceGetter(
+ this,
+ "gUrlUtil",
+ "@mozilla.org/url-classifier/utils;1",
+ "nsIUrlClassifierUtils"
+);
+
+// Handles request for TEST_TABLE_DATA_V4.
+let gHttpServV4 = null;
+
+const NEW_CLIENT_STATE = "sta\0te";
+const CHECKSUM =
+ "\x30\x67\xc7\x2c\x5e\x50\x1c\x31\xe3\xfe\xca\x73\xf0\x47\xdc\x34\x1a\x95\x63\x99\xec\x70\x5e\x0a\xee\x9e\xfb\x17\xa1\x55\x35\x78";
+
+Services.prefs.setBoolPref("browser.safebrowsing.debug", true);
+
+// The "\xFF\xFF" is to generate a base64 string with "/".
+Services.prefs.setCharPref("browser.safebrowsing.id", "Firefox\xFF\xFF");
+
+// Register tables.
+gListManager.registerTable(
+ TEST_TABLE_DATA_V4.tableName,
+ TEST_TABLE_DATA_V4.providerName,
+ TEST_TABLE_DATA_V4.updateUrl,
+ TEST_TABLE_DATA_V4.gethashUrl
+);
+
+// This is unfortunately needed since v4 gethash request
+// requires the threat type (table name) as well as the
+// state it's associated with. We have to run the update once
+// to have the state written.
+add_test(function test_update_v4() {
+ gListManager.disableUpdate(TEST_TABLE_DATA_V4.tableName);
+ gListManager.enableUpdate(TEST_TABLE_DATA_V4.tableName);
+
+ // Force table update.
+ Services.prefs.setCharPref(PREF_NEXTUPDATETIME_V4, "1");
+ gListManager.maybeToggleUpdateChecking();
+});
+
+add_test(function test_getHashRequestV4() {
+ let request = gUrlUtil.makeFindFullHashRequestV4(
+ [TEST_TABLE_DATA_V4.tableName],
+ [btoa(NEW_CLIENT_STATE)],
+ [btoa("0123"), btoa("1234567"), btoa("1111")].sort()
+ );
+ registerHandlerGethashV4("&$req=" + request);
+ let completeFinishedCnt = 0;
+
+ gCompleter.complete(
+ "0123",
+ TEST_TABLE_DATA_V4.gethashUrl,
+ TEST_TABLE_DATA_V4.tableName,
+ {
+ completionV4(hash, table, duration, fullhashes) {
+ equal(hash, "0123");
+ equal(table, TEST_TABLE_DATA_V4.tableName);
+ equal(duration, 120);
+ equal(fullhashes.length, 1);
+
+ let match = fullhashes
+ .QueryInterface(Ci.nsIArray)
+ .queryElementAt(0, Ci.nsIFullHashMatch);
+
+ equal(match.fullHash, "01234567890123456789012345678901");
+ equal(match.cacheDuration, 8);
+ info("completion: " + match.fullHash + ", " + table);
+ },
+
+ completionFinished(status) {
+ equal(status, Cr.NS_OK);
+ completeFinishedCnt++;
+ if (3 === completeFinishedCnt) {
+ run_next_test();
+ }
+ },
+ }
+ );
+
+ gCompleter.complete(
+ "1234567",
+ TEST_TABLE_DATA_V4.gethashUrl,
+ TEST_TABLE_DATA_V4.tableName,
+ {
+ completionV4(hash, table, duration, fullhashes) {
+ equal(hash, "1234567");
+ equal(table, TEST_TABLE_DATA_V4.tableName);
+ equal(duration, 120);
+ equal(fullhashes.length, 1);
+
+ let match = fullhashes
+ .QueryInterface(Ci.nsIArray)
+ .queryElementAt(0, Ci.nsIFullHashMatch);
+
+ equal(match.fullHash, "12345678901234567890123456789012");
+ equal(match.cacheDuration, 7);
+ info("completion: " + match.fullHash + ", " + table);
+ },
+
+ completionFinished(status) {
+ equal(status, Cr.NS_OK);
+ completeFinishedCnt++;
+ if (3 === completeFinishedCnt) {
+ run_next_test();
+ }
+ },
+ }
+ );
+
+ gCompleter.complete(
+ "1111",
+ TEST_TABLE_DATA_V4.gethashUrl,
+ TEST_TABLE_DATA_V4.tableName,
+ {
+ completionV4(hash, table, duration, fullhashes) {
+ equal(hash, "1111");
+ equal(table, TEST_TABLE_DATA_V4.tableName);
+ equal(duration, 120);
+ equal(fullhashes.length, 0);
+ },
+
+ completionFinished(status) {
+ equal(status, Cr.NS_OK);
+ completeFinishedCnt++;
+ if (3 === completeFinishedCnt) {
+ run_next_test();
+ }
+ },
+ }
+ );
+});
+
+add_test(function test_minWaitDuration() {
+ let failedComplete = function () {
+ gCompleter.complete(
+ "0123",
+ TEST_TABLE_DATA_V4.gethashUrl,
+ TEST_TABLE_DATA_V4.tableName,
+ {
+ completionFinished(status) {
+ equal(status, Cr.NS_ERROR_ABORT);
+ },
+ }
+ );
+ };
+
+ let successComplete = function () {
+ gCompleter.complete(
+ "1234567",
+ TEST_TABLE_DATA_V4.gethashUrl,
+ TEST_TABLE_DATA_V4.tableName,
+ {
+ completionV4(hash, table, duration, fullhashes) {
+ equal(hash, "1234567");
+ equal(table, TEST_TABLE_DATA_V4.tableName);
+ equal(fullhashes.length, 1);
+
+ let match = fullhashes
+ .QueryInterface(Ci.nsIArray)
+ .queryElementAt(0, Ci.nsIFullHashMatch);
+
+ equal(match.fullHash, "12345678901234567890123456789012");
+ equal(match.cacheDuration, 7);
+ info("completion: " + match.fullHash + ", " + table);
+ },
+
+ completionFinished(status) {
+ equal(status, Cr.NS_OK);
+ run_next_test();
+ },
+ }
+ );
+ };
+
+ let request = gUrlUtil.makeFindFullHashRequestV4(
+ [TEST_TABLE_DATA_V4.tableName],
+ [btoa(NEW_CLIENT_STATE)],
+ [btoa("1234567")]
+ );
+ registerHandlerGethashV4("&$req=" + request);
+
+ // The last gethash response contained a min wait duration 12 secs 10 nano
+ // So subsequent requests can happen only after the min wait duration
+ do_timeout(1000, failedComplete);
+ do_timeout(2000, failedComplete);
+ do_timeout(4000, failedComplete);
+ do_timeout(13000, successComplete);
+});
+
+function registerHandlerGethashV4(aExpectedQuery) {
+ gHttpServV4.registerPathHandler(GETHASH_PATH, null);
+ // V4 gethash handler.
+ gHttpServV4.registerPathHandler(GETHASH_PATH, function (request, response) {
+ equal(request.queryString, aExpectedQuery);
+
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.bodyOutputStream.write(
+ GETHASH_RESPONSE_CONTENT,
+ GETHASH_RESPONSE_CONTENT.length
+ );
+ });
+}
+
+function registerHandlerUpdateV4() {
+ // Update handler. Will respond a valid state to be verified in the
+ // gethash handler.
+ gHttpServV4.registerPathHandler(UPDATE_PATH, function (request, response) {
+ response.setHeader(
+ "Content-Type",
+ "application/vnd.google.safebrowsing-update",
+ false
+ );
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.bodyOutputStream.write(
+ UPDATE_RESPONSE_CONTENT,
+ UPDATE_RESPONSE_CONTENT.length
+ );
+
+ waitUntilMetaDataSaved(NEW_CLIENT_STATE, CHECKSUM, () => {
+ run_next_test();
+ });
+ });
+}
+
+function run_test() {
+ throwOnUpdateErrors();
+
+ gHttpServV4 = new HttpServer();
+ gHttpServV4.registerDirectory("/", do_get_cwd());
+
+ registerHandlerUpdateV4();
+ gHttpServV4.start(5555);
+ run_next_test();
+}
+
+registerCleanupFunction(function () {
+ stopThrowingOnUpdateErrors();
+});
diff --git a/toolkit/components/url-classifier/tests/unit/test_listmanager.js b/toolkit/components/url-classifier/tests/unit/test_listmanager.js
new file mode 100644
index 0000000000..751320f161
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_listmanager.js
@@ -0,0 +1,355 @@
+// These tables share the same updateURL.
+const TEST_TABLE_DATA_LIST = [
+ // 0:
+ {
+ tableName: "test-listmanager0-digest256",
+ providerName: "google",
+ updateUrl: "http://localhost:4444/safebrowsing/update",
+ gethashUrl: "http://localhost:4444/safebrowsing/gethash0",
+ },
+
+ // 1:
+ {
+ tableName: "test-listmanager1-digest256",
+ providerName: "google",
+ updateUrl: "http://localhost:4444/safebrowsing/update",
+ gethashUrl: "http://localhost:4444/safebrowsing/gethash1",
+ },
+
+ // 2.
+ {
+ tableName: "test-listmanager2-digest256",
+ providerName: "google",
+ updateUrl: "http://localhost:4444/safebrowsing/update",
+ gethashUrl: "http://localhost:4444/safebrowsing/gethash2",
+ },
+];
+
+// These tables have a different update URL (for v4).
+const TEST_TABLE_DATA_V4 = {
+ tableName: "test-phish-proto",
+ providerName: "google4",
+ updateUrl: "http://localhost:5555/safebrowsing/update?",
+ gethashUrl: "http://localhost:5555/safebrowsing/gethash-v4",
+};
+const TEST_TABLE_DATA_V4_DISABLED = {
+ tableName: "test-unwanted-proto",
+ providerName: "google4",
+ updateUrl: "http://localhost:5555/safebrowsing/update?",
+ gethashUrl: "http://localhost:5555/safebrowsing/gethash-v4",
+};
+
+const PREF_NEXTUPDATETIME =
+ "browser.safebrowsing.provider.google.nextupdatetime";
+const PREF_NEXTUPDATETIME_V4 =
+ "browser.safebrowsing.provider.google4.nextupdatetime";
+
+let gListManager = Cc["@mozilla.org/url-classifier/listmanager;1"].getService(
+ Ci.nsIUrlListManager
+);
+
+let gUrlUtils = Cc["@mozilla.org/url-classifier/utils;1"].getService(
+ Ci.nsIUrlClassifierUtils
+);
+
+// Global test server for serving safebrowsing updates.
+let gHttpServ = null;
+let gUpdateResponse = "";
+let gExpectedUpdateRequest = "";
+let gExpectedQueryV4 = "";
+
+// Handles request for TEST_TABLE_DATA_V4.
+let gHttpServV4 = null;
+
+// These two variables are used to synchronize the last two racing updates
+// (in terms of "update URL") in test_update_all_tables().
+let gUpdatedCntForTableData = 0; // For TEST_TABLE_DATA_LIST.
+let gIsV4Updated = false; // For TEST_TABLE_DATA_V4.
+
+const NEW_CLIENT_STATE = "sta\0te";
+const CHECKSUM =
+ "\x30\x67\xc7\x2c\x5e\x50\x1c\x31\xe3\xfe\xca\x73\xf0\x47\xdc\x34\x1a\x95\x63\x99\xec\x70\x5e\x0a\xee\x9e\xfb\x17\xa1\x55\x35\x78";
+
+Services.prefs.setBoolPref("browser.safebrowsing.debug", true);
+
+// The "\xFF\xFF" is to generate a base64 string with "/".
+Services.prefs.setCharPref("browser.safebrowsing.id", "Firefox\xFF\xFF");
+
+// Register tables.
+TEST_TABLE_DATA_LIST.forEach(function (t) {
+ gListManager.registerTable(
+ t.tableName,
+ t.providerName,
+ t.updateUrl,
+ t.gethashUrl
+ );
+});
+
+gListManager.registerTable(
+ TEST_TABLE_DATA_V4.tableName,
+ TEST_TABLE_DATA_V4.providerName,
+ TEST_TABLE_DATA_V4.updateUrl,
+ TEST_TABLE_DATA_V4.gethashUrl
+);
+
+// To test Bug 1302044.
+gListManager.registerTable(
+ TEST_TABLE_DATA_V4_DISABLED.tableName,
+ TEST_TABLE_DATA_V4_DISABLED.providerName,
+ TEST_TABLE_DATA_V4_DISABLED.updateUrl,
+ TEST_TABLE_DATA_V4_DISABLED.gethashUrl
+);
+
+const SERVER_INVOLVED_TEST_CASE_LIST = [
+ // - Do table0 update.
+ // - Server would respond "a:5:32:32\n[DATA]".
+ function test_update_table0() {
+ disableAllUpdates();
+
+ gListManager.enableUpdate(TEST_TABLE_DATA_LIST[0].tableName);
+ gExpectedUpdateRequest = TEST_TABLE_DATA_LIST[0].tableName + ";\n";
+
+ gUpdateResponse = "n:1000\ni:" + TEST_TABLE_DATA_LIST[0].tableName + "\n";
+ gUpdateResponse += readFileToString("data/digest2.chunk");
+
+ forceTableUpdate();
+ },
+
+ // - Do table0 update again. Since chunk 5 was added to table0 in the last
+ // update, the expected request contains "a:5".
+ // - Server would respond "s;2-12\n[DATA]".
+ function test_update_table0_with_existing_chunks() {
+ disableAllUpdates();
+
+ gListManager.enableUpdate(TEST_TABLE_DATA_LIST[0].tableName);
+ gExpectedUpdateRequest = TEST_TABLE_DATA_LIST[0].tableName + ";a:5\n";
+
+ gUpdateResponse = "n:1000\ni:" + TEST_TABLE_DATA_LIST[0].tableName + "\n";
+ gUpdateResponse += readFileToString("data/digest1.chunk");
+
+ forceTableUpdate();
+ },
+
+ // - Do all-table update.
+ // - Server would respond no chunk control.
+ //
+ // Note that this test MUST be the last one in the array since we rely on
+ // the number of sever-involved test case to synchronize the racing last
+ // two udpates for different URL.
+ function test_update_all_tables() {
+ disableAllUpdates();
+
+ // Enable all tables including TEST_TABLE_DATA_V4!
+ TEST_TABLE_DATA_LIST.forEach(function (t) {
+ gListManager.enableUpdate(t.tableName);
+ });
+
+ // We register two v4 tables but only enable one of them
+ // to verify that the disabled tables are not updated.
+ // See Bug 1302044.
+ gListManager.enableUpdate(TEST_TABLE_DATA_V4.tableName);
+ gListManager.disableUpdate(TEST_TABLE_DATA_V4_DISABLED.tableName);
+
+ // Expected results for v2.
+ gExpectedUpdateRequest =
+ TEST_TABLE_DATA_LIST[0].tableName +
+ ";a:5:s:2-12\n" +
+ TEST_TABLE_DATA_LIST[1].tableName +
+ ";\n" +
+ TEST_TABLE_DATA_LIST[2].tableName +
+ ";\n";
+ gUpdateResponse = "n:1000\n";
+
+ // We test the request against the query string since v4 request
+ // would be appened to the query string. The request is generated
+ // by protobuf API (binary) then encoded to base64 format.
+ let requestV4 = gUrlUtils.makeUpdateRequestV4(
+ [TEST_TABLE_DATA_V4.tableName],
+ [""]
+ );
+ gExpectedQueryV4 = "&$req=" + requestV4;
+
+ forceTableUpdate();
+ },
+];
+
+SERVER_INVOLVED_TEST_CASE_LIST.forEach(t => add_test(t));
+
+add_test(function test_partialUpdateV4() {
+ disableAllUpdates();
+
+ gListManager.enableUpdate(TEST_TABLE_DATA_V4.tableName);
+
+ // Since the new client state has been responded and saved in
+ // test_update_all_tables, this update request should send
+ // a partial update to the server.
+ let requestV4 = gUrlUtils.makeUpdateRequestV4(
+ [TEST_TABLE_DATA_V4.tableName],
+ [btoa(NEW_CLIENT_STATE)]
+ );
+ gExpectedQueryV4 = "&$req=" + requestV4;
+
+ forceTableUpdate();
+});
+
+// Tests nsIUrlListManager.getGethashUrl.
+add_test(function test_getGethashUrl() {
+ TEST_TABLE_DATA_LIST.forEach(function (t) {
+ equal(gListManager.getGethashUrl(t.tableName), t.gethashUrl);
+ });
+ equal(
+ gListManager.getGethashUrl(TEST_TABLE_DATA_V4.tableName),
+ TEST_TABLE_DATA_V4.gethashUrl
+ );
+ run_next_test();
+});
+
+function run_test() {
+ // Setup primary testing server.
+ gHttpServ = new HttpServer();
+ gHttpServ.registerDirectory("/", do_get_cwd());
+
+ gHttpServ.registerPathHandler(
+ "/safebrowsing/update",
+ function (request, response) {
+ let body = NetUtil.readInputStreamToString(
+ request.bodyInputStream,
+ request.bodyInputStream.available()
+ );
+
+ // Verify if the request is as expected.
+ equal(body, gExpectedUpdateRequest);
+
+ // Respond the update which is controlled by the test case.
+ response.setHeader(
+ "Content-Type",
+ "application/vnd.google.safebrowsing-update",
+ false
+ );
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.bodyOutputStream.write(gUpdateResponse, gUpdateResponse.length);
+
+ gUpdatedCntForTableData++;
+
+ if (gUpdatedCntForTableData !== SERVER_INVOLVED_TEST_CASE_LIST.length) {
+ // This is not the last test case so run the next once upon the
+ // the update success.
+ waitForUpdateSuccess(run_next_test);
+ return;
+ }
+
+ if (gIsV4Updated) {
+ run_next_test(); // All tests are done. Just finish.
+ return;
+ }
+
+ info("Waiting for TEST_TABLE_DATA_V4 to be tested ...");
+ }
+ );
+
+ gHttpServ.start(4444);
+
+ // Setup v4 testing server for the different update URL.
+ gHttpServV4 = new HttpServer();
+ gHttpServV4.registerDirectory("/", do_get_cwd());
+
+ gHttpServV4.registerPathHandler(
+ "/safebrowsing/update",
+ function (request, response) {
+ // V4 update request body should be empty.
+ equal(request.bodyInputStream.available(), 0);
+
+ // Not on the spec. Found in Chromium source code...
+ equal(request.getHeader("X-HTTP-Method-Override"), "POST");
+
+ // V4 update request uses GET.
+ equal(request.method, "GET");
+
+ // V4 append the base64 encoded request to the query string.
+ equal(request.queryString, gExpectedQueryV4);
+ equal(request.queryString.indexOf("+"), -1);
+ equal(request.queryString.indexOf("/"), -1);
+
+ // Respond a V2 compatible content for now. In the future we can
+ // send a meaningful response to test Bug 1284178 to see if the
+ // update is successfully stored to database.
+ response.setHeader(
+ "Content-Type",
+ "application/vnd.google.safebrowsing-update",
+ false
+ );
+ response.setStatusLine(request.httpVersion, 200, "OK");
+
+ // The protobuf binary represention of response:
+ //
+ // [
+ // {
+ // 'threat_type': 2, // SOCIAL_ENGINEERING_PUBLIC
+ // 'response_type': 2, // FULL_UPDATE
+ // 'new_client_state': 'sta\x00te', // NEW_CLIENT_STATE
+ // 'checksum': { "sha256": CHECKSUM }, // CHECKSUM
+ // 'additions': { 'compression_type': RAW,
+ // 'prefix_size': 4,
+ // 'raw_hashes': "00000001000000020000000300000004"}
+ // }
+ // ]
+ //
+ let content =
+ "\x0A\x4A\x08\x02\x20\x02\x2A\x18\x08\x01\x12\x14\x08\x04\x12\x10\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x3A\x06\x73\x74\x61\x00\x74\x65\x42\x22\x0A\x20\x30\x67\xC7\x2C\x5E\x50\x1C\x31\xE3\xFE\xCA\x73\xF0\x47\xDC\x34\x1A\x95\x63\x99\xEC\x70\x5E\x0A\xEE\x9E\xFB\x17\xA1\x55\x35\x78\x12\x08\x08\x08\x10\x80\x94\xEB\xDC\x03";
+
+ response.bodyOutputStream.write(content, content.length);
+
+ if (gIsV4Updated) {
+ // This falls to the case where test_partialUpdateV4 is running.
+ // We are supposed to have verified the update request contains
+ // the state we set in the previous request.
+ waitForUpdateSuccess(run_next_test);
+ return;
+ }
+
+ waitUntilMetaDataSaved(NEW_CLIENT_STATE, CHECKSUM, () => {
+ gIsV4Updated = true;
+
+ if (gUpdatedCntForTableData === SERVER_INVOLVED_TEST_CASE_LIST.length) {
+ // All tests are done!
+ run_next_test();
+ return;
+ }
+
+ info("Wait for all sever-involved tests to be done ...");
+ });
+ }
+ );
+
+ gHttpServV4.start(5555);
+
+ registerCleanupFunction(function () {
+ return (async function () {
+ await Promise.all([gHttpServ.stop(), gHttpServV4.stop()]);
+ })();
+ });
+
+ run_next_test();
+}
+
+// A trick to force updating tables. However, before calling this, we have to
+// call disableAllUpdates() first to clean up the updateCheckers in listmanager.
+function forceTableUpdate() {
+ throwOnUpdateErrors();
+ Services.prefs.setCharPref(PREF_NEXTUPDATETIME, "1");
+ Services.prefs.setCharPref(PREF_NEXTUPDATETIME_V4, "1");
+ gListManager.maybeToggleUpdateChecking();
+}
+
+function disableAllUpdates() {
+ stopThrowingOnUpdateErrors();
+ TEST_TABLE_DATA_LIST.forEach(t => gListManager.disableUpdate(t.tableName));
+ gListManager.disableUpdate(TEST_TABLE_DATA_V4.tableName);
+}
+
+function waitForUpdateSuccess(callback) {
+ Services.obs.addObserver(function listener() {
+ Services.obs.removeObserver(listener, "safebrowsing-update-finished");
+ callback();
+ }, "safebrowsing-update-finished");
+}
diff --git a/toolkit/components/url-classifier/tests/unit/test_malwaretable_pref.js b/toolkit/components/url-classifier/tests/unit/test_malwaretable_pref.js
new file mode 100644
index 0000000000..c7118f6e49
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_malwaretable_pref.js
@@ -0,0 +1,4 @@
+// Ensure that the default value of malwareTable is always in sorted order
+let originalValue = Services.prefs.getCharPref("urlclassifier.malwareTable");
+let sortedValue = originalValue.split(",").sort().join(",");
+Assert.equal(originalValue, sortedValue);
diff --git a/toolkit/components/url-classifier/tests/unit/test_partial.js b/toolkit/components/url-classifier/tests/unit/test_partial.js
new file mode 100644
index 0000000000..1220665063
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_partial.js
@@ -0,0 +1,607 @@
+/**
+ * DummyCompleter() lets tests easily specify the results of a partial
+ * hash completion request.
+ */
+function DummyCompleter() {
+ this.fragments = {};
+ this.queries = [];
+ this.tableName = "test-phish-simple";
+}
+
+DummyCompleter.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsIUrlClassifierHashCompleter"]),
+
+ complete(partialHash, gethashUrl, tableName, cb) {
+ this.queries.push(partialHash);
+ var fragments = this.fragments;
+ var self = this;
+ var doCallback = function () {
+ if (self.alwaysFail) {
+ cb.completionFinished(Cr.NS_ERROR_FAILURE);
+ return;
+ }
+ if (fragments[partialHash]) {
+ for (var i = 0; i < fragments[partialHash].length; i++) {
+ var chunkId = fragments[partialHash][i][0];
+ var hash = fragments[partialHash][i][1];
+ cb.completionV2(hash, self.tableName, chunkId);
+ }
+ }
+ cb.completionFinished(0);
+ };
+ executeSoon(doCallback);
+ },
+
+ getHash(fragment) {
+ var data = new TextEncoder().encode(fragment);
+ var ch = Cc["@mozilla.org/security/hash;1"].createInstance(
+ Ci.nsICryptoHash
+ );
+ ch.init(ch.SHA256);
+ ch.update(data, data.length);
+ var hash = ch.finish(false);
+ return hash.slice(0, 32);
+ },
+
+ addFragment(chunkId, fragment) {
+ this.addHash(chunkId, this.getHash(fragment));
+ },
+
+ // This method allows the caller to generate complete hashes that match the
+ // prefix of a real fragment, but have different complete hashes.
+ addConflict(chunkId, fragment) {
+ var realHash = this.getHash(fragment);
+ var invalidHash = this.getHash("blah blah blah blah blah");
+ this.addHash(chunkId, realHash.slice(0, 4) + invalidHash.slice(4, 32));
+ },
+
+ addHash(chunkId, hash) {
+ var partial = hash.slice(0, 4);
+ if (this.fragments[partial]) {
+ this.fragments[partial].push([chunkId, hash]);
+ } else {
+ this.fragments[partial] = [[chunkId, hash]];
+ }
+ },
+
+ compareQueries(fragments) {
+ var expectedQueries = [];
+ for (let i = 0; i < fragments.length; i++) {
+ expectedQueries.push(this.getHash(fragments[i]).slice(0, 4));
+ }
+ Assert.equal(this.queries.length, expectedQueries.length);
+ expectedQueries.sort();
+ this.queries.sort();
+ for (let i = 0; i < this.queries.length; i++) {
+ Assert.equal(this.queries[i], expectedQueries[i]);
+ }
+ },
+};
+
+function setupCompleter(table, hits, conflicts) {
+ var completer = new DummyCompleter();
+ completer.tableName = table;
+ for (let i = 0; i < hits.length; i++) {
+ let chunkId = hits[i][0];
+ let fragments = hits[i][1];
+ for (let j = 0; j < fragments.length; j++) {
+ completer.addFragment(chunkId, fragments[j]);
+ }
+ }
+ for (let i = 0; i < conflicts.length; i++) {
+ let chunkId = conflicts[i][0];
+ let fragments = conflicts[i][1];
+ for (let j = 0; j < fragments.length; j++) {
+ completer.addConflict(chunkId, fragments[j]);
+ }
+ }
+
+ dbservice.setHashCompleter(table, completer);
+
+ return completer;
+}
+
+function installCompleter(table, fragments, conflictFragments) {
+ return setupCompleter(table, fragments, conflictFragments);
+}
+
+function installFailingCompleter(table) {
+ var completer = setupCompleter(table, [], []);
+ completer.alwaysFail = true;
+ return completer;
+}
+
+// Helper assertion for checking dummy completer queries
+gAssertions.completerQueried = function (data, cb) {
+ var completer = data[0];
+ completer.compareQueries(data[1]);
+ cb();
+};
+
+function doTest(updates, assertions) {
+ doUpdateTest(updates, assertions, runNextTest, updateError);
+}
+
+// Test an add of two partial urls to a fresh database
+function testPartialAdds() {
+ var addUrls = ["foo.com/a", "foo.com/b", "bar.com/c"];
+ var update = buildPhishingUpdate([{ chunkNum: 1, urls: addUrls }], 4);
+
+ var completer = installCompleter("test-phish-simple", [[1, addUrls]], []);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1",
+ urlsExist: addUrls,
+ completerQueried: [completer, addUrls],
+ };
+
+ doTest([update], assertions);
+}
+
+function testPartialAddsWithConflicts() {
+ var addUrls = ["foo.com/a", "foo.com/b", "bar.com/c"];
+ var update = buildPhishingUpdate([{ chunkNum: 1, urls: addUrls }], 4);
+
+ // Each result will have both a real match and a conflict
+ var completer = installCompleter(
+ "test-phish-simple",
+ [[1, addUrls]],
+ [[1, addUrls]]
+ );
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1",
+ urlsExist: addUrls,
+ completerQueried: [completer, addUrls],
+ };
+
+ doTest([update], assertions);
+}
+
+// Test whether the fragmenting code does not cause duplicated completions
+function testFragments() {
+ var addUrls = ["foo.com/a/b/c", "foo.net/", "foo.com/c/"];
+ var update = buildPhishingUpdate([{ chunkNum: 1, urls: addUrls }], 4);
+
+ var completer = installCompleter("test-phish-simple", [[1, addUrls]], []);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1",
+ urlsExist: addUrls,
+ completerQueried: [completer, addUrls],
+ };
+
+ doTest([update], assertions);
+}
+
+// Test http://code.google.com/p/google-safe-browsing/wiki/Protocolv2Spec
+// section 6.2 example 1
+function testSpecFragments() {
+ var probeUrls = ["a.b.c/1/2.html?param=1"];
+
+ var addUrls = [
+ "a.b.c/1/2.html",
+ "a.b.c/",
+ "a.b.c/1/",
+ "b.c/1/2.html?param=1",
+ "b.c/1/2.html",
+ "b.c/",
+ "b.c/1/",
+ "a.b.c/1/2.html?param=1",
+ ];
+
+ var update = buildPhishingUpdate([{ chunkNum: 1, urls: addUrls }], 4);
+
+ var completer = installCompleter("test-phish-simple", [[1, addUrls]], []);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1",
+ urlsExist: probeUrls,
+ completerQueried: [completer, addUrls],
+ };
+
+ doTest([update], assertions);
+}
+
+// Test http://code.google.com/p/google-safe-browsing/wiki/Protocolv2Spec
+// section 6.2 example 2
+function testMoreSpecFragments() {
+ var probeUrls = ["a.b.c.d.e.f.g/1.html"];
+
+ var addUrls = [
+ "a.b.c.d.e.f.g/1.html",
+ "a.b.c.d.e.f.g/",
+ "c.d.e.f.g/1.html",
+ "c.d.e.f.g/",
+ "d.e.f.g/1.html",
+ "d.e.f.g/",
+ "e.f.g/1.html",
+ "e.f.g/",
+ "f.g/1.html",
+ "f.g/",
+ ];
+
+ var update = buildPhishingUpdate([{ chunkNum: 1, urls: addUrls }], 4);
+
+ var completer = installCompleter("test-phish-simple", [[1, addUrls]], []);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1",
+ urlsExist: probeUrls,
+ completerQueried: [completer, addUrls],
+ };
+
+ doTest([update], assertions);
+}
+
+function testFalsePositives() {
+ var addUrls = ["foo.com/a", "foo.com/b", "bar.com/c"];
+ var update = buildPhishingUpdate([{ chunkNum: 1, urls: addUrls }], 4);
+
+ // Each result will have no matching complete hashes and a non-matching
+ // conflict
+ var completer = installCompleter("test-phish-simple", [], [[1, addUrls]]);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1",
+ urlsDontExist: addUrls,
+ completerQueried: [completer, addUrls],
+ };
+
+ doTest([update], assertions);
+}
+
+function testEmptyCompleter() {
+ var addUrls = ["foo.com/a", "foo.com/b", "bar.com/c"];
+ var update = buildPhishingUpdate([{ chunkNum: 1, urls: addUrls }], 4);
+
+ // Completer will never return full hashes
+ var completer = installCompleter("test-phish-simple", [], []);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1",
+ urlsDontExist: addUrls,
+ completerQueried: [completer, addUrls],
+ };
+
+ doTest([update], assertions);
+}
+
+function testCompleterFailure() {
+ var addUrls = ["foo.com/a", "foo.com/b", "bar.com/c"];
+ var update = buildPhishingUpdate([{ chunkNum: 1, urls: addUrls }], 4);
+
+ // Completer will never return full hashes
+ var completer = installFailingCompleter("test-phish-simple");
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1",
+ urlsDontExist: addUrls,
+ completerQueried: [completer, addUrls],
+ };
+
+ doTest([update], assertions);
+}
+
+function testMixedSizesSameDomain() {
+ var add1Urls = ["foo.com/a"];
+ var add2Urls = ["foo.com/b"];
+
+ var update1 = buildPhishingUpdate([{ chunkNum: 1, urls: add1Urls }], 4);
+ var update2 = buildPhishingUpdate([{ chunkNum: 2, urls: add2Urls }], 32);
+
+ // We should only need to complete the partial hashes
+ var completer = installCompleter("test-phish-simple", [[1, add1Urls]], []);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1-2",
+ // both urls should match...
+ urlsExist: add1Urls.concat(add2Urls),
+ // ... but the completer should only be queried for the partial entry
+ completerQueried: [completer, add1Urls],
+ };
+
+ doTest([update1, update2], assertions);
+}
+
+function testMixedSizesDifferentDomains() {
+ var add1Urls = ["foo.com/a"];
+ var add2Urls = ["bar.com/b"];
+
+ var update1 = buildPhishingUpdate([{ chunkNum: 1, urls: add1Urls }], 4);
+ var update2 = buildPhishingUpdate([{ chunkNum: 2, urls: add2Urls }], 32);
+
+ // We should only need to complete the partial hashes
+ var completer = installCompleter("test-phish-simple", [[1, add1Urls]], []);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1-2",
+ // both urls should match...
+ urlsExist: add1Urls.concat(add2Urls),
+ // ... but the completer should only be queried for the partial entry
+ completerQueried: [completer, add1Urls],
+ };
+
+ doTest([update1, update2], assertions);
+}
+
+function testInvalidHashSize() {
+ var addUrls = ["foo.com/a", "foo.com/b", "bar.com/c"];
+ var update = buildPhishingUpdate([{ chunkNum: 1, urls: addUrls }], 12); // only 4 and 32 are legal hash sizes
+
+ var addUrls2 = ["zaz.com/a", "xyz.com/b"];
+ var update2 = buildPhishingUpdate([{ chunkNum: 2, urls: addUrls2 }], 4);
+
+ installCompleter("test-phish-simple", [[1, addUrls]], []);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:2",
+ urlsDontExist: addUrls,
+ };
+
+ // A successful update will trigger an error
+ doUpdateTest([update2, update], assertions, updateError, runNextTest);
+}
+
+function testWrongTable() {
+ var addUrls = ["foo.com/a"];
+ var update = buildPhishingUpdate([{ chunkNum: 1, urls: addUrls }], 4);
+ var completer = installCompleter(
+ "test-malware-simple", // wrong table
+ [[1, addUrls]],
+ []
+ );
+
+ // The above installCompleter installs the completer for test-malware-simple,
+ // we want it to be used for test-phish-simple too.
+ dbservice.setHashCompleter("test-phish-simple", completer);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1",
+ // The urls were added as phishing urls, but the completer is claiming
+ // that they are malware urls, and we trust the completer in this case.
+ // The result will be discarded, so we can only check for non-existence.
+ urlsDontExist: addUrls,
+ // Make sure the completer was actually queried.
+ completerQueried: [completer, addUrls],
+ };
+
+ doUpdateTest(
+ [update],
+ assertions,
+ function () {
+ // Give the dbservice a chance to (not) cache the result.
+ do_timeout(3000, function () {
+ // The miss earlier will have caused a miss to be cached.
+ // Resetting the completer does not count as an update,
+ // so we will not be probed again.
+ var newCompleter = installCompleter(
+ "test-malware-simple",
+ [[1, addUrls]],
+ []
+ );
+ dbservice.setHashCompleter("test-phish-simple", newCompleter);
+
+ var assertions1 = {
+ urlsDontExist: addUrls,
+ };
+ checkAssertions(assertions1, runNextTest);
+ });
+ },
+ updateError
+ );
+}
+
+function setupCachedResults(addUrls, part2) {
+ var update = buildPhishingUpdate([{ chunkNum: 1, urls: addUrls }], 4);
+
+ var completer = installCompleter("test-phish-simple", [[1, addUrls]], []);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1",
+ // Request the add url. This should cause the completion to be cached.
+ urlsExist: addUrls,
+ // Make sure the completer was actually queried.
+ completerQueried: [completer, addUrls],
+ };
+
+ doUpdateTest(
+ [update],
+ assertions,
+ function () {
+ // Give the dbservice a chance to cache the result.
+ do_timeout(3000, part2);
+ },
+ updateError
+ );
+}
+
+function testCachedResults() {
+ setupCachedResults(["foo.com/a"], function (add) {
+ // This is called after setupCachedResults(). Verify that
+ // checking the url again does not cause a completer request.
+
+ // install a new completer, this one should never be queried.
+ var newCompleter = installCompleter("test-phish-simple", [[1, []]], []);
+
+ var assertions = {
+ urlsExist: ["foo.com/a"],
+ completerQueried: [newCompleter, []],
+ };
+ checkAssertions(assertions, runNextTest);
+ });
+}
+
+function testCachedResultsWithSub() {
+ setupCachedResults(["foo.com/a"], function () {
+ // install a new completer, this one should never be queried.
+ var newCompleter = installCompleter("test-phish-simple", [[1, []]], []);
+
+ var removeUpdate = buildPhishingUpdate(
+ [{ chunkNum: 2, chunkType: "s", urls: ["1:foo.com/a"] }],
+ 4
+ );
+
+ var assertions = {
+ urlsDontExist: ["foo.com/a"],
+ completerQueried: [newCompleter, []],
+ };
+
+ doTest([removeUpdate], assertions);
+ });
+}
+
+function testCachedResultsWithExpire() {
+ setupCachedResults(["foo.com/a"], function () {
+ // install a new completer, this one should never be queried.
+ var newCompleter = installCompleter("test-phish-simple", [[1, []]], []);
+
+ var expireUpdate = "n:1000\ni:test-phish-simple\nad:1\n";
+
+ var assertions = {
+ urlsDontExist: ["foo.com/a"],
+ completerQueried: [newCompleter, []],
+ };
+ doTest([expireUpdate], assertions);
+ });
+}
+
+function testCachedResultsFailure() {
+ var existUrls = ["foo.com/a"];
+ setupCachedResults(existUrls, function () {
+ // This is called after setupCachedResults(). Verify that
+ // checking the url again does not cause a completer request.
+
+ // install a new completer, this one should never be queried.
+ var newCompleter = installCompleter("test-phish-simple", [[1, []]], []);
+
+ var assertions = {
+ urlsExist: existUrls,
+ completerQueried: [newCompleter, []],
+ };
+
+ checkAssertions(assertions, function () {
+ // Apply the update. The cached completes should be gone.
+ doErrorUpdate(
+ "test-phish-simple,test-malware-simple",
+ function () {
+ // Now the completer gets queried again.
+ var newCompleter2 = installCompleter(
+ "test-phish-simple",
+ [[1, existUrls]],
+ []
+ );
+ var assertions2 = {
+ tableData: "test-phish-simple;a:1",
+ urlsExist: existUrls,
+ completerQueried: [newCompleter2, existUrls],
+ };
+ checkAssertions(assertions2, runNextTest);
+ },
+ updateError
+ );
+ });
+ });
+}
+
+function testErrorList() {
+ var addUrls = ["foo.com/a", "foo.com/b", "bar.com/c"];
+ var update = buildPhishingUpdate([{ chunkNum: 1, urls: addUrls }], 4);
+ // The update failure should will kill the completes, so the above
+ // must be a prefix to get any hit at all past the update failure.
+
+ var completer = installCompleter("test-phish-simple", [[1, addUrls]], []);
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1",
+ urlsExist: addUrls,
+ // These are complete urls, and will only be completed if the
+ // list is stale.
+ completerQueried: [completer, addUrls],
+ };
+
+ // Apply the update.
+ doStreamUpdate(
+ update,
+ function () {
+ // Now the test-phish-simple and test-malware-simple tables are marked
+ // as fresh. Fake an update failure to mark them stale.
+ doErrorUpdate(
+ "test-phish-simple,test-malware-simple",
+ function () {
+ // Now the lists should be marked stale. Check assertions.
+ checkAssertions(assertions, runNextTest);
+ },
+ updateError
+ );
+ },
+ updateError
+ );
+}
+
+// Verify that different lists (test-phish-simple,
+// test-malware-simple) maintain their freshness separately.
+function testErrorListIndependent() {
+ var phishUrls = ["phish.com/a"];
+ var malwareUrls = ["attack.com/a"];
+ var update = buildPhishingUpdate([{ chunkNum: 1, urls: phishUrls }], 4);
+ // These have to persist past the update failure, so they must be prefixes,
+ // not completes.
+
+ update += buildMalwareUpdate([{ chunkNum: 2, urls: malwareUrls }], 32);
+
+ var completer = installCompleter("test-phish-simple", [[1, phishUrls]], []);
+
+ var assertions = {
+ tableData: "test-malware-simple;a:2\ntest-phish-simple;a:1",
+ urlsExist: phishUrls,
+ malwareUrlsExist: malwareUrls,
+ // Only this phishing urls should be completed, because only the phishing
+ // urls will be stale.
+ completerQueried: [completer, phishUrls],
+ };
+
+ // Apply the update.
+ doStreamUpdate(
+ update,
+ function () {
+ // Now the test-phish-simple and test-malware-simple tables are
+ // marked as fresh. Fake an update failure to mark *just*
+ // phishing data as stale.
+ doErrorUpdate(
+ "test-phish-simple",
+ function () {
+ // Now the lists should be marked stale. Check assertions.
+ checkAssertions(assertions, runNextTest);
+ },
+ updateError
+ );
+ },
+ updateError
+ );
+}
+
+function run_test() {
+ runTests([
+ testPartialAdds,
+ testPartialAddsWithConflicts,
+ testFragments,
+ testSpecFragments,
+ testMoreSpecFragments,
+ testFalsePositives,
+ testEmptyCompleter,
+ testCompleterFailure,
+ testMixedSizesSameDomain,
+ testMixedSizesDifferentDomains,
+ testInvalidHashSize,
+ testWrongTable,
+ testCachedResults,
+ testCachedResultsWithSub,
+ testCachedResultsWithExpire,
+ testCachedResultsFailure,
+ testErrorList,
+ testErrorListIndependent,
+ ]);
+}
+
+do_test_pending();
diff --git a/toolkit/components/url-classifier/tests/unit/test_platform_specific_threats.js b/toolkit/components/url-classifier/tests/unit/test_platform_specific_threats.js
new file mode 100644
index 0000000000..499c9e478c
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_platform_specific_threats.js
@@ -0,0 +1,104 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const { AppConstants } = ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+);
+
+let urlUtils = Cc["@mozilla.org/url-classifier/utils;1"].getService(
+ Ci.nsIUrlClassifierUtils
+);
+
+function testMobileOnlyThreats() {
+ // Mobile-only threat type(s):
+ // - goog-harmful-proto (POTENTIALLY_HARMFUL_APPLICATION)
+
+ (function testUpdateRequest() {
+ let requestWithPHA = urlUtils.makeUpdateRequestV4(
+ ["goog-phish-proto", "goog-harmful-proto"],
+ ["AAAAAA", "AAAAAA"]
+ );
+
+ let requestNoPHA = urlUtils.makeUpdateRequestV4(
+ ["goog-phish-proto"],
+ ["AAAAAA"]
+ );
+
+ if (AppConstants.platform === "android") {
+ notEqual(
+ requestWithPHA,
+ requestNoPHA,
+ "PHA (i.e. goog-harmful-proto) shouldn't be filtered on mobile platform."
+ );
+ } else {
+ equal(
+ requestWithPHA,
+ requestNoPHA,
+ "PHA (i.e. goog-harmful-proto) should be filtered on non-mobile platform."
+ );
+ }
+ })();
+
+ (function testFullHashRequest() {
+ let requestWithPHA = urlUtils.makeFindFullHashRequestV4(
+ ["goog-phish-proto", "goog-harmful-proto"],
+ ["", ""], // state.
+ [btoa("0123")]
+ ); // prefix.
+
+ let requestNoPHA = urlUtils.makeFindFullHashRequestV4(
+ ["goog-phish-proto"],
+ [""], // state.
+ [btoa("0123")]
+ ); // prefix.
+
+ if (AppConstants.platform === "android") {
+ notEqual(
+ requestWithPHA,
+ requestNoPHA,
+ "PHA (i.e. goog-harmful-proto) shouldn't be filtered on mobile platform."
+ );
+ } else {
+ equal(
+ requestWithPHA,
+ requestNoPHA,
+ "PHA (i.e. goog-harmful-proto) should be filtered on non-mobile platform."
+ );
+ }
+ })();
+}
+
+function testDesktopOnlyThreats() {
+ // Desktop-only threats:
+ // - goog-downloadwhite-proto (CSD_WHITELIST)
+ // - goog-badbinurl-proto (MALICIOUS_BINARY)
+
+ let requestWithDesktopOnlyThreats = urlUtils.makeUpdateRequestV4(
+ ["goog-phish-proto", "goog-downloadwhite-proto", "goog-badbinurl-proto"],
+ ["", "", ""]
+ );
+
+ let requestNoDesktopOnlyThreats = urlUtils.makeUpdateRequestV4(
+ ["goog-phish-proto"],
+ [""]
+ );
+
+ if (AppConstants.platform === "android") {
+ equal(
+ requestWithDesktopOnlyThreats,
+ requestNoDesktopOnlyThreats,
+ "Android shouldn't contain 'goog-downloadwhite-proto' and 'goog-badbinurl-proto'."
+ );
+ } else {
+ notEqual(
+ requestWithDesktopOnlyThreats,
+ requestNoDesktopOnlyThreats,
+ "Desktop should contain 'goog-downloadwhite-proto' and 'goog-badbinurl-proto'."
+ );
+ }
+}
+
+function run_test() {
+ testMobileOnlyThreats();
+ testDesktopOnlyThreats();
+}
diff --git a/toolkit/components/url-classifier/tests/unit/test_pref.js b/toolkit/components/url-classifier/tests/unit/test_pref.js
new file mode 100644
index 0000000000..3a72eceb8e
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_pref.js
@@ -0,0 +1,15 @@
+function run_test() {
+ let urlUtils = Cc["@mozilla.org/url-classifier/utils;1"].getService(
+ Ci.nsIUrlClassifierUtils
+ );
+
+ // The google protocol version should be "2.2" until we enable SB v4
+ // by default.
+ equal(urlUtils.getProtocolVersion("google"), "2.2");
+
+ // Mozilla protocol version will stick to "2.2".
+ equal(urlUtils.getProtocolVersion("mozilla"), "2.2");
+
+ // Unknown provider version will be "2.2".
+ equal(urlUtils.getProtocolVersion("unknown-provider"), "2.2");
+}
diff --git a/toolkit/components/url-classifier/tests/unit/test_prefixset.js b/toolkit/components/url-classifier/tests/unit/test_prefixset.js
new file mode 100644
index 0000000000..b45d4b6771
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_prefixset.js
@@ -0,0 +1,178 @@
+// newPset: returns an empty nsIUrlClassifierPrefixSet.
+function newPset() {
+ let pset = Cc["@mozilla.org/url-classifier/prefixset;1"].createInstance(
+ Ci.nsIUrlClassifierPrefixSet
+ );
+ pset.init("all");
+ return pset;
+}
+
+// arrContains: returns true if |arr| contains the element |target|. Uses binary
+// search and requires |arr| to be sorted.
+function arrContains(arr, target) {
+ let start = 0;
+ let end = arr.length - 1;
+ let i = 0;
+
+ while (end > start) {
+ i = start + ((end - start) >> 1);
+ let value = arr[i];
+
+ if (value < target) {
+ start = i + 1;
+ } else if (value > target) {
+ end = i - 1;
+ } else {
+ break;
+ }
+ }
+ if (start == end) {
+ i = start;
+ }
+
+ return !(i < 0 || i >= arr.length) && arr[i] == target;
+}
+
+// checkContents: Check whether the PrefixSet pset contains
+// the prefixes in the passed array.
+function checkContents(pset, prefixes) {
+ var outcount = {},
+ outset = {};
+ outset = pset.getPrefixes(outcount);
+ let inset = prefixes;
+ Assert.equal(inset.length, outset.length);
+ inset.sort((x, y) => x - y);
+ for (let i = 0; i < inset.length; i++) {
+ Assert.equal(inset[i], outset[i]);
+ }
+}
+
+function wrappedProbe(pset, prefix) {
+ return pset.contains(prefix);
+}
+
+// doRandomLookups: we use this to test for false membership with random input
+// over the range of prefixes (unsigned 32-bits integers).
+// pset: a nsIUrlClassifierPrefixSet to test.
+// prefixes: an array of prefixes supposed to make up the prefix set.
+// N: number of random lookups to make.
+function doRandomLookups(pset, prefixes, N) {
+ for (let i = 0; i < N; i++) {
+ let randInt = prefixes[0];
+ while (arrContains(prefixes, randInt)) {
+ randInt = Math.floor(Math.random() * Math.pow(2, 32));
+ }
+
+ Assert.ok(!wrappedProbe(pset, randInt));
+ }
+}
+
+// doExpectedLookups: we use this to test expected membership.
+// pset: a nsIUrlClassifierPrefixSet to test.
+// prefixes:
+function doExpectedLookups(pset, prefixes, N) {
+ for (let i = 0; i < N; i++) {
+ prefixes.forEach(function (x) {
+ dump("Checking " + x + "\n");
+ Assert.ok(wrappedProbe(pset, x));
+ });
+ }
+}
+
+// testBasicPset: A very basic test of the prefix set to make sure that it
+// exists and to give a basic example of its use.
+function testBasicPset() {
+ let pset = Cc["@mozilla.org/url-classifier/prefixset;1"].createInstance(
+ Ci.nsIUrlClassifierPrefixSet
+ );
+ let prefixes = [2, 50, 100, 2000, 78000, 1593203];
+ pset.setPrefixes(prefixes, prefixes.length);
+
+ Assert.ok(wrappedProbe(pset, 100));
+ Assert.ok(!wrappedProbe(pset, 100000));
+ Assert.ok(wrappedProbe(pset, 1593203));
+ Assert.ok(!wrappedProbe(pset, 999));
+ Assert.ok(!wrappedProbe(pset, 0));
+
+ checkContents(pset, prefixes);
+}
+
+function testDuplicates() {
+ let pset = Cc["@mozilla.org/url-classifier/prefixset;1"].createInstance(
+ Ci.nsIUrlClassifierPrefixSet
+ );
+ let prefixes = [1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 5, 6, 6, 7, 7, 9, 9, 9];
+ pset.setPrefixes(prefixes, prefixes.length);
+
+ Assert.ok(wrappedProbe(pset, 1));
+ Assert.ok(wrappedProbe(pset, 2));
+ Assert.ok(wrappedProbe(pset, 5));
+ Assert.ok(wrappedProbe(pset, 9));
+ Assert.ok(!wrappedProbe(pset, 4));
+ Assert.ok(!wrappedProbe(pset, 8));
+
+ checkContents(pset, prefixes);
+}
+
+function testSimplePset() {
+ let pset = newPset();
+ let prefixes = [1, 2, 100, 400, 123456789];
+ pset.setPrefixes(prefixes, prefixes.length);
+
+ doRandomLookups(pset, prefixes, 100);
+ doExpectedLookups(pset, prefixes, 1);
+
+ checkContents(pset, prefixes);
+}
+
+function testReSetPrefixes() {
+ let pset = newPset();
+ let prefixes = [1, 5, 100, 1000, 150000];
+ pset.setPrefixes(prefixes, prefixes.length);
+
+ doExpectedLookups(pset, prefixes, 1);
+
+ let secondPrefixes = [12, 50, 300, 2000, 5000, 200000];
+ pset.setPrefixes(secondPrefixes, secondPrefixes.length);
+
+ doExpectedLookups(pset, secondPrefixes, 1);
+ for (let i = 0; i < prefixes.length; i++) {
+ Assert.ok(!wrappedProbe(pset, prefixes[i]));
+ }
+
+ checkContents(pset, secondPrefixes);
+}
+
+function testTinySet() {
+ let pset = Cc["@mozilla.org/url-classifier/prefixset;1"].createInstance(
+ Ci.nsIUrlClassifierPrefixSet
+ );
+ let prefixes = [1];
+ pset.setPrefixes(prefixes, prefixes.length);
+
+ Assert.ok(wrappedProbe(pset, 1));
+ Assert.ok(!wrappedProbe(pset, 100000));
+ checkContents(pset, prefixes);
+
+ prefixes = [];
+ pset.setPrefixes(prefixes, prefixes.length);
+ Assert.ok(!wrappedProbe(pset, 1));
+ checkContents(pset, prefixes);
+}
+
+var tests = [
+ testBasicPset,
+ testSimplePset,
+ testReSetPrefixes,
+ testDuplicates,
+ testTinySet,
+];
+
+function run_test() {
+ // None of the tests use |executeSoon| or any sort of callbacks, so we can
+ // just run them in succession.
+ for (let i = 0; i < tests.length; i++) {
+ dump("Running " + tests[i].name + "\n");
+ tests[i]();
+ }
+}
diff --git a/toolkit/components/url-classifier/tests/unit/test_provider_url.js b/toolkit/components/url-classifier/tests/unit/test_provider_url.js
new file mode 100644
index 0000000000..8229448a9c
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_provider_url.js
@@ -0,0 +1,32 @@
+const { updateAppInfo } = ChromeUtils.importESModule(
+ "resource://testing-common/AppInfo.sys.mjs"
+);
+
+function updateVersion(version) {
+ updateAppInfo({ version });
+}
+
+add_test(function test_provider_url() {
+ let urls = [
+ "browser.safebrowsing.provider.google.updateURL",
+ "browser.safebrowsing.provider.google.gethashURL",
+ "browser.safebrowsing.provider.mozilla.updateURL",
+ "browser.safebrowsing.provider.mozilla.gethashURL",
+ ];
+
+ // FIXME: Most of these only worked in the past because calling
+ // `updateAppInfo` did not actually replace `Services.appinfo`, which
+ // the URL formatter uses.
+ // let versions = ["49.0", "49.0.1", "49.0a1", "49.0b1", "49.0esr", "49.0.1esr"];
+ let versions = ["49.0", "49.0.1"];
+
+ for (let version of versions) {
+ for (let url of urls) {
+ updateVersion(version);
+ let value = Services.urlFormatter.formatURLPref(url);
+ Assert.notEqual(value.indexOf("&appver=49.0&"), -1);
+ }
+ }
+
+ run_next_test();
+});
diff --git a/toolkit/components/url-classifier/tests/unit/test_rsListService.js b/toolkit/components/url-classifier/tests/unit/test_rsListService.js
new file mode 100644
index 0000000000..cd70f92885
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_rsListService.js
@@ -0,0 +1,424 @@
+/* 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/. */
+
+"use strict";
+
+/* Unit tests for the nsIUrlClassifierRemoteSettingsService implementation. */
+
+const { RemoteSettings } = ChromeUtils.importESModule(
+ "resource://services-settings/remote-settings.sys.mjs"
+);
+const { SBRS_UPDATE_MINIMUM_DELAY } = ChromeUtils.importESModule(
+ "resource://gre/modules/UrlClassifierRemoteSettingsService.sys.mjs"
+);
+
+const COLLECTION_NAME = "tracking-protection-lists";
+
+const REMOTE_SETTINGS_DATA = [
+ {
+ Name: "content-fingerprinting-track-digest256",
+ attachment: {
+ hash: "96a4a850a1a475001148fa8a3a5efea58951f7176d3624ad7614fbf32732ee48",
+ size: 948,
+ filename: "content-fingerprinting-track-digest256",
+ location:
+ "main-workspace/tracking-protection-lists/content-fingerprinting-track-digest256",
+ mimetype: "text/plain",
+ },
+ id: "content-fingerprinting-track-digest256",
+ Version: 1597417364,
+ },
+ {
+ Name: "mozplugin-block-digest256",
+ attachment: {
+ hash: "dd2b800c7e4bad17e1c79f3e530c0b94e0a039adf4566f30bc3c285a547fa4fc",
+ size: 3029,
+ filename: "mozplugin-block-digest256",
+ location:
+ "main-workspace/tracking-protection-lists/mozplugin-block-digest256",
+ mimetype: "text/plain",
+ },
+ id: "mozplugin-block-digest256",
+ Version: 1575583456,
+ },
+ {
+ Name: "google-trackwhite-digest256",
+ attachment: {
+ hash: "1cd6d9353e97d66ac737a9716cd3a33416d6a4884dd12dcd1d65266e4c81dfad",
+ size: 1470328,
+ filename: "google-trackwhite-digest256",
+ location:
+ "main-workspace/tracking-protection-lists/google-trackwhite-digest256",
+ mimetype: "text/plain",
+ },
+ id: "google-trackwhite-digest256",
+ Version: 1575583456,
+ },
+ // Entry with non-exist attachment
+ {
+ Name: "social-track-digest256",
+ attachment: {
+ location: "main-workspace/tracking-protection-lists/not-exist",
+ },
+ id: "social-track-digest256",
+ Version: 1111111111,
+ },
+ // Entry with corrupted attachment
+ {
+ Name: "analytic-track-digest256",
+ attachment: {
+ hash: "644a0662bcf7313570ee68490e3805f5cc7a0503c097f040525c28dc5bfe4c97",
+ size: 58,
+ filename: "invalid.chunk",
+ location: "main-workspace/tracking-protection-lists/invalid.chunk",
+ mimetype: "text/plain",
+ },
+ id: "analytic-track-digest256",
+ Version: 1111111111,
+ },
+];
+
+let gListService = Cc["@mozilla.org/url-classifier/list-service;1"].getService(
+ Ci.nsIUrlClassifierRemoteSettingsService
+);
+let gDbService = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(
+ Ci.nsIUrlClassifierDBService
+);
+
+class UpdateEvent extends EventTarget {}
+function waitForEvent(element, eventName) {
+ return new Promise(function (resolve) {
+ element.addEventListener(eventName, e => resolve(e.detail), { once: true });
+ });
+}
+
+function buildPayload(tables) {
+ let payload = ``;
+ for (let table of tables) {
+ payload += table[0];
+ if (table[1] != null) {
+ payload += `;a:${table[1]}`;
+ }
+ payload += `\n`;
+ }
+ return payload;
+}
+
+let server;
+add_setup(async function init() {
+ Services.prefs.setCharPref(
+ "browser.safebrowsing.provider.mozilla.updateURL",
+ `moz-sbrs://tracking-protection-list`
+ );
+ // Setup HTTP server for remote setting
+ server = new HttpServer();
+ server.start(-1);
+ registerCleanupFunction(() => server.stop(() => {}));
+
+ server.registerDirectory(
+ "/cdn/main-workspace/tracking-protection-lists/",
+ do_get_file("data")
+ );
+
+ server.registerPathHandler("/v1/", (request, response) => {
+ response.write(
+ JSON.stringify({
+ capabilities: {
+ attachments: {
+ base_url: `http://localhost:${server.identity.primaryPort}/cdn/`,
+ },
+ },
+ })
+ );
+ response.setHeader("Content-Type", "application/json; charset=UTF-8");
+ response.setStatusLine(null, 200, "OK");
+ });
+
+ Services.prefs.setCharPref(
+ "services.settings.server",
+ `http://localhost:${server.identity.primaryPort}/v1`
+ );
+
+ // Setup remote setting initial data
+ let db = await RemoteSettings(COLLECTION_NAME).db;
+ await db.importChanges({}, 42, REMOTE_SETTINGS_DATA);
+
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref(
+ "browser.safebrowsing.provider.mozilla.updateURL"
+ );
+ Services.prefs.clearUserPref("services.settings.server");
+ });
+});
+
+// Test updates from RemoteSettings when there is no local data
+add_task(async function test_empty_update() {
+ let updateEvent = new UpdateEvent();
+ let promise = waitForEvent(updateEvent, "update");
+
+ const TEST_TABLES = [
+ ["mozplugin-block-digest256", null], // empty
+ ["content-fingerprinting-track-digest256", null], // empty
+ ];
+
+ gListService.fetchList(buildPayload(TEST_TABLES), {
+ // nsIStreamListener observer
+ onStartRequest(request) {},
+ onDataAvailable(aRequest, aStream, aOffset, aCount) {
+ let stream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(
+ Ci.nsIScriptableInputStream
+ );
+ stream.init(aStream);
+ let event = new CustomEvent("update", {
+ detail: stream.readBytes(aCount),
+ });
+ updateEvent.dispatchEvent(event);
+ },
+ onStopRequest(request, status) {},
+ });
+
+ let expected = "n:" + SBRS_UPDATE_MINIMUM_DELAY + "\n";
+ for (const table of TEST_TABLES) {
+ expected += `i:${table[0]}\n` + readFileToString(`data/${table[0]}`);
+ }
+
+ Assert.equal(
+ await promise,
+ expected,
+ "Receive expected data from onDataAvailable"
+ );
+ gListService.clear();
+});
+
+// Test updates from RemoteSettings when we have an empty table,
+// a table with an older version, and a table which is up-to-date.
+add_task(async function test_update() {
+ let updateEvent = new UpdateEvent();
+ let promise = waitForEvent(updateEvent, "update");
+
+ const TEST_TABLES = [
+ ["mozplugin-block-digest256", 1575583456], // up-to-date
+ ["content-fingerprinting-track-digest256", 1575583456 - 1], // older version
+ ];
+
+ gListService.fetchList(buildPayload(TEST_TABLES), {
+ // observer
+ // nsIStreamListener observer
+ onStartRequest(request) {},
+ onDataAvailable(aRequest, aStream, aOffset, aCount) {
+ let stream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(
+ Ci.nsIScriptableInputStream
+ );
+ stream.init(aStream);
+ let event = new CustomEvent("update", {
+ detail: stream.readBytes(aCount),
+ });
+ updateEvent.dispatchEvent(event);
+ },
+ onStopRequest(request, status) {},
+ });
+
+ // Build request with no version
+ let expected = "n:" + SBRS_UPDATE_MINIMUM_DELAY + "\n";
+ for (const table of TEST_TABLES) {
+ if (["content-fingerprinting-track-digest256"].includes(table[0])) {
+ expected += `i:${table[0]}\n` + readFileToString(`data/${table[0]}`);
+ }
+ }
+
+ Assert.equal(
+ await promise,
+ expected,
+ "Receive expected data from onDataAvailable"
+ );
+ gListService.clear();
+});
+
+// Test updates from RemoteSettings service when all tables are up-to-date.
+add_task(async function test_no_update() {
+ let updateEvent = new UpdateEvent();
+ let promise = waitForEvent(updateEvent, "update");
+
+ const TEST_TABLES = [
+ ["mozplugin-block-digest256", 1575583456], // up-to-date
+ ["content-fingerprinting-track-digest256", 1597417364], // up-to-date
+ ];
+
+ gListService.fetchList(buildPayload(TEST_TABLES), {
+ // nsIStreamListener observer
+ onStartRequest(request) {},
+ onDataAvailable(aRequest, aStream, aOffset, aCount) {
+ let stream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(
+ Ci.nsIScriptableInputStream
+ );
+ stream.init(aStream);
+ let event = new CustomEvent("update", {
+ detail: stream.readBytes(aCount),
+ });
+ updateEvent.dispatchEvent(event);
+ },
+ onStopRequest(request, status) {},
+ });
+
+ // No data is expected
+ let expected = "n:" + SBRS_UPDATE_MINIMUM_DELAY + "\n";
+
+ Assert.equal(
+ await promise,
+ expected,
+ "Receive expected data from onDataAvailable"
+ );
+ gListService.clear();
+});
+
+add_test(function test_update() {
+ let streamUpdater = Cc[
+ "@mozilla.org/url-classifier/streamupdater;1"
+ ].getService(Ci.nsIUrlClassifierStreamUpdater);
+
+ // Download some updates, and don't continue until the downloads are done.
+ function updateSuccess(aEvent) {
+ Assert.equal(SBRS_UPDATE_MINIMUM_DELAY, aEvent);
+ info("All data processed");
+ run_next_test();
+ }
+ // Just throw if we ever get an update or download error.
+ function handleError(aEvent) {
+ do_throw("We didn't download or update correctly: " + aEvent);
+ }
+
+ streamUpdater.downloadUpdates(
+ "content-fingerprinting-track-digest256",
+ "content-fingerprinting-track-digest256;\n",
+ true,
+ "moz-sbrs://remote-setting",
+ updateSuccess,
+ handleError,
+ handleError
+ );
+});
+
+add_test(function test_url_not_denylisted() {
+ let uri = Services.io.newURI("http://example.com");
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ uri,
+ {}
+ );
+ gDbService.lookup(
+ principal,
+ "content-fingerprinting-track-digest256",
+ function handleEvent(aEvent) {
+ // This URI is not on any lists.
+ Assert.equal("", aEvent);
+ run_next_test();
+ }
+ );
+});
+
+add_test(function test_url_denylisted() {
+ let uri = Services.io.newURI("https://www.foresee.com");
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ uri,
+ {}
+ );
+ gDbService.lookup(
+ principal,
+ "content-fingerprinting-track-digest256",
+ function handleEvent(aEvent) {
+ Assert.equal("content-fingerprinting-track-digest256", aEvent);
+ run_next_test();
+ }
+ );
+});
+
+add_test(function test_update_download_error() {
+ let streamUpdater = Cc[
+ "@mozilla.org/url-classifier/streamupdater;1"
+ ].getService(Ci.nsIUrlClassifierStreamUpdater);
+
+ // Download some updates, and don't continue until the downloads are done.
+ function updateSuccessOrError(aEvent) {
+ do_throw("Should be downbload error");
+ }
+ // Just throw if we ever get an update or download error.
+ function downloadError(aEvent) {
+ run_next_test();
+ }
+
+ streamUpdater.downloadUpdates(
+ "social-track-digest256",
+ "social-track-digest256;\n",
+ true,
+ "moz-sbrs://remote-setting",
+ updateSuccessOrError,
+ updateSuccessOrError,
+ downloadError
+ );
+});
+
+add_test(function test_update_update_error() {
+ let streamUpdater = Cc[
+ "@mozilla.org/url-classifier/streamupdater;1"
+ ].getService(Ci.nsIUrlClassifierStreamUpdater);
+
+ // Download some updates, and don't continue until the downloads are done.
+ function updateSuccessOrDownloadError(aEvent) {
+ do_throw("Should be update error");
+ }
+ // Just throw if we ever get an update or download error.
+ function updateError(aEvent) {
+ run_next_test();
+ }
+
+ streamUpdater.downloadUpdates(
+ "analytic-track-digest256",
+ "analytic-track-digest256;\n",
+ true,
+ "moz-sbrs://remote-setting",
+ updateSuccessOrDownloadError,
+ updateError,
+ updateSuccessOrDownloadError
+ );
+});
+
+add_task(async function test_update_large_file() {
+ let updateEvent = new UpdateEvent();
+ let promise = waitForEvent(updateEvent, "update");
+
+ const TEST_TABLES = [
+ ["google-trackwhite-digest256", 1575583456 - 1], // up-to-date
+ ];
+
+ gListService.fetchList(buildPayload(TEST_TABLES), {
+ // observer
+ // nsIStreamListener observer
+ onStartRequest(request) {},
+ onDataAvailable(aRequest, aStream, aOffset, aCount) {
+ let stream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(
+ Ci.nsIScriptableInputStream
+ );
+ stream.init(aStream);
+ let event = new CustomEvent("update", {
+ detail: stream.readBytes(aCount),
+ });
+ updateEvent.dispatchEvent(event);
+ },
+ onStopRequest(request, status) {},
+ });
+
+ // Build request with no version
+ let expected = "n:" + SBRS_UPDATE_MINIMUM_DELAY + "\n";
+ for (const table of TEST_TABLES) {
+ if (["google-trackwhite-digest256"].includes(table[0])) {
+ expected += `i:${table[0]}\n` + readFileToString(`data/${table[0]}`);
+ }
+ }
+
+ Assert.equal(
+ await promise,
+ expected,
+ "Receive expected data from onDataAvailable"
+ );
+ gListService.clear();
+});
diff --git a/toolkit/components/url-classifier/tests/unit/test_safebrowsing_protobuf.js b/toolkit/components/url-classifier/tests/unit/test_safebrowsing_protobuf.js
new file mode 100644
index 0000000000..73426751cb
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_safebrowsing_protobuf.js
@@ -0,0 +1,29 @@
+function run_test() {
+ let urlUtils = Cc["@mozilla.org/url-classifier/utils;1"].getService(
+ Ci.nsIUrlClassifierUtils
+ );
+
+ // No list at all.
+ let requestNoList = urlUtils.makeUpdateRequestV4([], []);
+
+ // Only one valid list name.
+ let requestOneValid = urlUtils.makeUpdateRequestV4(
+ ["goog-phish-proto"],
+ ["AAAAAA"]
+ );
+
+ // Only one invalid list name.
+ let requestOneInvalid = urlUtils.makeUpdateRequestV4(
+ ["bad-list-name"],
+ ["AAAAAA"]
+ );
+
+ // One valid and one invalid list name.
+ let requestOneInvalidOneValid = urlUtils.makeUpdateRequestV4(
+ ["goog-phish-proto", "bad-list-name"],
+ ["AAAAAA", "AAAAAA"]
+ );
+
+ equal(requestNoList, requestOneInvalid);
+ equal(requestOneValid, requestOneInvalidOneValid);
+}
diff --git a/toolkit/components/url-classifier/tests/unit/test_shouldclassify.js b/toolkit/components/url-classifier/tests/unit/test_shouldclassify.js
new file mode 100644
index 0000000000..6abdbb5f85
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_shouldclassify.js
@@ -0,0 +1,166 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+*/
+
+"use strict";
+
+const { NetUtil } = ChromeUtils.importESModule(
+ "resource://gre/modules/NetUtil.sys.mjs"
+);
+const { UrlClassifierTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/UrlClassifierTestUtils.sys.mjs"
+);
+
+const defaultTopWindowURI = NetUtil.newURI("http://www.example.com/");
+
+var httpServer;
+var trackingOrigin;
+
+// ShouldClassify algorithm uses the following parameters:
+// 1. Ci.nsIChannel.LOAD_ BYPASS_URL_CLASSIFIER loadflags
+// 2. Content type
+// 3. triggering principal
+// 4. be Conservative
+// We test are the combinations here to make sure the algorithm is correct
+
+// const PARAM_LOAD_BYPASS_URL_CLASSIFIER = 1 << 0;
+const PARAM_CONTENT_POLICY_TYPE_DOCUMENT = 1 << 1;
+const PARAM_TRIGGERING_PRINCIPAL_SYSTEM = 1 << 2;
+const PARAM_CAP_BE_CONSERVATIVE = 1 << 3;
+const PARAM_MAX = 1 << 4;
+
+function getParameters(bitFlags) {
+ var params = {
+ loadFlags: Ci.nsIRequest.LOAD_NORMAL,
+ contentType: Ci.nsIContentPolicy.TYPE_OTHER,
+ system: false,
+ beConservative: false,
+ };
+
+ if (bitFlags & PARAM_TRIGGERING_PRINCIPAL_SYSTEM) {
+ params.loadFlags = Ci.nsIChannel.LOAD_BYPASS_URL_CLASSIFIER;
+ }
+
+ if (bitFlags & PARAM_CONTENT_POLICY_TYPE_DOCUMENT) {
+ params.contentType = Ci.nsIContentPolicy.TYPE_DOCUMENT;
+ }
+
+ if (bitFlags & PARAM_TRIGGERING_PRINCIPAL_SYSTEM) {
+ params.system = true;
+ }
+
+ if (bitFlags & PARAM_CAP_BE_CONSERVATIVE) {
+ params.beConservative = true;
+ }
+
+ return params;
+}
+
+function getExpectedResult(params) {
+ if (params.loadFlags & Ci.nsIChannel.LOAD_BYPASS_URL_CLASSIFIER) {
+ return false;
+ }
+ if (params.beConservative) {
+ return false;
+ }
+ if (
+ params.system &&
+ params.contentType != Ci.nsIContentPolicy.TYPE_DOCUMENT
+ ) {
+ return false;
+ }
+
+ return true;
+}
+
+function setupHttpServer() {
+ httpServer = new HttpServer();
+ httpServer.start(-1);
+ httpServer.identity.setPrimary(
+ "http",
+ "tracking.example.org",
+ httpServer.identity.primaryPort
+ );
+ httpServer.identity.add(
+ "http",
+ "example.org",
+ httpServer.identity.primaryPort
+ );
+ trackingOrigin =
+ "http://tracking.example.org:" + httpServer.identity.primaryPort;
+}
+
+function setupChannel(params) {
+ var channel;
+
+ if (params.system) {
+ channel = NetUtil.newChannel({
+ uri: trackingOrigin + "/evil.js",
+ loadUsingSystemPrincipal: true,
+ contentPolicyType: params.contentType,
+ });
+ } else {
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ NetUtil.newURI(trackingOrigin),
+ {}
+ );
+ channel = NetUtil.newChannel({
+ uri: trackingOrigin + "/evil.js",
+ loadingPrincipal: principal,
+ securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ contentPolicyType: params.contentType,
+ });
+ }
+
+ channel.QueryInterface(Ci.nsIHttpChannel);
+ channel.requestMethod = "GET";
+ channel.loadFlags |= params.loadFlags;
+ channel
+ .QueryInterface(Ci.nsIHttpChannelInternal)
+ .setTopWindowURIIfUnknown(defaultTopWindowURI);
+ channel.QueryInterface(Ci.nsIHttpChannelInternal).beConservative =
+ params.beConservative;
+
+ return channel;
+}
+
+add_task(async function testShouldClassify() {
+ Services.prefs.setBoolPref(
+ "privacy.trackingprotection.annotate_channels",
+ true
+ );
+ Services.prefs.setBoolPref("network.dns.native-is-localhost", true);
+
+ setupHttpServer();
+
+ await UrlClassifierTestUtils.addTestTrackers();
+
+ for (let i = 0; i < PARAM_MAX; i++) {
+ let params = getParameters(i);
+ let channel = setupChannel(params);
+
+ await new Promise(resolve => {
+ channel.asyncOpen({
+ onStartRequest: (request, context) => {
+ Assert.equal(
+ !!(
+ request.QueryInterface(Ci.nsIClassifiedChannel)
+ .classificationFlags &
+ Ci.nsIClassifiedChannel.CLASSIFIED_ANY_BASIC_TRACKING
+ ),
+ getExpectedResult(params)
+ );
+ request.cancel(Cr.NS_ERROR_ABORT);
+ resolve();
+ },
+
+ onDataAvailable: (request, context, stream, offset, count) => {},
+ onStopRequest: (request, context, status) => {},
+ });
+ });
+ }
+
+ UrlClassifierTestUtils.cleanupTestTrackers();
+
+ httpServer.stop(do_test_finished);
+});
diff --git a/toolkit/components/url-classifier/tests/unit/test_streamupdater.js b/toolkit/components/url-classifier/tests/unit/test_streamupdater.js
new file mode 100644
index 0000000000..1a2ee847d1
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_streamupdater.js
@@ -0,0 +1,244 @@
+function doTest(updates, assertions, expectError) {
+ if (expectError) {
+ doUpdateTest(updates, assertions, updateError, runNextTest);
+ } else {
+ doUpdateTest(updates, assertions, runNextTest, updateError);
+ }
+}
+
+// Never use the same URLs for multiple tests, because we aren't guaranteed
+// to reset the database between tests.
+function testFillDb() {
+ var add1Urls = ["zaz.com/a", "yxz.com/c"];
+
+ var update = "n:1000\n";
+ update += "i:test-phish-simple\n";
+
+ var update1 = buildBareUpdate([{ chunkNum: 1, urls: add1Urls }]);
+ update += "u:data:," + encodeURIComponent(update1) + "\n";
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1",
+ urlsExist: add1Urls,
+ };
+
+ doTest([update], assertions, false);
+}
+
+function testSimpleForward() {
+ var add1Urls = ["foo-simple.com/a", "bar-simple.com/c"];
+ var add2Urls = ["foo-simple.com/b"];
+ var add3Urls = ["bar-simple.com/d"];
+
+ var update = "n:1000\n";
+ update += "i:test-phish-simple\n";
+
+ var update1 = buildBareUpdate([{ chunkNum: 1, urls: add1Urls }]);
+ update += "u:data:," + encodeURIComponent(update1) + "\n";
+
+ var update2 = buildBareUpdate([{ chunkNum: 2, urls: add2Urls }]);
+ update += "u:data:," + encodeURIComponent(update2) + "\n";
+
+ var update3 = buildBareUpdate([{ chunkNum: 3, urls: add3Urls }]);
+ update += "u:data:," + encodeURIComponent(update3) + "\n";
+
+ var assertions = {
+ tableData: "test-phish-simple;a:1-3",
+ urlsExist: add1Urls.concat(add2Urls).concat(add3Urls),
+ };
+
+ doTest([update], assertions, false);
+}
+
+// Make sure that a nested forward (a forward within a forward) causes
+// the update to fail.
+function testNestedForward() {
+ var add1Urls = ["foo-nested.com/a", "bar-nested.com/c"];
+ var add2Urls = ["foo-nested.com/b"];
+
+ var update = "n:1000\n";
+ update += "i:test-phish-simple\n";
+
+ var update1 = buildBareUpdate([{ chunkNum: 1, urls: add1Urls }]);
+ update += "u:data:," + encodeURIComponent(update1) + "\n";
+
+ var update2 = buildBareUpdate([{ chunkNum: 2 }]);
+ var update3 = buildBareUpdate([{ chunkNum: 3, urls: add1Urls }]);
+
+ update2 += "u:data:," + encodeURIComponent(update3) + "\n";
+
+ update += "u:data:," + encodeURIComponent(update2) + "\n";
+
+ var assertions = {
+ tableData: "",
+ urlsDontExist: add1Urls.concat(add2Urls),
+ };
+
+ doTest([update], assertions, true);
+}
+
+// An invalid URL forward causes the update to fail.
+function testInvalidUrlForward() {
+ var add1Urls = ["foo-invalid.com/a", "bar-invalid.com/c"];
+
+ var update = buildPhishingUpdate([{ chunkNum: 1, urls: add1Urls }]);
+ update += "u:asdf://blah/blah\n"; // invalid URL scheme
+
+ // add1Urls is present, but that is an artifact of the way we do the test.
+ var assertions = {
+ tableData: "test-phish-simple;a:1",
+ urlsExist: add1Urls,
+ };
+
+ doTest([update], assertions, true);
+}
+
+// A failed network request causes the update to fail.
+function testErrorUrlForward() {
+ var add1Urls = ["foo-forward.com/a", "bar-forward.com/c"];
+
+ var update = buildPhishingUpdate([{ chunkNum: 1, urls: add1Urls }]);
+ update += "u:http://test.invalid/asdf/asdf\n"; // invalid URL scheme
+
+ // add1Urls is present, but that is an artifact of the way we do the test.
+ var assertions = {
+ tableData: "test-phish-simple;a:1",
+ urlsExist: add1Urls,
+ };
+
+ doTest([update], assertions, true);
+}
+
+function testMultipleTables() {
+ var add1Urls = ["foo-multiple.com/a", "bar-multiple.com/c"];
+ var add2Urls = ["foo-multiple.com/b"];
+ var add3Urls = ["bar-multiple.com/d"];
+ var add4Urls = ["bar-multiple.com/e"];
+ var add6Urls = ["bar-multiple.com/g"];
+
+ var update = "n:1000\n";
+ update += "i:test-phish-simple\n";
+
+ var update1 = buildBareUpdate([{ chunkNum: 1, urls: add1Urls }]);
+ update += "u:data:," + encodeURIComponent(update1) + "\n";
+
+ var update2 = buildBareUpdate([{ chunkNum: 2, urls: add2Urls }]);
+ update += "u:data:," + encodeURIComponent(update2) + "\n";
+
+ update += "i:test-malware-simple\n";
+
+ var update3 = buildBareUpdate([{ chunkNum: 3, urls: add3Urls }]);
+ update += "u:data:," + encodeURIComponent(update3) + "\n";
+
+ update += "i:test-unwanted-simple\n";
+ var update4 = buildBareUpdate([{ chunkNum: 4, urls: add4Urls }]);
+ update += "u:data:," + encodeURIComponent(update4) + "\n";
+
+ update += "i:test-block-simple\n";
+ var update6 = buildBareUpdate([{ chunkNum: 6, urls: add6Urls }]);
+ update += "u:data:," + encodeURIComponent(update6) + "\n";
+
+ var assertions = {
+ tableData:
+ "test-block-simple;a:6\ntest-malware-simple;a:3\ntest-phish-simple;a:1-2\ntest-unwanted-simple;a:4",
+ urlsExist: add1Urls.concat(add2Urls),
+ malwareUrlsExist: add3Urls,
+ unwantedUrlsExist: add4Urls,
+ blockedUrlsExist: add6Urls,
+ };
+
+ doTest([update], assertions, false);
+}
+
+function testUrlInMultipleTables() {
+ var add1Urls = ["foo-forward.com/a"];
+
+ var update = "n:1000\n";
+ update += "i:test-phish-simple\n";
+
+ var update1 = buildBareUpdate([{ chunkNum: 1, urls: add1Urls }]);
+ update += "u:data:," + encodeURIComponent(update1) + "\n";
+
+ update += "i:test-malware-simple\n";
+ var update2 = buildBareUpdate([{ chunkNum: 2, urls: add1Urls }]);
+ update += "u:data:," + encodeURIComponent(update2) + "\n";
+
+ update += "i:test-unwanted-simple\n";
+ var update3 = buildBareUpdate([{ chunkNum: 3, urls: add1Urls }]);
+ update += "u:data:," + encodeURIComponent(update3) + "\n";
+
+ var assertions = {
+ tableData:
+ "test-malware-simple;a:2\ntest-phish-simple;a:1\ntest-unwanted-simple;a:3",
+ urlExistInMultipleTables: {
+ url: add1Urls,
+ tables: "test-malware-simple,test-phish-simple,test-unwanted-simple",
+ },
+ };
+
+ doTest([update], assertions, false);
+}
+
+function Observer(callback) {
+ this.observe = callback;
+}
+
+Observer.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
+};
+
+// Tests a database reset request.
+function testReset() {
+ // The moz-phish-simple table is populated separately from the other update in
+ // a separate update request. Therefore it should not be reset when we run the
+ // updates later in this function.
+ var mozAddUrls = ["moz-reset.com/a"];
+ var mozUpdate = buildMozPhishingUpdate([{ chunkNum: 1, urls: mozAddUrls }]);
+
+ var dataUpdate = "data:," + encodeURIComponent(mozUpdate);
+
+ streamUpdater.downloadUpdates(
+ mozTables,
+ "",
+ true,
+ dataUpdate,
+ () => {},
+ updateError,
+ updateError
+ );
+
+ var addUrls1 = ["foo-reset.com/a", "foo-reset.com/b"];
+ var update1 = buildPhishingUpdate([{ chunkNum: 1, urls: addUrls1 }]);
+
+ var update2 = "n:1000\nr:pleasereset\n";
+
+ var addUrls3 = ["bar-reset.com/a", "bar-reset.com/b"];
+ var update3 = buildPhishingUpdate([{ chunkNum: 3, urls: addUrls3 }]);
+
+ var assertions = {
+ tableData: "moz-phish-simple;a:1\ntest-phish-simple;a:3", // tables that should still be there.
+ mozPhishingUrlsExist: mozAddUrls, // mozAddUrls added prior to the reset
+ // but it should still exist after reset.
+ urlsExist: addUrls3, // addUrls3 added after the reset.
+ urlsDontExist: addUrls1, // addUrls1 added prior to the reset
+ };
+
+ // Use these update responses in order. The update request only
+ // contains test-*-simple tables so the reset will only apply to these.
+ doTest([update1, update2, update3], assertions, false);
+}
+
+function run_test() {
+ runTests([
+ testFillDb,
+ testSimpleForward,
+ testNestedForward,
+ testInvalidUrlForward,
+ testErrorUrlForward,
+ testMultipleTables,
+ testUrlInMultipleTables,
+ testReset,
+ ]);
+}
+
+do_test_pending();
diff --git a/toolkit/components/url-classifier/tests/unit/test_threat_type_conversion.js b/toolkit/components/url-classifier/tests/unit/test_threat_type_conversion.js
new file mode 100644
index 0000000000..b1bec40b4f
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_threat_type_conversion.js
@@ -0,0 +1,50 @@
+function run_test() {
+ let urlUtils = Cc["@mozilla.org/url-classifier/utils;1"].getService(
+ Ci.nsIUrlClassifierUtils
+ );
+
+ // Test list name to threat type conversion.
+
+ equal(urlUtils.convertListNameToThreatType("goog-malware-proto"), 1);
+ equal(urlUtils.convertListNameToThreatType("googpub-phish-proto"), 2);
+ equal(urlUtils.convertListNameToThreatType("goog-unwanted-proto"), 3);
+ equal(urlUtils.convertListNameToThreatType("goog-harmful-proto"), 4);
+ equal(urlUtils.convertListNameToThreatType("goog-phish-proto"), 5);
+ equal(urlUtils.convertListNameToThreatType("goog-badbinurl-proto"), 7);
+ equal(urlUtils.convertListNameToThreatType("goog-downloadwhite-proto"), 9);
+
+ try {
+ urlUtils.convertListNameToThreatType("bad-list-name");
+ ok(false, "Bad list name should lead to exception.");
+ } catch (e) {}
+
+ try {
+ urlUtils.convertListNameToThreatType("bad-list-name");
+ ok(false, "Bad list name should lead to exception.");
+ } catch (e) {}
+
+ // Test threat type to list name conversion.
+ equal(urlUtils.convertThreatTypeToListNames(1), "goog-malware-proto");
+ equal(
+ urlUtils.convertThreatTypeToListNames(2),
+ "googpub-phish-proto,moztest-phish-proto,test-phish-proto"
+ );
+ equal(
+ urlUtils.convertThreatTypeToListNames(3),
+ "goog-unwanted-proto,moztest-unwanted-proto,test-unwanted-proto"
+ );
+ equal(urlUtils.convertThreatTypeToListNames(4), "goog-harmful-proto");
+ equal(urlUtils.convertThreatTypeToListNames(5), "goog-phish-proto");
+ equal(urlUtils.convertThreatTypeToListNames(7), "goog-badbinurl-proto");
+ equal(urlUtils.convertThreatTypeToListNames(9), "goog-downloadwhite-proto");
+
+ try {
+ urlUtils.convertThreatTypeToListNames(0);
+ ok(false, "Bad threat type should lead to exception.");
+ } catch (e) {}
+
+ try {
+ urlUtils.convertThreatTypeToListNames(100);
+ ok(false, "Bad threat type should lead to exception.");
+ } catch (e) {}
+}
diff --git a/toolkit/components/url-classifier/tests/unit/xpcshell.toml b/toolkit/components/url-classifier/tests/unit/xpcshell.toml
new file mode 100644
index 0000000000..561188ceb3
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/xpcshell.toml
@@ -0,0 +1,56 @@
+[DEFAULT]
+head = "head_urlclassifier.js"
+tags = "condprof"
+skip-if = ["os == 'android'"]
+support-files = ["data/**"]
+
+["test_addsub.js"]
+
+["test_backoff.js"]
+
+["test_bug1274685_unowned_list.js"]
+
+["test_canonicalization.js"]
+
+["test_channelClassifierService.js"]
+
+["test_dbservice.js"]
+skip-if = ["condprof"] # Bug 1769828
+
+["test_digest256.js"]
+run-sequentially = "very high failure rate in parallel"
+
+["test_exceptionListService.js"]
+tags = "remote-settings"
+
+["test_features.js"]
+
+["test_hashcompleter.js"]
+
+["test_hashcompleter_v4.js"]
+
+["test_listmanager.js"]
+run-sequentially = "very high failure rate in parallel"
+
+["test_malwaretable_pref.js"]
+
+["test_partial.js"]
+
+["test_platform_specific_threats.js"]
+
+["test_pref.js"]
+
+["test_prefixset.js"]
+
+["test_provider_url.js"]
+
+["test_rsListService.js"]
+tags = "remote-settings"
+
+["test_safebrowsing_protobuf.js"]
+
+["test_shouldclassify.js"]
+
+["test_streamupdater.js"]
+
+["test_threat_type_conversion.js"]