зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1511111 - Add nsIUrlClassifierSkipListService to integrate url-classifier with RemoteSettings and pref list updates. r=Ehsan,baku,leplatrem
Differential Revision: https://phabricator.services.mozilla.com/D18597 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
c0b00b66d1
Коммит
fde7df021a
|
@ -220,6 +220,10 @@
|
|||
@RESPATH@/browser/actors/*
|
||||
@RESPATH@/actors/*
|
||||
|
||||
; Safe Browsing
|
||||
@RESPATH@/components/UrlClassifierSkipListService.js
|
||||
@RESPATH@/components/UrlClassifierSkipListService.manifest
|
||||
|
||||
; ANGLE GLES-on-D3D rendering library
|
||||
#ifdef MOZ_ANGLE_RENDERER
|
||||
@BINPATH@/libEGL.dll
|
||||
|
|
|
@ -139,6 +139,10 @@
|
|||
@BINPATH@/modules/*
|
||||
@BINPATH@/actors/*
|
||||
|
||||
; Safe Browsing
|
||||
@BINPATH@/components/UrlClassifierSkipListService.js
|
||||
@BINPATH@/components/UrlClassifierSkipListService.manifest
|
||||
|
||||
; [Browser Chrome Files]
|
||||
@BINPATH@/chrome/toolkit@JAREXT@
|
||||
@BINPATH@/chrome/toolkit.manifest
|
||||
|
|
|
@ -24,17 +24,12 @@ void OnPrefsChange(const char* aPrefName, nsTArray<nsCString>* aArray) {
|
|||
Classifier::SplitTables(value, *aArray);
|
||||
}
|
||||
|
||||
void OnPrefSkipChange(const char* aPrefName, nsCString* aValue) {
|
||||
MOZ_ASSERT(aValue);
|
||||
|
||||
Preferences::GetCString(aPrefName, *aValue);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(UrlClassifierFeatureBase)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIUrlClassifierFeature)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIUrlClassifierFeature)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIUrlClassifierSkipListObserver)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_ADDREF(UrlClassifierFeatureBase)
|
||||
|
@ -78,10 +73,13 @@ void UrlClassifierFeatureBase::InitializePreferences() {
|
|||
}
|
||||
}
|
||||
|
||||
if (!mPrefSkipHosts.IsEmpty()) {
|
||||
Preferences::RegisterCallbackAndCall(OnPrefSkipChange, mPrefSkipHosts,
|
||||
&mSkipHosts);
|
||||
nsCOMPtr<nsIUrlClassifierSkipListService> skipListService =
|
||||
do_GetService("@mozilla.org/url-classifier/skip-list-service;1");
|
||||
if (NS_WARN_IF(!skipListService)) {
|
||||
return;
|
||||
}
|
||||
|
||||
skipListService->RegisterAndRunSkipListObserver(mName, mPrefSkipHosts, this);
|
||||
}
|
||||
|
||||
void UrlClassifierFeatureBase::ShutdownPreferences() {
|
||||
|
@ -96,12 +94,19 @@ void UrlClassifierFeatureBase::ShutdownPreferences() {
|
|||
}
|
||||
}
|
||||
|
||||
if (!mPrefSkipHosts.IsEmpty()) {
|
||||
Preferences::UnregisterCallback(OnPrefSkipChange, mPrefSkipHosts,
|
||||
&mSkipHosts);
|
||||
nsCOMPtr<nsIUrlClassifierSkipListService> skipListService =
|
||||
do_GetService("@mozilla.org/url-classifier/skip-list-service;1");
|
||||
if (skipListService) {
|
||||
skipListService->UnregisterSkipListObserver(mName, this);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierFeatureBase::OnSkipListUpdate(const nsACString& aList) {
|
||||
mSkipHosts = aList;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierFeatureBase::GetName(nsACString& aName) {
|
||||
aName = mName;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define mozilla_net_UrlClassifierFeatureBase_h
|
||||
|
||||
#include "nsIUrlClassifierFeature.h"
|
||||
#include "nsIUrlClassifierSkipListService.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/AntiTrackingCommon.h"
|
||||
|
@ -15,7 +16,8 @@
|
|||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class UrlClassifierFeatureBase : public nsIUrlClassifierFeature {
|
||||
class UrlClassifierFeatureBase : public nsIUrlClassifierFeature,
|
||||
public nsIUrlClassifierSkipListObserver {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
|
@ -38,6 +40,9 @@ class UrlClassifierFeatureBase : public nsIUrlClassifierFeature {
|
|||
NS_IMETHOD
|
||||
GetSkipHostList(nsACString& aList) override;
|
||||
|
||||
NS_IMETHOD
|
||||
OnSkipListUpdate(const nsACString& aList) override;
|
||||
|
||||
protected:
|
||||
UrlClassifierFeatureBase(const nsACString& aName,
|
||||
const nsACString& aPrefBlacklistTables,
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
this.UrlClassifierSkipListService = function() {};
|
||||
|
||||
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "RemoteSettings",
|
||||
"resource://services-settings/remote-settings.js");
|
||||
|
||||
const COLLECTION_NAME = "url-classifier-skip-urls";
|
||||
|
||||
class Feature {
|
||||
constructor(name, prefName) {
|
||||
this.name = name;
|
||||
this.prefName = prefName;
|
||||
this.observers = new Set();
|
||||
this.prefValue = null;
|
||||
this.remoteEntries = null;
|
||||
|
||||
if (prefName) {
|
||||
this.prefValue = Services.prefs.getStringPref(this.prefName, null);
|
||||
Services.prefs.addObserver(prefName, this);
|
||||
}
|
||||
|
||||
RemoteSettings(COLLECTION_NAME).on("sync", event => {
|
||||
let { data: { current } } = event;
|
||||
this.remoteEntries = current;
|
||||
this.notifyObservers();
|
||||
});
|
||||
}
|
||||
|
||||
async addObserver(observer) {
|
||||
// If the remote settings list hasn't been populated yet we have to make sure
|
||||
// to do it before firing the first notification.
|
||||
if (!this.remoteEntries) {
|
||||
this.remoteEntries = await RemoteSettings(COLLECTION_NAME).get({ syncIfEmpty: false });
|
||||
}
|
||||
|
||||
this.observers.add(observer);
|
||||
this.notifyObservers(observer);
|
||||
}
|
||||
|
||||
removeObserver(observer) {
|
||||
this.observers.delete(observer);
|
||||
}
|
||||
|
||||
observe(subject, topic, data) {
|
||||
if (topic != "nsPref:changed" || data != this.prefName) {
|
||||
Cu.reportError(`Unexpected event ${topic} with ${data}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.prefValue = Services.prefs.getStringPref(this.prefName, null);
|
||||
this.notifyObservers();
|
||||
}
|
||||
|
||||
notifyObservers(observer = null) {
|
||||
let entries = [];
|
||||
if (this.prefValue) {
|
||||
entries = this.prefValue.split(",");
|
||||
}
|
||||
|
||||
for (let entry of this.remoteEntries) {
|
||||
if (entry.feature == this.name) {
|
||||
entries.push(entry.pattern.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
let entriesAsString = entries.join(",");
|
||||
if (observer) {
|
||||
observer.onSkipListUpdate(entriesAsString);
|
||||
} else {
|
||||
for (let obs of this.observers) {
|
||||
obs.onSkipListUpdate(entriesAsString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UrlClassifierSkipListService.prototype = Object.freeze({
|
||||
classID: Components.ID("{b9f4fd03-9d87-4bfd-9958-85a821750ddc}"),
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsIUrlClassifierSkipListService]),
|
||||
_xpcom_factory: XPCOMUtils.generateSingletonFactory(UrlClassifierSkipListService),
|
||||
|
||||
features: {},
|
||||
|
||||
registerAndRunSkipListObserver(feature, prefName, observer) {
|
||||
if (!this.features[feature]) {
|
||||
this.features[feature] = new Feature(feature, prefName);
|
||||
}
|
||||
this.features[feature].addObserver(observer);
|
||||
},
|
||||
|
||||
unregisterSkipListObserver(feature, observer) {
|
||||
if (!this.features[feature]) {
|
||||
return;
|
||||
}
|
||||
this.features[feature].removeObserver(observer);
|
||||
},
|
||||
});
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([UrlClassifierSkipListService]);
|
|
@ -0,0 +1,2 @@
|
|||
component {b9f4fd03-9d87-4bfd-9958-85a821750ddc} UrlClassifierSkipListService.js
|
||||
contract @mozilla.org/url-classifier/skip-list-service;1 {b9f4fd03-9d87-4bfd-9958-85a821750ddc}
|
|
@ -10,10 +10,16 @@ with Files('**'):
|
|||
XPIDL_SOURCES += [
|
||||
'nsIURIClassifier.idl',
|
||||
'nsIUrlClassifierFeature.idl',
|
||||
'nsIUrlClassifierSkipListService.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'url-classifier'
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'UrlClassifierSkipListService.js',
|
||||
'UrlClassifierSkipListService.manifest',
|
||||
]
|
||||
|
||||
DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = True
|
||||
DEFINES['GOOGLE_PROTOBUF_NO_STATIC_INITIALIZER'] = True
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ interface nsIUrlClassifierFeature : nsISupports
|
|||
out ACString aPrefTableName);
|
||||
|
||||
/**
|
||||
* Returns true if this host has to be ignored also if blacklisted.
|
||||
* Returns a comma-separated list of hosts to be ignored.
|
||||
*/
|
||||
readonly attribute ACString skipHostList;
|
||||
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/* 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"
|
||||
|
||||
/**
|
||||
* Observer for skip list updates.
|
||||
*/
|
||||
[scriptable, function, uuid(f7c918e5-94bf-4b6e-9758-ef7bdab6af7e)]
|
||||
interface nsIUrlClassifierSkipListObserver : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called by nsIUrlClassifierSkipListService when the skip list
|
||||
* for a designated feature changes and when the observer is first registered.
|
||||
*
|
||||
* @param aList
|
||||
* A comma-separated list of url patterns, intended to be parsed
|
||||
* by nsContentUtils::IsURIInList.
|
||||
*/
|
||||
void onSkipListUpdate(in ACString aList);
|
||||
};
|
||||
|
||||
/**
|
||||
* A service that monitors updates to the skip list of url-classifier
|
||||
* feature from sources such as a local pref and remote settings updates.
|
||||
*/
|
||||
[scriptable, uuid(75c3d1a3-e977-4079-9e27-b3b56bdb76ea)]
|
||||
interface nsIUrlClassifierSkipListService : nsISupports
|
||||
{
|
||||
/**
|
||||
* Register a new observer to skip list updates. When the observer is
|
||||
* registered it is called immediately once. Afterwards it will be called
|
||||
* whenever the specified pref changes or when remote settings for
|
||||
* url-classifier features updates.
|
||||
*
|
||||
* @param aFeature
|
||||
* The feature for which to observe the skip list.
|
||||
*
|
||||
* @param aPrefName
|
||||
* (Optional) A pref name to monitor. The pref must be of string
|
||||
* type and contain a comma-separated list of URL patterns.
|
||||
*
|
||||
* @param aObserver
|
||||
* An nsIUrlClassifierSkipListObserver object or function that
|
||||
* will receive updates to the skip list as a comma-separated
|
||||
* string. Will be called immediately with the current skip
|
||||
* list value.
|
||||
*/
|
||||
void registerAndRunSkipListObserver(in ACString aFeature,
|
||||
in ACString aPrefName,
|
||||
in nsIUrlClassifierSkipListObserver aObserver);
|
||||
|
||||
/**
|
||||
* Unregister an observer.
|
||||
*
|
||||
* @param aFeature
|
||||
* The feature for which to stop observing.
|
||||
*
|
||||
* @param aObserver
|
||||
* The nsIUrlClassifierSkipListObserver object to unregister.
|
||||
*/
|
||||
void unregisterSkipListObserver(in ACString aFeature,
|
||||
in nsIUrlClassifierSkipListObserver aObserver);
|
||||
|
||||
};
|
|
@ -0,0 +1,103 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
/* Unit tests for the nsIUrlClassifierSkipListService implementation. */
|
||||
|
||||
const {RemoteSettings} = ChromeUtils.import("resource://services-settings/remote-settings.js");
|
||||
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
const COLLECTION_NAME = "url-classifier-skip-urls";
|
||||
const FEATURE_NAME = "tracking-annotation-test";
|
||||
const FEATURE_PREF_NAME = "urlclassifier.tracking-annotation-test";
|
||||
|
||||
XPCOMUtils.defineLazyGlobalGetters(this, ["EventTarget"]);
|
||||
|
||||
do_get_profile();
|
||||
|
||||
class UpdateEvent extends EventTarget { }
|
||||
function waitForEvent(element, eventName) {
|
||||
return new Promise(function(resolve) {
|
||||
element.addEventListener(eventName, e => resolve(e.detail), {once: true});
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function test_list_changes() {
|
||||
let skipListService = Cc["@mozilla.org/url-classifier/skip-list-service;1"]
|
||||
.getService(Ci.nsIUrlClassifierSkipListService);
|
||||
|
||||
// Make sure we have a pref initially, since the skip list service requires it.
|
||||
Services.prefs.setStringPref(FEATURE_PREF_NAME, "");
|
||||
|
||||
let updateEvent = new UpdateEvent();
|
||||
let obs = data => {
|
||||
let event = new CustomEvent("update", { detail: data });
|
||||
updateEvent.dispatchEvent(event);
|
||||
};
|
||||
|
||||
let records = [{
|
||||
id: "1",
|
||||
last_modified: 100000000000000000001,
|
||||
feature: FEATURE_NAME,
|
||||
pattern: "example.com",
|
||||
}];
|
||||
|
||||
// Add some initial data.
|
||||
let collection = await RemoteSettings(COLLECTION_NAME).openCollection();
|
||||
await collection.create(records[0], { synced: true });
|
||||
await collection.db.saveLastModified(42);
|
||||
|
||||
let promise = waitForEvent(updateEvent, "update");
|
||||
|
||||
skipListService.registerAndRunSkipListObserver(FEATURE_NAME, FEATURE_PREF_NAME, obs);
|
||||
|
||||
let list = await promise;
|
||||
|
||||
Assert.equal(list, "example.com", "Has one item in the list");
|
||||
|
||||
records.push({
|
||||
id: "2",
|
||||
last_modified: 100000000000000000002,
|
||||
feature: FEATURE_NAME,
|
||||
pattern: "MOZILLA.ORG",
|
||||
}, {
|
||||
id: "3",
|
||||
last_modified: 100000000000000000003,
|
||||
feature: "some-other-feature",
|
||||
pattern: "noinclude.com",
|
||||
}, {
|
||||
last_modified: 100000000000000000004,
|
||||
feature: FEATURE_NAME,
|
||||
pattern: "*.example.org",
|
||||
});
|
||||
|
||||
promise = waitForEvent(updateEvent, "update");
|
||||
|
||||
await RemoteSettings(COLLECTION_NAME).emit("sync", { data: {current: records} });
|
||||
|
||||
list = await promise;
|
||||
|
||||
Assert.equal(list, "example.com,mozilla.org,*.example.org", "Has several items in the list");
|
||||
|
||||
promise = waitForEvent(updateEvent, "update");
|
||||
|
||||
Services.prefs.setStringPref(FEATURE_PREF_NAME, "test.com");
|
||||
|
||||
list = await promise;
|
||||
|
||||
Assert.equal(list, "test.com,example.com,mozilla.org,*.example.org", "Has several items in the list");
|
||||
|
||||
promise = waitForEvent(updateEvent, "update");
|
||||
|
||||
Services.prefs.setStringPref(FEATURE_PREF_NAME, "test.com,whatever.com,*.abc.com");
|
||||
|
||||
list = await promise;
|
||||
|
||||
Assert.equal(list, "test.com,whatever.com,*.abc.com,example.com,mozilla.org,*.example.org", "Has several items in the list");
|
||||
|
||||
skipListService.unregisterSkipListObserver(FEATURE_NAME, obs);
|
||||
|
||||
await collection.clear();
|
||||
});
|
|
@ -17,6 +17,7 @@ support-files =
|
|||
[test_prefixset.js]
|
||||
[test_threat_type_conversion.js]
|
||||
[test_provider_url.js]
|
||||
[test_SkipListService.js]
|
||||
[test_streamupdater.js]
|
||||
[test_digest256.js]
|
||||
[test_listmanager.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче