From 7a8de5732db3738eafff2225e21dc73625c7aed4 Mon Sep 17 00:00:00 2001 From: Bennett Cyphers Date: Tue, 13 Oct 2020 20:58:01 -0700 Subject: [PATCH 1/9] Pass tracker type to _recordPrevalence --- src/js/constants.js | 7 +++++++ src/js/heuristicblocking.js | 15 ++++++++++----- src/js/webrequest.js | 7 +++++-- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/js/constants.js b/src/js/constants.js index 5a77d0215c..dbc012cc3e 100644 --- a/src/js/constants.js +++ b/src/js/constants.js @@ -50,5 +50,12 @@ exports.BLOCKED_ACTIONS = new Set([ exports.USER_COOKIEBLOCK, ]); +exports.TRACKER_TYPES = Object.freeze({ + COOKIE: "cookie", + COOKIE_SHARE: "cookie_share", + SUPERCOOKIE: "supercookie", + FINGERPRINT: "fingerprint", +}) + return exports; })(); diff --git a/src/js/heuristicblocking.js b/src/js/heuristicblocking.js index 2e73d2d5da..185da27478 100644 --- a/src/js/heuristicblocking.js +++ b/src/js/heuristicblocking.js @@ -141,7 +141,8 @@ HeuristicBlocker.prototype = { // check if there are tracking cookies if (hasCookieTracking(details, request_origin)) { - self._recordPrevalence(request_host, request_origin, tab_origin); + self._recordPrevalence(request_host, request_origin, tab_origin, + constants.TRACKER_TYPES.COOKIE); return {}; } @@ -251,7 +252,8 @@ HeuristicBlocker.prototype = { log("Found high-entropy cookie share from", tab_origin, "to", request_host, ":", entropy, "bits\n cookie:", cookie.name, '=', cookie.value, "\n arg:", key, "=", value, "\n substring:", s); - this._recordPrevalence(request_host, request_origin, tab_origin); + this._recordPrevalence(request_host, request_origin, tab_origin, + constants.TRACKER_TYPES.COOKIE_SHARE); return; } } @@ -265,8 +267,9 @@ HeuristicBlocker.prototype = { * @param {String} tracker_fqdn The fully qualified domain name of the tracker * @param {String} tracker_origin Base domain of the third party tracker * @param {String} page_origin Base domain of page where tracking occurred + * @param {String} tracker_type the kind of tracking action that was observed */ - updateTrackerPrevalence: function (tracker_fqdn, tracker_origin, page_origin) { + updateTrackerPrevalence: function (tracker_fqdn, tracker_origin, page_origin, tracker_type) { // abort if we already made a decision for this fqdn let action = this.storage.getAction(tracker_fqdn); if (action != constants.NO_TRACKING && action != constants.ALLOW) { @@ -276,7 +279,8 @@ HeuristicBlocker.prototype = { this._recordPrevalence( tracker_fqdn, tracker_origin, - page_origin + page_origin, + tracker_type ); }, @@ -292,8 +296,9 @@ HeuristicBlocker.prototype = { * @param {String} tracker_fqdn The FQDN of the third party tracker * @param {String} tracker_origin Base domain of the third party tracker * @param {String} page_origin Base domain of page where tracking occurred + * @param {String} tracker_type the kind of tracking action that was observed */ - _recordPrevalence: function (tracker_fqdn, tracker_origin, page_origin) { + _recordPrevalence: function (tracker_fqdn, tracker_origin, page_origin, tracker_type) { var snitchMap = this.storage.getStore('snitch_map'); var firstParties = []; if (snitchMap.hasItem(tracker_origin)) { diff --git a/src/js/webrequest.js b/src/js/webrequest.js index bb7469bf38..2d8c693bb3 100644 --- a/src/js/webrequest.js +++ b/src/js/webrequest.js @@ -448,7 +448,8 @@ function recordSupercookie(tab_id, frame_url) { badger.heuristicBlocking.updateTrackerPrevalence( frame_host, window.getBaseDomain(frame_host), - window.getBaseDomain(page_host) + window.getBaseDomain(page_host), + constants.TRACKER_TYPES.SUPERCOOKIE ); } @@ -515,7 +516,9 @@ function recordFingerprinting(tabId, msg) { // Mark this as a strike badger.heuristicBlocking.updateTrackerPrevalence( - script_host, script_origin, window.getBaseDomain(document_host)); + script_host, script_origin, window.getBaseDomain(document_host), + constants.TRACKER_TYPES.FINGERPRINT + ); } } // This is a canvas write From 0cef67e239f53aff5a951e91a70020595f08f4e4 Mon Sep 17 00:00:00 2001 From: Bennett Cyphers Date: Wed, 14 Oct 2020 12:30:11 -0700 Subject: [PATCH 2/9] Call function to share tracker data with EFF when community learning is enabled - Create new SettingsMap variable, shareLearning, and default to false - pass tab_id to _recordPrevalence so that it can determine whether learning is enabled on a tab - update isLearningEnabled so that it returns true if either local or community learning is enabled; let _recordPrevalence figure out which kind of learning should be done - Create stub for function to share data with remote server --- src/js/background.js | 15 +++++++++++- src/js/constants.js | 4 ++++ src/js/heuristicblocking.js | 47 +++++++++++++++++++++++++++++-------- src/js/webrequest.js | 3 ++- 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/js/background.js b/src/js/background.js index 6cdee4c602..ef621289ee 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -715,6 +715,7 @@ Badger.prototype = { migrationLevel: 0, seenComic: false, sendDNTSignal: true, + shareLearning: false, showCounter: true, showIntroPage: true, showNonTrackingDomains: false, @@ -930,13 +931,25 @@ Badger.prototype = { * and if tab_id is for an incognito window, * is learning in incognito windows enabled? */ - isLearningEnabled(tab_id) { + isLocalLearningEnabled(tab_id) { return ( this.getSettings().getItem("learnLocally") && incognito.learningEnabled(tab_id) ); }, + /** + * Is any kind of learning (local or community) enabled on this tab? + * + * TODO: should community learning happen in incognito tabs? + */ + isLearningEnabled(tab_id) { + return ( + this.getSettings().getItem("shareLearning") || + this.isLocalLearningEnabled(tab_id) + ); + }, + /** * Check if widget replacement functionality is enabled. */ diff --git a/src/js/constants.js b/src/js/constants.js index dbc012cc3e..909b004d31 100644 --- a/src/js/constants.js +++ b/src/js/constants.js @@ -40,6 +40,10 @@ var exports = { TRACKING_THRESHOLD: 3, MAX_COOKIE_ENTROPY: 12, + // The max amount of time (in milliseconds) that PB will wait before sharing a + // tracking action with EFF for community learning + MAX_CL_WAIT_TIME: 30 * 60 * 1000; // one half hour + DNT_POLICY_CHECK_INTERVAL: 1000, // one second }; diff --git a/src/js/heuristicblocking.js b/src/js/heuristicblocking.js index 185da27478..42c06267b9 100644 --- a/src/js/heuristicblocking.js +++ b/src/js/heuristicblocking.js @@ -141,7 +141,7 @@ HeuristicBlocker.prototype = { // check if there are tracking cookies if (hasCookieTracking(details, request_origin)) { - self._recordPrevalence(request_host, request_origin, tab_origin, + self._recordPrevalence(request_host, request_origin, tab_origin, tab_id, constants.TRACKER_TYPES.COOKIE); return {}; } @@ -162,7 +162,7 @@ HeuristicBlocker.prototype = { chrome.cookies.getAll(config, function (cookies) { cookies = cookies.filter(cookie => !cookie.httpOnly); if (cookies.length >= 1) { - self.pixelCookieShareAccounting(tab_url, tab_origin, details.url, request_host, request_origin, cookies); + self.pixelCookieShareAccounting(tab_id, details.url, request_host, request_origin, cookies); } }); } @@ -180,8 +180,10 @@ HeuristicBlocker.prototype = { * @param cookies are the result of chrome.cookies.getAll() * @returns {*} */ - pixelCookieShareAccounting: function (tab_url, tab_origin, request_url, request_host, request_origin, cookies) { + pixelCookieShareAccounting: function (tab_id, request_url, request_host, request_origin, cookies) { let params = (new URL(request_url)).searchParams, + tab_origin = self.tabOrigins[tab_id], + tab_url = self.tabUrls[tab_id], TRACKER_ENTROPY_THRESHOLD = 33, MIN_STR_LEN = 8; @@ -252,8 +254,10 @@ HeuristicBlocker.prototype = { log("Found high-entropy cookie share from", tab_origin, "to", request_host, ":", entropy, "bits\n cookie:", cookie.name, '=', cookie.value, "\n arg:", key, "=", value, "\n substring:", s); - this._recordPrevalence(request_host, request_origin, tab_origin, - constants.TRACKER_TYPES.COOKIE_SHARE); + this._recordPrevalence( + request_host, request_origin, tab_origin, tab_id, + constants.TRACKER_TYPES.COOKIE_SHARE + ); return; } } @@ -267,9 +271,11 @@ HeuristicBlocker.prototype = { * @param {String} tracker_fqdn The fully qualified domain name of the tracker * @param {String} tracker_origin Base domain of the third party tracker * @param {String} page_origin Base domain of page where tracking occurred + * @param {Integer} tab_id the ID of the tab the user is in * @param {String} tracker_type the kind of tracking action that was observed */ - updateTrackerPrevalence: function (tracker_fqdn, tracker_origin, page_origin, tracker_type) { + updateTrackerPrevalence: function (tracker_fqdn, tracker_origin, page_origin, + tab_id, tracker_type) { // abort if we already made a decision for this fqdn let action = this.storage.getAction(tracker_fqdn); if (action != constants.NO_TRACKING && action != constants.ALLOW) { @@ -280,6 +286,7 @@ HeuristicBlocker.prototype = { tracker_fqdn, tracker_origin, page_origin, + tab_id, tracker_type ); }, @@ -298,7 +305,7 @@ HeuristicBlocker.prototype = { * @param {String} page_origin Base domain of page where tracking occurred * @param {String} tracker_type the kind of tracking action that was observed */ - _recordPrevalence: function (tracker_fqdn, tracker_origin, page_origin, tracker_type) { + _recordPrevalence: function (tracker_fqdn, tracker_origin, page_origin, tab_id, tracker_type) { var snitchMap = this.storage.getStore('snitch_map'); var firstParties = []; if (snitchMap.hasItem(tracker_origin)) { @@ -315,9 +322,17 @@ HeuristicBlocker.prototype = { return; // We already know about the presence of this tracker on the given domain } - // record that we've seen this tracker on this domain (in snitch map) - firstParties.push(page_origin); - snitchMap.setItem(tracker_origin, firstParties); + // If local learning is enabled, record that we've seen this tracker on this + // domain (in snitch map) + if (badger.isLocalLearningEnabled(tab_id)) { + firstParties.push(page_origin); + snitchMap.setItem(tracker_origin, firstParties); + } + + // If community learning is enabled, queue up a request to the EFF server + if (badger.getSettings().getItem("shareLearning")) { + this.maybeShareTracker(page_origin, tracker_fqdn, tracker_type); + } // ALLOW indicates this is a tracker still below TRACKING_THRESHOLD // (vs. NO_TRACKING for resources we haven't seen perform tracking yet). @@ -334,6 +349,18 @@ HeuristicBlocker.prototype = { this.blocklistOrigin(tracker_origin, tracker_fqdn); } } + + /** + * Flip a coin, then maybe share information about the tracker we just saw + */ + maybeShareTracker(page_origin, tracker_fqdn, tracker_type) { + // 50% chance of sharing any given tracking action + if (Math.random() < 0.5) { + setTimeout(function() { + // TODO + }, Math.floor(Math.random() * constants.MAX_CL_WAIT_TIME)); + } + } }; diff --git a/src/js/webrequest.js b/src/js/webrequest.js index 2d8c693bb3..4307d25f26 100644 --- a/src/js/webrequest.js +++ b/src/js/webrequest.js @@ -449,6 +449,7 @@ function recordSupercookie(tab_id, frame_url) { frame_host, window.getBaseDomain(frame_host), window.getBaseDomain(page_host), + tab_id, constants.TRACKER_TYPES.SUPERCOOKIE ); } @@ -517,7 +518,7 @@ function recordFingerprinting(tabId, msg) { // Mark this as a strike badger.heuristicBlocking.updateTrackerPrevalence( script_host, script_origin, window.getBaseDomain(document_host), - constants.TRACKER_TYPES.FINGERPRINT + tabId, constants.TRACKER_TYPES.FINGERPRINT ); } } From c7e0ed3a25404c94a772dfeebdd5698fec699531 Mon Sep 17 00:00:00 2001 From: Bennett Cyphers Date: Mon, 19 Oct 2020 10:55:23 -0600 Subject: [PATCH 3/9] Add reporting code to maybeShareTracker, fix syntax errors --- src/js/constants.js | 6 +++++- src/js/heuristicblocking.js | 23 ++++++++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/js/constants.js b/src/js/constants.js index 909b004d31..7314ba19f1 100644 --- a/src/js/constants.js +++ b/src/js/constants.js @@ -42,7 +42,11 @@ var exports = { // The max amount of time (in milliseconds) that PB will wait before sharing a // tracking action with EFF for community learning - MAX_CL_WAIT_TIME: 30 * 60 * 1000; // one half hour + MAX_CL_WAIT_TIME: 0, //30 * 60 * 1000, // one half hour + + // The probability that any given tracking action will be logged to the + // community server, as a float from 0.0 to 1.0 + CL_PROBABILITY: 1.0, DNT_POLICY_CHECK_INTERVAL: 1000, // one second }; diff --git a/src/js/heuristicblocking.js b/src/js/heuristicblocking.js index 42c06267b9..018e39d42b 100644 --- a/src/js/heuristicblocking.js +++ b/src/js/heuristicblocking.js @@ -331,7 +331,7 @@ HeuristicBlocker.prototype = { // If community learning is enabled, queue up a request to the EFF server if (badger.getSettings().getItem("shareLearning")) { - this.maybeShareTracker(page_origin, tracker_fqdn, tracker_type); + this.maybeShareTracker(page_origin, tracker_origin, tracker_type); } // ALLOW indicates this is a tracker still below TRACKING_THRESHOLD @@ -348,16 +348,29 @@ HeuristicBlocker.prototype = { log('blocklisting origin', tracker_fqdn); this.blocklistOrigin(tracker_origin, tracker_fqdn); } - } + }, /** * Flip a coin, then maybe share information about the tracker we just saw */ - maybeShareTracker(page_origin, tracker_fqdn, tracker_type) { + maybeShareTracker: function(page_origin, tracker_origin, tracker_type) { // 50% chance of sharing any given tracking action - if (Math.random() < 0.5) { + if (Math.random() < constants.CL_PROBABILITY) { setTimeout(function() { - // TODO + fetch("http://localhost:8080", { + method: "POST", + body: JSON.stringify({ + tracker_data: { + page_origin: page_origin, + tracker_origin: tracker_origin, + tracker_type: tracker_type, + } + }) + }).then(res => { + if (!res.ok) { + console.log("tracking action logging failed:", res); + } + }); }, Math.floor(Math.random() * constants.MAX_CL_WAIT_TIME)); } } From d19ab03309a4a794ac88b46e5cbb5a8c7825988f Mon Sep 17 00:00:00 2001 From: Bennett Cyphers Date: Mon, 19 Oct 2020 11:24:34 -0600 Subject: [PATCH 4/9] Remove call to updateTrackerPrevalence from BadgerStorage.merge Since _recordPrevalence now checks for local learning enabled internally, update BadgerStorage.merge() to modify the blocklist directly. --- src/js/heuristicblocking.js | 5 +---- src/js/storage.js | 22 +++++++++++++++++----- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/js/heuristicblocking.js b/src/js/heuristicblocking.js index 018e39d42b..87e818fc3d 100644 --- a/src/js/heuristicblocking.js +++ b/src/js/heuristicblocking.js @@ -340,11 +340,8 @@ HeuristicBlocker.prototype = { this.storage.setupHeuristicAction(tracker_fqdn, constants.ALLOW); this.storage.setupHeuristicAction(tracker_origin, constants.ALLOW); - // Blocking based on outbound cookies - var httpRequestPrevalence = firstParties.length; - // block the origin if it has been seen on multiple first party domains - if (httpRequestPrevalence >= constants.TRACKING_THRESHOLD) { + if (firstParties.length >= constants.TRACKING_THRESHOLD) { log('blocklisting origin', tracker_fqdn); this.blocklistOrigin(tracker_origin, tracker_fqdn); } diff --git a/src/js/storage.js b/src/js/storage.js index 9074a41d87..8e19d43f3d 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -645,12 +645,24 @@ BadgerStorage.prototype = { } else if (self.name == "snitch_map") { for (let tracker_origin in mapData) { let firstPartyOrigins = mapData[tracker_origin]; + let firstParties = []; + if (self.hasItem(tracker_origin)) { + firstParties = self.getItem(tracker_origin); + } + + // this uses the same logic as updateTrackerPrevalence, but ignores + // checks for local learning and community learning for (let i = 0; i < firstPartyOrigins.length; i++) { - badger.heuristicBlocking.updateTrackerPrevalence( - tracker_origin, - tracker_origin, - firstPartyOrigins[i] - ); + firstParties.push(firstPartyOrigins[i]); + self.setItem(tracker_origin, firstParties); + badger.storage.setupHeuristicAction(tracker_origin, constants.ALLOW); + + // block the origin if it has been seen on multiple first party domains + if (firstParties.length >= constants.TRACKING_THRESHOLD) { + log('blocklisting origin', tracker_origin); + badger.heuristicBlocking.blocklistOrigin(tracker_origin, + tracker_origin); + } } } } From e55e0a30c9f2ec8edd1f6f9492150909e76fea08 Mon Sep 17 00:00:00 2001 From: Bennett Cyphers Date: Mon, 19 Oct 2020 16:31:15 -0600 Subject: [PATCH 5/9] more local learning code is behind if statement --- src/js/heuristicblocking.js | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/js/heuristicblocking.js b/src/js/heuristicblocking.js index 87e818fc3d..d5b374d1b6 100644 --- a/src/js/heuristicblocking.js +++ b/src/js/heuristicblocking.js @@ -322,28 +322,28 @@ HeuristicBlocker.prototype = { return; // We already know about the presence of this tracker on the given domain } + // If community learning is enabled, queue up a request to the EFF server + if (badger.getSettings().getItem("shareLearning")) { + this.maybeShareTracker(page_origin, tracker_origin, tracker_type); + } + // If local learning is enabled, record that we've seen this tracker on this // domain (in snitch map) if (badger.isLocalLearningEnabled(tab_id)) { firstParties.push(page_origin); snitchMap.setItem(tracker_origin, firstParties); - } - // If community learning is enabled, queue up a request to the EFF server - if (badger.getSettings().getItem("shareLearning")) { - this.maybeShareTracker(page_origin, tracker_origin, tracker_type); - } - - // ALLOW indicates this is a tracker still below TRACKING_THRESHOLD - // (vs. NO_TRACKING for resources we haven't seen perform tracking yet). - // see https://github.com/EFForg/privacybadger/pull/1145#discussion_r96676710 - this.storage.setupHeuristicAction(tracker_fqdn, constants.ALLOW); - this.storage.setupHeuristicAction(tracker_origin, constants.ALLOW); + // ALLOW indicates this is a tracker still below TRACKING_THRESHOLD + // (vs. NO_TRACKING for resources we haven't seen perform tracking yet). + // see https://github.com/EFForg/privacybadger/pull/1145#discussion_r96676710 + this.storage.setupHeuristicAction(tracker_fqdn, constants.ALLOW); + this.storage.setupHeuristicAction(tracker_origin, constants.ALLOW); - // block the origin if it has been seen on multiple first party domains - if (firstParties.length >= constants.TRACKING_THRESHOLD) { - log('blocklisting origin', tracker_fqdn); - this.blocklistOrigin(tracker_origin, tracker_fqdn); + // block the origin if it has been seen on multiple first party domains + if (firstParties.length >= constants.TRACKING_THRESHOLD) { + log('blocklisting origin', tracker_fqdn); + this.blocklistOrigin(tracker_origin, tracker_fqdn); + } } }, @@ -351,7 +351,7 @@ HeuristicBlocker.prototype = { * Flip a coin, then maybe share information about the tracker we just saw */ maybeShareTracker: function(page_origin, tracker_origin, tracker_type) { - // 50% chance of sharing any given tracking action + // Some chance of sharing any given tracking action if (Math.random() < constants.CL_PROBABILITY) { setTimeout(function() { fetch("http://localhost:8080", { @@ -368,6 +368,7 @@ HeuristicBlocker.prototype = { console.log("tracking action logging failed:", res); } }); + // share info after a random delay }, Math.floor(Math.random() * constants.MAX_CL_WAIT_TIME)); } } From c0c146753628b7bc4e0bd79ae055f559a8313023 Mon Sep 17 00:00:00 2001 From: Bennett Cyphers Date: Mon, 19 Oct 2020 16:51:06 -0600 Subject: [PATCH 6/9] Add community learning to options page --- src/_locales/en_US/messages.json | 8 ++++++++ src/js/heuristicblocking.js | 14 ++++++++------ src/js/options.js | 12 ++++++++++++ src/skin/options.html | 11 +++++++++++ 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/_locales/en_US/messages.json b/src/_locales/en_US/messages.json index f10fe37701..59b9317474 100644 --- a/src/_locales/en_US/messages.json +++ b/src/_locales/en_US/messages.json @@ -173,6 +173,14 @@ "message": "Learn in Private/Incognito windows", "description": "Checkbox label on the general settings page" }, + "options_community_learning_setting": { + "message": "Enable community learning and share data about trackers", + "description": "Checkbox label on the general settings page" + }, + "options_community_learning_warning": { + "message": "When you enable community learning, your browser will share some information it collects about trackers with EFF. Specifically, each time your instance of Privacy Badger observes a particular tracker on a website that it has not seen before, it will share the origin (top-level domain +1) of both the tracker and the website, as well as the type of tracking action that it observed. EFF will only use this information for generating community learning lists, and will never share personal information with third parties. For more details, see our privacy policy: https://link.to.come", + "description": "Checkbox label on the general settings page" + }, "options_incognito_warning": { "message": "Enabling learning in Private/Incognito windows may leave traces of your private browsing history on your computer. By default, Privacy Badger will block trackers it already knows about in Private/Incognito windows, but it won't learn about new trackers. You might want to enable this option if a lot of your browsing happens in Private/Incognito windows.", "description": "Tooltip on the general settings page" diff --git a/src/js/heuristicblocking.js b/src/js/heuristicblocking.js index d5b374d1b6..effb6720e7 100644 --- a/src/js/heuristicblocking.js +++ b/src/js/heuristicblocking.js @@ -104,8 +104,10 @@ HeuristicBlocker.prototype = { * @param {Boolean} check_for_cookie_share whether to check for cookie sharing */ heuristicBlockingAccounting: function (details, check_for_cookie_share) { + let tab_id = details.tabId; + // ignore requests that are outside a tabbed window - if (details.tabId < 0 || !badger.isLearningEnabled(details.tabId)) { + if (tab_id < 0 || !badger.isLearningEnabled(tab_id)) { return {}; } @@ -115,12 +117,12 @@ HeuristicBlocker.prototype = { // if this is a main window request, update tab data and quit if (details.type == "main_frame") { - self.tabOrigins[details.tabId] = request_origin; - self.tabUrls[details.tabId] = details.url; + self.tabOrigins[tab_id] = request_origin; + self.tabUrls[tab_id] = details.url; return {}; } - let tab_origin = self.tabOrigins[details.tabId]; + let tab_origin = self.tabOrigins[tab_id]; // ignore first-party requests if (!tab_origin || !utils.isThirdPartyDomain(request_origin, tab_origin)) { @@ -147,10 +149,10 @@ HeuristicBlocker.prototype = { } // check for cookie sharing iff this is an image in the top-level frame, and the request URL has parameters - if (check_for_cookie_share && details.type == 'image' && details.frameId === 0 && details.url.indexOf('?') > -1) { + if (false && details.type == 'image' && details.frameId === 0 && details.url.indexOf('?') > -1) { // get all non-HttpOnly cookies for the top-level frame // and pass those to the cookie-share accounting function - let tab_url = self.tabUrls[details.tabId]; + let tab_url = self.tabUrls[tab_id]; let config = { url: tab_url diff --git a/src/js/options.js b/src/js/options.js index 7ca70080ed..1b2119516f 100644 --- a/src/js/options.js +++ b/src/js/options.js @@ -190,6 +190,18 @@ function loadOptions() { }); }); + $('#community-learning-checkbox') + .prop("checked", OPTIONS_DATA.settings.shareLearning) + .on("click", (event) => { + const enabled = $(event.currentTarget).prop("checked"); + chrome.runtime.sendMessage({ + type: "updateSettings", + data: { + shareLearning: enabled + } + }, function () {}); + }); + $('#show-nontracking-domains-checkbox') .prop("disabled", OPTIONS_DATA.settings.learnLocally ? false : "disabled") .prop("checked", ( diff --git a/src/skin/options.html b/src/skin/options.html index 7361139c74..f5f16af785 100644 --- a/src/skin/options.html +++ b/src/skin/options.html @@ -235,6 +235,17 @@

+
+ +