--- a/browser/base/content/browser-safebrowsing.js
+++ b/browser/base/content/browser-safebrowsing.js
@@ -34,15 +34,32 @@ var gSafeBrowsing = {
if (uri && (uri.schemeIs("http") || uri.schemeIs("https")))
broadcaster.removeAttribute("disabled");
else
broadcaster.setAttribute("disabled", true);
},
/**
* Used to report a phishing page or a false positive
- * @param name String One of "Phish", "Error", "Malware" or "MalwareError"
+ *
+ * @param name
+ * String One of "PhishMistake", "MalwareMistake", or "Phish"
+ * @param info
+ * Information about the reasons for blocking the resource.
+ * In the case false positive, it may contain SafeBrowsing
+ * matching list and provider of the list
* @return String the report phishing URL.
*/
- getReportURL(name) {
- return SafeBrowsing.getReportURL(name, gBrowser.currentURI);
+ getReportURL(name, info) {
+ let reportInfo = info;
+ if (!reportInfo) {
+ let pageUri = gBrowser.currentURI.clone();
+
+ // Remove the query to avoid including potentially sensitive data
+ if (pageUri instanceof Ci.nsIURL) {
+ pageUri.query = "";
+ }
+
+ reportInfo = { uri: pageUri.asciiSpec };
+ }
+ return SafeBrowsing.getReportURL(name, reportInfo);
}
}
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2967,17 +2967,18 @@ var BrowserOnClick = {
msg.data.isTopFrame, msg.data.location,
msg.data.securityInfoAsString);
break;
case "Browser:OpenCaptivePortalPage":
CaptivePortalWatcher.ensureCaptivePortalTab();
break;
case "Browser:SiteBlockedError":
this.onAboutBlocked(msg.data.elementId, msg.data.reason,
- msg.data.isTopFrame, msg.data.location);
+ msg.data.isTopFrame, msg.data.location,
+ msg.data.blockedInfo);
break;
case "Browser:EnableOnlineMode":
if (Services.io.offline) {
// Reset network state and refresh the page.
Services.io.offline = false;
msg.target.reload();
}
break;
@@ -3092,17 +3093,17 @@ var BrowserOnClick = {
let detailedInfo = getDetailedCertErrorInfo(location,
securityInfo);
gClipboardHelper.copyString(detailedInfo);
break;
}
},
- onAboutBlocked(elementId, reason, isTopFrame, location) {
+ onAboutBlocked(elementId, reason, isTopFrame, location, blockedInfo) {
// Depending on what page we are displaying here (malware/phishing/unwanted)
// use the right strings and links for each.
let bucketName = "";
let sendTelemetry = false;
if (reason === "malware") {
sendTelemetry = true;
bucketName = "WARNING_MALWARE_PAGE_";
} else if (reason === "phishing") {
@@ -3135,23 +3136,23 @@ var BrowserOnClick = {
openHelpLink("phishing-malware", false, "current");
break;
case "ignoreWarningButton":
if (gPrefService.getBoolPref("browser.safebrowsing.allowOverride")) {
if (sendTelemetry) {
secHistogram.add(nsISecTel[bucketName + "IGNORE_WARNING"]);
}
- this.ignoreWarningButton(reason);
+ this.ignoreWarningButton(reason, blockedInfo);
}
break;
}
},
- ignoreWarningButton(reason) {
+ ignoreWarningButton(reason, blockedInfo) {
// Allow users to override and continue through to the site,
// but add a notify bar as a reminder, so that they don't lose
// track after, e.g., tab switching.
gBrowser.loadURIWithFlags(gBrowser.currentURI.spec,
nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
null, null, null);
Services.perms.add(gBrowser.currentURI, "safe-browsing",
@@ -3161,33 +3162,43 @@ var BrowserOnClick = {
let buttons = [{
label: gNavigatorBundle.getString("safebrowsing.getMeOutOfHereButton.label"),
accessKey: gNavigatorBundle.getString("safebrowsing.getMeOutOfHereButton.accessKey"),
callback() { getMeOutOfHere(); }
}];
let title;
if (reason === "malware") {
- title = gNavigatorBundle.getString("safebrowsing.reportedAttackSite");
- buttons[1] = {
- label: gNavigatorBundle.getString("safebrowsing.notAnAttackButton.label"),
- accessKey: gNavigatorBundle.getString("safebrowsing.notAnAttackButton.accessKey"),
- callback() {
- openUILinkIn(gSafeBrowsing.getReportURL("MalwareMistake"), "tab");
- }
- };
+ let reportUrl = gSafeBrowsing.getReportURL("MalwareMistake", blockedInfo);
+
+ // There's no button if we can not get report url, for example if the provider
+ // of blockedInfo is not Google
+ if (reportUrl) {
+ buttons[1] = {
+ label: gNavigatorBundle.getString("safebrowsing.notAnAttackButton.label"),
+ accessKey: gNavigatorBundle.getString("safebrowsing.notAnAttackButton.accessKey"),
+ callback() {
+ openUILinkIn(reportUrl, "tab");
+ }
+ };
+ }
} else if (reason === "phishing") {
- title = gNavigatorBundle.getString("safebrowsing.deceptiveSite");
- buttons[1] = {
- label: gNavigatorBundle.getString("safebrowsing.notADeceptiveSiteButton.label"),
- accessKey: gNavigatorBundle.getString("safebrowsing.notADeceptiveSiteButton.accessKey"),
- callback() {
- openUILinkIn(gSafeBrowsing.getReportURL("PhishMistake"), "tab");
- }
- };
+ let reportUrl = gSafeBrowsing.getReportURL("PhishMistake", blockedInfo);
+
+ // There's no button if we can not get report url, for example if the provider
+ // of blockedInfo is not Google
+ if (reportUrl) {
+ buttons[1] = {
+ label: gNavigatorBundle.getString("safebrowsing.notADeceptiveSiteButton.label"),
+ accessKey: gNavigatorBundle.getString("safebrowsing.notADeceptiveSiteButton.accessKey"),
+ callback() {
+ openUILinkIn(reportUrl, "tab");
+ }
+ };
+ }
} else if (reason === "unwanted") {
title = gNavigatorBundle.getString("safebrowsing.reportedUnwantedSite");
// There is no button for reporting errors since Google doesn't currently
// provide a URL endpoint for these reports.
}
let notificationBox = gBrowser.getNotificationBox();
let value = "blocked-badware-page";
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -570,21 +570,46 @@ var ClickEventHandler = {
onAboutBlocked(targetElement, ownerDoc) {
var reason = "phishing";
if (/e=malwareBlocked/.test(ownerDoc.documentURI)) {
reason = "malware";
} else if (/e=unwantedBlocked/.test(ownerDoc.documentURI)) {
reason = "unwanted";
}
+
+ let docShell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell);
+ let blockedInfo = {};
+ if (docShell.failedChannel) {
+ let classifiedChannel = docShell.failedChannel.
+ QueryInterface(Ci.nsIClassifiedChannel);
+ if (classifiedChannel) {
+ let httpChannel = docShell.failedChannel.QueryInterface(Ci.nsIHttpChannel);
+
+ let reportUri = httpChannel.URI.clone();
+
+ // Remove the query to avoid leaking sensitive data
+ if (reportUri instanceof Ci.nsIURL) {
+ reportUri.query = "";
+ }
+
+ blockedInfo = { list: classifiedChannel.matchedList,
+ provider: classifiedChannel.matchedProvider,
+ uri: reportUri.asciiSpec };
+ }
+ }
+
sendAsyncMessage("Browser:SiteBlockedError", {
location: ownerDoc.location.href,
reason,
elementId: targetElement.getAttribute("id"),
- isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView)
+ isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView),
+ blockedInfo
});
},
onAboutNetError(event, documentURI) {
let elmId = event.originalTarget.getAttribute("id");
if (elmId == "returnButton") {
sendAsyncMessage("Browser:SSLErrorGoBack", {});
return;
--- a/dom/ipc/PURLClassifier.ipdl
+++ b/dom/ipc/PURLClassifier.ipdl
@@ -3,27 +3,28 @@
*/
/* 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/. */
using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
include protocol PContent;
+include PURLClassifierInfo;
namespace mozilla {
namespace dom {
-union MaybeResult {
- nsresult;
+union MaybeInfo {
+ ClassifierInfo;
void_t;
};
protocol PURLClassifier
{
manager PContent;
child:
- async __delete__(MaybeResult status);
+ async __delete__(MaybeInfo info, nsresult errorCode);
};
} // namespace dom
} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/ipc/PURLClassifierInfo.ipdlh
@@ -0,0 +1,17 @@
+/* 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/. */
+
+namespace mozilla {
+namespace dom {
+
+struct ClassifierInfo {
+ nsCString list;
+ nsCString provider;
+ nsCString prefix;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+
--- a/dom/ipc/URLClassifierChild.cpp
+++ b/dom/ipc/URLClassifierChild.cpp
@@ -1,20 +1,24 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "URLClassifierChild.h"
#include "nsComponentManagerUtils.h"
+#include "nsIURI.h"
using namespace mozilla::dom;
mozilla::ipc::IPCResult
-URLClassifierChild::Recv__delete__(const MaybeResult& aResult)
+URLClassifierChild::Recv__delete__(const MaybeInfo& aInfo,
+ const nsresult& aResult)
{
MOZ_ASSERT(mCallback);
- if (aResult.type() == MaybeResult::Tnsresult) {
- mCallback->OnClassifyComplete(aResult.get_nsresult());
+ if (aInfo.type() == MaybeInfo::TClassifierInfo) {
+ mCallback->OnClassifyComplete(aResult, aInfo.get_ClassifierInfo().list(),
+ aInfo.get_ClassifierInfo().provider(),
+ aInfo.get_ClassifierInfo().prefix());
}
return IPC_OK();
}
--- a/dom/ipc/URLClassifierChild.h
+++ b/dom/ipc/URLClassifierChild.h
@@ -17,17 +17,18 @@ class URLClassifierChild : public PURLCl
{
public:
URLClassifierChild() = default;
void SetCallback(nsIURIClassifierCallback* aCallback)
{
mCallback = aCallback;
}
- mozilla::ipc::IPCResult Recv__delete__(const MaybeResult& aResult) override;
+ mozilla::ipc::IPCResult Recv__delete__(const MaybeInfo& aInfo,
+ const nsresult& aResult) override;
private:
~URLClassifierChild() = default;
nsCOMPtr<nsIURIClassifierCallback> mCallback;
};
} // namespace dom
--- a/dom/ipc/URLClassifierParent.cpp
+++ b/dom/ipc/URLClassifierParent.cpp
@@ -37,29 +37,37 @@ URLClassifierParent::StartClassify(nsIPr
// on its callback if some classification actually happens.
*aSuccess = false;
ClassificationFailed();
}
return IPC_OK();
}
nsresult
-URLClassifierParent::OnClassifyComplete(nsresult aRv)
+URLClassifierParent::OnClassifyComplete(nsresult aErrorCode,
+ const nsACString& aList,
+ const nsACString& aProvider,
+ const nsACString& aPrefix)
{
if (mIPCOpen) {
- Unused << Send__delete__(this, aRv);
+ ClassifierInfo info;
+ info.list() = aList;
+ info.prefix() = aPrefix;
+ info.provider() = aProvider;
+
+ Unused << Send__delete__(this, info, aErrorCode);
}
return NS_OK;
}
void
URLClassifierParent::ClassificationFailed()
{
if (mIPCOpen) {
- Unused << Send__delete__(this, void_t());
+ Unused << Send__delete__(this, void_t(), NS_ERROR_FAILURE);
}
}
void
URLClassifierParent::ActorDestroy(ActorDestroyReason aWhy)
{
mIPCOpen = false;
}
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -97,16 +97,17 @@ IPDL_SOURCES += [
'PDatePicker.ipdl',
'PDocumentRenderer.ipdl',
'PFilePicker.ipdl',
'PPluginWidget.ipdl',
'PProcessHangMonitor.ipdl',
'PScreenManager.ipdl',
'PTabContext.ipdlh',
'PURLClassifier.ipdl',
+ 'PURLClassifierInfo.ipdlh',
'ServiceWorkerConfiguration.ipdlh',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_TARGET'] == 'Darwin':
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5180,28 +5180,31 @@ pref("browser.safebrowsing.downloads.rem
pref("browser.safebrowsing.debug", false);
// The protocol version we communicate with google server.
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=");
+pref("browser.safebrowsing.provider.google.reportMalwareMistakeURL", "https://%LOCALE%.malware-error.mozilla.com/?hl=%LOCALE%&url=");
+
// Prefs for v4.
pref("browser.safebrowsing.provider.google4.pver", "4");
pref("browser.safebrowsing.provider.google4.lists", "goog-phish-proto,googpub-phish-proto,goog-malware-proto,goog-unwanted-proto");
pref("browser.safebrowsing.provider.google4.updateURL", "https://safebrowsing.googleapis.com/v4/threatListUpdates:fetch?$ct=application/x-protobuf&key=%GOOGLE_API_KEY%");
// Leave it empty until we roll out v4 hash completion feature. See Bug 1323856.
pref("browser.safebrowsing.provider.google4.gethashURL", "");
pref("browser.safebrowsing.provider.google4.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site=");
-
-pref("browser.safebrowsing.reportPhishMistakeURL", "https://%LOCALE%.phish-error.mozilla.com/?hl=%LOCALE%&url=");
+pref("browser.safebrowsing.provider.google4.reportPhishMistakeURL", "https://%LOCALE%.phish-error.mozilla.com/?hl=%LOCALE%&url=");
+pref("browser.safebrowsing.provider.google4.reportMalwareMistakeURL", "https://%LOCALE%.malware-error.mozilla.com/?hl=%LOCALE%&url=");
+
pref("browser.safebrowsing.reportPhishURL", "https://%LOCALE%.phish-report.mozilla.com/?hl=%LOCALE%&url=");
-pref("browser.safebrowsing.reportMalwareMistakeURL", "https://%LOCALE%.malware-error.mozilla.com/?hl=%LOCALE%&url=");
// The table and global pref for blocking plugin content
pref("browser.safebrowsing.blockedURIs.enabled", true);
pref("urlclassifier.blockedTable", "test-block-simple,mozplugin-block-digest256");
// The protocol version we communicate with mozilla server.
pref("browser.safebrowsing.provider.mozilla.pver", "2.2");
pref("browser.safebrowsing.provider.mozilla.lists", "base-track-digest256,mozstd-trackwhite-digest256,content-track-digest256,mozplugin-block-digest256,mozplugin2-block-digest256");
--- a/netwerk/base/moz.build
+++ b/netwerk/base/moz.build
@@ -27,16 +27,17 @@ XPIDL_SOURCES += [
'nsICacheInfoChannel.idl',
'nsICachingChannel.idl',
'nsICancelable.idl',
'nsICaptivePortalService.idl',
'nsIChannel.idl',
'nsIChannelEventSink.idl',
'nsIChannelWithDivertableParentListener.idl',
'nsIChildChannel.idl',
+ 'nsIClassifiedChannel.idl',
'nsIClassOfService.idl',
'nsIContentSniffer.idl',
'nsICryptoFIPSInfo.idl',
'nsICryptoHash.idl',
'nsICryptoHMAC.idl',
'nsIDashboard.idl',
'nsIDashboardEventNotifier.idl',
'nsIDeprecationWarner.idl',
--- a/netwerk/base/nsChannelClassifier.cpp
+++ b/netwerk/base/nsChannelClassifier.cpp
@@ -306,17 +306,18 @@ nsChannelClassifier::NotifyTrackingProte
void
nsChannelClassifier::Start()
{
nsresult rv = StartInternal();
if (NS_FAILED(rv)) {
// If we aren't getting a callback for any reason, assume a good verdict and
// make sure we resume the channel if necessary.
- OnClassifyComplete(NS_OK);
+ OnClassifyComplete(NS_OK, NS_LITERAL_CSTRING(""),NS_LITERAL_CSTRING(""),
+ NS_LITERAL_CSTRING(""));
}
}
nsresult
nsChannelClassifier::StartInternal()
{
// Should only be called in the parent process.
MOZ_ASSERT(XRE_IsParentProcess());
@@ -572,29 +573,43 @@ nsChannelClassifier::SameLoadingURI(nsID
}
bool equals = false;
nsresult rv = docURI->EqualsExceptRef(channelLoadingURI, &equals);
return NS_SUCCEEDED(rv) && equals;
}
// static
nsresult
-nsChannelClassifier::SetBlockedTrackingContent(nsIChannel *channel)
+nsChannelClassifier::SetBlockedContent(nsIChannel *channel,
+ nsresult aErrorCode,
+ const nsACString& aList,
+ const nsACString& aProvider,
+ const nsACString& aPrefix)
{
+ NS_ENSURE_ARG(!aList.IsEmpty());
+ NS_ENSURE_ARG(!aPrefix.IsEmpty());
+
// Can be called in EITHER the parent or child process.
nsCOMPtr<nsIParentChannel> parentChannel;
NS_QueryNotificationCallbacks(channel, parentChannel);
if (parentChannel) {
- // This channel is a parent-process proxy for a child process request. The
- // actual channel will be notified via the status passed to
- // nsIRequest::Cancel and do this for us.
+ // This channel is a parent-process proxy for a child process request.
+ // Tell the child process channel to do this instead.
+ parentChannel->SetClassifierMatchedInfo(aList, aProvider, aPrefix);
return NS_OK;
}
nsresult rv;
+ nsCOMPtr<nsIClassifiedChannel> classifiedChannel = do_QueryInterface(channel, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (classifiedChannel) {
+ classifiedChannel->SetMatchedInfo(aList, aProvider, aPrefix);
+ }
+
nsCOMPtr<mozIDOMWindowProxy> win;
nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, NS_OK);
rv = thirdPartyUtil->GetTopWindowForChannel(channel, getter_AddRefs(win));
NS_ENSURE_SUCCESS(rv, NS_OK);
auto* pwin = nsPIDOMWindowOuter::From(win);
nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
@@ -617,31 +632,42 @@ nsChannelClassifier::SetBlockedTrackingC
nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell, &rv);
NS_ENSURE_SUCCESS(rv, NS_OK);
uint32_t state = 0;
nsCOMPtr<nsISecureBrowserUI> securityUI;
docShell->GetSecurityUI(getter_AddRefs(securityUI));
if (!securityUI) {
return NS_OK;
}
- doc->SetHasTrackingContentBlocked(true);
securityUI->GetState(&state);
- state |= nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
+ if (aErrorCode == NS_ERROR_TRACKING_URI) {
+ doc->SetHasTrackingContentBlocked(true);
+ state |= nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
+ } else {
+ state |= nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
+ }
+
eventSink->OnSecurityChange(nullptr, state);
// Log a warning to the web console.
nsCOMPtr<nsIURI> uri;
channel->GetURI(getter_AddRefs(uri));
NS_ConvertUTF8toUTF16 spec(uri->GetSpecOrDefault());
const char16_t* params[] = { spec.get() };
+ const char* message = (aErrorCode == NS_ERROR_TRACKING_URI) ?
+ "TrackingUriBlocked" : "UnsafeUriBlocked";
+ nsCString category = (aErrorCode == NS_ERROR_TRACKING_URI) ?
+ NS_LITERAL_CSTRING("Tracking Protection") :
+ NS_LITERAL_CSTRING("Safe Browsing");
+
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
- NS_LITERAL_CSTRING("Tracking Protection"),
+ category,
doc,
nsContentUtils::eNECKO_PROPERTIES,
- "TrackingUriBlocked",
+ message,
params, ArrayLength(params));
return NS_OK;
}
nsresult
nsChannelClassifier::IsTrackerWhitelisted()
{
@@ -702,17 +728,20 @@ nsChannelClassifier::IsTrackerWhiteliste
}
LOG(("nsChannelClassifier[%p]: %s is not in the whitelist",
this, whitelistEntry.get()));
return NS_ERROR_TRACKING_URI;
}
NS_IMETHODIMP
-nsChannelClassifier::OnClassifyComplete(nsresult aErrorCode)
+nsChannelClassifier::OnClassifyComplete(nsresult aErrorCode,
+ const nsACString& aList,
+ const nsACString& aProvider,
+ const nsACString& aPrefix)
{
// Should only be called in the parent process.
MOZ_ASSERT(XRE_IsParentProcess());
if (aErrorCode == NS_ERROR_TRACKING_URI &&
NS_SUCCEEDED(IsTrackerWhitelisted())) {
LOG(("nsChannelClassifier[%p]:OnClassifyComplete tracker found "
"in whitelist so we won't block it", this));
@@ -769,22 +798,21 @@ nsChannelClassifier::OnClassifyComplete(
if (LOG_ENABLED()) {
nsCOMPtr<nsIURI> uri;
mChannel->GetURI(getter_AddRefs(uri));
LOG(("nsChannelClassifier[%p]: cancelling channel %p for %s "
"with error code %s", this, mChannel.get(),
uri->GetSpecOrDefault().get(), errorName.get()));
}
- // Channel will be cancelled (page element blocked) due to tracking.
+ // Channel will be cancelled (page element blocked) due to tracking
+ // protection or Safe Browsing.
// Do update the security state of the document and fire a security
// change event.
- if (aErrorCode == NS_ERROR_TRACKING_URI) {
- SetBlockedTrackingContent(mChannel);
- }
+ SetBlockedContent(mChannel, aErrorCode, aList, aProvider, aPrefix);
mChannel->Cancel(aErrorCode);
}
LOG(("nsChannelClassifier[%p]: resuming channel %p from "
"OnClassifyComplete", this, mChannel.get()));
mChannel->Resume();
}
--- a/netwerk/base/nsChannelClassifier.h
+++ b/netwerk/base/nsChannelClassifier.h
@@ -56,18 +56,22 @@ private:
// Checks that the channel was loaded by the URI currently loaded in aDoc
static bool SameLoadingURI(nsIDocument *aDoc, nsIChannel *aChannel);
nsresult ShouldEnableTrackingProtectionInternal(nsIChannel *aChannel,
bool *result);
bool AddonMayLoad(nsIChannel *aChannel, nsIURI *aUri);
public:
- // If we are blocking tracking content, update the corresponding flag in
- // the respective docshell and call nsISecurityEventSink::onSecurityChange.
- static nsresult SetBlockedTrackingContent(nsIChannel *channel);
+ // If we are blocking content, update the corresponding flag in the respective
+ // docshell and call nsISecurityEventSink::onSecurityChange.
+ static nsresult SetBlockedContent(nsIChannel *channel,
+ nsresult aErrorCode,
+ const nsACString& aList,
+ const nsACString& aProvider,
+ const nsACString& aPrefix);
static nsresult NotifyTrackingProtectionDisabled(nsIChannel *aChannel);
};
} // namespace net
} // namespace mozilla
#endif
new file mode 100644
--- /dev/null
+++ b/netwerk/base/nsIClassifiedChannel.idl
@@ -0,0 +1,47 @@
+/* -*- 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 "nsISupports.idl"
+
+/**
+ * nsIClassifiedChannel
+ *
+ * A channel may optionally implement this interface if it carries classified
+ * result information of channel classifier. The information contains, for
+ * example, the name of matched table and the name of matched provider.
+ */
+[scriptable, uuid(70cf6091-a1de-4aa8-8224-058f8964be31)]
+interface nsIClassifiedChannel : nsISupports
+{
+ /**
+ * Sets matched info of the classified channel.
+ *
+ * @param aList
+ * Name of the Safe Browsing list that matched (e.g. goog-phish-shavar).
+ * @param aProvider
+ * Name of the Safe Browsing provider that matched (e.g. google)
+ * @param aPrefix
+ * Hash prefix of URL that matched Safe Browsing list.
+ */
+ void setMatchedInfo(in ACString aList,
+ in ACString aProvider,
+ in ACString aPrefix);
+
+ /**
+ * Name of the list that matched
+ */
+ readonly attribute ACString matchedList;
+
+ /**
+ * Name of provider that matched
+ */
+ readonly attribute ACString matchedProvider;
+
+ /**
+ * Hash prefix of URL that matched
+ */
+ readonly attribute ACString matchedPrefix;
+
+};
--- a/netwerk/base/nsIParentChannel.idl
+++ b/netwerk/base/nsIParentChannel.idl
@@ -29,16 +29,29 @@ interface nsIParentChannel : nsIStreamLi
[noscript] void setParentListener(in HttpChannelParentListener listener);
/**
* Called to notify the HttpChannelChild that tracking protection was
* disabled for this load.
*/
[noscript] void notifyTrackingProtectionDisabled();
+ /**
+ * Called to set matched information when URL matches SafeBrowsing list.
+ * @param aList
+ * Name of the list that matched
+ * @param aProvider
+ * Name of provider that matched
+ * @param aPrefix
+ * String represents hash prefix that matched
+ */
+ [noscript] void setClassifierMatchedInfo(in ACString aList,
+ in ACString aProvider,
+ in ACString aPrefix);
+
/**
* Called to notify the HttpChannelChild that the resource being loaded
* is on the tracking protection list.
*/
[noscript] void notifyTrackingResource();
/**
* Called to invoke deletion of the IPC protocol.
--- a/netwerk/base/nsIURIClassifier.idl
+++ b/netwerk/base/nsIURIClassifier.idl
@@ -24,18 +24,27 @@ interface nsIURIClassifierCallback : nsI
* Called by the URI classifier service when it is done checking a URI.
*
* Clients are responsible for associating callback objects with classify()
* calls.
*
* @param aErrorCode
* The error code with which the channel should be cancelled, or
* NS_OK if the load should continue normally.
+ * @param aList
+ * Name of the list that matched
+ * @param aProvider
+ * Name of provider that matched
+ * @param aPrefix
+ * Hash prefix of URL that matched
*/
- void onClassifyComplete(in nsresult aErrorCode);
+ void onClassifyComplete(in nsresult aErrorCode,
+ in ACString aList,
+ in ACString aProvider,
+ in ACString aPrefix);
};
/**
* The URI classifier service checks a URI against lists of phishing
* and malware sites.
*/
[scriptable, uuid(596620cc-76e3-4133-9d90-360e59a794cf)]
interface nsIURIClassifier : nsISupports
--- a/netwerk/locales/en-US/necko.properties
+++ b/netwerk/locales/en-US/necko.properties
@@ -34,16 +34,17 @@ DirColMTime=Last Modified
DirFileLabel=File:
PhishingAuth=You are about to visit “%1$S”. This site may be attempting to trick you into thinking you are visiting a different site. Use extreme caution.
PhishingAuthAccept=I understand and will be very careful
SuperfluousAuth=You are about to log in to the site “%1$S” with the username “%2$S”, but the website does not require authentication. This may be an attempt to trick you.\n\nIs “%1$S” the site you want to visit?
AutomaticAuth=You are about to log in to the site “%1$S” with the username “%2$S”.
TrackingUriBlocked=The resource at “%1$S” was blocked because tracking protection is enabled.
+UnsafeUriBlocked=The resource at “%1$S” was blocked by Safe Browsing.
# LOCALIZATION NOTE (APIDeprecationWarning):
# %1$S is the deprecated API; %2$S is the API function that should be used.
APIDeprecationWarning=Warning: ‘%1$S’ deprecated, please use ‘%2$S’
# LOCALIZATION NOTE (nsICookieManagerDeprecated): don't localize originAttributes.
# %1$S is the deprecated API; %2$S is the interface suffix that the given deprecated API belongs to.
nsICookieManagerAPIDeprecated=“%1$S” is changed. Update your code and pass the correct originAttributes. Read more on MDN: https://developer.mozilla.org/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsICookieManager%2$S
--- a/netwerk/protocol/data/DataChannelParent.cpp
+++ b/netwerk/protocol/data/DataChannelParent.cpp
@@ -45,16 +45,25 @@ DataChannelParent::NotifyTrackingProtect
NS_IMETHODIMP
DataChannelParent::NotifyTrackingResource()
{
// Nothing to do.
return NS_OK;
}
NS_IMETHODIMP
+DataChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
+ const nsACString& aProvider,
+ const nsACString& aPrefix)
+{
+ // nothing to do
+ return NS_OK;
+}
+
+NS_IMETHODIMP
DataChannelParent::Delete()
{
// Nothing to do.
return NS_OK;
}
void
DataChannelParent::ActorDestroy(ActorDestroyReason why)
--- a/netwerk/protocol/ftp/FTPChannelParent.cpp
+++ b/netwerk/protocol/ftp/FTPChannelParent.cpp
@@ -566,16 +566,25 @@ FTPChannelParent::NotifyTrackingProtecti
NS_IMETHODIMP
FTPChannelParent::NotifyTrackingResource()
{
// One day, this should probably be filled in.
return NS_OK;
}
NS_IMETHODIMP
+FTPChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
+ const nsACString& aProvider,
+ const nsACString& aPrefix)
+{
+ // One day, this should probably be filled in.
+ return NS_OK;
+}
+
+NS_IMETHODIMP
FTPChannelParent::Delete()
{
if (mIPCClosed || !SendDeleteSelf())
return NS_ERROR_UNEXPECTED;
return NS_OK;
}
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -340,16 +340,17 @@ NS_INTERFACE_MAP_BEGIN(HttpBaseChannel)
NS_INTERFACE_MAP_ENTRY(nsIFormPOSTActionChannel)
NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2)
NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
NS_INTERFACE_MAP_ENTRY(nsITraceableChannel)
NS_INTERFACE_MAP_ENTRY(nsIPrivateBrowsingChannel)
NS_INTERFACE_MAP_ENTRY(nsITimedChannel)
NS_INTERFACE_MAP_ENTRY(nsIConsoleReportCollector)
NS_INTERFACE_MAP_ENTRY(nsIThrottledInputChannel)
+ NS_INTERFACE_MAP_ENTRY(nsIClassifiedChannel)
if (aIID.Equals(NS_GET_IID(HttpBaseChannel))) {
foundInterface = static_cast<nsIWritablePropertyBag*>(this);
} else
NS_INTERFACE_MAP_END_INHERITING(nsHashPropertyBag)
//-----------------------------------------------------------------------------
// HttpBaseChannel::nsIRequest
//-----------------------------------------------------------------------------
@@ -3377,16 +3378,51 @@ bool
HttpBaseChannel::SameOriginWithOriginalUri(nsIURI *aURI)
{
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
nsresult rv = ssm->CheckSameOriginURI(aURI, mOriginalURI, false);
return (NS_SUCCEEDED(rv));
}
+//-----------------------------------------------------------------------------
+// HttpBaseChannel::nsIClassifiedChannel
+
+NS_IMETHODIMP
+HttpBaseChannel::GetMatchedList(nsACString& aList)
+{
+ aList = mMatchedList;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::GetMatchedProvider(nsACString& aProvider)
+{
+ aProvider = mMatchedProvider;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::GetMatchedPrefix(nsACString& aPrefix)
+{
+ aPrefix = mMatchedPrefix;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HttpBaseChannel::SetMatchedInfo(const nsACString& aList,
+ const nsACString& aProvider,
+ const nsACString& aPrefix) {
+ NS_ENSURE_ARG(!aList.IsEmpty());
+
+ mMatchedList = aList;
+ mMatchedProvider = aProvider;
+ mMatchedPrefix = aPrefix;
+ return NS_OK;
+}
//-----------------------------------------------------------------------------
// HttpBaseChannel::nsITimedChannel
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpBaseChannel::SetTimingEnabled(bool enabled) {
mTimingEnabled = enabled;
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -24,16 +24,17 @@
#include "nsIFormPOSTActionChannel.h"
#include "nsIUploadChannel2.h"
#include "nsIProgressEventSink.h"
#include "nsIURI.h"
#include "nsIEffectiveTLDService.h"
#include "nsIStringEnumerator.h"
#include "nsISupportsPriority.h"
#include "nsIClassOfService.h"
+#include "nsIClassifiedChannel.h"
#include "nsIApplicationCache.h"
#include "nsIResumableChannel.h"
#include "nsITraceableChannel.h"
#include "nsILoadContext.h"
#include "nsILoadInfo.h"
#include "mozilla/net/NeckoCommon.h"
#include "nsThreadUtils.h"
#include "PrivateBrowsingChannel.h"
@@ -85,28 +86,30 @@ class HttpBaseChannel : public nsHashPro
, public nsIClassOfService
, public nsIResumableChannel
, public nsITraceableChannel
, public PrivateBrowsingChannel<HttpBaseChannel>
, public nsITimedChannel
, public nsIForcePendingChannel
, public nsIConsoleReportCollector
, public nsIThrottledInputChannel
+ , public nsIClassifiedChannel
{
protected:
virtual ~HttpBaseChannel();
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIUPLOADCHANNEL
NS_DECL_NSIFORMPOSTACTIONCHANNEL
NS_DECL_NSIUPLOADCHANNEL2
NS_DECL_NSITRACEABLECHANNEL
NS_DECL_NSITIMEDCHANNEL
NS_DECL_NSITHROTTLEDINPUTCHANNEL
+ NS_DECL_NSICLASSIFIEDCHANNEL
NS_DECLARE_STATIC_IID_ACCESSOR(HTTP_BASE_CHANNEL_IID)
HttpBaseChannel();
virtual nsresult Init(nsIURI *aURI, uint32_t aCaps, nsProxyInfo *aProxyInfo,
uint32_t aProxyResolveFlags,
nsIURI *aProxyURI,
@@ -595,16 +598,21 @@ protected:
nsCString mAvailableCachedAltDataType;
bool mForceMainDocumentChannel;
bool mIsTrackingResource;
nsID mChannelId;
nsString mIntegrityMetadata;
+
+ // Classified channel's matched information
+ nsCString mMatchedList;
+ nsCString mMatchedProvider;
+ nsCString mMatchedPrefix;
};
NS_DEFINE_STATIC_IID_ACCESSOR(HttpBaseChannel, HTTP_BASE_CHANNEL_IID)
// Share some code while working around C++'s absurd inability to handle casting
// of member functions between base/derived types.
// - We want to store member function pointer to call at resume time, but one
// such function--HandleAsyncAbort--we want to share between the
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -964,18 +964,33 @@ void
HttpChannelChild::DoOnStopRequest(nsIRequest* aRequest, nsresult aChannelStatus, nsISupports* aContext)
{
LOG(("HttpChannelChild::DoOnStopRequest [this=%p]\n", this));
MOZ_ASSERT(!mIsPending);
// NB: We use aChannelStatus here instead of mStatus because if there was an
// nsCORSListenerProxy on this request, it will override the tracking
// protection's return value.
- if (aChannelStatus == NS_ERROR_TRACKING_URI) {
- nsChannelClassifier::SetBlockedTrackingContent(this);
+ if (aChannelStatus == NS_ERROR_TRACKING_URI ||
+ aChannelStatus == NS_ERROR_MALWARE_URI ||
+ aChannelStatus == NS_ERROR_UNWANTED_URI ||
+ aChannelStatus == NS_ERROR_BLOCKED_URI ||
+ aChannelStatus == NS_ERROR_PHISHING_URI) {
+ nsCString list, provider, prefix;
+
+ nsresult rv = GetMatchedList(list);
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ rv = GetMatchedProvider(provider);
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ rv = GetMatchedPrefix(prefix);
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ nsChannelClassifier::SetBlockedContent(this, aChannelStatus, list, provider, prefix);
}
MOZ_ASSERT(!mOnStopRequestCalled,
"We should not call OnStopRequest twice");
// In theory mListener should not be null, but in practice sometimes it is.
MOZ_ASSERT(mListener);
if (mListener) {
@@ -1513,16 +1528,24 @@ HttpChannelChild::FlushedForDiversion()
// down. After it is set, no OnStart/OnData/OnStop callbacks should be
// received from the parent channel, nor dequeued from the ChannelEventQueue.
mFlushedForDiversion = true;
SendDivertComplete();
}
mozilla::ipc::IPCResult
+HttpChannelChild::RecvSetClassifierMatchedInfo(const ClassifierInfo& aInfo)
+{
+ SetMatchedInfo(aInfo.list(), aInfo.provider(), aInfo.prefix());
+ return IPC_OK();
+}
+
+
+mozilla::ipc::IPCResult
HttpChannelChild::RecvDivertMessages()
{
LOG(("HttpChannelChild::RecvDivertMessages [this=%p]\n", this));
MOZ_RELEASE_ASSERT(mDivertingToParent);
MOZ_RELEASE_ASSERT(mSuspendCount > 0);
// DivertTo() has been called on parent, so we can now start sending queued
// IPDL messages back to parent listener.
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -103,16 +103,17 @@ public:
void AddIPDLReference();
void ReleaseIPDLReference();
bool IsSuspended();
mozilla::ipc::IPCResult RecvNotifyTrackingProtectionDisabled() override;
mozilla::ipc::IPCResult RecvNotifyTrackingResource() override;
void FlushedForDiversion();
+ mozilla::ipc::IPCResult RecvSetClassifierMatchedInfo(const ClassifierInfo& aInfo) override;
protected:
mozilla::ipc::IPCResult RecvOnStartRequest(const nsresult& channelStatus,
const nsHttpResponseHead& responseHead,
const bool& useResponseHead,
const nsHttpHeaderArray& requestHeaders,
const bool& isFromCache,
const bool& cacheEntryAvailable,
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -36,16 +36,17 @@
#include "mozilla/BasePrincipal.h"
#include "nsCORSListenerProxy.h"
#include "nsIIPCSerializableInputStream.h"
#include "nsIPrompt.h"
#include "nsIWindowWatcher.h"
#include "nsIDocument.h"
#include "nsStringStream.h"
#include "nsQueryObject.h"
+#include "nsIURIClassifier.h"
using mozilla::BasePrincipal;
using namespace mozilla::dom;
using namespace mozilla::ipc;
namespace mozilla {
namespace net {
@@ -1359,16 +1360,32 @@ NS_IMETHODIMP
HttpChannelParent::NotifyTrackingProtectionDisabled()
{
if (!mIPCClosed)
Unused << SendNotifyTrackingProtectionDisabled();
return NS_OK;
}
NS_IMETHODIMP
+HttpChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
+ const nsACString& aProvider,
+ const nsACString& aPrefix)
+{
+ if (!mIPCClosed) {
+ ClassifierInfo info;
+ info.list() = aList;
+ info.prefix() = aPrefix;
+ info.provider() = aProvider;
+
+ Unused << SendSetClassifierMatchedInfo(info);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
HttpChannelParent::NotifyTrackingResource()
{
if (!mIPCClosed)
Unused << SendNotifyTrackingResource();
return NS_OK;
}
NS_IMETHODIMP
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -5,16 +5,17 @@
* 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 protocol PNecko;
include InputStreamParams;
include URIParams;
include PBackgroundSharedTypes;
include NeckoChannelParams;
+include PURLClassifierInfo;
include protocol PBlob; //FIXME: bug #792908
include "mozilla/net/NeckoMessageUtils.h";
using class nsHttpHeaderArray from "nsHttpHeaderArray.h";
using mozilla::net::NetAddr from "mozilla/net/DNS.h";
using struct mozilla::net::ResourceTimingStruct from "mozilla/net/TimingStruct.h";
@@ -163,16 +164,19 @@ child:
// Tell child to delete channel (all IPDL deletes must be done from child to
// avoid races: see bug 591708).
async DeleteSelf();
// Tell the child to issue a deprecation warning.
async IssueDeprecationWarning(uint32_t warning, bool asError);
+ // Tell the child information of matched URL againts SafeBrowsing list
+ async SetClassifierMatchedInfo(ClassifierInfo info);
+
both:
// After receiving this message, the parent also calls
// SendFinishInterceptedRedirect, and makes sure not to send any more messages
// after that. When receiving this message, the child will call
// Send__delete__() and complete the steps required to finish the redirect.
async FinishInterceptedRedirect();
async SetPriority(int16_t priority);
--- a/toolkit/components/url-classifier/Entries.h
+++ b/toolkit/components/url-classifier/Entries.h
@@ -12,20 +12,17 @@
#include "nsTArray.h"
#include "nsString.h"
#include "nsICryptoHash.h"
#include "nsNetUtil.h"
#include "nsIOutputStream.h"
#include "nsClassHashtable.h"
#include "nsDataHashtable.h"
-
-#if DEBUG
#include "plbase64.h"
-#endif
namespace mozilla {
namespace safebrowsing {
#define PREFIX_SIZE 4
#define COMPLETE_SIZE 32
// This is the struct that contains 4-byte hash prefixes.
@@ -80,24 +77,26 @@ struct SafebrowsingHash
bool operator!=(const self_type& aOther) const {
return Comparator::Compare(buf, aOther.buf) != 0;
}
bool operator<(const self_type& aOther) const {
return Comparator::Compare(buf, aOther.buf) < 0;
}
-#ifdef DEBUG
void ToString(nsACString& aStr) const {
uint32_t len = ((sHashSize + 2) / 3) * 4;
+
+ // Capacity should be one greater than length, because PL_Base64Encode
+ // will not be null-terminated, while nsCString requires it.
aStr.SetCapacity(len + 1);
PL_Base64Encode((char*)buf, sHashSize, aStr.BeginWriting());
aStr.BeginWriting()[len] = '\0';
+ aStr.SetLength(len);
}
-#endif
void ToHexString(nsACString& aStr) const {
static const char* const lut = "0123456789ABCDEF";
// 32 bytes is the longest hash
size_t len = 32;
aStr.SetCapacity(2 * len);
for (size_t i = 0; i < len; ++i) {
--- a/toolkit/components/url-classifier/SafeBrowsing.jsm
+++ b/toolkit/components/url-classifier/SafeBrowsing.jsm
@@ -141,44 +141,47 @@ this.SafeBrowsing = {
trackingProtectionWhitelists: [],
blockedLists: [],
updateURL: null,
gethashURL: null,
reportURL: null,
- getReportURL: function(kind, URI) {
+ getReportURL: function(kind, info) {
let pref;
switch (kind) {
case "Phish":
pref = "browser.safebrowsing.reportPhishURL";
break;
+
case "PhishMistake":
- pref = "browser.safebrowsing.reportPhishMistakeURL";
- break;
case "MalwareMistake":
- pref = "browser.safebrowsing.reportMalwareMistakeURL";
+ pref = "browser.safebrowsing.provider." + info.provider + ".report" + kind + "URL";
break;
default:
let err = "SafeBrowsing getReportURL() called with unknown kind: " + kind;
Components.utils.reportError(err);
throw err;
}
- let reportUrl = Services.urlFormatter.formatURLPref(pref);
- let pageUri = URI.clone();
+ if (!info.list || !info.uri) {
+ return null;
+ }
- // Remove the query to avoid including potentially sensitive data
- if (pageUri instanceof Ci.nsIURL)
- pageUri.query = '';
+ let reportUrl = Services.urlFormatter.formatURLPref(pref);
+ // formatURLPref might return "about:blank" if getting the pref fails
+ if (reportUrl == "about:blank") {
+ reportUrl = null;
+ }
- reportUrl += encodeURIComponent(pageUri.asciiSpec);
-
+ if (reportUrl) {
+ reportUrl += encodeURIComponent(info.uri);
+ }
return reportUrl;
},
observe: function(aSubject, aTopic, aData) {
// skip nextupdatetime and lastupdatetime
if (aData.indexOf("lastupdatetime") >= 0 || aData.indexOf("nextupdatetime") >= 0) {
return;
}
--- a/toolkit/components/url-classifier/nsIUrlClassifierDBService.idl
+++ b/toolkit/components/url-classifier/nsIUrlClassifierDBService.idl
@@ -225,8 +225,25 @@ interface nsIUrlClassifierLookupCallback
* @param results
* If this parameter is null, there were no results found.
* If not, it contains an array of nsUrlClassifierEntry objects
* with possible matches. The callee is responsible for freeing
* this array.
*/
void lookupComplete(in ResultArray results);
};
+
+/**
+ * This is an internal helper interface which is called after each
+ * classify completes to provide and handle a set of possible results,
+ * which the main thread may need to expand using an nsIURIClassifierCallback.
+ */
+[uuid(091adf98-28a5-473d-8dec-5b34b4e62496)]
+interface nsIUrlClassifierClassifyCallback : nsISupports
+{
+ /**
+ * The function is called each time the URL matches a Safe Browsing list
+ * The function could be called multiple times if URL matches multiple lists
+ *
+ */
+ void handleResult(in ACString aList,
+ in ACString aPrefix);
+};
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -1109,16 +1109,19 @@ nsUrlClassifierLookupCallback::HandleRes
return mCallback->HandleEvent(NS_LITERAL_CSTRING(""));
}
MOZ_ASSERT(mPendingCompletions == 0, "HandleResults() should never be "
"called while there are pending completions");
LOG(("nsUrlClassifierLookupCallback::HandleResults [%p, %" PRIuSIZE " results]",
this, mResults->Length()));
+ nsCOMPtr<nsIUrlClassifierClassifyCallback> classifyCallback =
+ do_QueryInterface(mCallback);
+
nsTArray<nsCString> tables;
// Build a stringified list of result tables.
for (uint32_t i = 0; i < mResults->Length(); i++) {
LookupResult& result = mResults->ElementAt(i);
// Leave out results that weren't confirmed, as their existence on
// the list can't be verified. Also leave out randomly-generated
// noise.
@@ -1133,16 +1136,22 @@ nsUrlClassifierLookupCallback::HandleRes
}
LOG(("Confirmed result %s from table %s",
result.PartialHashHex().get(), result.mTableName.get()));
if (tables.IndexOf(result.mTableName) == nsTArray<nsCString>::NoIndex) {
tables.AppendElement(result.mTableName);
}
+
+ if (classifyCallback) {
+ nsCString prefixString;
+ result.hash.fixedLengthPrefix.ToString(prefixString);
+ classifyCallback->HandleResult(result.mTableName, prefixString);
+ }
}
// TODO: Bug 1333328, Refactor cache miss mechanism for v2.
// Some parts of this gethash request generated no hits at all.
// Prefixes must have been removed from the database since our last update.
// Save the prefixes we checked to prevent repeated requests
// until the next update.
nsAutoPtr<PrefixArray> cacheMisses(new PrefixArray());
@@ -1168,48 +1177,123 @@ nsUrlClassifierLookupCallback::HandleRes
if (i != 0)
tableStr.Append(',');
tableStr.Append(tables[i]);
}
return mCallback->HandleEvent(tableStr);
}
+struct Provider {
+ nsCString name;
+ uint8_t priority;
+};
+
+// Order matters
+// Provider which is not included in this table has the lowest priority 0
+static const Provider kBuiltInProviders[] = {
+ { NS_LITERAL_CSTRING("mozilla"), 1 },
+ { NS_LITERAL_CSTRING("google4"), 2 },
+ { NS_LITERAL_CSTRING("google"), 3 },
+};
// -------------------------------------------------------------------------
-// Helper class for nsIURIClassifier implementation, translates table names
-// to nsIURIClassifier enums.
+// Helper class for nsIURIClassifier implementation, handle classify result and
+// send back to nsIURIClassifier
-class nsUrlClassifierClassifyCallback final : public nsIUrlClassifierCallback
+class nsUrlClassifierClassifyCallback final : public nsIUrlClassifierCallback,
+ public nsIUrlClassifierClassifyCallback
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIURLCLASSIFIERCALLBACK
+ NS_DECL_NSIURLCLASSIFIERCLASSIFYCALLBACK
explicit nsUrlClassifierClassifyCallback(nsIURIClassifierCallback *c)
: mCallback(c)
{}
private:
- ~nsUrlClassifierClassifyCallback() {}
+
+ struct ClassifyMatchedInfo {
+ nsCString table;
+ nsCString prefix;
+ Provider provider;
+ nsresult errorCode;
+ };
+
+ ~nsUrlClassifierClassifyCallback() {};
nsCOMPtr<nsIURIClassifierCallback> mCallback;
+ nsTArray<ClassifyMatchedInfo> mMatchedArray;
};
NS_IMPL_ISUPPORTS(nsUrlClassifierClassifyCallback,
- nsIUrlClassifierCallback)
+ nsIUrlClassifierCallback,
+ nsIUrlClassifierClassifyCallback)
NS_IMETHODIMP
nsUrlClassifierClassifyCallback::HandleEvent(const nsACString& tables)
{
nsresult response = TablesToResponse(tables);
- mCallback->OnClassifyComplete(response);
+ ClassifyMatchedInfo* matchedInfo = nullptr;
+
+ if (NS_FAILED(response)) {
+ // Filter all matched info which has correct response
+ // In the case multiple tables found, use the higher priority provider
+ nsTArray<ClassifyMatchedInfo> matches;
+ for (uint32_t i = 0; i < mMatchedArray.Length(); i++) {
+ if (mMatchedArray[i].errorCode == response &&
+ (!matchedInfo ||
+ matchedInfo->provider.priority < mMatchedArray[i].provider.priority)) {
+ matchedInfo = &mMatchedArray[i];
+ }
+ }
+ }
+
+ nsCString provider = matchedInfo ? matchedInfo->provider.name : EmptyCString();
+ nsCString prefix = matchedInfo ? matchedInfo->prefix : EmptyCString();
+ nsCString table = matchedInfo ? matchedInfo->table : EmptyCString();
+
+ mCallback->OnClassifyComplete(response, table, provider, prefix);
return NS_OK;
}
+NS_IMETHODIMP
+nsUrlClassifierClassifyCallback::HandleResult(const nsACString& aTable,
+ const nsACString& aPrefix)
+{
+ LOG(("nsUrlClassifierClassifyCallback::HandleResult [%p, table %s prefix %s]",
+ this, PromiseFlatCString(aTable).get(), PromiseFlatCString(aPrefix).get()));
+
+ if (NS_WARN_IF(aTable.IsEmpty()) || NS_WARN_IF(aPrefix.IsEmpty())) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ ClassifyMatchedInfo* matchedInfo = mMatchedArray.AppendElement();
+ matchedInfo->table = aTable;
+ matchedInfo->prefix = aPrefix;
+
+ nsCOMPtr<nsIUrlClassifierUtils> urlUtil =
+ do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
+
+ nsCString provider;
+ nsresult rv = urlUtil->GetProvider(aTable, provider);
+
+ matchedInfo->provider.name = NS_SUCCEEDED(rv) ? provider : EmptyCString();
+ matchedInfo->provider.priority = 0;
+ for (uint8_t i = 0; i < ArrayLength(kBuiltInProviders); i++) {
+ if (kBuiltInProviders[i].name.Equals(matchedInfo->provider.name)) {
+ matchedInfo->provider.priority = kBuiltInProviders[i].priority;
+ }
+ }
+ matchedInfo->errorCode = TablesToResponse(aTable);
+
+ return NS_OK;
+}
// -------------------------------------------------------------------------
// Proxy class implementation
NS_IMPL_ADDREF(nsUrlClassifierDBService)
NS_IMPL_RELEASE(nsUrlClassifierDBService)
NS_INTERFACE_MAP_BEGIN(nsUrlClassifierDBService)
// Only nsIURIClassifier is supported in the content process!
@@ -1498,16 +1582,17 @@ nsUrlClassifierDBService::Classify(nsIPr
if (!(mCheckMalware || mCheckPhishing || aTrackingProtectionEnabled ||
mCheckBlockedURIs)) {
*result = false;
return NS_OK;
}
RefPtr<nsUrlClassifierClassifyCallback> callback =
new nsUrlClassifierClassifyCallback(c);
+
if (!callback) return NS_ERROR_OUT_OF_MEMORY;
nsAutoCString tables;
BuildTables(aTrackingProtectionEnabled, tables);
nsresult rv = LookupURI(aPrincipal, tables, callback, false, result);
if (rv == NS_ERROR_MALFORMED_URI) {
*result = false;
--- a/uriloader/base/nsIWebProgressListener.idl
+++ b/uriloader/base/nsIWebProgressListener.idl
@@ -195,29 +195,33 @@ interface nsIWebProgressListener : nsISu
*
* STATE_LOADED_MIXED_DISPLAY_CONTENT
* Mixed display content has been loaded. State should be STATE_IS_BROKEN.
*/
const unsigned long STATE_BLOCKED_MIXED_DISPLAY_CONTENT = 0x00000100;
const unsigned long STATE_LOADED_MIXED_DISPLAY_CONTENT = 0x00000200;
/**
- * Tracking content flags
+ * Safe Browsing blocking content flags
*
* May be set in addition to the State security Flags, to indicate that
- * tracking content has been encountered.
+ * tracking or unsafe content has been encountered.
*
* STATE_BLOCKED_TRACKING_CONTENT
* Tracking content has been blocked from loading.
*
* STATE_LOADED_TRACKING_CONTENT
* Tracking content has been loaded.
+ *
+ * STATE_BLOCKED_UNSAFE_CONTENT
+ * Content which againts SafeBrowsing list has been blocked from loading.
*/
- const unsigned long STATE_BLOCKED_TRACKING_CONTENT = 0x00001000;
- const unsigned long STATE_LOADED_TRACKING_CONTENT = 0x00002000;
+ const unsigned long STATE_BLOCKED_TRACKING_CONTENT = 0x00001000;
+ const unsigned long STATE_LOADED_TRACKING_CONTENT = 0x00002000;
+ const unsigned long STATE_BLOCKED_UNSAFE_CONTENT = 0x00004000;
/**
* Security Strength Flags
*
* These flags describe the security strength and accompany STATE_IS_SECURE
* in a call to the onSecurityChange method. These flags are mutually
* exclusive.
*
--- a/uriloader/exthandler/nsExternalProtocolHandler.cpp
+++ b/uriloader/exthandler/nsExternalProtocolHandler.cpp
@@ -399,16 +399,24 @@ NS_IMETHODIMP nsExtProtocolChannel::SetP
}
NS_IMETHODIMP nsExtProtocolChannel::NotifyTrackingProtectionDisabled()
{
// nothing to do
return NS_OK;
}
+NS_IMETHODIMP nsExtProtocolChannel::SetClassifierMatchedInfo(const nsACString& aList,
+ const nsACString& aProvider,
+ const nsACString& aPrefix)
+{
+ // nothing to do
+ return NS_OK;
+}
+
NS_IMETHODIMP nsExtProtocolChannel::NotifyTrackingResource()
{
// nothing to do
return NS_OK;
}
NS_IMETHODIMP nsExtProtocolChannel::Delete()
{