Line data Source code
1 : //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "nsAutoPtr.h"
7 : #include "nsCOMPtr.h"
8 : #include "nsAppDirectoryServiceDefs.h"
9 : #include "nsArrayUtils.h"
10 : #include "nsCRT.h"
11 : #include "nsICryptoHash.h"
12 : #include "nsICryptoHMAC.h"
13 : #include "nsIDirectoryService.h"
14 : #include "nsIKeyModule.h"
15 : #include "nsIObserverService.h"
16 : #include "nsIPermissionManager.h"
17 : #include "nsIPrefBranch.h"
18 : #include "nsIPrefService.h"
19 : #include "nsIProperties.h"
20 : #include "nsToolkitCompsCID.h"
21 : #include "nsIXULRuntime.h"
22 : #include "nsUrlClassifierDBService.h"
23 : #include "nsUrlClassifierUtils.h"
24 : #include "nsUrlClassifierProxies.h"
25 : #include "nsURILoader.h"
26 : #include "nsString.h"
27 : #include "nsReadableUtils.h"
28 : #include "nsTArray.h"
29 : #include "nsNetCID.h"
30 : #include "nsThreadUtils.h"
31 : #include "nsProxyRelease.h"
32 : #include "nsString.h"
33 : #include "mozilla/Atomics.h"
34 : #include "mozilla/DebugOnly.h"
35 : #include "mozilla/ErrorNames.h"
36 : #include "mozilla/Mutex.h"
37 : #include "mozilla/Preferences.h"
38 : #include "mozilla/SizePrintfMacros.h"
39 : #include "mozilla/TimeStamp.h"
40 : #include "mozilla/Telemetry.h"
41 : #include "mozilla/Logging.h"
42 : #include "prnetdb.h"
43 : #include "Entries.h"
44 : #include "HashStore.h"
45 : #include "Classifier.h"
46 : #include "ProtocolParser.h"
47 : #include "mozilla/Attributes.h"
48 : #include "nsIPrincipal.h"
49 : #include "Classifier.h"
50 : #include "ProtocolParser.h"
51 : #include "nsContentUtils.h"
52 : #include "mozilla/dom/ContentChild.h"
53 : #include "mozilla/dom/PermissionMessageUtils.h"
54 : #include "mozilla/dom/URLClassifierChild.h"
55 : #include "mozilla/ipc/URIUtils.h"
56 : #include "nsProxyRelease.h"
57 :
58 : namespace mozilla {
59 : namespace safebrowsing {
60 :
61 : nsresult
62 0 : TablesToResponse(const nsACString& tables)
63 : {
64 0 : if (tables.IsEmpty()) {
65 0 : return NS_OK;
66 : }
67 :
68 : // We don't check mCheckMalware and friends because disabled tables are
69 : // never included
70 0 : if (FindInReadable(NS_LITERAL_CSTRING("-malware-"), tables)) {
71 0 : return NS_ERROR_MALWARE_URI;
72 : }
73 0 : if (FindInReadable(NS_LITERAL_CSTRING("-phish-"), tables)) {
74 0 : return NS_ERROR_PHISHING_URI;
75 : }
76 0 : if (FindInReadable(NS_LITERAL_CSTRING("-unwanted-"), tables)) {
77 0 : return NS_ERROR_UNWANTED_URI;
78 : }
79 0 : if (FindInReadable(NS_LITERAL_CSTRING("-track-"), tables)) {
80 0 : return NS_ERROR_TRACKING_URI;
81 : }
82 0 : if (FindInReadable(NS_LITERAL_CSTRING("-block-"), tables)) {
83 0 : return NS_ERROR_BLOCKED_URI;
84 : }
85 0 : return NS_OK;
86 : }
87 :
88 : } // namespace safebrowsing
89 : } // namespace mozilla
90 :
91 : using namespace mozilla;
92 : using namespace mozilla::safebrowsing;
93 :
94 : // MOZ_LOG=UrlClassifierDbService:5
95 : LazyLogModule gUrlClassifierDbServiceLog("UrlClassifierDbService");
96 : #define LOG(args) MOZ_LOG(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug, args)
97 : #define LOG_ENABLED() MOZ_LOG_TEST(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug)
98 :
99 : #define GETHASH_NOISE_PREF "urlclassifier.gethashnoise"
100 : #define GETHASH_NOISE_DEFAULT 4
101 :
102 : // 30 minutes as the maximum negative cache duration.
103 : #define MAXIMUM_NEGATIVE_CACHE_DURATION_SEC (30 * 60 * 1000)
104 :
105 : class nsUrlClassifierDBServiceWorker;
106 :
107 : // Singleton instance.
108 : static nsUrlClassifierDBService* sUrlClassifierDBService;
109 :
110 : nsIThread* nsUrlClassifierDBService::gDbBackgroundThread = nullptr;
111 :
112 : // Once we've committed to shutting down, don't do work in the background
113 : // thread.
114 : static bool gShuttingDownThread = false;
115 :
116 : static uint32_t sGethashNoise = GETHASH_NOISE_DEFAULT;
117 :
118 38 : NS_IMPL_ISUPPORTS(nsUrlClassifierDBServiceWorker,
119 : nsIUrlClassifierDBService)
120 :
121 1 : nsUrlClassifierDBServiceWorker::nsUrlClassifierDBServiceWorker()
122 : : mInStream(false)
123 : , mGethashNoise(0)
124 1 : , mPendingLookupLock("nsUrlClassifierDBServerWorker.mPendingLookupLock")
125 : {
126 1 : }
127 :
128 0 : nsUrlClassifierDBServiceWorker::~nsUrlClassifierDBServiceWorker()
129 : {
130 0 : NS_ASSERTION(!mClassifier,
131 : "Db connection not closed, leaking memory! Call CloseDb "
132 : "to close the connection.");
133 0 : }
134 :
135 : nsresult
136 1 : nsUrlClassifierDBServiceWorker::Init(uint32_t aGethashNoise,
137 : nsCOMPtr<nsIFile> aCacheDir,
138 : nsUrlClassifierDBService *aDBService)
139 : {
140 1 : mGethashNoise = aGethashNoise;
141 1 : mCacheDir = aCacheDir;
142 1 : mDBService = aDBService;
143 :
144 1 : ResetUpdate();
145 :
146 1 : return NS_OK;
147 : }
148 :
149 : nsresult
150 0 : nsUrlClassifierDBServiceWorker::QueueLookup(const nsACString& spec,
151 : const nsACString& tables,
152 : nsIUrlClassifierLookupCallback* callback)
153 : {
154 0 : MutexAutoLock lock(mPendingLookupLock);
155 0 : if (gShuttingDownThread) {
156 0 : return NS_ERROR_ABORT;
157 : }
158 :
159 0 : PendingLookup* lookup = mPendingLookups.AppendElement();
160 0 : if (!lookup) return NS_ERROR_OUT_OF_MEMORY;
161 :
162 0 : lookup->mStartTime = TimeStamp::Now();
163 0 : lookup->mKey = spec;
164 0 : lookup->mCallback = callback;
165 0 : lookup->mTables = tables;
166 :
167 0 : return NS_OK;
168 : }
169 :
170 : nsresult
171 1 : nsUrlClassifierDBServiceWorker::DoLocalLookup(const nsACString& spec,
172 : const nsACString& tables,
173 : LookupResultArray* results)
174 : {
175 1 : if (gShuttingDownThread) {
176 0 : return NS_ERROR_ABORT;
177 : }
178 :
179 1 : MOZ_ASSERT(!NS_IsMainThread(), "DoLocalLookup must be on background thread");
180 1 : if (!results) {
181 0 : return NS_ERROR_FAILURE;
182 : }
183 : // Bail if we haven't been initialized on the background thread.
184 1 : if (!mClassifier) {
185 0 : return NS_ERROR_NOT_AVAILABLE;
186 : }
187 :
188 : // We ignore failures from Check because we'd rather return the
189 : // results that were found than fail.
190 1 : mClassifier->Check(spec, tables, *results);
191 :
192 1 : LOG(("Found %" PRIuSIZE " results.", results->Length()));
193 1 : return NS_OK;
194 : }
195 :
196 : static nsresult
197 0 : ProcessLookupResults(LookupResultArray* results, nsTArray<nsCString>& tables)
198 : {
199 : // Build the result array.
200 0 : for (uint32_t i = 0; i < results->Length(); i++) {
201 0 : LookupResult& result = results->ElementAt(i);
202 0 : MOZ_ASSERT(!result.mNoise, "Lookup results should not have noise added");
203 0 : LOG(("Found result from table %s", result.mTableName.get()));
204 0 : if (tables.IndexOf(result.mTableName) == nsTArray<nsCString>::NoIndex) {
205 0 : tables.AppendElement(result.mTableName);
206 : }
207 : }
208 0 : return NS_OK;
209 : }
210 :
211 : /**
212 : * Lookup up a key in the database is a two step process:
213 : *
214 : * a) First we look for any Entries in the database that might apply to this
215 : * url. For each URL there are one or two possible domain names to check:
216 : * the two-part domain name (example.com) and the three-part name
217 : * (www.example.com). We check the database for both of these.
218 : * b) If we find any entries, we check the list of fragments for that entry
219 : * against the possible subfragments of the URL as described in the
220 : * "Simplified Regular Expression Lookup" section of the protocol doc.
221 : */
222 : nsresult
223 0 : nsUrlClassifierDBServiceWorker::DoLookup(const nsACString& spec,
224 : const nsACString& tables,
225 : nsIUrlClassifierLookupCallback* c)
226 : {
227 0 : if (gShuttingDownThread) {
228 0 : c->LookupComplete(nullptr);
229 0 : return NS_ERROR_NOT_INITIALIZED;
230 : }
231 :
232 0 : PRIntervalTime clockStart = 0;
233 0 : if (LOG_ENABLED()) {
234 0 : clockStart = PR_IntervalNow();
235 : }
236 :
237 0 : nsAutoPtr<LookupResultArray> results(new LookupResultArray());
238 0 : if (!results) {
239 0 : c->LookupComplete(nullptr);
240 0 : return NS_ERROR_OUT_OF_MEMORY;
241 : }
242 :
243 0 : nsresult rv = DoLocalLookup(spec, tables, results);
244 0 : if (NS_FAILED(rv)) {
245 0 : c->LookupComplete(nullptr);
246 0 : return rv;
247 : }
248 :
249 0 : LOG(("Found %" PRIuSIZE " results.", results->Length()));
250 :
251 :
252 0 : if (LOG_ENABLED()) {
253 0 : PRIntervalTime clockEnd = PR_IntervalNow();
254 0 : LOG(("query took %dms\n",
255 : PR_IntervalToMilliseconds(clockEnd - clockStart)));
256 : }
257 :
258 0 : for (uint32_t i = 0; i < results->Length(); i++) {
259 0 : const LookupResult& lookupResult = results->ElementAt(i);
260 :
261 0 : if (!lookupResult.Confirmed() &&
262 0 : mDBService->CanComplete(lookupResult.mTableName)) {
263 :
264 : // We're going to be doing a gethash request, add some extra entries.
265 : // Note that we cannot pass the first two by reference, because we
266 : // add to completes, whicah can cause completes to reallocate and move.
267 0 : AddNoise(lookupResult.hash.fixedLengthPrefix,
268 : lookupResult.mTableName,
269 0 : mGethashNoise, *results);
270 0 : break;
271 : }
272 : }
273 :
274 : // At this point ownership of 'results' is handed to the callback.
275 0 : c->LookupComplete(results.forget());
276 :
277 0 : return NS_OK;
278 : }
279 :
280 : nsresult
281 1 : nsUrlClassifierDBServiceWorker::HandlePendingLookups()
282 : {
283 1 : if (gShuttingDownThread) {
284 0 : return NS_ERROR_ABORT;
285 : }
286 :
287 2 : MutexAutoLock lock(mPendingLookupLock);
288 1 : while (mPendingLookups.Length() > 0) {
289 0 : PendingLookup lookup = mPendingLookups[0];
290 0 : mPendingLookups.RemoveElementAt(0);
291 : {
292 0 : MutexAutoUnlock unlock(mPendingLookupLock);
293 0 : DoLookup(lookup.mKey, lookup.mTables, lookup.mCallback);
294 : }
295 0 : double lookupTime = (TimeStamp::Now() - lookup.mStartTime).ToMilliseconds();
296 0 : Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LOOKUP_TIME_2,
297 0 : static_cast<uint32_t>(lookupTime));
298 : }
299 :
300 1 : return NS_OK;
301 : }
302 :
303 : nsresult
304 0 : nsUrlClassifierDBServiceWorker::AddNoise(const Prefix aPrefix,
305 : const nsCString tableName,
306 : uint32_t aCount,
307 : LookupResultArray& results)
308 : {
309 0 : if (gShuttingDownThread) {
310 0 : return NS_ERROR_ABORT;
311 : }
312 :
313 0 : if (aCount < 1) {
314 0 : return NS_OK;
315 : }
316 :
317 0 : PrefixArray noiseEntries;
318 0 : nsresult rv = mClassifier->ReadNoiseEntries(aPrefix, tableName,
319 0 : aCount, &noiseEntries);
320 0 : NS_ENSURE_SUCCESS(rv, rv);
321 :
322 0 : for (uint32_t i = 0; i < noiseEntries.Length(); i++) {
323 0 : LookupResult *result = results.AppendElement();
324 0 : if (!result)
325 0 : return NS_ERROR_OUT_OF_MEMORY;
326 :
327 0 : result->hash.fixedLengthPrefix = noiseEntries[i];
328 0 : result->mNoise = true;
329 0 : result->mPartialHashLength = PREFIX_SIZE; // Noise is always 4-byte,
330 0 : result->mTableName.Assign(tableName);
331 : }
332 :
333 0 : return NS_OK;
334 : }
335 :
336 : // Lookup a key in the db.
337 : NS_IMETHODIMP
338 0 : nsUrlClassifierDBServiceWorker::Lookup(nsIPrincipal* aPrincipal,
339 : const nsACString& aTables,
340 : nsIUrlClassifierCallback* c)
341 : {
342 0 : if (gShuttingDownThread) {
343 0 : return NS_ERROR_ABORT;
344 : }
345 :
346 0 : return HandlePendingLookups();
347 : }
348 :
349 : NS_IMETHODIMP
350 2 : nsUrlClassifierDBServiceWorker::GetTables(nsIUrlClassifierCallback* c)
351 : {
352 2 : if (gShuttingDownThread) {
353 0 : return NS_ERROR_NOT_INITIALIZED;
354 : }
355 :
356 2 : nsresult rv = OpenDb();
357 2 : if (NS_FAILED(rv)) {
358 0 : NS_ERROR("Unable to open SafeBrowsing database");
359 0 : return NS_ERROR_FAILURE;
360 : }
361 :
362 2 : NS_ENSURE_SUCCESS(rv, rv);
363 :
364 4 : nsAutoCString response;
365 2 : mClassifier->TableRequest(response);
366 2 : LOG(("GetTables: %s", response.get()));
367 2 : c->HandleEvent(response);
368 :
369 2 : return rv;
370 : }
371 :
372 : void
373 0 : nsUrlClassifierDBServiceWorker::ResetStream()
374 : {
375 0 : LOG(("ResetStream"));
376 0 : mInStream = false;
377 0 : mProtocolParser = nullptr;
378 0 : }
379 :
380 : void
381 1 : nsUrlClassifierDBServiceWorker::ResetUpdate()
382 : {
383 1 : LOG(("ResetUpdate"));
384 1 : mUpdateWaitSec = 0;
385 1 : mUpdateStatus = NS_OK;
386 1 : mUpdateObserver = nullptr;
387 1 : }
388 :
389 : NS_IMETHODIMP
390 0 : nsUrlClassifierDBServiceWorker::SetHashCompleter(const nsACString &tableName,
391 : nsIUrlClassifierHashCompleter *completer)
392 : {
393 0 : return NS_ERROR_NOT_IMPLEMENTED;
394 : }
395 :
396 : NS_IMETHODIMP
397 2 : nsUrlClassifierDBServiceWorker::BeginUpdate(nsIUrlClassifierUpdateObserver *observer,
398 : const nsACString &tables)
399 : {
400 2 : LOG(("nsUrlClassifierDBServiceWorker::BeginUpdate [%s]", PromiseFlatCString(tables).get()));
401 :
402 2 : if (gShuttingDownThread) {
403 0 : return NS_ERROR_NOT_INITIALIZED;
404 : }
405 :
406 2 : NS_ENSURE_STATE(!mUpdateObserver);
407 :
408 2 : nsresult rv = OpenDb();
409 2 : if (NS_FAILED(rv)) {
410 0 : NS_ERROR("Unable to open SafeBrowsing database");
411 0 : return NS_ERROR_FAILURE;
412 : }
413 :
414 2 : mUpdateStatus = NS_OK;
415 2 : mUpdateObserver = observer;
416 2 : Classifier::SplitTables(tables, mUpdateTables);
417 :
418 2 : return NS_OK;
419 : }
420 :
421 : // Called from the stream updater.
422 : NS_IMETHODIMP
423 1 : nsUrlClassifierDBServiceWorker::BeginStream(const nsACString &table)
424 : {
425 1 : LOG(("nsUrlClassifierDBServiceWorker::BeginStream"));
426 1 : MOZ_ASSERT(!NS_IsMainThread(), "Streaming must be on the background thread");
427 :
428 1 : if (gShuttingDownThread) {
429 0 : return NS_ERROR_NOT_INITIALIZED;
430 : }
431 :
432 1 : NS_ENSURE_STATE(mUpdateObserver);
433 1 : NS_ENSURE_STATE(!mInStream);
434 :
435 1 : mInStream = true;
436 :
437 1 : NS_ASSERTION(!mProtocolParser, "Should not have a protocol parser.");
438 :
439 : // Check if we should use protobuf to parse the update.
440 1 : bool useProtobuf = false;
441 7 : for (size_t i = 0; i < mUpdateTables.Length(); i++) {
442 : bool isCurProtobuf =
443 6 : StringEndsWith(mUpdateTables[i], NS_LITERAL_CSTRING("-proto"));
444 :
445 6 : if (0 == i) {
446 : // Use the first table name to decice if all the subsequent tables
447 : // should be '-proto'.
448 1 : useProtobuf = isCurProtobuf;
449 1 : continue;
450 : }
451 :
452 5 : if (useProtobuf != isCurProtobuf) {
453 : NS_WARNING("Cannot mix 'proto' tables with other types "
454 0 : "within the same provider.");
455 0 : break;
456 : }
457 : }
458 :
459 0 : mProtocolParser = (useProtobuf ? static_cast<ProtocolParser*>(new ProtocolParserProtobuf())
460 2 : : static_cast<ProtocolParser*>(new ProtocolParserV2()));
461 1 : if (!mProtocolParser)
462 0 : return NS_ERROR_OUT_OF_MEMORY;
463 :
464 1 : mProtocolParser->Init(mCryptoHash);
465 :
466 1 : if (!table.IsEmpty()) {
467 0 : mProtocolParser->SetCurrentTable(table);
468 : }
469 :
470 1 : mProtocolParser->SetRequestedTables(mUpdateTables);
471 :
472 1 : return NS_OK;
473 : }
474 :
475 : /**
476 : * Updating the database:
477 : *
478 : * The Update() method takes a series of chunks separated with control data,
479 : * as described in
480 : * http://code.google.com/p/google-safe-browsing/wiki/Protocolv2Spec
481 : *
482 : * It will iterate through the control data until it reaches a chunk. By
483 : * the time it reaches a chunk, it should have received
484 : * a) the table to which this chunk applies
485 : * b) the type of chunk (add, delete, expire add, expire delete).
486 : * c) the chunk ID
487 : * d) the length of the chunk.
488 : *
489 : * For add and subtract chunks, it needs to read the chunk data (expires
490 : * don't have any data). Chunk data is a list of URI fragments whose
491 : * encoding depends on the type of table (which is indicated by the end
492 : * of the table name):
493 : * a) tables ending with -exp are a zlib-compressed list of URI fragments
494 : * separated by newlines.
495 : * b) tables ending with -sha128 have the form
496 : * [domain][N][frag0]...[fragN]
497 : * 16 1 16 16
498 : * If N is 0, the domain is reused as a fragment.
499 : * c) any other tables are assumed to be a plaintext list of URI fragments
500 : * separated by newlines.
501 : *
502 : * Update() can be fed partial data; It will accumulate data until there is
503 : * enough to act on. Finish() should be called when there will be no more
504 : * data.
505 : */
506 : NS_IMETHODIMP
507 1 : nsUrlClassifierDBServiceWorker::UpdateStream(const nsACString& chunk)
508 : {
509 1 : if (gShuttingDownThread) {
510 0 : return NS_ERROR_NOT_INITIALIZED;
511 : }
512 :
513 1 : NS_ENSURE_STATE(mInStream);
514 :
515 1 : HandlePendingLookups();
516 :
517 : // Feed the chunk to the parser.
518 1 : return mProtocolParser->AppendStream(chunk);
519 : }
520 :
521 : NS_IMETHODIMP
522 1 : nsUrlClassifierDBServiceWorker::FinishStream()
523 : {
524 1 : if (gShuttingDownThread) {
525 0 : LOG(("shutting down"));
526 0 : return NS_ERROR_NOT_INITIALIZED;
527 : }
528 :
529 1 : NS_ENSURE_STATE(mInStream);
530 1 : NS_ENSURE_STATE(mUpdateObserver);
531 :
532 1 : mInStream = false;
533 :
534 1 : mProtocolParser->End();
535 :
536 1 : if (NS_SUCCEEDED(mProtocolParser->Status())) {
537 1 : if (mProtocolParser->UpdateWaitSec()) {
538 1 : mUpdateWaitSec = mProtocolParser->UpdateWaitSec();
539 : }
540 : // XXX: Only allow forwards from the initial update?
541 : const nsTArray<ProtocolParser::ForwardedUpdate> &forwards =
542 1 : mProtocolParser->Forwards();
543 1 : for (uint32_t i = 0; i < forwards.Length(); i++) {
544 0 : const ProtocolParser::ForwardedUpdate &forward = forwards[i];
545 0 : mUpdateObserver->UpdateUrlRequested(forward.url, forward.table);
546 : }
547 : // Hold on to any TableUpdate objects that were created by the
548 : // parser.
549 1 : mTableUpdates.AppendElements(mProtocolParser->GetTableUpdates());
550 1 : mProtocolParser->ForgetTableUpdates();
551 :
552 : #ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
553 : // The assignment involves no string copy since the source string is sharable.
554 1 : mRawTableUpdates = mProtocolParser->GetRawTableUpdates();
555 : #endif
556 : } else {
557 0 : LOG(("nsUrlClassifierDBService::FinishStream Failed to parse the stream "
558 : "using mProtocolParser."));
559 0 : mUpdateStatus = mProtocolParser->Status();
560 : }
561 1 : mUpdateObserver->StreamFinished(mProtocolParser->Status(), 0);
562 :
563 1 : if (NS_SUCCEEDED(mUpdateStatus)) {
564 1 : if (mProtocolParser->ResetRequested()) {
565 0 : mClassifier->ResetTables(Classifier::Clear_All, mUpdateTables);
566 : }
567 : } else {
568 0 : mUpdateStatus = NS_ERROR_UC_UPDATE_PROTOCOL_PARSER_ERROR;
569 : }
570 :
571 1 : mProtocolParser = nullptr;
572 :
573 1 : return NS_OK;
574 : }
575 :
576 : NS_IMETHODIMP
577 2 : nsUrlClassifierDBServiceWorker::FinishUpdate()
578 : {
579 2 : LOG(("nsUrlClassifierDBServiceWorker::FinishUpdate"));
580 :
581 2 : MOZ_ASSERT(!NS_IsMainThread(), "nsUrlClassifierDBServiceWorker::FinishUpdate "
582 : "NUST NOT be on the main thread.");
583 :
584 2 : if (gShuttingDownThread) {
585 0 : return NS_ERROR_NOT_INITIALIZED;
586 : }
587 :
588 2 : MOZ_ASSERT(!mProtocolParser, "Should have been nulled out in FinishStream() "
589 : "or never created.");
590 :
591 2 : NS_ENSURE_STATE(mUpdateObserver);
592 :
593 2 : if (NS_FAILED(mUpdateStatus)) {
594 0 : LOG(("nsUrlClassifierDBServiceWorker::FinishUpdate() Not running "
595 : "ApplyUpdate() since the update has already failed."));
596 0 : return NotifyUpdateObserver(mUpdateStatus);
597 : }
598 :
599 2 : if (mTableUpdates.IsEmpty()) {
600 1 : LOG(("Nothing to update. Just notify update observer."));
601 1 : return NotifyUpdateObserver(NS_OK);
602 : }
603 :
604 2 : RefPtr<nsUrlClassifierDBServiceWorker> self = this;
605 2 : nsresult rv = mClassifier->AsyncApplyUpdates(&mTableUpdates,
606 12 : [=] (nsresult aRv) -> void {
607 : #ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
608 2 : if (NS_FAILED(aRv) &&
609 1 : NS_ERROR_OUT_OF_MEMORY != aRv &&
610 : NS_ERROR_UC_UPDATE_SHUTDOWNING != aRv) {
611 0 : self->mClassifier->DumpRawTableUpdates(mRawTableUpdates);
612 : }
613 : // Invalidate the raw table updates.
614 1 : self->mRawTableUpdates = EmptyCString();
615 : #endif
616 :
617 1 : self->NotifyUpdateObserver(aRv);
618 2 : });
619 :
620 1 : if (NS_FAILED(rv)) {
621 0 : LOG(("Failed to start async update. Notify immediately."));
622 0 : NotifyUpdateObserver(rv);
623 : }
624 :
625 1 : return rv;
626 : }
627 :
628 : nsresult
629 2 : nsUrlClassifierDBServiceWorker::NotifyUpdateObserver(nsresult aUpdateStatus)
630 : {
631 2 : MOZ_ASSERT(!NS_IsMainThread(), "nsUrlClassifierDBServiceWorker::NotifyUpdateObserver "
632 : "NUST NOT be on the main thread.");
633 :
634 2 : LOG(("nsUrlClassifierDBServiceWorker::NotifyUpdateObserver"));
635 :
636 : // We've either
637 : // 1) failed starting a download stream
638 : // 2) succeeded in starting a download stream but failed to obtain
639 : // table updates
640 : // 3) succeeded in obtaining table updates but failed to build new
641 : // tables.
642 : // 4) succeeded in building new tables but failed to take them.
643 : // 5) succeeded in taking new tables.
644 :
645 2 : mUpdateStatus = aUpdateStatus;
646 :
647 : nsCOMPtr<nsIUrlClassifierUtils> urlUtil =
648 4 : do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
649 :
650 4 : nsCString provider;
651 : // Assume that all the tables in update should have the same provider.
652 2 : urlUtil->GetTelemetryProvider(mUpdateTables.SafeElementAt(0, EmptyCString()), provider);
653 :
654 2 : nsresult updateStatus = mUpdateStatus;
655 2 : if (NS_FAILED(mUpdateStatus)) {
656 0 : updateStatus = NS_ERROR_GET_MODULE(mUpdateStatus) == NS_ERROR_MODULE_URL_CLASSIFIER ?
657 : mUpdateStatus : NS_ERROR_UC_UPDATE_UNKNOWN;
658 : }
659 :
660 : // Do not record telemetry for testing tables.
661 2 : if (!provider.Equals(TESTING_TABLE_PROVIDER_NAME)) {
662 1 : Telemetry::Accumulate(Telemetry::URLCLASSIFIER_UPDATE_ERROR, provider,
663 2 : NS_ERROR_GET_CODE(updateStatus));
664 : }
665 :
666 2 : if (!mUpdateObserver) {
667 : // In the normal shutdown process, CancelUpdate() would NOT be
668 : // called prior to NotifyUpdateObserver(). However, CancelUpdate()
669 : // is a public API which can be called in the test case at any point.
670 : // If the call sequence is FinishUpdate() then CancelUpdate(), the later
671 : // might be executed before NotifyUpdateObserver() which is triggered
672 : // by the update thread. In this case, we will get null mUpdateObserver.
673 : NS_WARNING("CancelUpdate() is called before we asynchronously call "
674 0 : "NotifyUpdateObserver() in FinishUpdate().");
675 :
676 : // The DB cleanup will be done in CancelUpdate() so we can just return.
677 0 : return NS_OK;
678 : }
679 :
680 : // Null out mUpdateObserver before notifying so that BeginUpdate()
681 : // becomes available prior to callback.
682 4 : nsCOMPtr<nsIUrlClassifierUpdateObserver> updateObserver = nullptr;
683 2 : updateObserver.swap(mUpdateObserver);
684 :
685 2 : if (NS_SUCCEEDED(mUpdateStatus)) {
686 2 : LOG(("Notifying success: %d", mUpdateWaitSec));
687 2 : updateObserver->UpdateSuccess(mUpdateWaitSec);
688 0 : } else if (NS_ERROR_NOT_IMPLEMENTED == mUpdateStatus) {
689 0 : LOG(("Treating NS_ERROR_NOT_IMPLEMENTED a successful update "
690 : "but still mark it spoiled."));
691 0 : updateObserver->UpdateSuccess(0);
692 0 : mClassifier->ResetTables(Classifier::Clear_Cache, mUpdateTables);
693 : } else {
694 0 : if (LOG_ENABLED()) {
695 0 : nsAutoCString errorName;
696 0 : mozilla::GetErrorName(mUpdateStatus, errorName);
697 0 : LOG(("Notifying error: %s (%" PRIu32 ")", errorName.get(),
698 : static_cast<uint32_t>(mUpdateStatus)));
699 : }
700 :
701 0 : updateObserver->UpdateError(mUpdateStatus);
702 : /*
703 : * mark the tables as spoiled(clear cache in LookupCache), we don't want to
704 : * block hosts longer than normal because our update failed
705 : */
706 0 : mClassifier->ResetTables(Classifier::Clear_Cache, mUpdateTables);
707 : }
708 :
709 2 : return NS_OK;
710 : }
711 :
712 : NS_IMETHODIMP
713 0 : nsUrlClassifierDBServiceWorker::ResetDatabase()
714 : {
715 0 : nsresult rv = OpenDb();
716 :
717 0 : if (NS_SUCCEEDED(rv)) {
718 0 : mClassifier->Reset();
719 : }
720 :
721 0 : rv = CloseDb();
722 0 : NS_ENSURE_SUCCESS(rv, rv);
723 :
724 0 : return NS_OK;
725 : }
726 :
727 : NS_IMETHODIMP
728 0 : nsUrlClassifierDBServiceWorker::ReloadDatabase()
729 : {
730 0 : nsTArray<nsCString> tables;
731 0 : nsresult rv = mClassifier->ActiveTables(tables);
732 0 : NS_ENSURE_SUCCESS(rv, rv);
733 :
734 : // This will null out mClassifier
735 0 : rv = CloseDb();
736 0 : NS_ENSURE_SUCCESS(rv, rv);
737 :
738 : // Create new mClassifier and load prefixset and completions from disk.
739 0 : rv = OpenDb();
740 0 : NS_ENSURE_SUCCESS(rv, rv);
741 :
742 0 : return NS_OK;
743 : }
744 :
745 : NS_IMETHODIMP
746 0 : nsUrlClassifierDBServiceWorker::ClearCache()
747 : {
748 0 : nsTArray<nsCString> tables;
749 0 : nsresult rv = mClassifier->ActiveTables(tables);
750 0 : NS_ENSURE_SUCCESS(rv, rv);
751 :
752 0 : mClassifier->ResetTables(Classifier::Clear_Cache, tables);
753 :
754 0 : return NS_OK;
755 : }
756 :
757 : NS_IMETHODIMP
758 0 : nsUrlClassifierDBServiceWorker::CancelUpdate()
759 : {
760 0 : LOG(("nsUrlClassifierDBServiceWorker::CancelUpdate"));
761 :
762 0 : if (mUpdateObserver) {
763 0 : LOG(("UpdateObserver exists, cancelling"));
764 :
765 0 : mUpdateStatus = NS_BINDING_ABORTED;
766 :
767 0 : mUpdateObserver->UpdateError(mUpdateStatus);
768 :
769 : /*
770 : * mark the tables as spoiled(clear cache in LookupCache), we don't want to
771 : * block hosts longer than normal because our update failed
772 : */
773 0 : mClassifier->ResetTables(Classifier::Clear_Cache, mUpdateTables);
774 :
775 0 : ResetStream();
776 0 : ResetUpdate();
777 : } else {
778 0 : LOG(("No UpdateObserver, nothing to cancel"));
779 : }
780 :
781 0 : return NS_OK;
782 : }
783 :
784 : void
785 0 : nsUrlClassifierDBServiceWorker::FlushAndDisableAsyncUpdate()
786 : {
787 0 : LOG(("nsUrlClassifierDBServiceWorker::FlushAndDisableAsyncUpdate()"));
788 :
789 0 : if (mClassifier) {
790 0 : mClassifier->FlushAndDisableAsyncUpdate();
791 : }
792 0 : }
793 :
794 : // Allows the main thread to delete the connection which may be in
795 : // a background thread.
796 : // XXX This could be turned into a single shutdown event so the logic
797 : // is simpler in nsUrlClassifierDBService::Shutdown.
798 : nsresult
799 0 : nsUrlClassifierDBServiceWorker::CloseDb()
800 : {
801 0 : if (mClassifier) {
802 0 : mClassifier->Close();
803 0 : mClassifier = nullptr;
804 : }
805 :
806 0 : mCryptoHash = nullptr;
807 0 : LOG(("urlclassifier db closed\n"));
808 :
809 0 : return NS_OK;
810 : }
811 :
812 : nsresult
813 0 : nsUrlClassifierDBServiceWorker::CacheCompletions(CacheResultArray *results)
814 : {
815 0 : if (gShuttingDownThread) {
816 0 : return NS_ERROR_ABORT;
817 : }
818 :
819 0 : LOG(("nsUrlClassifierDBServiceWorker::CacheCompletions [%p]", this));
820 0 : if (!mClassifier) {
821 0 : return NS_OK;
822 : }
823 :
824 : // Ownership is transferred in to us
825 0 : nsAutoPtr<CacheResultArray> resultsPtr(results);
826 :
827 0 : if (resultsPtr->Length() == 0) {
828 0 : return NS_OK;
829 : }
830 :
831 0 : if (IsSameAsLastResults(*resultsPtr)) {
832 0 : LOG(("Skipping completions that have just been cached already."));
833 0 : return NS_OK;
834 : }
835 :
836 : // Only cache results for tables that we have, don't take
837 : // in tables we might accidentally have hit during a completion.
838 : // This happens due to goog vs googpub lists existing.
839 0 : nsTArray<nsCString> tables;
840 0 : nsresult rv = mClassifier->ActiveTables(tables);
841 0 : NS_ENSURE_SUCCESS(rv, rv);
842 :
843 0 : nsTArray<TableUpdate*> updates;
844 :
845 0 : for (uint32_t i = 0; i < resultsPtr->Length(); i++) {
846 0 : bool activeTable = false;
847 0 : CacheResult* result = resultsPtr->ElementAt(i).get();
848 :
849 0 : for (uint32_t table = 0; table < tables.Length(); table++) {
850 0 : if (tables[table].Equals(result->table)) {
851 0 : activeTable = true;
852 0 : break;
853 : }
854 : }
855 0 : if (activeTable) {
856 0 : nsAutoPtr<ProtocolParser> pParse;
857 0 : pParse = result->Ver() == CacheResult::V2 ?
858 0 : static_cast<ProtocolParser*>(new ProtocolParserV2()) :
859 0 : static_cast<ProtocolParser*>(new ProtocolParserProtobuf());
860 :
861 0 : TableUpdate* tu = pParse->GetTableUpdate(result->table);
862 :
863 0 : rv = CacheResultToTableUpdate(result, tu);
864 0 : if (NS_FAILED(rv)) {
865 : // We can bail without leaking here because ForgetTableUpdates
866 : // hasn't been called yet.
867 0 : return rv;
868 : }
869 0 : updates.AppendElement(tu);
870 0 : pParse->ForgetTableUpdates();
871 : } else {
872 0 : LOG(("Completion received, but table is not active, so not caching."));
873 : }
874 : }
875 :
876 0 : mClassifier->ApplyFullHashes(&updates);
877 0 : mLastResults = Move(resultsPtr);
878 0 : return NS_OK;
879 : }
880 :
881 : nsresult
882 0 : nsUrlClassifierDBServiceWorker::CacheResultToTableUpdate(CacheResult* aCacheResult,
883 : TableUpdate* aUpdate)
884 : {
885 0 : auto tuV2 = TableUpdate::Cast<TableUpdateV2>(aUpdate);
886 0 : if (tuV2) {
887 0 : auto result = CacheResult::Cast<CacheResultV2>(aCacheResult);
888 0 : MOZ_ASSERT(result);
889 :
890 0 : if (result->miss) {
891 0 : return tuV2->NewMissPrefix(result->prefix);
892 : } else {
893 0 : LOG(("CacheCompletion hash %X, Addchunk %d", result->completion.ToUint32(),
894 : result->addChunk));
895 :
896 0 : nsresult rv = tuV2->NewAddComplete(result->addChunk, result->completion);
897 0 : if (NS_FAILED(rv)) {
898 0 : return rv;
899 : }
900 0 : return tuV2->NewAddChunk(result->addChunk);
901 : }
902 : }
903 :
904 0 : auto tuV4 = TableUpdate::Cast<TableUpdateV4>(aUpdate);
905 0 : if (tuV4) {
906 0 : auto result = CacheResult::Cast<CacheResultV4>(aCacheResult);
907 0 : MOZ_ASSERT(result);
908 :
909 0 : if (LOG_ENABLED()) {
910 0 : const FullHashExpiryCache& fullHashes = result->response.fullHashes;
911 0 : for (auto iter = fullHashes.ConstIter(); !iter.Done(); iter.Next()) {
912 : Completion completion;
913 0 : completion.Assign(iter.Key());
914 0 : LOG(("CacheCompletion(v4) hash %X, CacheExpireTime %" PRId64,
915 : completion.ToUint32(), iter.Data()));
916 : }
917 : }
918 :
919 0 : tuV4->NewFullHashResponse(result->prefix, result->response);
920 0 : return NS_OK;
921 : }
922 :
923 : // tableUpdate object should be either V2 or V4.
924 0 : return NS_ERROR_FAILURE;
925 : }
926 :
927 : nsresult
928 5 : nsUrlClassifierDBServiceWorker::OpenDb()
929 : {
930 5 : if (gShuttingDownThread) {
931 0 : return NS_ERROR_ABORT;
932 : }
933 :
934 5 : MOZ_ASSERT(!NS_IsMainThread(), "Must initialize DB on background thread");
935 : // Connection already open, don't do anything.
936 5 : if (mClassifier) {
937 4 : return NS_OK;
938 : }
939 :
940 : nsresult rv;
941 1 : mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
942 1 : NS_ENSURE_SUCCESS(rv, rv);
943 :
944 2 : nsAutoPtr<Classifier> classifier(new Classifier());
945 1 : if (!classifier) {
946 0 : return NS_ERROR_OUT_OF_MEMORY;
947 : }
948 :
949 1 : rv = classifier->Open(*mCacheDir);
950 1 : NS_ENSURE_SUCCESS(rv, rv);
951 :
952 1 : mClassifier = classifier;
953 :
954 1 : return NS_OK;
955 : }
956 :
957 : NS_IMETHODIMP
958 0 : nsUrlClassifierDBServiceWorker::ClearLastResults()
959 : {
960 0 : MOZ_ASSERT(!NS_IsMainThread(), "Must be on the background thread");
961 0 : if (mLastResults) {
962 0 : mLastResults->Clear();
963 : }
964 0 : return NS_OK;
965 : }
966 :
967 : nsresult
968 0 : nsUrlClassifierDBServiceWorker::GetCacheInfo(const nsACString& aTable,
969 : nsIUrlClassifierCacheInfo** aCache)
970 : {
971 0 : MOZ_ASSERT(!NS_IsMainThread(), "Must be on the background thread");
972 0 : if (!mClassifier) {
973 0 : return NS_ERROR_NOT_AVAILABLE;
974 : }
975 :
976 0 : mClassifier->GetCacheInfo(aTable, aCache);
977 0 : return NS_OK;
978 : }
979 :
980 : bool
981 0 : nsUrlClassifierDBServiceWorker::IsSameAsLastResults(CacheResultArray& aResult)
982 : {
983 0 : if (!mLastResults || mLastResults->Length() != aResult.Length()) {
984 0 : return false;
985 : }
986 :
987 0 : bool equal = true;
988 0 : for (uint32_t i = 0; i < mLastResults->Length() && equal; i++) {
989 0 : CacheResult* lhs = mLastResults->ElementAt(i).get();
990 0 : CacheResult* rhs = aResult[i].get();
991 :
992 0 : if (lhs->Ver() != rhs->Ver()) {
993 0 : return false;
994 : }
995 :
996 0 : if (lhs->Ver() == CacheResult::V2) {
997 0 : equal = *(CacheResult::Cast<CacheResultV2>(lhs)) ==
998 0 : *(CacheResult::Cast<CacheResultV2>(rhs));
999 0 : } else if (lhs->Ver() == CacheResult::V4) {
1000 0 : equal = *(CacheResult::Cast<CacheResultV4>(lhs)) ==
1001 0 : *(CacheResult::Cast<CacheResultV4>(rhs));
1002 : }
1003 : }
1004 :
1005 0 : return equal;
1006 : }
1007 :
1008 : // -------------------------------------------------------------------------
1009 : // nsUrlClassifierLookupCallback
1010 : //
1011 : // This class takes the results of a lookup found on the worker thread
1012 : // and handles any necessary partial hash expansions before calling
1013 : // the client callback.
1014 :
1015 : class nsUrlClassifierLookupCallback final : public nsIUrlClassifierLookupCallback
1016 : , public nsIUrlClassifierHashCompleterCallback
1017 : {
1018 : public:
1019 : NS_DECL_THREADSAFE_ISUPPORTS
1020 : NS_DECL_NSIURLCLASSIFIERLOOKUPCALLBACK
1021 : NS_DECL_NSIURLCLASSIFIERHASHCOMPLETERCALLBACK
1022 :
1023 0 : nsUrlClassifierLookupCallback(nsUrlClassifierDBService *dbservice,
1024 : nsIUrlClassifierCallback *c)
1025 0 : : mDBService(dbservice)
1026 : , mResults(nullptr)
1027 : , mPendingCompletions(0)
1028 0 : , mCallback(c)
1029 0 : {}
1030 :
1031 : private:
1032 : ~nsUrlClassifierLookupCallback();
1033 :
1034 : nsresult HandleResults();
1035 : nsresult ProcessComplete(CacheResult* aCacheResult);
1036 : nsresult CacheMisses();
1037 :
1038 : RefPtr<nsUrlClassifierDBService> mDBService;
1039 : nsAutoPtr<LookupResultArray> mResults;
1040 :
1041 : // Completed results to send back to the worker for caching.
1042 : nsAutoPtr<CacheResultArray> mCacheResults;
1043 :
1044 : uint32_t mPendingCompletions;
1045 : nsCOMPtr<nsIUrlClassifierCallback> mCallback;
1046 : };
1047 :
1048 0 : NS_IMPL_ISUPPORTS(nsUrlClassifierLookupCallback,
1049 : nsIUrlClassifierLookupCallback,
1050 : nsIUrlClassifierHashCompleterCallback)
1051 :
1052 0 : nsUrlClassifierLookupCallback::~nsUrlClassifierLookupCallback()
1053 : {
1054 0 : if (mCallback) {
1055 : NS_ReleaseOnMainThread(
1056 0 : "nsUrlClassifierLookupCallback::mCallback", mCallback.forget());
1057 : }
1058 0 : }
1059 :
1060 : NS_IMETHODIMP
1061 0 : nsUrlClassifierLookupCallback::LookupComplete(nsTArray<LookupResult>* results)
1062 : {
1063 0 : NS_ASSERTION(mResults == nullptr,
1064 : "Should only get one set of results per nsUrlClassifierLookupCallback!");
1065 :
1066 0 : if (!results) {
1067 0 : HandleResults();
1068 0 : return NS_OK;
1069 : }
1070 :
1071 0 : mResults = results;
1072 :
1073 : // Check the results entries that need to be completed.
1074 0 : for (uint32_t i = 0; i < results->Length(); i++) {
1075 0 : LookupResult& result = results->ElementAt(i);
1076 :
1077 : // We will complete partial matches and matches that are stale.
1078 0 : if (!result.Confirmed()) {
1079 0 : nsCOMPtr<nsIUrlClassifierHashCompleter> completer;
1080 0 : nsCString gethashUrl;
1081 : nsresult rv;
1082 0 : nsCOMPtr<nsIUrlListManager> listManager = do_GetService(
1083 0 : "@mozilla.org/url-classifier/listmanager;1", &rv);
1084 0 : NS_ENSURE_SUCCESS(rv, rv);
1085 0 : rv = listManager->GetGethashUrl(result.mTableName, gethashUrl);
1086 0 : NS_ENSURE_SUCCESS(rv, rv);
1087 0 : LOG(("The match from %s needs to be completed at %s",
1088 : result.mTableName.get(), gethashUrl.get()));
1089 : // gethashUrls may be empty in 2 cases: test tables, and on startup where
1090 : // we may have found a prefix in an existing table before the listmanager
1091 : // has registered the table. In the second case we should not call
1092 : // complete.
1093 0 : if ((!gethashUrl.IsEmpty() ||
1094 0 : StringBeginsWith(result.mTableName, NS_LITERAL_CSTRING("test"))) &&
1095 0 : mDBService->GetCompleter(result.mTableName,
1096 0 : getter_AddRefs(completer))) {
1097 :
1098 : // Bug 1323953 - Send the first 4 bytes for completion no matter how
1099 : // long we matched the prefix.
1100 0 : nsresult rv = completer->Complete(result.PartialHash(),
1101 : gethashUrl,
1102 : result.mTableName,
1103 0 : this);
1104 0 : if (NS_SUCCEEDED(rv)) {
1105 0 : mPendingCompletions++;
1106 : }
1107 : } else {
1108 : // For tables with no hash completer, a complete hash match is
1109 : // good enough, we'll consider it is valid.
1110 0 : if (result.Complete()) {
1111 0 : result.mConfirmed = true;
1112 0 : LOG(("Skipping completion in a table without a valid completer (%s).",
1113 : result.mTableName.get()));
1114 : } else {
1115 0 : NS_WARNING("Partial match in a table without a valid completer, ignoring partial match.");
1116 : }
1117 : }
1118 : }
1119 : }
1120 :
1121 0 : LOG(("nsUrlClassifierLookupCallback::LookupComplete [%p] "
1122 : "%u pending completions", this, mPendingCompletions));
1123 0 : if (mPendingCompletions == 0) {
1124 : // All results were complete, we're ready!
1125 0 : HandleResults();
1126 : }
1127 :
1128 0 : return NS_OK;
1129 : }
1130 :
1131 : NS_IMETHODIMP
1132 0 : nsUrlClassifierLookupCallback::CompletionFinished(nsresult status)
1133 : {
1134 0 : if (LOG_ENABLED()) {
1135 0 : nsAutoCString errorName;
1136 0 : mozilla::GetErrorName(status, errorName);
1137 0 : LOG(("nsUrlClassifierLookupCallback::CompletionFinished [%p, %s]",
1138 : this, errorName.get()));
1139 : }
1140 :
1141 0 : mPendingCompletions--;
1142 0 : if (mPendingCompletions == 0) {
1143 0 : HandleResults();
1144 : }
1145 :
1146 0 : return NS_OK;
1147 : }
1148 :
1149 : NS_IMETHODIMP
1150 0 : nsUrlClassifierLookupCallback::CompletionV2(const nsACString& aCompleteHash,
1151 : const nsACString& aTableName,
1152 : uint32_t aChunkId)
1153 : {
1154 0 : LOG(("nsUrlClassifierLookupCallback::Completion [%p, %s, %d]",
1155 : this, PromiseFlatCString(aTableName).get(), aChunkId));
1156 :
1157 0 : MOZ_ASSERT(!StringEndsWith(aTableName, NS_LITERAL_CSTRING("-proto")));
1158 :
1159 0 : nsAutoPtr<CacheResultV2> result(new CacheResultV2);
1160 :
1161 0 : result->table = aTableName;
1162 0 : result->prefix.Assign(aCompleteHash);
1163 0 : result->completion.Assign(aCompleteHash);
1164 0 : result->addChunk = aChunkId;
1165 :
1166 0 : return ProcessComplete(result.forget());
1167 : }
1168 :
1169 : NS_IMETHODIMP
1170 0 : nsUrlClassifierLookupCallback::CompletionV4(const nsACString& aPartialHash,
1171 : const nsACString& aTableName,
1172 : uint32_t aNegativeCacheDuration,
1173 : nsIArray* aFullHashes)
1174 : {
1175 0 : LOG(("nsUrlClassifierLookupCallback::CompletionV4 [%p, %s, %d]",
1176 : this, PromiseFlatCString(aTableName).get(), aNegativeCacheDuration));
1177 :
1178 0 : MOZ_ASSERT(StringEndsWith(aTableName, NS_LITERAL_CSTRING("-proto")));
1179 :
1180 0 : if(!aFullHashes) {
1181 0 : return NS_ERROR_INVALID_ARG;
1182 : }
1183 :
1184 0 : if (aNegativeCacheDuration > MAXIMUM_NEGATIVE_CACHE_DURATION_SEC) {
1185 0 : LOG(("Negative cache duration too large, clamping it down to"
1186 : "a reasonable value."));
1187 0 : aNegativeCacheDuration = MAXIMUM_NEGATIVE_CACHE_DURATION_SEC;
1188 : }
1189 :
1190 0 : nsAutoPtr<CacheResultV4> result(new CacheResultV4);
1191 :
1192 0 : int64_t nowSec = PR_Now() / PR_USEC_PER_SEC;
1193 :
1194 0 : result->table = aTableName;
1195 0 : result->prefix.Assign(aPartialHash);
1196 0 : result->response.negativeCacheExpirySec = nowSec + aNegativeCacheDuration;
1197 :
1198 : // Fill in positive cache entries.
1199 0 : uint32_t fullHashCount = 0;
1200 0 : nsresult rv = aFullHashes->GetLength(&fullHashCount);
1201 0 : if (NS_FAILED(rv)) {
1202 0 : return rv;
1203 : }
1204 :
1205 0 : for (uint32_t i = 0; i < fullHashCount; i++) {
1206 0 : nsCOMPtr<nsIFullHashMatch> match = do_QueryElementAt(aFullHashes, i);
1207 :
1208 0 : nsCString fullHash;
1209 0 : match->GetFullHash(fullHash);
1210 :
1211 : uint32_t duration;
1212 0 : match->GetCacheDuration(&duration);
1213 :
1214 0 : result->response.fullHashes.Put(fullHash, nowSec + duration);
1215 : }
1216 :
1217 0 : return ProcessComplete(result.forget());
1218 : }
1219 :
1220 : nsresult
1221 0 : nsUrlClassifierLookupCallback::ProcessComplete(CacheResult* aCacheResult)
1222 : {
1223 : // Send this completion to the store for caching.
1224 0 : if (!mCacheResults) {
1225 0 : mCacheResults = new CacheResultArray();
1226 0 : if (!mCacheResults) {
1227 0 : return NS_ERROR_OUT_OF_MEMORY;
1228 : }
1229 : }
1230 :
1231 : // OK if this fails, we just won't cache the item.
1232 0 : mCacheResults->AppendElement(aCacheResult);
1233 :
1234 : // Check if this matched any of our results.
1235 0 : for (uint32_t i = 0; i < mResults->Length(); i++) {
1236 0 : LookupResult& result = mResults->ElementAt(i);
1237 :
1238 : // Now, see if it verifies a lookup
1239 0 : if (!result.mNoise
1240 0 : && result.mTableName.Equals(aCacheResult->table)
1241 0 : && aCacheResult->findCompletion(result.CompleteHash())) {
1242 0 : result.mProtocolConfirmed = true;
1243 : }
1244 : }
1245 :
1246 0 : return NS_OK;
1247 : }
1248 :
1249 : nsresult
1250 0 : nsUrlClassifierLookupCallback::HandleResults()
1251 : {
1252 0 : if (!mResults) {
1253 : // No results, this URI is clean.
1254 0 : LOG(("nsUrlClassifierLookupCallback::HandleResults [%p, no results]", this));
1255 0 : return mCallback->HandleEvent(NS_LITERAL_CSTRING(""));
1256 : }
1257 0 : MOZ_ASSERT(mPendingCompletions == 0, "HandleResults() should never be "
1258 : "called while there are pending completions");
1259 :
1260 0 : LOG(("nsUrlClassifierLookupCallback::HandleResults [%p, %" PRIuSIZE " results]",
1261 : this, mResults->Length()));
1262 :
1263 : nsCOMPtr<nsIUrlClassifierClassifyCallback> classifyCallback =
1264 0 : do_QueryInterface(mCallback);
1265 :
1266 0 : nsTArray<nsCString> tables;
1267 : // Build a stringified list of result tables.
1268 0 : for (uint32_t i = 0; i < mResults->Length(); i++) {
1269 0 : LookupResult& result = mResults->ElementAt(i);
1270 :
1271 : // Leave out results that weren't confirmed, as their existence on
1272 : // the list can't be verified. Also leave out randomly-generated
1273 : // noise.
1274 0 : if (result.mNoise) {
1275 0 : LOG(("Skipping result %s from table %s (noise)",
1276 : result.PartialHashHex().get(), result.mTableName.get()));
1277 0 : continue;
1278 : }
1279 :
1280 0 : if (!result.Confirmed()) {
1281 0 : LOG(("Skipping result %s from table %s (not confirmed)",
1282 : result.PartialHashHex().get(), result.mTableName.get()));
1283 0 : continue;
1284 : }
1285 :
1286 0 : LOG(("Confirmed result %s from table %s",
1287 : result.PartialHashHex().get(), result.mTableName.get()));
1288 :
1289 0 : if (tables.IndexOf(result.mTableName) == nsTArray<nsCString>::NoIndex) {
1290 0 : tables.AppendElement(result.mTableName);
1291 : }
1292 :
1293 0 : if (classifyCallback) {
1294 0 : nsCString prefixString;
1295 0 : result.hash.fixedLengthPrefix.ToString(prefixString);
1296 0 : classifyCallback->HandleResult(result.mTableName, prefixString);
1297 : }
1298 : }
1299 :
1300 : // Some parts of this gethash request generated no hits at all.
1301 : // Save the prefixes we checked to prevent repeated requests.
1302 0 : CacheMisses();
1303 :
1304 0 : if (mCacheResults) {
1305 : // This hands ownership of the cache results array back to the worker
1306 : // thread.
1307 0 : mDBService->CacheCompletions(mCacheResults.forget());
1308 : }
1309 :
1310 0 : nsAutoCString tableStr;
1311 0 : for (uint32_t i = 0; i < tables.Length(); i++) {
1312 0 : if (i != 0)
1313 0 : tableStr.Append(',');
1314 0 : tableStr.Append(tables[i]);
1315 : }
1316 :
1317 0 : return mCallback->HandleEvent(tableStr);
1318 : }
1319 :
1320 : nsresult
1321 0 : nsUrlClassifierLookupCallback::CacheMisses()
1322 : {
1323 0 : for (uint32_t i = 0; i < mResults->Length(); i++) {
1324 0 : const LookupResult &result = mResults->ElementAt(i);
1325 : // Skip V4 because cache information is already included in the
1326 : // fullhash response so we don't need to manually add it here.
1327 0 : if (!result.mProtocolV2 || result.Confirmed() || result.mNoise) {
1328 0 : continue;
1329 : }
1330 :
1331 0 : if (!mCacheResults) {
1332 0 : mCacheResults = new CacheResultArray();
1333 0 : if (!mCacheResults) {
1334 0 : return NS_ERROR_OUT_OF_MEMORY;
1335 : }
1336 : }
1337 :
1338 0 : auto cacheResult = new CacheResultV2;
1339 :
1340 0 : cacheResult->table = result.mTableName;
1341 0 : cacheResult->prefix = result.hash.fixedLengthPrefix;
1342 0 : cacheResult->miss = true;
1343 0 : mCacheResults->AppendElement(cacheResult);
1344 : }
1345 0 : return NS_OK;
1346 : }
1347 :
1348 0 : struct Provider {
1349 : nsCString name;
1350 : uint8_t priority;
1351 : };
1352 :
1353 : // Order matters
1354 : // Provider which is not included in this table has the lowest priority 0
1355 3 : static const Provider kBuiltInProviders[] = {
1356 6 : { NS_LITERAL_CSTRING("mozilla"), 1 },
1357 6 : { NS_LITERAL_CSTRING("google4"), 2 },
1358 6 : { NS_LITERAL_CSTRING("google"), 3 },
1359 21 : };
1360 :
1361 : // -------------------------------------------------------------------------
1362 : // Helper class for nsIURIClassifier implementation, handle classify result and
1363 : // send back to nsIURIClassifier
1364 :
1365 : class nsUrlClassifierClassifyCallback final : public nsIUrlClassifierCallback,
1366 : public nsIUrlClassifierClassifyCallback
1367 : {
1368 : public:
1369 : NS_DECL_THREADSAFE_ISUPPORTS
1370 : NS_DECL_NSIURLCLASSIFIERCALLBACK
1371 : NS_DECL_NSIURLCLASSIFIERCLASSIFYCALLBACK
1372 :
1373 0 : explicit nsUrlClassifierClassifyCallback(nsIURIClassifierCallback *c)
1374 0 : : mCallback(c)
1375 0 : {}
1376 :
1377 : private:
1378 :
1379 0 : struct ClassifyMatchedInfo {
1380 : nsCString table;
1381 : nsCString prefix;
1382 : Provider provider;
1383 : nsresult errorCode;
1384 : };
1385 :
1386 0 : ~nsUrlClassifierClassifyCallback() {};
1387 :
1388 : nsCOMPtr<nsIURIClassifierCallback> mCallback;
1389 : nsTArray<ClassifyMatchedInfo> mMatchedArray;
1390 : };
1391 :
1392 0 : NS_IMPL_ISUPPORTS(nsUrlClassifierClassifyCallback,
1393 : nsIUrlClassifierCallback,
1394 : nsIUrlClassifierClassifyCallback)
1395 :
1396 : NS_IMETHODIMP
1397 0 : nsUrlClassifierClassifyCallback::HandleEvent(const nsACString& tables)
1398 : {
1399 0 : nsresult response = TablesToResponse(tables);
1400 0 : ClassifyMatchedInfo* matchedInfo = nullptr;
1401 :
1402 0 : if (NS_FAILED(response)) {
1403 : // Filter all matched info which has correct response
1404 : // In the case multiple tables found, use the higher priority provider
1405 0 : nsTArray<ClassifyMatchedInfo> matches;
1406 0 : for (uint32_t i = 0; i < mMatchedArray.Length(); i++) {
1407 0 : if (mMatchedArray[i].errorCode == response &&
1408 0 : (!matchedInfo ||
1409 0 : matchedInfo->provider.priority < mMatchedArray[i].provider.priority)) {
1410 0 : matchedInfo = &mMatchedArray[i];
1411 : }
1412 : }
1413 : }
1414 :
1415 0 : nsCString provider = matchedInfo ? matchedInfo->provider.name : EmptyCString();
1416 0 : nsCString prefix = matchedInfo ? matchedInfo->prefix : EmptyCString();
1417 0 : nsCString table = matchedInfo ? matchedInfo->table : EmptyCString();
1418 :
1419 0 : mCallback->OnClassifyComplete(response, table, provider, prefix);
1420 0 : return NS_OK;
1421 : }
1422 :
1423 : NS_IMETHODIMP
1424 0 : nsUrlClassifierClassifyCallback::HandleResult(const nsACString& aTable,
1425 : const nsACString& aPrefix)
1426 : {
1427 0 : LOG(("nsUrlClassifierClassifyCallback::HandleResult [%p, table %s prefix %s]",
1428 : this, PromiseFlatCString(aTable).get(), PromiseFlatCString(aPrefix).get()));
1429 :
1430 0 : if (NS_WARN_IF(aTable.IsEmpty()) || NS_WARN_IF(aPrefix.IsEmpty())) {
1431 0 : return NS_ERROR_INVALID_ARG;
1432 : }
1433 :
1434 0 : ClassifyMatchedInfo* matchedInfo = mMatchedArray.AppendElement();
1435 0 : matchedInfo->table = aTable;
1436 0 : matchedInfo->prefix = aPrefix;
1437 :
1438 : nsCOMPtr<nsIUrlClassifierUtils> urlUtil =
1439 0 : do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
1440 :
1441 0 : nsCString provider;
1442 0 : nsresult rv = urlUtil->GetProvider(aTable, provider);
1443 :
1444 0 : matchedInfo->provider.name = NS_SUCCEEDED(rv) ? provider : EmptyCString();
1445 0 : matchedInfo->provider.priority = 0;
1446 0 : for (uint8_t i = 0; i < ArrayLength(kBuiltInProviders); i++) {
1447 0 : if (kBuiltInProviders[i].name.Equals(matchedInfo->provider.name)) {
1448 0 : matchedInfo->provider.priority = kBuiltInProviders[i].priority;
1449 : }
1450 : }
1451 0 : matchedInfo->errorCode = TablesToResponse(aTable);
1452 :
1453 0 : return NS_OK;
1454 : }
1455 :
1456 : // -------------------------------------------------------------------------
1457 : // Proxy class implementation
1458 :
1459 70 : NS_IMPL_ADDREF(nsUrlClassifierDBService)
1460 38 : NS_IMPL_RELEASE(nsUrlClassifierDBService)
1461 56 : NS_INTERFACE_MAP_BEGIN(nsUrlClassifierDBService)
1462 : // Only nsIURIClassifier is supported in the content process!
1463 56 : NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIUrlClassifierDBService, XRE_IsParentProcess())
1464 51 : NS_INTERFACE_MAP_ENTRY(nsIURIClassifier)
1465 43 : NS_INTERFACE_MAP_ENTRY(nsIUrlClassifierInfo)
1466 43 : NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIObserver, XRE_IsParentProcess())
1467 32 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURIClassifier)
1468 16 : NS_INTERFACE_MAP_END
1469 :
1470 : /* static */ nsUrlClassifierDBService*
1471 1 : nsUrlClassifierDBService::GetInstance(nsresult *result)
1472 : {
1473 1 : *result = NS_OK;
1474 1 : if (!sUrlClassifierDBService) {
1475 1 : sUrlClassifierDBService = new nsUrlClassifierDBService();
1476 1 : if (!sUrlClassifierDBService) {
1477 0 : *result = NS_ERROR_OUT_OF_MEMORY;
1478 0 : return nullptr;
1479 : }
1480 :
1481 1 : NS_ADDREF(sUrlClassifierDBService); // addref the global
1482 :
1483 1 : *result = sUrlClassifierDBService->Init();
1484 1 : if (NS_FAILED(*result)) {
1485 0 : NS_RELEASE(sUrlClassifierDBService);
1486 0 : return nullptr;
1487 : }
1488 : } else {
1489 : // Already exists, just add a ref
1490 0 : NS_ADDREF(sUrlClassifierDBService); // addref the return result
1491 : }
1492 1 : return sUrlClassifierDBService;
1493 : }
1494 :
1495 :
1496 1 : nsUrlClassifierDBService::nsUrlClassifierDBService()
1497 : : mCheckMalware(CHECK_MALWARE_DEFAULT)
1498 : , mCheckPhishing(CHECK_PHISHING_DEFAULT)
1499 : , mCheckBlockedURIs(CHECK_BLOCKED_DEFAULT)
1500 13 : , mInUpdate(false)
1501 : {
1502 1 : }
1503 :
1504 0 : nsUrlClassifierDBService::~nsUrlClassifierDBService()
1505 : {
1506 0 : sUrlClassifierDBService = nullptr;
1507 0 : }
1508 :
1509 : void
1510 8 : AppendTables(const nsCString& aTables, nsCString &outTables)
1511 : {
1512 8 : if (!aTables.IsEmpty()) {
1513 8 : if (!outTables.IsEmpty()) {
1514 7 : outTables.Append(',');
1515 : }
1516 8 : outTables.Append(aTables);
1517 : }
1518 8 : }
1519 :
1520 : nsresult
1521 1 : nsUrlClassifierDBService::ReadTablesFromPrefs()
1522 : {
1523 1 : mCheckMalware = Preferences::GetBool(CHECK_MALWARE_PREF,
1524 : CHECK_MALWARE_DEFAULT);
1525 1 : mCheckPhishing = Preferences::GetBool(CHECK_PHISHING_PREF,
1526 : CHECK_PHISHING_DEFAULT);
1527 1 : mCheckBlockedURIs = Preferences::GetBool(CHECK_BLOCKED_PREF,
1528 : CHECK_BLOCKED_DEFAULT);
1529 :
1530 2 : nsCString allTables;
1531 2 : nsCString tables;
1532 :
1533 1 : mBaseTables.Truncate();
1534 1 : mTrackingProtectionTables.Truncate();
1535 :
1536 1 : Preferences::GetCString(PHISH_TABLE_PREF, &allTables);
1537 1 : if (mCheckPhishing) {
1538 0 : AppendTables(allTables, mBaseTables);
1539 : }
1540 :
1541 1 : Preferences::GetCString(MALWARE_TABLE_PREF, &tables);
1542 1 : AppendTables(tables, allTables);
1543 1 : if (mCheckMalware) {
1544 0 : AppendTables(tables, mBaseTables);
1545 : }
1546 :
1547 1 : Preferences::GetCString(BLOCKED_TABLE_PREF, &tables);
1548 1 : AppendTables(tables, allTables);
1549 1 : if (mCheckBlockedURIs) {
1550 0 : AppendTables(tables, mBaseTables);
1551 : }
1552 :
1553 1 : Preferences::GetCString(DOWNLOAD_BLOCK_TABLE_PREF, &tables);
1554 1 : AppendTables(tables, allTables);
1555 :
1556 1 : Preferences::GetCString(DOWNLOAD_ALLOW_TABLE_PREF, &tables);
1557 1 : AppendTables(tables, allTables);
1558 :
1559 1 : Preferences::GetCString(TRACKING_TABLE_PREF, &tables);
1560 1 : AppendTables(tables, allTables);
1561 1 : AppendTables(tables, mTrackingProtectionTables);
1562 :
1563 1 : Preferences::GetCString(TRACKING_WHITELIST_TABLE_PREF, &tables);
1564 1 : AppendTables(tables, allTables);
1565 1 : AppendTables(tables, mTrackingProtectionTables);
1566 :
1567 1 : Classifier::SplitTables(allTables, mGethashTables);
1568 :
1569 1 : Preferences::GetCString(DISALLOW_COMPLETION_TABLE_PREF, &tables);
1570 1 : Classifier::SplitTables(tables, mDisallowCompletionsTables);
1571 :
1572 2 : return NS_OK;
1573 : }
1574 :
1575 : nsresult
1576 1 : nsUrlClassifierDBService::Init()
1577 : {
1578 1 : MOZ_ASSERT(NS_IsMainThread(), "Must initialize DB service on main thread");
1579 2 : nsCOMPtr<nsIXULRuntime> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
1580 1 : if (appInfo) {
1581 1 : bool inSafeMode = false;
1582 1 : appInfo->GetInSafeMode(&inSafeMode);
1583 1 : if (inSafeMode) {
1584 0 : return NS_ERROR_NOT_AVAILABLE;
1585 : }
1586 : }
1587 :
1588 1 : switch (XRE_GetProcessType()) {
1589 : case GeckoProcessType_Default:
1590 : // The parent process is supported.
1591 1 : break;
1592 : case GeckoProcessType_Content:
1593 : // In a content process, we simply forward all requests to the parent process,
1594 : // so we can skip the initialization steps here.
1595 : // Note that since we never register an observer, Shutdown() will also never
1596 : // be called in the content process.
1597 0 : return NS_OK;
1598 : default:
1599 : // No other process type is supported!
1600 0 : return NS_ERROR_NOT_AVAILABLE;
1601 : }
1602 :
1603 1 : sGethashNoise = Preferences::GetUint(GETHASH_NOISE_PREF,
1604 : GETHASH_NOISE_DEFAULT);
1605 1 : ReadTablesFromPrefs();
1606 : nsresult rv;
1607 :
1608 : {
1609 : // Force PSM loading on main thread
1610 2 : nsCOMPtr<nsICryptoHash> dummy = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
1611 1 : NS_ENSURE_SUCCESS(rv, rv);
1612 : }
1613 :
1614 : {
1615 : // Force nsIUrlClassifierUtils loading on main thread.
1616 : nsCOMPtr<nsIUrlClassifierUtils> dummy =
1617 2 : do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID, &rv);
1618 1 : NS_ENSURE_SUCCESS(rv, rv);
1619 : }
1620 :
1621 : // Directory providers must also be accessed on the main thread.
1622 2 : nsCOMPtr<nsIFile> cacheDir;
1623 1 : rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
1624 2 : getter_AddRefs(cacheDir));
1625 1 : if (NS_FAILED(rv)) {
1626 0 : rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
1627 0 : getter_AddRefs(cacheDir));
1628 0 : if (NS_FAILED(rv)) {
1629 0 : return rv;
1630 : }
1631 : }
1632 :
1633 : // Start the background thread.
1634 1 : rv = NS_NewNamedThread("URL Classifier", &gDbBackgroundThread);
1635 1 : if (NS_FAILED(rv))
1636 0 : return rv;
1637 :
1638 1 : mWorker = new nsUrlClassifierDBServiceWorker();
1639 1 : if (!mWorker)
1640 0 : return NS_ERROR_OUT_OF_MEMORY;
1641 :
1642 1 : rv = mWorker->Init(sGethashNoise, cacheDir, this);
1643 1 : if (NS_FAILED(rv)) {
1644 0 : mWorker = nullptr;
1645 0 : return rv;
1646 : }
1647 :
1648 : // Proxy for calling the worker on the background thread
1649 2 : mWorkerProxy = new UrlClassifierDBServiceWorkerProxy(mWorker);
1650 1 : rv = mWorkerProxy->OpenDb();
1651 1 : if (NS_FAILED(rv)) {
1652 0 : return rv;
1653 : }
1654 :
1655 : // Add an observer for shutdown
1656 : nsCOMPtr<nsIObserverService> observerService =
1657 2 : mozilla::services::GetObserverService();
1658 1 : if (!observerService)
1659 0 : return NS_ERROR_FAILURE;
1660 :
1661 : // The application is about to quit
1662 1 : observerService->AddObserver(this, "quit-application", false);
1663 1 : observerService->AddObserver(this, "profile-before-change", false);
1664 :
1665 : // XXX: Do we *really* need to be able to change all of these at runtime?
1666 : // Note: These observers should only be added when everything else above has
1667 : // succeeded. Failing to do so can cause long shutdown times in certain
1668 : // situations. See Bug 1247798 and Bug 1244803.
1669 : Preferences::AddUintVarCache(&sGethashNoise, GETHASH_NOISE_PREF,
1670 1 : GETHASH_NOISE_DEFAULT);
1671 :
1672 12 : for (uint8_t i = 0; i < kObservedPrefs.Length(); i++) {
1673 11 : Preferences::AddStrongObserver(this, kObservedPrefs[i].get());
1674 : }
1675 :
1676 1 : return NS_OK;
1677 : }
1678 :
1679 : // nsChannelClassifier is the only consumer of this interface.
1680 : NS_IMETHODIMP
1681 4 : nsUrlClassifierDBService::Classify(nsIPrincipal* aPrincipal,
1682 : nsIEventTarget* aEventTarget,
1683 : bool aTrackingProtectionEnabled,
1684 : nsIURIClassifierCallback* c,
1685 : bool* result)
1686 : {
1687 4 : NS_ENSURE_ARG(aPrincipal);
1688 :
1689 4 : if (XRE_IsContentProcess()) {
1690 : using namespace mozilla::dom;
1691 :
1692 0 : ContentChild* content = ContentChild::GetSingleton();
1693 0 : MOZ_ASSERT(content);
1694 :
1695 : auto actor = static_cast<URLClassifierChild*>
1696 0 : (content->AllocPURLClassifierChild(IPC::Principal(aPrincipal),
1697 : aTrackingProtectionEnabled,
1698 0 : result));
1699 0 : MOZ_ASSERT(actor);
1700 :
1701 0 : if (aEventTarget) {
1702 0 : content->SetEventTargetForActor(actor, aEventTarget);
1703 : } else {
1704 : // In the case null event target we should use systemgroup event target
1705 0 : NS_WARNING(("Null event target, we should use SystemGroup to do labelling"));
1706 : nsCOMPtr<nsIEventTarget> systemGroupEventTarget
1707 0 : = mozilla::SystemGroup::EventTargetFor(mozilla::TaskCategory::Other);
1708 0 : content->SetEventTargetForActor(actor, systemGroupEventTarget);
1709 : }
1710 0 : if (!content->SendPURLClassifierConstructor(actor, IPC::Principal(aPrincipal),
1711 : aTrackingProtectionEnabled,
1712 : result)) {
1713 0 : *result = false;
1714 0 : return NS_ERROR_FAILURE;
1715 : }
1716 :
1717 0 : actor->SetCallback(c);
1718 0 : return NS_OK;
1719 : }
1720 :
1721 4 : NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
1722 :
1723 8 : if (!(mCheckMalware || mCheckPhishing || aTrackingProtectionEnabled ||
1724 4 : mCheckBlockedURIs)) {
1725 4 : *result = false;
1726 4 : return NS_OK;
1727 : }
1728 :
1729 : RefPtr<nsUrlClassifierClassifyCallback> callback =
1730 0 : new nsUrlClassifierClassifyCallback(c);
1731 :
1732 0 : if (!callback) return NS_ERROR_OUT_OF_MEMORY;
1733 :
1734 0 : nsCString tables = mBaseTables;
1735 0 : if (aTrackingProtectionEnabled) {
1736 0 : AppendTables(mTrackingProtectionTables, tables);
1737 : }
1738 :
1739 0 : nsresult rv = LookupURI(aPrincipal, tables, callback, false, result);
1740 0 : if (rv == NS_ERROR_MALFORMED_URI) {
1741 0 : *result = false;
1742 : // The URI had no hostname, don't try to classify it.
1743 0 : return NS_OK;
1744 : }
1745 0 : NS_ENSURE_SUCCESS(rv, rv);
1746 :
1747 0 : return NS_OK;
1748 : }
1749 :
1750 : NS_IMETHODIMP
1751 0 : nsUrlClassifierDBService::ClassifyLocal(nsIURI *aURI,
1752 : const nsACString& aTables,
1753 : nsACString& aTableResults)
1754 : {
1755 0 : nsTArray<nsCString> results;
1756 0 : ClassifyLocalWithTables(aURI, aTables, results);
1757 :
1758 : // Convert the result array to a comma separated string
1759 0 : aTableResults.AssignLiteral("");
1760 0 : bool first = true;
1761 0 : for (nsCString& result : results) {
1762 0 : if (first) {
1763 0 : first = false;
1764 : } else {
1765 0 : aTableResults.AppendLiteral(",");
1766 : }
1767 0 : aTableResults.Append(result);
1768 : }
1769 0 : return NS_OK;
1770 : }
1771 :
1772 : NS_IMETHODIMP
1773 1 : nsUrlClassifierDBService::AsyncClassifyLocalWithTables(nsIURI *aURI,
1774 : const nsACString& aTables,
1775 : nsIURIClassifierCallback* aCallback)
1776 : {
1777 1 : MOZ_ASSERT(NS_IsMainThread(), "AsyncClassifyLocalWithTables must be called "
1778 : "on main thread");
1779 :
1780 1 : if (XRE_IsContentProcess()) {
1781 : using namespace mozilla::dom;
1782 : using namespace mozilla::ipc;
1783 :
1784 0 : ContentChild* content = ContentChild::GetSingleton();
1785 0 : MOZ_ASSERT(content);
1786 :
1787 0 : auto actor = new URLClassifierLocalChild();
1788 :
1789 : // TODO: Bug 1353701 - Supports custom event target for labelling.
1790 : nsCOMPtr<nsIEventTarget> systemGroupEventTarget
1791 0 : = mozilla::SystemGroup::EventTargetFor(mozilla::TaskCategory::Other);
1792 0 : content->SetEventTargetForActor(actor, systemGroupEventTarget);
1793 :
1794 0 : URIParams uri;
1795 0 : SerializeURI(aURI, uri);
1796 0 : nsAutoCString tables(aTables);
1797 0 : if (!content->SendPURLClassifierLocalConstructor(actor, uri, tables)) {
1798 0 : return NS_ERROR_FAILURE;
1799 : }
1800 :
1801 0 : actor->SetCallback(aCallback);
1802 0 : return NS_OK;
1803 : }
1804 :
1805 1 : if (gShuttingDownThread) {
1806 0 : return NS_ERROR_ABORT;
1807 : }
1808 :
1809 : using namespace mozilla::Telemetry;
1810 1 : auto startTime = TimeStamp::Now(); // For telemetry.
1811 :
1812 2 : nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
1813 1 : NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
1814 :
1815 2 : nsAutoCString key;
1816 : // Canonicalize the url
1817 : nsCOMPtr<nsIUrlClassifierUtils> utilsService =
1818 2 : do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
1819 1 : nsresult rv = utilsService->GetKeyForURI(uri, key);
1820 1 : NS_ENSURE_SUCCESS(rv, rv);
1821 :
1822 2 : auto worker = mWorker;
1823 2 : nsCString tables(aTables);
1824 :
1825 : // Since aCallback will be passed around threads...
1826 : nsMainThreadPtrHandle<nsIURIClassifierCallback> callback(
1827 : new nsMainThreadPtrHolder<nsIURIClassifierCallback>(
1828 2 : "nsIURIClassifierCallback", aCallback));
1829 :
1830 3 : nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
1831 : "nsUrlClassifierDBService::AsyncClassifyLocalWithTables",
1832 4 : [worker, key, tables, callback, startTime]() -> void {
1833 :
1834 2 : nsCString matchedLists;
1835 2 : nsAutoPtr<LookupResultArray> results(new LookupResultArray());
1836 1 : if (results) {
1837 1 : nsresult rv = worker->DoLocalLookup(key, tables, results);
1838 1 : if (NS_SUCCEEDED(rv)) {
1839 1 : for (uint32_t i = 0; i < results->Length(); i++) {
1840 0 : if (i > 0) {
1841 0 : matchedLists.AppendLiteral(",");
1842 : }
1843 0 : matchedLists.Append(results->ElementAt(i).mTableName);
1844 : }
1845 : }
1846 : }
1847 :
1848 3 : nsCOMPtr<nsIRunnable> cbRunnable = NS_NewRunnableFunction(
1849 : "nsUrlClassifierDBService::AsyncClassifyLocalWithTables",
1850 4 : [callback, matchedLists, startTime]() -> void {
1851 : // Measure the time diff between calling and callback.
1852 1 : AccumulateDelta_impl<Millisecond>::compute(
1853 1 : Telemetry::URLCLASSIFIER_ASYNC_CLASSIFYLOCAL_TIME, startTime);
1854 :
1855 : // |callback| is captured as const value so ...
1856 1 : auto cb = const_cast<nsIURIClassifierCallback*>(callback.get());
1857 2 : cb->OnClassifyComplete(NS_OK, // Not used.
1858 : matchedLists,
1859 1 : EmptyCString(), // provider. (Not used)
1860 2 : EmptyCString()); // prefix. (Not used)
1861 4 : });
1862 :
1863 1 : NS_DispatchToMainThread(cbRunnable);
1864 4 : });
1865 :
1866 1 : return gDbBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL);
1867 : }
1868 :
1869 : NS_IMETHODIMP
1870 0 : nsUrlClassifierDBService::ClassifyLocalWithTables(nsIURI *aURI,
1871 : const nsACString& aTables,
1872 : nsTArray<nsCString>& aTableResults)
1873 : {
1874 0 : MOZ_ASSERT(NS_IsMainThread(), "ClassifyLocalWithTables must be on main thread");
1875 0 : if (gShuttingDownThread) {
1876 0 : return NS_ERROR_ABORT;
1877 : }
1878 :
1879 : nsresult rv;
1880 0 : if (XRE_IsContentProcess()) {
1881 : using namespace mozilla::dom;
1882 : using namespace mozilla::ipc;
1883 0 : URIParams uri;
1884 0 : SerializeURI(aURI, uri);
1885 0 : nsAutoCString tables(aTables);
1886 0 : bool result = ContentChild::GetSingleton()->SendClassifyLocal(uri, tables,
1887 : &rv,
1888 0 : &aTableResults);
1889 0 : if (result) {
1890 0 : return rv;
1891 : }
1892 0 : return NS_ERROR_FAILURE;
1893 : }
1894 :
1895 0 : AUTO_PROFILER_LABEL("nsUrlClassifierDBService::ClassifyLocalWithTables",
1896 : OTHER);
1897 0 : Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_CLASSIFYLOCAL_TIME> timer;
1898 :
1899 0 : nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
1900 0 : NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
1901 :
1902 0 : nsAutoCString key;
1903 : // Canonicalize the url
1904 : nsCOMPtr<nsIUrlClassifierUtils> utilsService =
1905 0 : do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
1906 0 : rv = utilsService->GetKeyForURI(uri, key);
1907 0 : NS_ENSURE_SUCCESS(rv, rv);
1908 :
1909 0 : nsAutoPtr<LookupResultArray> results(new LookupResultArray());
1910 0 : if (!results) {
1911 0 : return NS_ERROR_OUT_OF_MEMORY;
1912 : }
1913 :
1914 : // In unittests, we may not have been initalized, so don't crash.
1915 0 : rv = mWorkerProxy->DoLocalLookup(key, aTables, results);
1916 0 : if (NS_SUCCEEDED(rv)) {
1917 0 : rv = ProcessLookupResults(results, aTableResults);
1918 0 : NS_ENSURE_SUCCESS(rv, rv);
1919 : }
1920 0 : return NS_OK;
1921 : }
1922 :
1923 : NS_IMETHODIMP
1924 0 : nsUrlClassifierDBService::Lookup(nsIPrincipal* aPrincipal,
1925 : const nsACString& tables,
1926 : nsIUrlClassifierCallback* c)
1927 : {
1928 0 : NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
1929 :
1930 : bool dummy;
1931 0 : return LookupURI(aPrincipal, tables, c, true, &dummy);
1932 : }
1933 :
1934 : nsresult
1935 0 : nsUrlClassifierDBService::LookupURI(nsIPrincipal* aPrincipal,
1936 : const nsACString& tables,
1937 : nsIUrlClassifierCallback* c,
1938 : bool forceLookup,
1939 : bool *didLookup)
1940 : {
1941 0 : NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
1942 0 : NS_ENSURE_ARG(aPrincipal);
1943 :
1944 0 : if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
1945 0 : *didLookup = false;
1946 0 : return NS_OK;
1947 : }
1948 :
1949 0 : nsCOMPtr<nsIURI> uri;
1950 0 : nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
1951 0 : NS_ENSURE_SUCCESS(rv, rv);
1952 0 : NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
1953 :
1954 0 : uri = NS_GetInnermostURI(uri);
1955 0 : NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
1956 :
1957 0 : nsAutoCString key;
1958 : // Canonicalize the url
1959 : nsCOMPtr<nsIUrlClassifierUtils> utilsService =
1960 0 : do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
1961 0 : rv = utilsService->GetKeyForURI(uri, key);
1962 0 : if (NS_FAILED(rv))
1963 0 : return rv;
1964 :
1965 0 : if (forceLookup) {
1966 0 : *didLookup = true;
1967 : } else {
1968 0 : bool clean = false;
1969 :
1970 0 : if (!clean) {
1971 : nsCOMPtr<nsIPermissionManager> permissionManager =
1972 0 : services::GetPermissionManager();
1973 :
1974 0 : if (permissionManager) {
1975 : uint32_t perm;
1976 0 : rv = permissionManager->TestPermissionFromPrincipal(aPrincipal,
1977 0 : "safe-browsing", &perm);
1978 0 : NS_ENSURE_SUCCESS(rv, rv);
1979 :
1980 0 : clean |= (perm == nsIPermissionManager::ALLOW_ACTION);
1981 : }
1982 : }
1983 :
1984 0 : *didLookup = !clean;
1985 0 : if (clean) {
1986 0 : return NS_OK;
1987 : }
1988 : }
1989 :
1990 : // Create an nsUrlClassifierLookupCallback object. This object will
1991 : // take care of confirming partial hash matches if necessary before
1992 : // calling the client's callback.
1993 : nsCOMPtr<nsIUrlClassifierLookupCallback> callback =
1994 0 : new nsUrlClassifierLookupCallback(this, c);
1995 0 : if (!callback)
1996 0 : return NS_ERROR_OUT_OF_MEMORY;
1997 :
1998 : nsCOMPtr<nsIUrlClassifierLookupCallback> proxyCallback =
1999 0 : new UrlClassifierLookupCallbackProxy(callback);
2000 :
2001 : // Queue this lookup and call the lookup function to flush the queue if
2002 : // necessary.
2003 0 : rv = mWorker->QueueLookup(key, tables, proxyCallback);
2004 0 : NS_ENSURE_SUCCESS(rv, rv);
2005 :
2006 : // This seems to just call HandlePendingLookups.
2007 0 : nsAutoCString dummy;
2008 0 : return mWorkerProxy->Lookup(nullptr, dummy, nullptr);
2009 : }
2010 :
2011 : NS_IMETHODIMP
2012 2 : nsUrlClassifierDBService::GetTables(nsIUrlClassifierCallback* c)
2013 : {
2014 2 : NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
2015 :
2016 : // The proxy callback uses the current thread.
2017 : nsCOMPtr<nsIUrlClassifierCallback> proxyCallback =
2018 4 : new UrlClassifierCallbackProxy(c);
2019 :
2020 2 : return mWorkerProxy->GetTables(proxyCallback);
2021 : }
2022 :
2023 : NS_IMETHODIMP
2024 0 : nsUrlClassifierDBService::SetHashCompleter(const nsACString &tableName,
2025 : nsIUrlClassifierHashCompleter *completer)
2026 : {
2027 0 : if (completer) {
2028 0 : mCompleters.Put(tableName, completer);
2029 : } else {
2030 0 : mCompleters.Remove(tableName);
2031 : }
2032 0 : ClearLastResults();
2033 0 : return NS_OK;
2034 : }
2035 :
2036 : NS_IMETHODIMP
2037 0 : nsUrlClassifierDBService::ClearLastResults()
2038 : {
2039 0 : NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
2040 :
2041 0 : return mWorkerProxy->ClearLastResults();
2042 : }
2043 :
2044 : NS_IMETHODIMP
2045 2 : nsUrlClassifierDBService::BeginUpdate(nsIUrlClassifierUpdateObserver *observer,
2046 : const nsACString &updateTables)
2047 : {
2048 2 : NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
2049 :
2050 2 : if (mInUpdate) {
2051 0 : LOG(("Already updating, not available"));
2052 0 : return NS_ERROR_NOT_AVAILABLE;
2053 : }
2054 2 : if (mWorker->IsBusyUpdating()) {
2055 : // |mInUpdate| used to work well because "notifying update observer"
2056 : // is synchronously done in Worker::FinishUpdate(). Even if the
2057 : // update observer hasn't been notified at this point, we can still
2058 : // dispatch BeginUpdate() since it will NOT be run until the
2059 : // previous Worker::FinishUpdate() returns.
2060 : //
2061 : // However, some tasks in Worker::FinishUpdate() have been moved to
2062 : // another thread. The update observer will NOT be notified when
2063 : // Worker::FinishUpdate() returns. If we only check |mInUpdate|,
2064 : // the following sequence might happen on worker thread:
2065 : //
2066 : // Worker::FinishUpdate() // for update 1
2067 : // Worker::BeginUpdate() // for update 2
2068 : // Worker::NotifyUpdateObserver() // for update 1
2069 : //
2070 : // So, we have to find out a way to reject BeginUpdate() right here
2071 : // if the previous update observer hasn't been notified.
2072 : //
2073 : // Directly probing the worker's state is the most lightweight solution.
2074 : // No lock is required since Worker::BeginUpdate() and
2075 : // Worker::NotifyUpdateObserver() are by nature mutual exclusive.
2076 : // (both run on worker thread.)
2077 0 : LOG(("The previous update observer hasn't been notified."));
2078 0 : return NS_ERROR_NOT_AVAILABLE;
2079 : }
2080 :
2081 2 : mInUpdate = true;
2082 :
2083 : // The proxy observer uses the current thread
2084 : nsCOMPtr<nsIUrlClassifierUpdateObserver> proxyObserver =
2085 4 : new UrlClassifierUpdateObserverProxy(observer);
2086 :
2087 2 : return mWorkerProxy->BeginUpdate(proxyObserver, updateTables);
2088 : }
2089 :
2090 : NS_IMETHODIMP
2091 1 : nsUrlClassifierDBService::BeginStream(const nsACString &table)
2092 : {
2093 1 : NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
2094 :
2095 1 : return mWorkerProxy->BeginStream(table);
2096 : }
2097 :
2098 : NS_IMETHODIMP
2099 1 : nsUrlClassifierDBService::UpdateStream(const nsACString& aUpdateChunk)
2100 : {
2101 1 : NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
2102 :
2103 1 : return mWorkerProxy->UpdateStream(aUpdateChunk);
2104 : }
2105 :
2106 : NS_IMETHODIMP
2107 1 : nsUrlClassifierDBService::FinishStream()
2108 : {
2109 1 : NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
2110 :
2111 1 : return mWorkerProxy->FinishStream();
2112 : }
2113 :
2114 : NS_IMETHODIMP
2115 2 : nsUrlClassifierDBService::FinishUpdate()
2116 : {
2117 2 : NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
2118 :
2119 2 : mInUpdate = false;
2120 :
2121 2 : return mWorkerProxy->FinishUpdate();
2122 : }
2123 :
2124 :
2125 : NS_IMETHODIMP
2126 0 : nsUrlClassifierDBService::CancelUpdate()
2127 : {
2128 0 : NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
2129 :
2130 0 : mInUpdate = false;
2131 :
2132 0 : return mWorkerProxy->CancelUpdate();
2133 : }
2134 :
2135 : NS_IMETHODIMP
2136 0 : nsUrlClassifierDBService::ResetDatabase()
2137 : {
2138 0 : NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
2139 :
2140 0 : if (mWorker->IsBusyUpdating()) {
2141 0 : LOG(("Failed to ResetDatabase because of the unfinished update."));
2142 0 : return NS_ERROR_FAILURE;
2143 : }
2144 :
2145 0 : return mWorkerProxy->ResetDatabase();
2146 : }
2147 :
2148 : NS_IMETHODIMP
2149 0 : nsUrlClassifierDBService::ReloadDatabase()
2150 : {
2151 0 : NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
2152 :
2153 0 : if (mWorker->IsBusyUpdating()) {
2154 0 : LOG(("Failed to ReloadDatabase because of the unfinished update."));
2155 0 : return NS_ERROR_FAILURE;
2156 : }
2157 :
2158 0 : return mWorkerProxy->ReloadDatabase();
2159 : }
2160 :
2161 : NS_IMETHODIMP
2162 0 : nsUrlClassifierDBService::ClearCache()
2163 : {
2164 0 : NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
2165 :
2166 0 : return mWorkerProxy->ClearCache();
2167 : }
2168 :
2169 :
2170 : NS_IMETHODIMP
2171 0 : nsUrlClassifierDBService::GetCacheInfo(const nsACString& aTable,
2172 : nsIUrlClassifierCacheInfo** aCache)
2173 : {
2174 0 : NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
2175 :
2176 0 : return mWorkerProxy->GetCacheInfo(aTable, aCache);
2177 : }
2178 :
2179 : nsresult
2180 0 : nsUrlClassifierDBService::CacheCompletions(CacheResultArray *results)
2181 : {
2182 0 : NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
2183 :
2184 0 : return mWorkerProxy->CacheCompletions(results);
2185 : }
2186 :
2187 : bool
2188 0 : nsUrlClassifierDBService::CanComplete(const nsACString &aTableName)
2189 : {
2190 0 : return mGethashTables.Contains(aTableName) &&
2191 0 : !mDisallowCompletionsTables.Contains(aTableName);
2192 : }
2193 :
2194 : bool
2195 0 : nsUrlClassifierDBService::GetCompleter(const nsACString &tableName,
2196 : nsIUrlClassifierHashCompleter **completer)
2197 : {
2198 : // If we have specified a completer, go ahead and query it. This is only
2199 : // used by tests.
2200 0 : if (mCompleters.Get(tableName, completer)) {
2201 0 : return true;
2202 : }
2203 :
2204 0 : if (!CanComplete(tableName)) {
2205 0 : return false;
2206 : }
2207 :
2208 : // Otherwise, call gethash to find the hash completions.
2209 0 : return NS_SUCCEEDED(CallGetService(NS_URLCLASSIFIERHASHCOMPLETER_CONTRACTID,
2210 : completer));
2211 : }
2212 :
2213 : NS_IMETHODIMP
2214 0 : nsUrlClassifierDBService::Observe(nsISupports *aSubject, const char *aTopic,
2215 : const char16_t *aData)
2216 : {
2217 0 : if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
2218 : nsresult rv;
2219 0 : nsCOMPtr<nsIPrefBranch> prefs(do_QueryInterface(aSubject, &rv));
2220 0 : NS_ENSURE_SUCCESS(rv, rv);
2221 : Unused << prefs;
2222 :
2223 0 : if (kObservedPrefs.Contains(NS_ConvertUTF16toUTF8(aData))) {
2224 0 : ReadTablesFromPrefs();
2225 : }
2226 0 : } else if (!strcmp(aTopic, "quit-application")) {
2227 : // Tell the update thread to finish as soon as possible.
2228 0 : gShuttingDownThread = true;
2229 0 : } else if (!strcmp(aTopic, "profile-before-change")) {
2230 0 : gShuttingDownThread = true;
2231 0 : Shutdown();
2232 : } else {
2233 0 : return NS_ERROR_UNEXPECTED;
2234 : }
2235 :
2236 0 : return NS_OK;
2237 : }
2238 :
2239 : // Join the background thread if it exists.
2240 : nsresult
2241 0 : nsUrlClassifierDBService::Shutdown()
2242 : {
2243 0 : LOG(("shutting down db service\n"));
2244 0 : MOZ_ASSERT(XRE_IsParentProcess());
2245 :
2246 0 : if (!gDbBackgroundThread) {
2247 0 : return NS_OK;
2248 : }
2249 :
2250 0 : Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_SHUTDOWN_TIME> timer;
2251 :
2252 0 : mCompleters.Clear();
2253 :
2254 0 : nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
2255 0 : if (prefs) {
2256 0 : for (uint8_t i = 0; i < kObservedPrefs.Length(); i++) {
2257 0 : prefs->RemoveObserver(kObservedPrefs[i].get(), this);
2258 : }
2259 : }
2260 :
2261 : // 1. Synchronize with worker thread and update thread by
2262 : // *synchronously* dispatching an event to worker thread
2263 : // for shutting down the update thread. The reason not
2264 : // shutting down update thread directly from main thread
2265 : // is to avoid racing for Classifier::mUpdateThread
2266 : // between main thread and the worker thread. (Both threads
2267 : // would access Classifier::mUpdateThread.)
2268 : using Worker = nsUrlClassifierDBServiceWorker;
2269 0 : RefPtr<nsIRunnable> r = NewRunnableMethod(
2270 : "nsUrlClassifierDBServiceWorker::FlushAndDisableAsyncUpdate",
2271 : mWorker,
2272 0 : &Worker::FlushAndDisableAsyncUpdate);
2273 0 : SyncRunnable::DispatchToThread(gDbBackgroundThread, r);
2274 :
2275 : // At this point the update thread has been shut down and
2276 : // the worker thread should only have at most one event,
2277 : // which is the callback event.
2278 :
2279 : // 2. Send CancelUpdate() event to notify the dangling update.
2280 : // (i.e. BeginUpdate is called but FinishUpdate is not.)
2281 : // and CloseDb() to clear mClassifier. They will be the last two
2282 : // events on the worker thread in the shutdown process.
2283 0 : DebugOnly<nsresult> rv;
2284 0 : rv = mWorkerProxy->CancelUpdate();
2285 0 : MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to post 'cancel update' event");
2286 0 : rv = mWorkerProxy->CloseDb();
2287 0 : MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to post 'close db' event");
2288 0 : mWorkerProxy = nullptr;
2289 :
2290 : // 3. Invalidate XPCOM APIs by nulling out gDbBackgroundThread
2291 : // since every API checks gDbBackgroundThread first. This has
2292 : // to be done before calling nsIThread.shutdown because it
2293 : // will cause the pending events on the joining thread to
2294 : // be processed.
2295 0 : nsIThread *backgroundThread = nullptr;
2296 0 : Swap(backgroundThread, gDbBackgroundThread);
2297 :
2298 : // 4. Wait until the worker thread is down.
2299 0 : if (backgroundThread) {
2300 0 : backgroundThread->Shutdown();
2301 0 : NS_RELEASE(backgroundThread);
2302 : }
2303 :
2304 0 : mWorker = nullptr;
2305 0 : return NS_OK;
2306 : }
2307 :
2308 : nsIThread*
2309 10 : nsUrlClassifierDBService::BackgroundThread()
2310 : {
2311 10 : return gDbBackgroundThread;
2312 : }
2313 :
2314 : // static
2315 : bool
2316 33 : nsUrlClassifierDBService::ShutdownHasStarted()
2317 : {
2318 33 : return gShuttingDownThread;
2319 : }
|