--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -1110,18 +1110,19 @@ BrowserGlue.prototype = {
// It's important that SafeBrowsing is initialized reasonably
// early, so we use a maximum timeout for it.
Services.tm.idleDispatchToMainThread(() => {
SafeBrowsing.init();
// Login reputation depends on the Safe Browsing API.
if (Services.prefs.getBoolPref("browser.safebrowsing.passwords.enabled")) {
- Cc["@mozilla.org/reputationservice/login-reputation-service;1"]
- .getService(Ci.ILoginReputationService);
+ let reputationService = Cc["@mozilla.org/reputationservice/login-reputation-service;1"]
+ .getService(Ci.ILoginReputationService);
+ reputationService.init();
}
}, 5000);
if (AppConstants.MOZ_CRASHREPORTER) {
UnsubmittedCrashHandler.scheduleCheckForUnsubmittedCrashReports();
}
if (AppConstants.platform == "win") {
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5452,17 +5452,17 @@ pref("browser.safebrowsing.downloads.rem
pref("browser.safebrowsing.downloads.remote.timeout_ms", 10000);
pref("browser.safebrowsing.downloads.remote.url", "https://sb-ssl.google.com/safebrowsing/clientreport/download?key=%GOOGLE_API_KEY%");
pref("browser.safebrowsing.downloads.remote.block_dangerous", true);
pref("browser.safebrowsing.downloads.remote.block_dangerous_host", true);
pref("browser.safebrowsing.downloads.remote.block_potentially_unwanted", true);
pref("browser.safebrowsing.downloads.remote.block_uncommon", true);
// Password protection
-pref("browser.safebrowsing.passwords.enabled", false);
+pref("browser.safebrowsing.passwords.enabled", true);
// Google Safe Browsing provider (legacy)
pref("browser.safebrowsing.provider.google.pver", "2.2");
pref("browser.safebrowsing.provider.google.lists", "goog-badbinurl-shavar,goog-downloadwhite-digest256,goog-phish-shavar,googpub-phish-shavar,goog-malware-shavar,goog-unwanted-shavar");
pref("browser.safebrowsing.provider.google.updateURL", "https://safebrowsing.google.com/safebrowsing/downloads?client=SAFEBROWSING_ID&appver=%MAJOR_VERSION%&pver=2.2&key=%GOOGLE_API_KEY%");
pref("browser.safebrowsing.provider.google.gethashURL", "https://safebrowsing.google.com/safebrowsing/gethash?client=SAFEBROWSING_ID&appver=%MAJOR_VERSION%&pver=2.2");
pref("browser.safebrowsing.provider.google.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site=");
pref("browser.safebrowsing.provider.google.reportPhishMistakeURL", "https://%LOCALE%.phish-error.mozilla.com/?hl=%LOCALE%&url=");
--- a/toolkit/components/passwordmgr/LoginHelper.jsm
+++ b/toolkit/components/passwordmgr/LoginHelper.jsm
@@ -32,17 +32,17 @@ this.LoginHelper = {
/**
* Warning: these only update if a logger was created.
*/
debug: Services.prefs.getBoolPref("signon.debug"),
formlessCaptureEnabled: Services.prefs.getBoolPref("signon.formlessCapture.enabled"),
schemeUpgrades: Services.prefs.getBoolPref("signon.schemeUpgrades"),
insecureAutofill: Services.prefs.getBoolPref("signon.autofillForms.http"),
showInsecureFieldWarning: Services.prefs.getBoolPref("security.insecure_field_warning.contextual.enabled"),
- loginReputationEnabled: Services.prefs.getBoolPref("browser.safebrowsing.passwords.enabled");
+ loginReputationEnabled: Services.prefs.getBoolPref("browser.safebrowsing.passwords.enabled"),
createLogger(aLogPrefix) {
let getMaxLogLevel = () => {
return this.debug ? "debug" : "warn";
};
// Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref.
let ConsoleAPI = Cu.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
@@ -60,18 +60,18 @@ this.LoginHelper = {
this.insecureAutofill = Services.prefs.getBoolPref("signon.autofillForms.http");
logger.maxLogLevel = getMaxLogLevel();
});
Services.prefs.addObserver("security.insecure_field_warning.", () => {
this.showInsecureFieldWarning = Services.prefs.getBoolPref("security.insecure_field_warning.contextual.enabled");
});
- Services.prefs.addObserver("browser.safebrowsing.passwords.enabled"), () => {
- this.loginReputationEnabled = Services.prefs.getBoolPref("browser.safebrowsing.passwords.enabled");
+ Services.prefs.addObserver("browser.safebrowsing.passwords.enabled", () => {
+ this.loginReputationEnabled = Services.prefs.getBoolPref("browser.safebrowsing.passwords.enabled");
});
return logger;
},
/**
* Due to the way the signons2.txt file is formatted, we need to make
* sure certain field values or characters do not cause the file to
--- a/toolkit/components/passwordmgr/LoginManagerParent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerParent.jsm
@@ -287,18 +287,18 @@ var LoginManagerParent = {
// doesn't support structured cloning.
var jsLogins = LoginHelper.loginsToVanillaObjects(matchingLogins);
target.messageManager.sendAsyncMessage("RemoteLogins:loginsAutoCompleted", {
requestId,
logins: jsLogins,
});
},
- doQueryLoginReputation(formOrigin, actionOrigin,
- requestId, target) {
+ doQueryLoginReputation({ formOrigin, actionOrigin,
+ requestId }, target) {
let service = Cc["@mozilla.org/reputationservice/login-reputation-service;1"].
getService(Ci.ILoginReputationService);
let param = {
formUrl: formOrigin,
actionUrl: actionOrigin,
};
--- a/toolkit/components/passwordmgr/nsLoginManager.js
+++ b/toolkit/components/passwordmgr/nsLoginManager.js
@@ -552,17 +552,18 @@ LoginManager.prototype = {
if (this._queryLoginReputationPromise !== promise) {
// If the login reputation query was canceled before we got our
// results, don't bother reporting them.
return;
}
this._queryLoginReputationPromise = null;
- log.debug("QueryLoginReputation invoked. Result is:", aResult);
+ log.debug("QueryLoginReputation invoked. Result is:" + aResult);
+ dump("QueryLoginReputation invoked. Result is:" + aResult + "\n");
aCallback.onQueryReputationCompletion();
});
},
stopQueryLoginReputation() {
this._queryLoginReputationPromise = null;
}
--- a/toolkit/components/reputationservice/ILoginReputation.idl
+++ b/toolkit/components/reputationservice/ILoginReputation.idl
@@ -29,11 +29,13 @@ interface ILoginReputationQuery : nsISup
interface ILoginReputationQueryCallback : nsISupports {
// aResult should be one of the const value defined in ILoginReputationResult
// interface.
void onQueryComplete(in uint16_t aResult);
};
[scriptable, uuid(1b3f1dfe-ce3a-486b-953e-ce5ac863eff9)]
interface ILoginReputationService : nsISupports {
+ void init();
+
void queryReputation(in ILoginReputationQuery aQuery,
in ILoginReputationQueryCallback aCallback);
};
--- a/toolkit/components/reputationservice/LoginReputation.cpp
+++ b/toolkit/components/reputationservice/LoginReputation.cpp
@@ -1,22 +1,319 @@
/* -*- Mode: C++; tab-width: 4; 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 "LoginReputation.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Logging.h"
using namespace mozilla;
// MOZ_LOG=LoginReputation:5
-LazyLogModule LoginReputationService::prlog("LoginReputation");
-#define LR_LOG(args) MOZ_LOG(LoginReputationService::prlog, mozilla::LogLevel::Debug, args)
-#define LR_LOG_ENABLED() MOZ_LOG_TEST(LoginReputationService::prlog, mozilla::LogLevel::Debug)
+LazyLogModule gLoginReputationLogModule("LoginReputation");
+#define LR_LOG(args) MOZ_LOG(gLoginReputationLogModule, mozilla::LogLevel::Debug, args)
+#define LR_LOG_ENABLED() MOZ_LOG_TEST(gLoginReputationLogModule, mozilla::LogLevel::Debug)
+
+static Atomic<bool> gShuttingDown(false);
+
+// This is for debug.
+static
+nsCString ReputationResultToString(ReputationResult aResult)
+{
+ switch(aResult) {
+ case ReputationResult::LOW_REPUTATION: return nsCString("Low Reputation");
+ case ReputationResult::SAFE: return nsCString("Safe");
+ case ReputationResult::PHISHING: return nsCString("Phishing");
+ default: return nsCString("Unspecified");
+ }
+}
+
+// -------------------------------------------------------------------------
+// RemoteLookup
+//
+// For non-whitelisted sites, we will do a remote lookup against a google
+// service and receive one of the following verdicts:
+// 1. safe
+// 2. low-reputation
+// 3. phishing
+//
+// This class owns a worker thread which is responsible for sending lookup
+// request to the server and parse response. Response is also stored in verdict
+// cache.
+//
+class RemoteLookup final : public nsISupports
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ RemoteLookup() = default;
+
+ nsresult Init();
+ nsresult Uninit();
+
+ RefPtr<ReputationPromise> QueryRemoteLookup(ILoginReputationQuery* aParam);
+
+private:
+ ~RemoteLookup() = default;
+
+ RefPtr<ReputationPromise> QueryInternal(ILoginReputationQuery* aParam);
+
+ nsCOMPtr<nsIThread> mThread;
+};
+
+NS_IMPL_ISUPPORTS0(RemoteLookup);
+
+nsresult
+RemoteLookup::Init()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsresult rv = NS_NewNamedThread("LR RemoteLookup", getter_AddRefs(mThread));
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to create remote lookup thread for login reputation");
+ }
+
+ // TODO : Bug 1416647 - Support verdict cache for password phishing.
+ // Load verdict cache when Init, but task should be post to worke
+ // thread.
+
+ return rv;
+}
+
+nsresult
+RemoteLookup::Uninit()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (mThread) {
+ mThread->Shutdown();
+ mThread = nullptr;
+ }
+
+ return NS_OK;
+}
+
+RefPtr<ReputationPromise>
+RemoteLookup::QueryRemoteLookup(ILoginReputationQuery* aParam)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (gShuttingDown) {
+ return ReputationPromise::CreateAndReject(NS_ERROR_ABORT, __func__);
+ }
+
+ RefPtr<RemoteLookup> self = this;
+
+ return InvokeAsync(mThread, __func__, [self, aParam]() {
+ return self->QueryInternal(aParam);
+ });
+}
+
+RefPtr<ReputationPromise>
+RemoteLookup::QueryInternal(ILoginReputationQuery* aParam)
+{
+ // This must be on the worker thread.
+ MOZ_ASSERT(!NS_IsMainThread());
+ MOZ_ASSERT(aParam);
+
+ if (gShuttingDown) {
+ return ReputationPromise::CreateAndReject(NS_ERROR_ABORT, __func__);
+ }
+
+ ReputationResult result = ReputationResult::SAFE;
+
+ // TODO: Bug 1413732 - Query the login reputation service.
+ // This is where we should implement verdict cache and remote lookup.
+
+ return ReputationPromise::CreateAndResolve(result, __func__);
+}
+
+// -------------------------------------------------------------------------
+// TrustBasedWhitelisting
+//
+// Sites that we could trust
+// - bookmark/homepage
+// - permissions
+// - saved password
+// - content setting
+//
+class TrustBasedWhitelisting final : public nsISupports
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ TrustBasedWhitelisting() = default;
+
+ nsresult Init();
+ nsresult Uninit();
+
+ RefPtr<ReputationPromise> QueryTrustBasedWhitelist(ILoginReputationQuery* aParam);
+
+private:
+ ~TrustBasedWhitelisting() = default;
+
+ RefPtr<ReputationPromise> QueryInternal(ILoginReputationQuery* aParam);
+
+ nsCOMPtr<nsIThread> mThread;
+};
+
+NS_IMPL_ISUPPORTS0(TrustBasedWhitelisting);
+nsresult
+TrustBasedWhitelisting::Init()
+{
+ nsresult rv = NS_NewNamedThread("LR WhiteList", getter_AddRefs(mThread));
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to create white list thread for login reputation");
+ }
+
+ return rv;
+}
+
+nsresult
+TrustBasedWhitelisting::Uninit()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (mThread) {
+ mThread->Shutdown();
+ mThread = nullptr;
+ }
+
+ return NS_OK;
+}
+
+RefPtr<ReputationPromise>
+TrustBasedWhitelisting::QueryTrustBasedWhitelist(ILoginReputationQuery* aParam)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (gShuttingDown) {
+ return ReputationPromise::CreateAndReject(NS_ERROR_ABORT, __func__);
+ }
+
+ RefPtr<TrustBasedWhitelisting> self = this;
+
+ return InvokeAsync(mThread, __func__, [self, aParam]() {
+ return self->QueryInternal(aParam);
+ });
+}
+
+RefPtr<ReputationPromise>
+TrustBasedWhitelisting::QueryInternal(ILoginReputationQuery* aParam)
+{
+ // This must be on the worker thread.
+ MOZ_ASSERT(!NS_IsMainThread());
+ MOZ_ASSERT(aParam);
+
+ if (gShuttingDown) {
+ return ReputationPromise::CreateAndReject(NS_ERROR_ABORT, __func__);
+ }
+
+ // TODO : Bug 1416653 - Support trust-based whitelisting heuristics.
+ // This is where we should implement our trust-based whitelisting
+ // algorithm. Set result to UNSPECIFIED for now.
+ ReputationResult result = ReputationResult::UNSPECIFIED;
+
+ return ReputationPromise::CreateAndResolve(result, __func__);
+}
+
+// -------------------------------------------------------------------------
+// CSDQuery
+//
+// This class is a wrapper that encapsulate asynchronous callback API provided
+// by DBService into a MozPromise callback.
+//
+class CSDQuery final : public nsIURIClassifierCallback
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURICLASSIFIERCALLBACK
+
+ RefPtr<ReputationPromise> ClassifyLocalWithTables(nsIURI *aURI);
+
+ CSDQuery() = default;
+
+private:
+ ~CSDQuery() = default;
+
+ void GetWhiteListTables(nsACString& aTables);
+
+ MozPromiseHolder<ReputationPromise> mPromiseHolder;
+};
+
+NS_IMPL_ISUPPORTS(CSDQuery, nsIURIClassifierCallback)
+
+RefPtr<ReputationPromise>
+CSDQuery::ClassifyLocalWithTables(nsIURI *aURI)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsAutoCString tables;
+ GetWhiteListTables(tables);
+
+ nsresult rv;
+ nsCOMPtr<nsIURIClassifier> uriClassifier =
+ do_GetService(NS_URLCLASSIFIERDBSERVICE_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return ReputationPromise::CreateAndReject(rv, __func__);
+ }
+
+ // AsyncClassifyLocalWithTables won't trigger a gethash request.
+ // So this operation should be fast.
+ rv = uriClassifier->AsyncClassifyLocalWithTables(aURI, tables, this);
+ if (NS_FAILED(rv)) {
+ return ReputationPromise::CreateAndReject(rv, __func__);
+ }
+
+ RefPtr<ReputationPromise> p = mPromiseHolder.Ensure(__func__);
+ return p;
+}
+
+nsresult
+CSDQuery::OnClassifyComplete(nsresult aErrorCode,
+ const nsACString& aLists,
+ const nsACString& aProvider,
+ const nsACString& aFullHash)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ int64_t now = PR_Now() / PR_USEC_PER_MSEC;
+ LR_LOG(("OnClassifyComplete : list = %s", aLists.BeginReading()));
+
+ if (NS_FAILED(aErrorCode)) {
+ mPromiseHolder.Reject(aErrorCode, __func__);
+ return NS_OK;
+ }
+
+ ReputationResult result = aLists.IsEmpty() ?
+ ReputationResult::UNSPECIFIED : ReputationResult::SAFE;
+
+ LR_LOG(("CSD returns login reputation result = %s",
+ ReputationResultToString(result).get()));
+
+ mPromiseHolder.Resolve(result, __func__);
+
+ return NS_OK;
+}
+
+void
+CSDQuery::GetWhiteListTables(nsACString& aTables)
+{
+ aTables.AssignLiteral("goog-passwordwhite-proto");
+}
+
+// -------------------------------------------------------------------------
+// LoginReputationService
+//
+// TODO : Add description
+//
+//
NS_IMPL_ISUPPORTS(LoginReputationService,
ILoginReputationService)
LoginReputationService*
LoginReputationService::gLoginReputationService = nullptr;
already_AddRefed<LoginReputationService>
LoginReputationService::GetSingleton()
@@ -30,31 +327,260 @@ LoginReputationService::GetSingleton()
LoginReputationService::LoginReputationService()
{
LR_LOG(("Login reputation service starting up"));
}
LoginReputationService::~LoginReputationService()
{
LR_LOG(("Login reputation service shutting down"));
+
+ gShuttingDown = true;
+ Shutdown();
+}
+
+NS_IMETHODIMP
+LoginReputationService::Init()
+{
+ LR_LOG(("Init login reputation service"));
+
+ nsresult rv;
+
+ mRemote = new RemoteLookup();
+ rv = mRemote->Init();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ mTrust = new TrustBasedWhitelisting();
+ rv = mTrust->Init();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // Add an observer for shutdown
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (os) {
+ os->AddObserver(this, "quit-application", false);
+ os->AddObserver(this, "profile-before-change", false);
+ }
+
+ return NS_OK;
}
NS_IMETHODIMP
LoginReputationService::QueryReputation(ILoginReputationQuery* aQuery,
ILoginReputationQueryCallback* aCallback)
{
- LR_LOG(("Starting login reputation check [query=%p]", aQuery));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (gShuttingDown) {
+ return NS_ERROR_ABORT;
+ }
+
NS_ENSURE_ARG_POINTER(aQuery);
NS_ENSURE_ARG_POINTER(aCallback);
if (LR_LOG_ENABLED()) {
nsAutoCString formUrl, actionUrl;
aQuery->GetFormUrl(formUrl);
aQuery->GetActionUrl(actionUrl);
- LR_LOG(("Login reputation query parameters: formUrl(%s), actionUrl(%s)",
+ LR_LOG(("Query login reputation started, formUrl(%s), actionUrl(%s)",
formUrl.BeginReading(), actionUrl.BeginReading()));
}
- // XXX: Always return SAFE for now.
- aCallback->OnQueryComplete(ILoginReputationResult::SAFE);
+ // mQueryRequests is an array used to maintain the ownership of |QueryRequest|
+ // so we could ensure the |QueryRequest| is always valid until Finish() is
+ // called or LoginReputationService is shutdown.
+ auto* request =
+ mQueryRequests.AppendElement(MakeUnique<QueryRequest>(aQuery, aCallback));
+
+ return QueryReputationInternal(request->get());
+}
+
+nsresult
+LoginReputationService::QueryReputationInternal(QueryRequest* aRequest)
+{
+ MOZ_ASSERT(aRequest);
+
+ // Check whitelist first so we don't have to perform a remote lookup for every
+ // request. There are two whitelist we could check now, the first one is
+ // CSD_WHITE list, the second one is trust based whitelist (check if the url
+ // matches bookmarks, history ...etc).
+ // We do these two whitelist checks in parallel to minimize the reputation query
+ // response time.
+ auto csdPromise = QueryCSDWhitelistTable(aRequest);
+ auto trustPromise = QueryTrustBasedWhitelist(aRequest);
+
+ // Both promises must not be empty, if failure happened in above function
+ // calls, rejected promise will be returned.
+ MOZ_ASSERT(csdPromise && trustPromise);
+
+ nsTArray<RefPtr<ReputationPromise>> promises = {
+ csdPromise, trustPromise,
+ };
+
+ // Back to main-thread when both CSD query and trust-based whitelist
+ // check are done. Return if any check return SAFE, otherwise we will trigger
+ // the remote lookup here.
+ RefPtr<LoginReputationService> self = this;
+
+ ReputationPromise::All(GetCurrentThreadSerialEventTarget(), promises)->Then(
+ GetCurrentThreadSerialEventTarget(), __func__,
+ [self, aRequest](const nsTArray<ReputationResult>& aResolveValues) -> void {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Promise::All is done when CSD and turst-based whitelist are both resolved.
+ MOZ_ASSERT(aResolveValues.Length() == 2);
+
+ self->mCSDQueries.RemoveElementAt(0);
+
+ // As long as one of the resolved value is SAFE, then we are done.
+ if (aResolveValues.Contains(ReputationResult::SAFE)) {
+ self->Finish(aRequest, ReputationResult::SAFE);
+ } else {
+ // Perform remote lookup when non of any whitelisting method returns
+ // SAFE.
+ self->QueryRemoteLookup(aRequest);
+ }
+ },
+ [self, aRequest]() {
+ self->Finish(aRequest, ReputationResult::UNSPECIFIED);
+ });
return NS_OK;
}
+
+// Question: If something wrong here, should we still do the remote lookup ???
+RefPtr<ReputationPromise>
+LoginReputationService::QueryCSDWhitelistTable(QueryRequest* aRequest)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsAutoCString formUrl;
+ aRequest->mParam->GetFormUrl(formUrl);
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), formUrl);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return ReputationPromise::CreateAndReject(rv, __func__);
+ }
+
+ RefPtr<CSDQuery> csdQuery = new CSDQuery();
+ mCSDQueries.AppendElement(csdQuery);
+
+ return csdQuery->ClassifyLocalWithTables(uri);
+}
+
+RefPtr<ReputationPromise>
+LoginReputationService::QueryTrustBasedWhitelist(QueryRequest* aRequest)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ return mTrust->QueryTrustBasedWhitelist(aRequest->mParam);
+}
+
+nsresult
+LoginReputationService::QueryRemoteLookup(QueryRequest* aRequest)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RefPtr<LoginReputationService> self = this;
+
+ // This is the last step of query reputation, call Finish() with
+ // the reputation result.
+ mRemote->QueryRemoteLookup(aRequest->mParam)->Then(
+ GetCurrentThreadSerialEventTarget(), __func__,
+ [self, aRequest](ReputationResult aResolveValue) -> void {
+ self->mRemoteLookupRequest.Complete();
+ self->Finish(aRequest, aResolveValue);
+ },
+ [self, aRequest]() {
+ self->mRemoteLookupRequest.Complete();
+ self->Finish(aRequest, ReputationResult::UNSPECIFIED);
+ })->Track(mRemoteLookupRequest);
+
+ return NS_OK;
+}
+
+nsresult
+LoginReputationService::Finish(const QueryRequest* aRequest, ReputationResult aResult)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aRequest);
+
+ // Since we are shutting down, don't bother call back to child process.
+ if (gShuttingDown) {
+ return NS_OK;
+ }
+
+ if (LR_LOG_ENABLED()) {
+ LR_LOG(("Query login reputation finished, result is %s",
+ ReputationResultToString(aResult).get()));
+ }
+
+ // TODO : Add a testcase to make sure enum(ReputationResult) is sync with definition
+ // in ILoginReputationResult and csd.pb.cc
+ aRequest->mCallback->OnQueryComplete(static_cast<uint16_t>(aResult));
+
+ // QueryRequest may not follow the same order when we queued it in ::QueryReputation
+ // because one query request may finish earilier than the other.
+ uint32_t idx = 0;
+ for (; idx < mQueryRequests.Length(); idx++) {
+ if (mQueryRequests[idx].get() == aRequest) {
+ break;
+ }
+ }
+
+ MOZ_ASSERT(idx < mQueryRequests.Length());
+ mQueryRequests.RemoveElementAt(idx);
+
+ return NS_OK;
+}
+
+nsresult
+LoginReputationService::Shutdown()
+{
+ MOZ_ASSERT(gShuttingDown);
+
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (os) {
+ os->RemoveObserver(this, "quit-application");
+ os->RemoveObserver(this, "profile-before-change");
+ }
+
+ mRemoteLookupRequest.DisconnectIfExists();
+
+ // Wait until worker threads are shutdown
+ if (mRemote) {
+ mRemote->Uninit();
+ mRemote = nullptr;
+ }
+
+ if (mTrust) {
+ mTrust->Uninit();
+ mTrust = nullptr;
+ }
+
+ mCSDQueries.Clear();
+ mQueryRequests.Clear();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+LoginReputationService::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *aData)
+{
+ if (!strcmp(aTopic, "quit-application")) {
+ // Prepare to shutdown, won't allow any query request after 'gShuttingDown'
+ // is set.
+ gShuttingDown = true;
+ } else if (!strcmp(aTopic, "profile-before-change")) {
+ gShuttingDown = true;
+ Shutdown();
+ } else {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
--- a/toolkit/components/reputationservice/LoginReputation.h
+++ b/toolkit/components/reputationservice/LoginReputation.h
@@ -2,35 +2,94 @@
/* 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 LoginReputation_h__
#define LoginReputation_h__
#include "ILoginReputation.h"
+#include "nsIURIClassifier.h"
+#include "nsIObserver.h"
#include "mozilla/Logging.h"
+#include "mozilla/MozPromise.h"
+
+class TrustBasedWhitelisting;
+class RemoteLookup;
+class CSDQuery;
+
+namespace mozilla {
-class LoginReputationService final : public ILoginReputationService
+// The values list below should sync with definition in ILoginReputationResult
+enum ReputationResult {
+ UNSPECIFIED = 0,
+ SAFE = 1,
+ LOW_REPUTATION = 2,
+ PHISHING = 3,
+};
+
+typedef MozPromise<ReputationResult, nsresult, false> ReputationPromise;
+
+class LoginReputationService final : public ILoginReputationService,
+ public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_ILOGINREPUTATIONSERVICE
+ NS_DECL_NSIOBSERVER
public:
static already_AddRefed<LoginReputationService> GetSingleton();
private:
+ struct QueryRequest {
+ QueryRequest(ILoginReputationQuery* aParam,
+ ILoginReputationQueryCallback* aCallback) :
+ mParam(aParam),
+ mCallback(aCallback)
+ {
+ }
+
+ nsCOMPtr<ILoginReputationQuery> mParam;
+ nsCOMPtr<ILoginReputationQueryCallback> mCallback;
+ };
+
/**
* Global singleton object for holding this factory service.
*/
static LoginReputationService* gLoginReputationService;
- /**
- * MOZ_LOG=LoginReputation:5
- */
- static mozilla::LazyLogModule prlog;
-
LoginReputationService();
~LoginReputationService();
+
+ nsresult QueryReputationInternal(QueryRequest* aRequest);
+
+ RefPtr<ReputationPromise> QueryCSDWhitelistTable(QueryRequest* aRequest);
+
+ RefPtr<ReputationPromise> QueryTrustBasedWhitelist(QueryRequest* aRequest);
+
+ nsresult QueryRemoteLookup(QueryRequest* aRequest);
+
+ // Called when a query request is finished.
+ nsresult Finish(const QueryRequest* aRequest, ReputationResult aResult);
+
+ // Join the worker threads.
+ nsresult Shutdown();
+
+ // This class implements trust-based whitelisting algorithm.
+ RefPtr<TrustBasedWhitelisting> mTrust;
+
+ // This class implements remote server lookup.
+ RefPtr<RemoteLookup> mRemote;
+
+ // Ongoing query requests which is added when ::QueryReputation API is called.
+ nsTArray<UniquePtr<QueryRequest>> mQueryRequests;
+
+ // Queries that are waiting for callback from ::AsyncClassifyLocalWithTables.
+ nsTArray<RefPtr<CSDQuery>> mCSDQueries;
+
+ // RemoteLookup promise request tracker.
+ MozPromiseRequestHolder<ReputationPromise> mRemoteLookupRequest;
};
+} // namespace mozilla
+
#endif // LoginReputation_h__
--- a/toolkit/components/satchel/nsFormFillController.cpp
+++ b/toolkit/components/satchel/nsFormFillController.cpp
@@ -1447,20 +1447,16 @@ nsFormFillController::StopControllingInp
mController->GetInput(getter_AddRefs(input));
if (input == this) {
MOZ_LOG(sLogger, LogLevel::Verbose,
("StopControllingInput: Nulled controller input for %p", this));
mController->SetInput(nullptr);
}
}
- if (mLoginManager) {
- mLoginManager->StopQueryLoginReputation();
- }
-
MOZ_LOG(sLogger, LogLevel::Verbose,
("StopControllingInput: Stopped controlling %p", mFocusedInput));
if (mFocusedInputNode) {
MaybeRemoveMutationObserver(mFocusedInputNode);
mFocusedInputNode = nullptr;
mFocusedInput = nullptr;
}