From 7d4784d921231b142793ccaceedfbd096ba4a4e6 Mon Sep 17 00:00:00 2001 From: Christopher Brind Date: Tue, 3 Dec 2019 18:37:43 +0000 Subject: [PATCH] update to use TDS (#533) --- Core/AppUrls.swift | 40 +- Core/CohortRequest.swift | 66 - Core/ContentBlockerLoader.swift | 40 +- Core/ContentBlockerRequest.swift | 16 +- Core/ContentBlockerStringCache.swift | 45 +- Core/DetectedTracker.swift | 44 +- Core/DisconnectMeStore.swift | 110 - Core/DisconnectMeTracker.swift | 68 - Core/DisconnectMeTrackersParser.swift | 82 - Core/EasylistStore.swift | 113 - Core/EntityMapping.swift | 58 +- Core/EntityMappingStore.swift | 55 - Core/FileStore.swift | 84 + Core/Instruments.swift | 21 +- Core/JavascriptLoader.swift | 10 - Core/Pixel.swift | 22 +- Core/PrevalenceStore.swift | 55 - Core/PrivacyPractices.swift | 6 +- Core/SiteRating.swift | 94 +- Core/StorageCache.swift | 74 +- Core/StorageCacheProvider.swift | 7 +- Core/SurrogateStore.swift | 89 - Core/TabInstrumentation.swift | 28 +- Core/TrackerData.swift | 124 + Core/TrackerDataManager.swift | 116 + Core/WKWebViewConfigurationExtension.swift | 138 +- Core/contentblocker.js | 594 +- Core/detection.js | 36 +- Core/disconnectme.js | 48 - Core/easylist-cached.js | 68 - Core/easylist-parsing.js | 57 - Core/messaging.js | 12 - Core/prevalence.json | 1 - Core/tlds.js | 36 - Core/trackerData.json | 17034 ++++++++++++++++ DuckDuckGo.xcodeproj/project.pbxproj | 120 +- DuckDuckGo/AppDelegate.swift | 5 + DuckDuckGo/Grade.swift | 4 + DuckDuckGo/NetworkLeaderboard.swift | 21 +- DuckDuckGo/PrivacyProtectionController.swift | 11 +- .../PrivacyProtectionFooterController.swift | 3 +- .../PrivacyProtectionHeaderController.swift | 8 +- ...otectionNetworkLeaderboardController.swift | 4 +- .../PrivacyProtectionOverviewController.swift | 2 +- ...yProtectionTrackerNetworksController.swift | 49 +- ...SiteRatingPrivacyProtectionExtension.swift | 12 +- DuckDuckGo/SiteRatingView.swift | 2 +- DuckDuckGo/TabViewController.swift | 54 +- DuckDuckGo/UserText.swift | 2 - DuckDuckGo/WhitelistManager.swift | 11 +- DuckDuckGoTests/APIRequestTests.swift | 4 +- .../ContentBlockerLoaderTests.swift | 88 +- .../ContentBlockerStringCacheTests.swift | 53 +- DuckDuckGoTests/DetectedTrackerTests.swift | 53 +- DuckDuckGoTests/DisconnectMeStoreTests.swift | 119 - .../DisconnectMeTrackerTests.swift | 82 - .../DisconnectMeTrackersParserTests.swift | 84 - .../DownloadedEntityMappingStoreTests.swift | 44 - DuckDuckGoTests/EasylistStoreTests.swift | 110 - .../EmbeddedPrevalenceStoreTests.swift | 37 - DuckDuckGoTests/EntityMappingTests.swift | 91 +- DuckDuckGoTests/FileStoreTests.swift | 87 + DuckDuckGoTests/InitHelpers.swift | 16 +- DuckDuckGoTests/KnownTrackerTests.swift | 49 + .../MockEntityMapping.swift | 29 +- DuckDuckGoTests/NetworkLeaderboardTests.swift | 21 + DuckDuckGoTests/PrivacyPracticesTests.swift | 40 +- ...rivacyProtectionTrackerNetworksTests.swift | 85 +- ...atingPrivacyProtectionExtensionTests.swift | 63 +- DuckDuckGoTests/SiteRatingTests.swift | 95 +- DuckDuckGoTests/SurrogateTests.swift | 83 - DuckDuckGoTests/TrackerDataManagerTests.swift | 109 + Instruments/Instruments.instrpkg | 68 +- performance/simple.html | 7 + performance/startServer.sh | 1 + performance/trackers.html | 47 + 76 files changed, 18622 insertions(+), 2742 deletions(-) delete mode 100644 Core/CohortRequest.swift delete mode 100644 Core/DisconnectMeStore.swift delete mode 100644 Core/DisconnectMeTracker.swift delete mode 100644 Core/DisconnectMeTrackersParser.swift delete mode 100644 Core/EasylistStore.swift delete mode 100644 Core/EntityMappingStore.swift create mode 100644 Core/FileStore.swift delete mode 100644 Core/PrevalenceStore.swift delete mode 100644 Core/SurrogateStore.swift create mode 100644 Core/TrackerData.swift create mode 100644 Core/TrackerDataManager.swift delete mode 100644 Core/disconnectme.js delete mode 100644 Core/easylist-cached.js delete mode 100644 Core/easylist-parsing.js delete mode 100644 Core/prevalence.json delete mode 100644 Core/tlds.js create mode 100644 Core/trackerData.json delete mode 100644 DuckDuckGoTests/DisconnectMeStoreTests.swift delete mode 100644 DuckDuckGoTests/DisconnectMeTrackerTests.swift delete mode 100644 DuckDuckGoTests/DisconnectMeTrackersParserTests.swift delete mode 100644 DuckDuckGoTests/DownloadedEntityMappingStoreTests.swift delete mode 100644 DuckDuckGoTests/EasylistStoreTests.swift delete mode 100644 DuckDuckGoTests/EmbeddedPrevalenceStoreTests.swift create mode 100644 DuckDuckGoTests/FileStoreTests.swift create mode 100644 DuckDuckGoTests/KnownTrackerTests.swift rename Core/blockerdata.js => DuckDuckGoTests/MockEntityMapping.swift (52%) delete mode 100644 DuckDuckGoTests/SurrogateTests.swift create mode 100644 DuckDuckGoTests/TrackerDataManagerTests.swift create mode 100644 performance/simple.html create mode 100755 performance/startServer.sh create mode 100644 performance/trackers.html diff --git a/Core/AppUrls.swift b/Core/AppUrls.swift index a80a5c4bbd..3efb35d785 100644 --- a/Core/AppUrls.swift +++ b/Core/AppUrls.swift @@ -28,24 +28,24 @@ public struct AppUrls { } static let base = ProcessInfo.processInfo.environment["BASE_URL", default: "https://duckduckgo.com"] + static let staticBase = "https://staticcdn.duckduckgo.com" + static let autocomplete = "\(base)/ac/" - static let disconnectMeBlockList = "\(base)/contentblocking.js?l=disconnect" - static let easylistBlockList = "\(base)/contentblocking.js?l=easylist" - static let easylistPrivacyBlockList = "\(base)/contentblocking.js?l=easyprivacy" - static let trackersWhitelist = "\(base)/contentblocking/trackers-whitelist.txt" + static let surrogates = "\(base)/contentblocking.js?l=surrogates" - static let entitylist = "\(base)/contentblocking.js?l=entitylist2" + static let temporaryWhitelist = "\(base)/contentblocking/trackers-whitelist-temporary.txt" + static let trackerDataSet = "\(staticBase)/trackerblocking/tds.json" + static let atb = "\(base)/atb.js\(devMode)" static let exti = "\(base)/exti/\(devMode)" static let feedback = "\(base)/feedback.js?type=app-feedback" static let faviconService = "\(base)/ip3/%@.ico" - static let staticBase = "https://staticcdn.duckduckgo.com" static let httpsBloomFilter = "\(staticBase)/https/https-mobile-bloom.bin?cache-version=1" static let httpsBloomFilterSpec = "\(staticBase)/https/https-mobile-bloom-spec.json?cache-version=1" static let httpsWhitelist = "\(staticBase)/https/https-mobile-whitelist.json?cache-version=1" static let httpsLookupService = "\(base)/smarter_encryption.js" - + static let pixelBase = ProcessInfo.processInfo.environment["PIXEL_BASE_URL", default: "https://improving.duckduckgo.com"] static let pixel = "\(pixelBase)/t/%@_ios_%@" } @@ -78,28 +78,16 @@ public struct AppUrls { return URL(string: Url.autocomplete)!.addParam(name: Param.search, value: text) } - public var disconnectMeBlockList: URL { - return URL(string: Url.disconnectMeBlockList)! - } - - public var easylistBlockList: URL { - return URL(string: Url.easylistBlockList)! - } - - public var easylistPrivacyBlockList: URL { - return URL(string: Url.easylistPrivacyBlockList)! - } - - public var trackersWhitelist: URL { - return URL(string: Url.trackersWhitelist)! - } - public var surrogates: URL { return URL(string: Url.surrogates)! } - - public var entitylist: URL { - return URL(string: Url.entitylist)! + + public var trackerDataSet: URL { + return URL(string: Url.trackerDataSet)! + } + + public var temporaryWhitelist: URL { + return URL(string: Url.temporaryWhitelist)! } public var feedback: URL { diff --git a/Core/CohortRequest.swift b/Core/CohortRequest.swift deleted file mode 100644 index 98510a78df..0000000000 --- a/Core/CohortRequest.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// CohortRequest.swift -// DuckDuckGo -// -// Copyright © 2017 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import Alamofire - -public typealias CohortRequestCompletion = (Cohort?, Error?) -> Swift.Void - -public class CohortRequest { - - private let appUrls = AppUrls() - private let parser = CohortParser() - - public init() {} - - public func execute(completion: @escaping CohortRequestCompletion) { - Logger.log(text: "Requesting cohort...") - Alamofire.request(appUrls.cohort) - .validate(statusCode: 200..<300) - .responseData(queue: DispatchQueue.global(qos: .utility)) { response in - Logger.log(text: "Cohort request completed with result \(response.result)") - self.handleResponse(response: response, completion: completion) - } - } - - private func handleResponse(response: Alamofire.DataResponse, completion: @escaping CohortRequestCompletion) { - if let error = response.result.error { - complete(completion, withCohort: nil, error: error) - return - } - - guard let data = response.result.value else { - complete(completion, withCohort: nil, error: ApiRequestError.noData) - return - } - - do { - let cohort = try self.parser.convert(fromJsonData: data) - complete(completion, withCohort: cohort, error: nil) - } catch { - complete(completion, withCohort: nil, error: error) - } - } - - private func complete(_ completion: @escaping CohortRequestCompletion, withCohort cohort: Cohort?, error: Error?) { - DispatchQueue.main.async { - completion(cohort, error) - } - } -} diff --git a/Core/ContentBlockerLoader.swift b/Core/ContentBlockerLoader.swift index 9e1fd989bc..302e5c8430 100644 --- a/Core/ContentBlockerLoader.swift +++ b/Core/ContentBlockerLoader.swift @@ -26,24 +26,23 @@ public class ContentBlockerLoader { private let httpsUpgradeStore: HTTPSUpgradeStore = HTTPSUpgradePersistence() private let etagStorage: BlockerListETagStorage + private let fileStore: FileStore private var newData = DataDict() private var etags = EtagDict() - init(etagStorage: BlockerListETagStorage = UserDefaultsETagStorage()) { + init(etagStorage: BlockerListETagStorage = UserDefaultsETagStorage(), fileStore: FileStore = FileStore()) { self.etagStorage = etagStorage + self.fileStore = fileStore } - func checkForUpdates(with store: EtagOOSCheckStore, - dataSource: ContentBlockerRemoteDataSource = ContentBlockerRequest()) -> Bool { + func checkForUpdates(dataSource: ContentBlockerRemoteDataSource = ContentBlockerRequest()) -> Bool { - EasylistStore.removeLegacyLists() - self.newData.removeAll() self.etags.removeAll() let semaphore = DispatchSemaphore(value: 0) - let numberOfRequests = startRequests(with: semaphore, store: store, dataSource: dataSource) + let numberOfRequests = startRequests(with: semaphore, dataSource: dataSource) for _ in 0 ..< numberOfRequests { semaphore.wait() @@ -57,8 +56,7 @@ public class ContentBlockerLoader { func applyUpdate(to cache: StorageCacheUpdating) { for (config, info) in newData { - if (cache.update(config, with: info)), - let etag = etags[config] { + if cache.update(config, with: info), let etag = etags[config] { etagStorage.set(etag: etag, for: config) } else { Logger.log(text: "Failed to apply update to \(config.rawValue)") @@ -67,13 +65,11 @@ public class ContentBlockerLoader { } private func startRequests(with semaphore: DispatchSemaphore, - store: EtagOOSCheckStore, dataSource: ContentBlockerRemoteDataSource) -> Int { - request(.entitylist, with: dataSource, store: store, semaphore) - request(.disconnectMe, with: dataSource, store: store, semaphore) - request(.trackersWhitelist, with: dataSource, store: store, semaphore) - request(.surrogates, with: dataSource, store: store, semaphore) + request(.surrogates, with: dataSource, semaphore) + request(.trackerDataSet, with: dataSource, semaphore) + request(.temporaryWhitelist, with: dataSource, semaphore) requestHttpsUpgrade(dataSource, semaphore) requestHttpsWhitelist(dataSource, semaphore) @@ -82,7 +78,6 @@ public class ContentBlockerLoader { fileprivate func request(_ configuration: ContentBlockerRequest.Configuration, with contentBlockerRequest: ContentBlockerRemoteDataSource, - store: EtagOOSCheckStore, _ semaphore: DispatchSemaphore) { contentBlockerRequest.request(configuration) { response in @@ -94,22 +89,7 @@ public class ContentBlockerLoader { let isCached = etag != nil && self.etagStorage.etag(for: configuration) == etag self.etags[configuration] = etag - if isCached { - switch configuration { - case .disconnectMe: - if !store.hasDisconnectMeData { - self.newData[configuration] = data - Pixel.fire(pixel: .etagStoreOOSWithDisconnectMeFix) - } - case .trackersWhitelist: - if !store.hasEasylistData { - self.newData[configuration] = data - Pixel.fire(pixel: .etagStoreOOSWithEasylistFix) - } - default: - break - } - } else { + if !isCached || !self.fileStore.hasData(forConfiguration: configuration) { self.newData[configuration] = data } diff --git a/Core/ContentBlockerRequest.swift b/Core/ContentBlockerRequest.swift index e436d2cf3f..09562faf2a 100644 --- a/Core/ContentBlockerRequest.swift +++ b/Core/ContentBlockerRequest.swift @@ -32,17 +32,14 @@ class ContentBlockerRequest: ContentBlockerRemoteDataSource { case error case success(etag: String?, data: Data) } - + enum Configuration: String { - case disconnectMe = "disconnectme" - case easylist = "easylist" - case easylistPrivacy = "easyprivacy" - case trackersWhitelist case httpsBloomFilterSpec case httpsBloomFilter case httpsWhitelist case surrogates - case entitylist = "entitylist2" + case trackerDataSet + case temporaryWhitelist } var requestCount = 0 @@ -71,15 +68,12 @@ class ContentBlockerRequest: ContentBlockerRemoteDataSource { let appUrls = AppUrls() switch list { - case .disconnectMe: return appUrls.disconnectMeBlockList - case .easylist: return appUrls.easylistBlockList - case .easylistPrivacy: return appUrls.easylistPrivacyBlockList case .httpsBloomFilterSpec: return appUrls.httpsBloomFilterSpec case .httpsBloomFilter: return appUrls.httpsBloomFilter case .httpsWhitelist: return appUrls.httpsWhitelist - case .trackersWhitelist: return appUrls.trackersWhitelist case .surrogates: return appUrls.surrogates - case .entitylist: return appUrls.entitylist + case .trackerDataSet: return appUrls.trackerDataSet + case .temporaryWhitelist: return appUrls.temporaryWhitelist } } } diff --git a/Core/ContentBlockerStringCache.swift b/Core/ContentBlockerStringCache.swift index d4e0d86d52..242bbb4949 100644 --- a/Core/ContentBlockerStringCache.swift +++ b/Core/ContentBlockerStringCache.swift @@ -21,50 +21,11 @@ import Foundation public class ContentBlockerStringCache { - struct Constants { - // bump the cache version if you know the cache should be invalidated on the next release - static let cacheVersion = 5 - static let cacheVersionKey = "com.duckduckgo.contentblockerstringcache.version" - } - - private var cacheDir: URL { + public static func removeLegacyData() { + let fileManager = FileManager.default let groupName = ContentBlockerStoreConstants.groupName - return fileManager.containerURL(forSecurityApplicationGroupIdentifier: groupName)!.appendingPathComponent("string-cache") - } - - private var fileManager: FileManager { - return FileManager.default - } - - public init(userDefaults: UserDefaults = UserDefaults.standard) { - let lastSeenVersion = userDefaults.integer(forKey: Constants.cacheVersionKey) - if lastSeenVersion < Constants.cacheVersion { - clearCache() - userDefaults.set(Constants.cacheVersion, forKey: Constants.cacheVersionKey) - } - } - - private func clearCache() { + let cacheDir = fileManager.containerURL(forSecurityApplicationGroupIdentifier: groupName)!.appendingPathComponent("string-cache") try? fileManager.removeItem(atPath: cacheDir.path) } - public func get(named name: String) -> String? { - return try? String(contentsOf: persistenceLocation(for: name), encoding: .utf8) - } - - public func put(name: String, value: String) { - try? value.write(to: persistenceLocation(for: name), atomically: true, encoding: .utf8) - } - - public func remove(named name: String) { - try? fileManager.removeItem(at: persistenceLocation(for: name)) - } - - private func persistenceLocation(for name: String) -> URL { - try? fileManager.createDirectory(at: cacheDir, withIntermediateDirectories: true, attributes: nil) - let location = cacheDir.appendingPathComponent(name) - Logger.log(text: "cache \(name) \(location)") - return location - } - } diff --git a/Core/DetectedTracker.swift b/Core/DetectedTracker.swift index f879bc93f4..bfc08170e8 100644 --- a/Core/DetectedTracker.swift +++ b/Core/DetectedTracker.swift @@ -19,49 +19,41 @@ import Foundation -// Populated with relevant info at the point of detection. If networkName or category are nil, they are genuinely not known. +// Populated with relevant info at the point of detection. public struct DetectedTracker { public let url: String + public let knownTracker: KnownTracker? + public let entity: Entity? public let blocked: Bool - public let networkName: String? - public let category: String? - - public init(url: String, networkName: String?, category: String?, blocked: Bool) { + + public init(url: String, knownTracker: KnownTracker?, entity: Entity?, blocked: Bool) { self.url = url - self.networkName = networkName - self.category = category + self.knownTracker = knownTracker + self.entity = entity self.blocked = blocked } public var domain: String? { return URL(string: url)?.host } - - public var isIpTracker: Bool { - return URL.isValidIpHost(domain ?? "") - } public var networkNameForDisplay: String { - return networkName ?? domain ?? url + return entity?.displayName ?? domain ?? url } } -extension DetectedTracker: Hashable { - - public func hash( into hasher: inout Hasher) { - hasher.combine(url) - hasher.combine(blocked) - hasher.combine(networkName) - hasher.combine(category) - } - +extension DetectedTracker: Hashable, Equatable { + public static func == (lhs: DetectedTracker, rhs: DetectedTracker) -> Bool { - return lhs.url == rhs.url - && lhs.blocked == rhs.blocked - && lhs.networkName == rhs.networkName - && lhs.category == rhs.category + return ((lhs.entity != nil || rhs.entity != nil) && lhs.entity?.displayName == rhs.entity?.displayName) + && lhs.domain ?? "" == rhs.domain ?? "" } - + + public func hash(into hasher: inout Hasher) { + hasher.combine(self.entity?.displayName) + hasher.combine(self.domain) + } + } diff --git a/Core/DisconnectMeStore.swift b/Core/DisconnectMeStore.swift deleted file mode 100644 index ec7ac68a0c..0000000000 --- a/Core/DisconnectMeStore.swift +++ /dev/null @@ -1,110 +0,0 @@ -// -// DisconnectMeStore.swift -// DuckDuckGo -// -// Copyright © 2017 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -public class DisconnectMeStore { - - struct CacheKeys { - static let disconnectJsonBanned = "disconnect-json-banned" - static let disconnectJsonAllowed = "disconnect-json-allowed" - } - - public static var persistenceLocation: URL { - let path = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: ContentBlockerStoreConstants.groupName) - return path!.appendingPathComponent("disconnectme.json") - } - - private lazy var stringCache = ContentBlockerStringCache() - - var hasData: Bool { - return !trackers.isEmpty - } - - public var trackers: [String: DisconnectMeTracker] = [:] - - var bannedTrackersJson: String { - if let cached = stringCache.get(named: CacheKeys.disconnectJsonBanned) { - return cached - } - if let json = try? convertToInjectableJson(trackers.filter(byCategory: DisconnectMeTracker.Category.banned)) { - stringCache.put(name: CacheKeys.disconnectJsonBanned, value: json) - return json - } - return "{}" - } - - var allowedTrackersJson: String { - if let cached = stringCache.get(named: CacheKeys.disconnectJsonAllowed) { - return cached - } - if let json = try? convertToInjectableJson(trackers.filter(byCategory: DisconnectMeTracker.Category.allowed)) { - stringCache.put(name: CacheKeys.disconnectJsonAllowed, value: json) - return json - } - return "{}" - } - - func persist(data: Data) throws { - Logger.log(items: "DisconnectMeStore", DisconnectMeStore.persistenceLocation) - try data.write(to: DisconnectMeStore.persistenceLocation, options: .atomic) - loadTrackers() - invalidateCache() - } - - public init() { - let spid = Instruments.shared.startTimedEvent(.loadingDisconnectMeStore) - stringCache = ContentBlockerStringCache() - loadTrackers() - Instruments.shared.endTimedEvent(for: spid) - } - - private func loadTrackers() { - do { - let data = try Data(contentsOf: DisconnectMeStore.persistenceLocation) - self.trackers = try DisconnectMeTrackersParser().convert(fromJsonData: data) - } catch { - Logger.log(items: "error parsing json for disconnect", error) - self.trackers = [:] - } - } - - private func invalidateCache() { - stringCache.remove(named: CacheKeys.disconnectJsonAllowed) - stringCache.remove(named: CacheKeys.disconnectJsonBanned) - } - - private func convertToInjectableJson(_ trackers: [String: DisconnectMeTracker]) throws -> String { - let simplifiedTrackers = trackers.mapValues { $0.parentUrl?.host } - let json = try JSONSerialization.data(withJSONObject: simplifiedTrackers, options: .prettyPrinted) - if let jsonString = String(data: json, encoding: .utf8) { - return jsonString - } - return "" - } - - public func networkNameAndCategory(forDomain domain: String) -> ( networkName: String?, category: String? ) { - let lowercasedDomain = domain.lowercased() - if let tracker = trackers.first(where: { lowercasedDomain == $0.key || lowercasedDomain.hasSuffix(".\($0.key)") })?.value { - return ( tracker.networkName, tracker.category?.rawValue ) - } - return ( nil, nil ) - } - -} diff --git a/Core/DisconnectMeTracker.swift b/Core/DisconnectMeTracker.swift deleted file mode 100644 index 07bf685fa0..0000000000 --- a/Core/DisconnectMeTracker.swift +++ /dev/null @@ -1,68 +0,0 @@ -// -// DisconnectMeTracker.swift -// DuckDuckGo -// -// Copyright © 2017 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -public class DisconnectMeTracker: NSObject { - - public enum Category: String { - case analytics = "Analytics" - case advertising = "Advertising" - case social = "Social" - case disconnect = "Disconnect" - case content = "Content" - - static let banned: [Category] = [.analytics, .advertising, .social] - static let allowed: [Category] = [.disconnect, .content] - static let all: [Category] = banned + allowed - } - - public let url: String - public let networkName: String? - public let parentUrl: URL? - public let category: Category? - - public init(url: String, networkName: String?, parentUrl: URL? = nil, category: Category? = nil) { - self.url = url - self.networkName = networkName - self.parentUrl = parentUrl - self.category = category - } - - public override func isEqual(_ other: Any?) -> Bool { - guard let other = other as? DisconnectMeTracker else { return false } - return url == other.url && networkName == other.networkName && category == other.category - } - - public override var hash: Int { - return "\(url) \(String(describing: networkName)) \(String(describing: category))".hash - } - -} - -extension Dictionary where Key: ExpressibleByStringLiteral, Value: DisconnectMeTracker { - - func filter(byCategory categoryFilter: [DisconnectMeTracker.Category]) -> [Key: Value] { - let filtered = filter { element -> Bool in - guard let category = element.value.category else { return false } - return categoryFilter.contains(category) - } - return filtered - } -} diff --git a/Core/DisconnectMeTrackersParser.swift b/Core/DisconnectMeTrackersParser.swift deleted file mode 100644 index ece7317091..0000000000 --- a/Core/DisconnectMeTrackersParser.swift +++ /dev/null @@ -1,82 +0,0 @@ -// -// DisconnectMeTrackersParser.swift -// DuckDuckGo -// -// Copyright © 2017 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -public struct DisconnectMeTrackersParser { - - func convert(fromJsonData data: Data) throws -> [String: DisconnectMeTracker] { - - guard let anyJson = try? JSONSerialization.jsonObject(with: data) else { - throw JsonError.invalidJson - } - - guard let json = anyJson as? [String: Any] else { - throw JsonError.invalidJson - } - - guard let jsonCategories = json["categories"] as? [String: [Any]] else { - throw JsonError.invalidJson - } - - var trackers = [String: DisconnectMeTracker]() - for (categoryName, jsonTrackers) in jsonCategories { - try parse(categoryName: categoryName, fromJson: jsonTrackers, into: &trackers) - } - return trackers - } - - private func parse(categoryName: String, fromJson jsonTrackers: [Any], into trackers: inout [String: DisconnectMeTracker]) throws { - let category = DisconnectMeTracker.Category.all.filter({ $0.rawValue == categoryName }).first - - for jsonTracker in jsonTrackers { - - guard let tracker = jsonTracker as? [String: Any] else { throw JsonError.invalidJson } - guard let networkName = tracker.keys.first else { throw JsonError.typeMismatch } - guard let network = tracker[networkName] as? [String: Any] else { throw JsonError.typeMismatch } - guard let baseUrl = baseUrl(fromNetwork: network) else { throw JsonError.typeMismatch } - guard let parentDomain = parseDomain(fromUrl: baseUrl) else { throw JsonError.typeMismatch } - guard let urls = network[baseUrl] as? [String] else { throw JsonError.typeMismatch } - - trackers[parentDomain] = DisconnectMeTracker(url: parentDomain, networkName: networkName, category: category) - for url in urls { - trackers[url] = DisconnectMeTracker(url: url, networkName: networkName, parentUrl: URL(string: baseUrl), category: category) - } - - } - } - - private func baseUrl(fromNetwork network: [String: Any]) -> String? { - if let baseUrl = network.keys.first, baseUrl != "dnt" { return baseUrl } - return network.keys.dropFirst().first - } - - private func parseDomain(fromUrl url: String) -> String? { - var urlToConvert = url - if !url.starts(with: "http") { - urlToConvert = "http://\(url)" - } - - guard let host = URL(string: urlToConvert)?.host else { - return nil - } - return host.replacingOccurrences(of: "www.", with: "") - } - -} diff --git a/Core/EasylistStore.swift b/Core/EasylistStore.swift deleted file mode 100644 index 4836d70847..0000000000 --- a/Core/EasylistStore.swift +++ /dev/null @@ -1,113 +0,0 @@ -// -// EasylistStore.swift -// DuckDuckGo -// -// Copyright © 2017 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -import Foundation - -class EasylistStore { - - struct CacheNames { - - static let easylist = "easylist" - static let easylistPrivacy = "easylist-privacy" - static let easylistWhitelist = "easylist-whitelist" - - } - - enum Easylist: String { - - case easylist - case easylistPrivacy - case easylistWhitelist - - } - - var hasData: Bool { - return exists(type: .easylistWhitelist) - } - - var easylistPrivacy: String? { - return load(.easylistPrivacy) - } - - var easylist: String? { - return load(.easylist) - } - - var easylistWhitelist: String? { - return load(.easylistWhitelist) - } - - func load(_ type: Easylist) -> String? { - guard let data = try? Data(contentsOf: persistenceLocation(type: type)) else { - return nil - } - return String(data: data, encoding: .utf8) - } - - func persistEasylist(data: Data) { - persistAndPrepareForInjection(data: data, as: .easylist, withCacheName: CacheNames.easylist) - } - - func persistEasylistPrivacy(data: Data) { - persistAndPrepareForInjection(data: data, as: .easylistPrivacy, withCacheName: CacheNames.easylistPrivacy) - } - - func persistEasylistWhitelist(data: Data) -> Bool { - return persistAndPrepareForInjection(data: data, as: .easylistWhitelist, withCacheName: CacheNames.easylistWhitelist) - } - - private func exists(type: Easylist) -> Bool { - return (try? persistenceLocation(type: type).checkResourceIsReachable()) ?? false - } - - @discardableResult - private func persistAndPrepareForInjection(data: Data, as type: Easylist, withCacheName cacheName: String) -> Bool { - guard let escapedEasylist = escapedString(from: data) else { return false } - do { - try persist(escapedEasylist: escapedEasylist, to: persistenceLocation(type: type)) - EasylistStore.invalidateCache(named: cacheName) - return true - } catch { - Logger.log(text: "failed to write \(type): \(error)") - return false - } - } - - private func persistenceLocation(type: Easylist) -> URL { - let path = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: ContentBlockerStoreConstants.groupName) - return path!.appendingPathComponent("\(type.rawValue).txt") - } - - private static func invalidateCache(named name: String) { - ContentBlockerStringCache().remove(named: name) - } - - private func persist(escapedEasylist: String, to: URL) throws { - try escapedEasylist.write(to: to, atomically: true, encoding: .utf8) - } - - static func removeLegacyLists() { - invalidateCache(named: CacheNames.easylist) - invalidateCache(named: CacheNames.easylistPrivacy) - } - - private func escapedString(from data: Data) -> String? { - return String(data: data, encoding: .utf8)?.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "`", with: "\\`") - } - -} diff --git a/Core/EntityMapping.swift b/Core/EntityMapping.swift index 4c30954b80..134df1641c 100644 --- a/Core/EntityMapping.swift +++ b/Core/EntityMapping.swift @@ -21,62 +21,10 @@ import Foundation public class EntityMapping { - private struct Entity: Decodable { - - let properties: [String]? - let resources: [String]? - - } - - private let entities: [String: String] - - public init(store: EntityMappingStore) { - - if let data = store.load(), let entities = try? EntityMapping.process(data) { - self.entities = entities - } else { - self.entities = [:] - } - - } - - func findEntity(forHost host: String) -> String? { - var parts = host.split(separator: ".") - - while !parts.isEmpty { - if let entity = entities[parts.joined(separator: ".")] { return entity } - parts = Array(parts.dropFirst()) - } - - return nil - } - - private static func process(_ data: Data) throws -> [String: String] { - if let decoded = decode(data) { - var entities = [String: String]() - - decoded.forEach { - let entityName = $0.key - $0.value.properties?.forEach { - entities[$0] = entityName - } - $0.value.resources?.forEach { - entities[$0] = entityName - } - } - - return entities - } - return [:] - } + public init() { } - private static func decode(_ data: Data) -> [String: Entity]? { - do { - return try JSONDecoder().decode([String: Entity].self, from: data) - } catch { - Logger.log(items: error) - } - return nil + public func findEntity(forHost host: String) -> Entity? { + return TrackerDataManager.shared.findEntity(forHost: host) } } diff --git a/Core/EntityMappingStore.swift b/Core/EntityMappingStore.swift deleted file mode 100644 index 433bc26d15..0000000000 --- a/Core/EntityMappingStore.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// EntityMappingStore.swift -// DuckDuckGo -// -// Copyright © 2017 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -public protocol EntityMappingStore { - - func load() -> Data? - - func persist(data: Data) -> Bool - -} - -public class DownloadedEntityMappingStore: EntityMappingStore { - - static let filename = "entitylist2.json" - - public init() { } - - public func load() -> Data? { - return try? Data(contentsOf: persistenceLocation()) - } - - public func persist(data: Data) -> Bool { - do { - try data.write(to: persistenceLocation(), options: .atomic) - return true - } catch { - Logger.log(items: error) - return false - } - } - - private func persistenceLocation() -> URL { - let path = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: ContentBlockerStoreConstants.groupName) - return path!.appendingPathComponent(DownloadedEntityMappingStore.filename) - } - -} diff --git a/Core/FileStore.swift b/Core/FileStore.swift new file mode 100644 index 0000000000..8bf7e5dfd8 --- /dev/null +++ b/Core/FileStore.swift @@ -0,0 +1,84 @@ +// +// FileStore.swift +// DuckDuckGo +// +// Copyright © 2018 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +public class FileStore { + + struct Constants { + static let legacyFiles = [ + "disconnectme.json", + "easylistWhitelist.txt", + "entitylist2.json", + "surrogate.js" + ] + } + + private let groupIdentifier: String = ContentBlockerStoreConstants.groupName + + public init() { } + + /// Remove all legacy data. + /// + /// Removes files listed in `Constants.legacyFiles` + /// + public func removeLegacyData() { + let path = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: groupIdentifier) + Constants.legacyFiles.forEach { + try? FileManager.default.removeItem(at: path!.appendingPathComponent($0)) + } + } + + func persist(_ data: Data?, forConfiguration config: ContentBlockerRequest.Configuration) -> Bool { + guard let data = data else { return false } + do { + try data.write(to: persistenceLocation(forConfiguration: config)) + return true + } catch { + Pixel.fire(pixel: .fileStoreWriteFailed, error: error, withAdditionalParameters: ["config": config.rawValue ]) + return false + } + } + + func loadAsString(forConfiguration config: ContentBlockerRequest.Configuration) -> String? { + return try? String(contentsOf: persistenceLocation(forConfiguration: config)) + } + + func loadAsData(forConfiguration config: ContentBlockerRequest.Configuration) -> Data? { + do { + return try Data(contentsOf: persistenceLocation(forConfiguration: config)) + } catch { + let nserror = error as NSError + if nserror.domain != NSCocoaErrorDomain || nserror.code != NSFileReadNoSuchFileError { + Pixel.fire(pixel: .trackerDataCouldNotBeLoaded, error: error) + } + return nil + } + } + + func hasData(forConfiguration config: ContentBlockerRequest.Configuration) -> Bool { + return FileManager.default.fileExists(atPath: persistenceLocation(forConfiguration: config).path) + } + + func persistenceLocation(forConfiguration config: ContentBlockerRequest.Configuration) -> URL { + let path = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: groupIdentifier) + return path!.appendingPathComponent(config.rawValue) + } + +} diff --git a/Core/Instruments.swift b/Core/Instruments.swift index d24ad7fdd1..54a61ee677 100644 --- a/Core/Instruments.swift +++ b/Core/Instruments.swift @@ -31,26 +31,20 @@ public class Instruments { case tabInitialisation case clearingData + + case injectScripts } static public let shared = Instruments() - private var eventsLog: OSLog? + static var eventsLog = OSLog(subsystem: "com.duckduckgo.instrumentation", category: "Events") - private init() { - if #available(iOSApplicationExtension 12.0, *) { - eventsLog = OSLog(subsystem: "com.duckduckgo.instrumentation", - category: "Events") - } - } - public func startTimedEvent(_ event: TimedEvent, info: String? = nil) -> Any? { - if #available(iOSApplicationExtension 12.0, *), - let log = eventsLog { - let id = OSSignpostID(log: log) + if #available(iOSApplicationExtension 12.0, *) { + let id = OSSignpostID(log: Instruments.eventsLog) os_signpost(.begin, - log: log, + log: Instruments.eventsLog, name: "Timed Event", signpostID: id, "Event: %@ info: %@", event.rawValue, info ?? "") @@ -61,10 +55,9 @@ public class Instruments { public func endTimedEvent(for spid: Any?, result: String? = nil) { if #available(iOSApplicationExtension 12.0, *), - let log = eventsLog, let id = spid as? OSSignpostID { os_signpost(.end, - log: log, + log: Instruments.eventsLog, name: "Timed Event", signpostID: id, "Result: %@", result ?? "") diff --git a/Core/JavascriptLoader.swift b/Core/JavascriptLoader.swift index 73f9fd0256..2be2021cd9 100644 --- a/Core/JavascriptLoader.swift +++ b/Core/JavascriptLoader.swift @@ -25,20 +25,10 @@ public class JavascriptLoader { public enum Script: String { case findinpage case document - case disconnectme case contentblocker - case apbfilter = "abp-filter-parser-packed" - case apbfilterES2015 = "abp-filter-parser-packed-es2015" - case tlds case messaging case debugMessagingEnabled = "debug-messaging-enabled" case debugMessagingDisabled = "debug-messaging-disabled" - case bloom = "bloom-filter-packed" - case bloomES2015 = "bloom-filter-packed-es2015" - case cachedEasylist = "easylist-cached" - case easylistParsing = "easylist-parsing" - case blockerData = "blockerdata" - case surrogate case detection } diff --git a/Core/Pixel.swift b/Core/Pixel.swift index f16e9bbe8a..e490b5c28d 100644 --- a/Core/Pixel.swift +++ b/Core/Pixel.swift @@ -168,8 +168,9 @@ public enum PixelName: String { case notificationOptIn = "m_ne" case notificationOptOut = "m_nd" - case etagStoreOOSWithDisconnectMeFix = "m_d_dcf_oos" - case etagStoreOOSWithEasylistFix = "m_d_elf_oos" + case brokenSiteReported = "m_bsr" + + // debug pixels: case dbMigrationError = "m_d_dbme" case dbRemovalError = "m_d_dbre" @@ -179,7 +180,12 @@ public enum PixelName: String { case dbSaveBloomFilterError = "m_d_dbsb" case configurationFetchInfo = "m_d_cfgfetch" - case brokenSiteReported = "m_bsr" + + case trackerDataParseFailed = "m_d_tds_p" + case trackerDataReloadFailed = "m_d_tds_r" + case trackerDataCouldNotBeLoaded = "m_d_tds_l" + case fileStoreWriteFailed = "m_d_fswf" + } public struct PixelParameters { @@ -232,12 +238,12 @@ public class Pixel { extension Pixel { - public static func fire(pixel: PixelName, error: Error) { + public static func fire(pixel: PixelName, error: Error, withAdditionalParameters params: [String: String?] = [:]) { let nsError = error as NSError - - let params: [String: String?] = ["e": "\(nsError.code)", "d": nsError.domain] - - fire(pixel: pixel, withAdditionalParameters: params) + var newParams = params + newParams["e"] = "\(nsError.code)" + newParams["d"] = nsError.domain + fire(pixel: pixel, withAdditionalParameters: newParams) } } diff --git a/Core/PrevalenceStore.swift b/Core/PrevalenceStore.swift deleted file mode 100644 index b17c642896..0000000000 --- a/Core/PrevalenceStore.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// PrevalenceStore.swift -// DuckDuckGo -// -// Copyright © 2017 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -public protocol PrevalenceStore { - - var prevalences: [String: Double] { get } - - func isMajorNetwork(named: String?) -> Bool - -} - -public class EmbeddedPrevalenceStore: PrevalenceStore { - - struct Constants { - static let fileName = "prevalence.json" - static let majorNetworkPrevalence = 7.0 - } - - public private(set) var prevalences: [String: Double] - - public init() { - let bundle = Bundle(for: type(of: self)) - let fileLoader = FileLoader() - guard let data = try? fileLoader.load(fileName: Constants.fileName, fromBundle: bundle) else { - fatalError("Unable to load \(Constants.fileName) from bundle \(bundle)") - } - guard let prevalences = try? JSONDecoder().decode([String: Double].self, from: data) else { - fatalError("Unable to json decode \(Constants.fileName)") - } - self.prevalences = prevalences - } - - public func isMajorNetwork(named: String?) -> Bool { - return prevalences[named ?? ""] ?? 0.0 > Constants.majorNetworkPrevalence - } - -} diff --git a/Core/PrivacyPractices.swift b/Core/PrivacyPractices.swift index b587aa4942..83bd698ea9 100644 --- a/Core/PrivacyPractices.swift +++ b/Core/PrivacyPractices.swift @@ -53,8 +53,8 @@ public class PrivacyPractices { let derivedScore = $0.value.derivedScore if let entity = entityMapping.findEntity(forHost: $0.key) { - if entityScores[entity] == nil || entityScores[entity]! < derivedScore { - entityScores[entity] = derivedScore + if entityScores[entity.displayName ?? ""] == nil || entityScores[entity.displayName ?? ""]! < derivedScore { + entityScores[entity.displayName ?? ""] = derivedScore } } @@ -73,7 +73,7 @@ public class PrivacyPractices { func findPractice(forHost host: String) -> Practice { guard let domain = tld.domain(host) else { return Constants.unknown } guard let term = termsOfServiceStore.terms[domain] else { return Constants.unknown} - let entityScore = entityScores[entityMapping.findEntity(forHost: domain) ?? ""] + let entityScore = entityScores[entityMapping.findEntity(forHost: domain)?.displayName ?? ""] return Practice(score: entityScore ?? term.derivedScore, summary: term.summary, goodReasons: term.goodReasons, diff --git a/Core/SiteRating.swift b/Core/SiteRating.swift index e99865158d..26966e0a31 100644 --- a/Core/SiteRating.swift +++ b/Core/SiteRating.swift @@ -21,6 +21,10 @@ import Foundation public class SiteRating { + struct Constants { + static let majorNetworkPrevalence = 7.0 + } + public enum EncryptionType { case unencrypted, mixed, encrypted, forced } @@ -39,36 +43,32 @@ public class SiteRating { public let url: URL public let httpsForced: Bool public let privacyPractice: PrivacyPractices.Practice - public let isMajorTrackerNetwork: Bool public var hasOnlySecureContent: Bool public var finishedLoading = false - public private (set) var trackersDetected = [DetectedTracker: Int]() - public private (set) var trackersBlocked = [DetectedTracker: Int]() - - let prevalenceStore: PrevalenceStore + public private (set) var trackersDetected = Set() + public private (set) var trackersBlocked = Set() private let grade = Grade() private let cache = GradeCache.shared + private let entity: Entity? public init(url: URL, httpsForced: Bool = false, entityMapping: EntityMapping, - privacyPractices: PrivacyPractices, - prevalenceStore: PrevalenceStore) { + privacyPractices: PrivacyPractices) { Logger.log(text: "new SiteRating(url: \(url), httpsForced: \(httpsForced))") if let host = url.host, let entity = entityMapping.findEntity(forHost: host) { - self.grade.setParentEntity(named: entity, withPrevalence: prevalenceStore.prevalences[entity]) - self.isMajorTrackerNetwork = prevalenceStore.isMajorNetwork(named: entity) + self.grade.setParentEntity(named: entity.displayName ?? "", withPrevalence: entity.prevalence ?? 0) + self.entity = entity } else { - self.isMajorTrackerNetwork = false + entity = nil } self.url = url self.httpsForced = httpsForced - self.prevalenceStore = prevalenceStore self.hasOnlySecureContent = url.isHttps() self.privacyPractice = privacyPractices.findPractice(forHost: url.host ?? "") @@ -94,87 +94,51 @@ public class SiteRating { return .unencrypted } - public var uniqueMajorTrackerNetworksDetected: Int { - return uniqueMajorTrackerNetworks(trackers: trackersDetected) + public var majorTrackerNetworksDetected: Int { + return trackersDetected.filter({ $0.entity?.prevalence ?? 0 >= Constants.majorNetworkPrevalence }).count } - public var uniqueMajorTrackerNetworksBlocked: Int { - return uniqueMajorTrackerNetworks(trackers: trackersBlocked) + public var majorTrackerNetworksBlocked: Int { + return trackersBlocked.filter({ $0.entity?.prevalence ?? 0 >= Constants.majorNetworkPrevalence }).count } - public var uniqueTrackerNetworksDetected: Int { - return uniqueTrackerNetworks(trackers: trackersDetected) + public var trackerNetworksDetected: Int { + return trackersDetected.filter({ $0.entity?.prevalence ?? 0 < Constants.majorNetworkPrevalence }).count } public var uniqueTrackerNetworksBlocked: Int { - return uniqueTrackerNetworks(trackers: trackersBlocked) + return trackersBlocked.filter({ $0.entity?.prevalence ?? 0 < Constants.majorNetworkPrevalence }).count } public var containsMajorTracker: Bool { - return trackersDetected.contains(where: majorNetworkFilter) + return majorTrackerNetworksBlocked > 0 || majorTrackerNetworksDetected > 0 } - - public var containsIpTracker: Bool { - return trackersDetected.contains(where: { $0.key.isIpTracker }) + + public var isMajorTrackerNetwork: Bool { + return entity?.prevalence ?? 0 >= Constants.majorNetworkPrevalence } public func trackerDetected(_ tracker: DetectedTracker) { - let detectedCount = trackersDetected[tracker] ?? 0 - trackersDetected[tracker] = detectedCount + 1 - - let entity = tracker.networkNameForDisplay - + let entity = tracker.entity if tracker.blocked { - let blockCount = trackersBlocked[tracker] ?? 0 - trackersBlocked[tracker] = blockCount + 1 - grade.addEntityBlocked(named: entity, withPrevalence: prevalenceStore.prevalences[entity]) + trackersBlocked.insert(tracker) + grade.addEntityBlocked(named: entity?.displayName ?? "", withPrevalence: entity?.prevalence ?? 0) } else { - grade.addEntityNotBlocked(named: entity, withPrevalence: prevalenceStore.prevalences[entity]) + trackersDetected.insert(tracker) + grade.addEntityNotBlocked(named: entity?.displayName ?? "", withPrevalence: entity?.prevalence ?? 0) } } - public var uniqueTrackersDetected: Int { - return trackersDetected.count - } - - public var uniqueTrackersBlocked: Int { - return trackersBlocked.count - } - public var totalTrackersDetected: Int { - return trackersDetected.reduce(0) { $0 + $1.value } + return trackersDetected.count } public var totalTrackersBlocked: Int { - return trackersBlocked.reduce(0) { $0 + $1.value } - } - - public var majorNetworkTrackersDetected: [DetectedTracker: Int] { - return trackersDetected.filter(majorNetworkFilter) - } - - public var majorNetworkTrackersBlocked: [DetectedTracker: Int] { - return trackersBlocked.filter(majorNetworkFilter) + return trackersBlocked.count } public func isFor(_ url: URL?) -> Bool { return self.url.host == url?.host } - private func uniqueMajorTrackerNetworks(trackers: [DetectedTracker: Int]) -> Int { - let trackers = trackers - .filter(majorNetworkFilter) - .keys - .compactMap({ $0.networkName }) - return Set(trackers).count - } - - private func uniqueTrackerNetworks(trackers: [DetectedTracker: Int]) -> Int { - return Set(trackers.keys.compactMap({ $0.networkName ?? $0.domain })).count - } - - private func majorNetworkFilter(trackerDetected: (DetectedTracker, Int)) -> Bool { - return prevalenceStore.isMajorNetwork(named: trackerDetected.0.networkName) - } - } diff --git a/Core/StorageCache.swift b/Core/StorageCache.swift index 524f3817b1..6bc6c1dd0b 100644 --- a/Core/StorageCache.swift +++ b/Core/StorageCache.swift @@ -19,12 +19,6 @@ import Foundation -protocol EtagOOSCheckStore { - - var hasDisconnectMeData: Bool { get } - var hasEasylistData: Bool { get } -} - protocol StorageCacheUpdating { func update(_ configuration: ContentBlockerRequest.Configuration, with data: Any) -> Bool @@ -32,55 +26,27 @@ protocol StorageCacheUpdating { public class StorageCache: StorageCacheUpdating { - let easylistStore = EasylistStore() - let surrogateStore = SurrogateStore() - - public let disconnectMeStore = DisconnectMeStore() + public let fileStore = FileStore() public let httpsUpgradeStore: HTTPSUpgradeStore = HTTPSUpgradePersistence() - public let entityMappingStore: EntityMappingStore = DownloadedEntityMappingStore() - public var entityMapping: EntityMapping public let configuration: ContentBlockerConfigurationStore = ContentBlockerConfigurationUserDefaults() // Read only public let tld: TLD public let termsOfServiceStore: TermsOfServiceStore - public let prevalenceStore: PrevalenceStore public init() { - entityMapping = EntityMapping(store: entityMappingStore) tld = TLD() termsOfServiceStore = EmbeddedTermsOfServiceStore() - prevalenceStore = EmbeddedPrevalenceStore() } - public init(tld: TLD, termsOfServiceStore: TermsOfServiceStore, prevalenceStore: PrevalenceStore) { - entityMapping = EntityMapping(store: entityMappingStore) + public init(tld: TLD, termsOfServiceStore: TermsOfServiceStore) { self.tld = tld self.termsOfServiceStore = termsOfServiceStore - self.prevalenceStore = prevalenceStore - } - - public var hasData: Bool { - return disconnectMeStore.hasData && easylistStore.hasData } - // swiftlint:disable cyclomatic_complexity func update(_ configuration: ContentBlockerRequest.Configuration, with data: Any) -> Bool { - switch configuration { - case .trackersWhitelist: - guard let data = data as? Data else { return false } - return easylistStore.persistEasylistWhitelist(data: data) - - case .disconnectMe: - guard let data = data as? Data else { return false } - do { - try disconnectMeStore.persist(data: data) - return true - } catch { - return false - } case .httpsWhitelist: guard let whitelist = data as? [String] else { return false } return httpsUpgradeStore.persistWhitelist(domains: whitelist) @@ -91,29 +57,25 @@ public class StorageCache: StorageCacheUpdating { HTTPSUpgrade.shared.loadData() return result - case .surrogates: - guard let data = data as? Data else { return false } - return surrogateStore.parseAndPersist(data: data) + case .httpsBloomFilterSpec: + return false - case .entitylist: - guard let data = data as? Data else { return false } - let result = entityMappingStore.persist(data: data) - entityMapping = EntityMapping(store: entityMappingStore) - return result + case .surrogates: + return fileStore.persist(data as? Data, forConfiguration: configuration) - default: + case .trackerDataSet: + if fileStore.persist(data as? Data, forConfiguration: configuration) { + if TrackerDataManager.shared.reload() != .downloaded { + Pixel.fire(pixel: .trackerDataReloadFailed) + return false + } + return true + } return false + + case .temporaryWhitelist: + return fileStore.persist(data as? Data, forConfiguration: configuration) + } } - // swiftlint:enable cyclomatic_complexity -} - -extension StorageCache: EtagOOSCheckStore { - - var hasDisconnectMeData: Bool { - return disconnectMeStore.hasData - } - var hasEasylistData: Bool { - return easylistStore.hasData - } } diff --git a/Core/StorageCacheProvider.swift b/Core/StorageCacheProvider.swift index d4658b0ebe..db78166886 100644 --- a/Core/StorageCacheProvider.swift +++ b/Core/StorageCacheProvider.swift @@ -49,18 +49,17 @@ public class StorageCacheProvider { public func update(completion: @escaping StorageCacheUpdateCompletion) { - type(of: self).updateQueue.async { + Self.updateQueue.async { let loader = ContentBlockerLoader() let currentCache = self.current - guard loader.checkForUpdates(with: currentCache) else { + guard loader.checkForUpdates() else { completion(nil) return } let newCache = StorageCache(tld: currentCache.tld, - termsOfServiceStore: currentCache.termsOfServiceStore, - prevalenceStore: currentCache.prevalenceStore) + termsOfServiceStore: currentCache.termsOfServiceStore) loader.applyUpdate(to: newCache) self.current = newCache diff --git a/Core/SurrogateStore.swift b/Core/SurrogateStore.swift deleted file mode 100644 index 6dceefdf8e..0000000000 --- a/Core/SurrogateStore.swift +++ /dev/null @@ -1,89 +0,0 @@ -// -// SurrogateStore.swift -// DuckDuckGo -// -// Copyright © 2018 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -class SurrogateStore { - - private let groupIdentifier: String - - public private(set) var jsFunctions: [String: String]? - - init(groupIdentifier: String = ContentBlockerStoreConstants.groupName) { - self.groupIdentifier = groupIdentifier - jsFunctions = NSDictionary(contentsOf: persistenceLocation()) as? [String: String] - } - - @discardableResult - func parseAndPersist(data: Data) -> Bool { - guard let surrogateFile = String(data: data, encoding: .utf8) else { return false } - let jsFunctions = SurrogateParser.parse(lines: surrogateFile.components(separatedBy: .newlines)) - guard let plist = jsFunctions as NSDictionary? else { return false } - if plist.write(to: persistenceLocation(), atomically: true) { - self.jsFunctions = jsFunctions - return true - } - return false - } - - private func persistenceLocation() -> URL { - let path = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: groupIdentifier) - return path!.appendingPathComponent("surrogate.js") - } - -} - -class SurrogateParser { - - static func parse(lines: [String]) -> [String: String] { - var jsDict = [String: String]() - - var resourceName: String? - var jsFunction: String? - - for line in lines { - - guard !line.hasPrefix("#") else { continue } - - // We can only cope with scripts anyway, see contentblocker.js -> loadSurrogate(url) - if line.hasSuffix("application/javascript") { - resourceName = line.components(separatedBy: " ")[0] - jsFunction = "" - continue - } - - guard jsFunction != nil else { continue } - - jsFunction = "\(jsFunction!)\(line)\n" - - if line.trimWhitespace() == "" { - jsDict[resourceName!] = jsFunction?.trimWhitespace() - jsFunction = nil - resourceName = nil - } - } - - if let resourceName = resourceName { - jsDict[resourceName] = jsFunction?.trimWhitespace() - } - - return jsDict - } - -} diff --git a/Core/TabInstrumentation.swift b/Core/TabInstrumentation.swift index 5955e9f959..86c40b5c3e 100644 --- a/Core/TabInstrumentation.swift +++ b/Core/TabInstrumentation.swift @@ -73,23 +73,29 @@ public class TabInstrumentation { // MARK: - JS events public func request(url: String, allowedIn timeInMs: Double) { - request(url: url, blocked: false, in: timeInMs) + request(url: url, isTracker: false, blocked: false, in: timeInMs) } - public func request(url: String, blockedIn timeInMs: Double) { - request(url: url, blocked: true, in: timeInMs) + public func tracker(url: String, allowedIn timeInMs: Double, reason: String?) { + request(url: url, isTracker: true, blocked: false, reason: reason ?? "?", in: timeInMs) } - private func request(url: String, blocked: Bool, in timeInMs: Double) { + public func tracker(url: String, blockedIn timeInMs: Double) { + request(url: url, isTracker: true, blocked: true, in: timeInMs) + } + + private func request(url: String, isTracker: Bool, blocked: Bool, reason: String = "", in timeInMs: Double) { if #available(iOSApplicationExtension 12.0, *) { let currentURL = self.currentURL ?? "unknown" + let requestType = isTracker ? "Tracker" : "Regular" let status = blocked ? "Blocked" : "Allowed" + // 0 is treated as 1ms - let timeInNS: UInt64 = timeInMs > 0 ? UInt64(timeInMs * 1000 * 1000) : 1000000 + let timeInNS: UInt64 = timeInMs.asNanos os_log(.debug, log: type(of: self).tabsLog, - "[%@] Request: %@ - %@ in %llu", currentURL, url, status, timeInNS) + "[%@] Request: %@ - %@ - %@ (%@) in %llu", currentURL, url, requestType, status, reason, timeInNS) } } @@ -97,7 +103,7 @@ public class TabInstrumentation { if #available(iOSApplicationExtension 12.0, *) { let currentURL = self.currentURL ?? "unknown" // 0 is treated as 1ms - let timeInNS: UInt64 = timeInMs > 0 ? UInt64(timeInMs * 1000 * 1000) : 1000000 + let timeInNS: UInt64 = timeInMs.asNanos os_log(.debug, log: type(of: self).tabsLog, @@ -105,3 +111,11 @@ public class TabInstrumentation { } } } + +extension Double { + + var asNanos: UInt64 { + return self > 0 ? UInt64(self * 1000 * 1000) : 1000000 + } + +} diff --git a/Core/TrackerData.swift b/Core/TrackerData.swift new file mode 100644 index 0000000000..d88618049c --- /dev/null +++ b/Core/TrackerData.swift @@ -0,0 +1,124 @@ +// +// TrackerData.swift +// Core +// +// Copyright © 2019 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +public struct TrackerData: Codable { + + public typealias EntityName = String + public typealias TrackerDomain = String + + public struct TrackerRules { + + let tracker: KnownTracker + + } + + public let trackers: [TrackerDomain: KnownTracker] + public let entities: [EntityName: Entity] + public let domains: [TrackerDomain: EntityName] + + public init(trackers: [String: KnownTracker], entities: [String: Entity], domains: [String: String]) { + self.trackers = trackers + self.entities = entities + self.domains = domains + } + + func relatedDomains(for owner: KnownTracker.Owner?) -> [String]? { + return entities[owner?.name ?? ""]?.domains + } + +} + +public struct KnownTracker: Codable, Equatable { + + public static func == (lhs: KnownTracker, rhs: KnownTracker) -> Bool { + return lhs.domain == rhs.domain + } + + public struct Owner: Codable { + + public let name: String? + public let displayName: String? + + } + + public struct Rule: Codable, Hashable, Equatable { + + // swiftlint:disable nesting + public struct Matching: Codable, Hashable { + + public let domains: [String]? + public let types: [String]? + + } + // swiftlint:enable nesting + + public let rule: String? + public let surrogate: String? + public let action: ActionType? + public let options: Matching? + public let exceptions: Matching? + + } + + public enum ActionType: String, Codable { + case block + case ignore + } + + enum CodingKeys: String, CodingKey { + case domain + case owner + case categories + case rules + case prevalence + case defaultAction = "default" + case subdomains + } + + public let domain: String? + public let defaultAction: ActionType? + public let owner: Owner? + public let prevalence: Double? + public let subdomains: [String]? + public let categories: [String]? + public let rules: [Rule]? + +} + +extension KnownTracker { + + static let displayCategories = [ + "Analytics", "Advertising", "Social Network" + ] + + public var category: String? { + return categories?.first(where: { Self.displayCategories.contains($0) }) + } + +} + +public struct Entity: Codable, Hashable { + + public let displayName: String? + public let domains: [String]? + public let prevalence: Double? + +} diff --git a/Core/TrackerDataManager.swift b/Core/TrackerDataManager.swift new file mode 100644 index 0000000000..3493cf16b5 --- /dev/null +++ b/Core/TrackerDataManager.swift @@ -0,0 +1,116 @@ +// +// TrackerDataManager.swift +// Core +// +// Copyright © 2019 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +public class TrackerDataManager { + + public enum DataSet { + + case embedded + case embeddedFallback + case downloaded + + } + + public static let shared = TrackerDataManager() + + private(set) public var trackerData: TrackerData! + + init() { + reload() + } + + @discardableResult + public func reload() -> DataSet { + + let dataSet: DataSet + let data: Data + + if let loadedData = FileStore().loadAsData(forConfiguration: .trackerDataSet) { + data = loadedData + dataSet = .downloaded + } else { + data = Self.loadEmbeddedAsData() + dataSet = .embedded + } + + do { + // This maigh fail if the downloaded data is corrupt or format has changed unexpectedly + trackerData = try JSONDecoder().decode(TrackerData.self, from: data) + } catch { + // This should NEVER fail + let trackerData = try? JSONDecoder().decode(TrackerData.self, from: Self.loadEmbeddedAsData()) + self.trackerData = trackerData! + Pixel.fire(pixel: .trackerDataParseFailed, error: error) + return .embeddedFallback + } + + return dataSet + } + + public func findTracker(forUrl url: String) -> KnownTracker? { + guard let host = URL(string: url)?.host else { return nil } + for host in variations(of: host) { + if let tracker = trackerData.trackers[host] { + return tracker + } + } + return nil + } + + public func findEntity(byName name: String) -> Entity? { + return trackerData.entities[name] + } + + public func findEntity(forHost host: String) -> Entity? { + for host in variations(of: host) { + if let entityName = trackerData.domains[host] { + return trackerData.entities[entityName] + } + } + return nil + } + + private func variations(of host: String) -> [String] { + var parts = host.components(separatedBy: ".") + var domains = [String]() + while parts.count > 1 { + let domain = parts.joined(separator: ".") + domains.append(domain) + parts.removeFirst() + } + return domains + } + + static var embeddedUrl: URL { + return Bundle(for: Self.self).url(forResource: "trackerData", withExtension: "json")! + } + + static func loadEmbeddedAsData() -> Data { + let json = try? Data(contentsOf: embeddedUrl) + return json! + } + + static func loadEmbeddedAsString() -> String { + let json = try? String(contentsOf: embeddedUrl, encoding: .utf8) + return json! + } + +} diff --git a/Core/WKWebViewConfigurationExtension.swift b/Core/WKWebViewConfigurationExtension.swift index 7b86af69c3..b0f83b02c0 100644 --- a/Core/WKWebViewConfigurationExtension.swift +++ b/Core/WKWebViewConfigurationExtension.swift @@ -87,7 +87,7 @@ extension WKWebViewConfiguration { } -private class Loader { +private struct Loader { struct CacheNames { @@ -97,24 +97,37 @@ private class Loader { let cache = ContentBlockerStringCache() let javascriptLoader = JavascriptLoader() - let storageCache: StorageCache let userContentController: WKUserContentController let injectContentBlockingScripts: Bool + + let whitelist: String + let surrogates: String + let trackerData: String init(contentController: WKUserContentController, storageCache: StorageCache, injectContentBlockingScripts: Bool) { - self.storageCache = storageCache self.userContentController = contentController self.injectContentBlockingScripts = injectContentBlockingScripts + + self.whitelist = (WhitelistManager().domains?.joined(separator: "\n") ?? "") + + "\n" + + (storageCache.fileStore.loadAsString(forConfiguration: .temporaryWhitelist) ?? "") + self.surrogates = storageCache.fileStore.loadAsString(forConfiguration: .surrogates) ?? "" + + // Encode whatever the tracker data manager is using to ensure it's in sync and because we know it will work + let encodedTrackerData = try? JSONEncoder().encode(TrackerDataManager.shared.trackerData) + self.trackerData = String(data: encodedTrackerData!, encoding: .utf8)! } func load() { - Logger.log(text: "Loading scripts") + let spid = Instruments.shared.startTimedEvent(.injectScripts) loadDocumentLevelScripts() if injectContentBlockingScripts { loadContentBlockingScripts() } + + Instruments.shared.endTimedEvent(for: spid) } private func loadDocumentLevelScripts() { @@ -127,20 +140,17 @@ private class Loader { private func loadContentBlockingScripts() { loadContentBlockerDependencyScripts() - loadBlockerData() - load(scripts: [ .disconnectme, .contentblocker ], forMainFrameOnly: false) + javascriptLoader.load(script: .contentblocker, withReplacements: [ + "${whitelist}": whitelist, + "${trackerData}": trackerData, + "${surrogates}": surrogates + ], into: userContentController, forMainFrameOnly: false) load(scripts: [ .detection ], forMainFrameOnly: false) } private func loadContentBlockerDependencyScripts() { - let tlds = storageCache.tld + load(scripts: [ .messaging ], forMainFrameOnly: false) - if #available(iOS 10, *) { - load(scripts: [ .messaging, .apbfilter], forMainFrameOnly: false) - } else { - load(scripts: [ .messaging, .apbfilterES2015 ], forMainFrameOnly: false) - } - if isDebugBuild { javascriptLoader.load(script: .debugMessagingEnabled, into: userContentController, @@ -150,86 +160,6 @@ private class Loader { into: userContentController, forMainFrameOnly: false) } - - javascriptLoader.load(script: .tlds, withReplacements: [ "${tlds}": tlds.json ], into: userContentController, forMainFrameOnly: false) - } - - private func loadBlockerData() { - - let surrogates = loadSurrogateJson(storageCache.surrogateStore) - let whitelist = storageCache.configuration.domainWhitelist.toJsonLookupString() - let disconnectMeStore = storageCache.disconnectMeStore - - javascriptLoader.load(script: .blockerData, withReplacements: [ - "${blocking_enabled}": "true", - "${disconnectmeBanned}": disconnectMeStore.bannedTrackersJson, - "${disconnectmeAllowed}": disconnectMeStore.allowedTrackersJson, - "${whitelist}": whitelist, - "${surrogates}": surrogates - ], - into: userContentController, - forMainFrameOnly: false) - - loadEasylist() - - } - - private func loadSurrogateJson(_ store: SurrogateStore) -> String { - if let surrogateJson = cache.get(named: CacheNames.surrogateJson) { - Logger.log(text: "Using cached surrogate json") - return surrogateJson - } - - guard let functions = store.jsFunctions else { return "{}" } - let functionUris = functions.mapValues({ "data:application/javascript;base64,\($0.toBase64())" }) - guard let jsonData = try? JSONEncoder().encode(functionUris) else { return "{}" } - guard let surrogateJson = String(data: jsonData, encoding: .utf8) else { return "{}" } - cache.put(name: CacheNames.surrogateJson, value: surrogateJson) - Logger.log(text: "Caching surrogate json") - return surrogateJson - } - - fileprivate func injectCompiledEasylist(_ cachedEasylistWhitelist: String) { - Logger.log(text: "using cached easylist") - - if #available(iOS 10, *) { - javascriptLoader.load(.bloom, into: userContentController, forMainFrameOnly: false) - } else { - javascriptLoader.load(.bloomES2015, into: userContentController, forMainFrameOnly: false) - } - - javascriptLoader.load(script: .cachedEasylist, withReplacements: [ - "${easylist_privacy_json}": "{}", - "${easylist_general_json}": "{}", - "${easylist_whitelist_json}": cachedEasylistWhitelist ], - into: userContentController, - forMainFrameOnly: false) - } - - fileprivate func injectRawEasylist(_ easylistWhitelist: String) { - Logger.log(text: "parsing easylist") - - javascriptLoader.load(script: .easylistParsing, withReplacements: [ - "${easylist_privacy}": "", - "${easylist_general}": "", - "${easylist_whitelist}": easylistWhitelist ], - into: userContentController, - forMainFrameOnly: false) - - } - - private func loadEasylist() { - - if let cachedEasylistWhitelist = cache.get(named: EasylistStore.CacheNames.easylistWhitelist) { - injectCompiledEasylist(cachedEasylistWhitelist) - return - } - - let easylistStore = storageCache.easylistStore - - if let easylistWhitelist = easylistStore.easylistWhitelist { - injectRawEasylist(easylistWhitelist) - } } private func load(scripts: [JavascriptLoader.Script], forMainFrameOnly: Bool = true) { @@ -239,25 +169,3 @@ private class Loader { } } - -fileprivate extension Set where Element == String { - - func toJsonLookupString() -> String { - return reduce("{", { (result, next) -> String in - let separator = result != "{" ? ", " : "" - return "\(result)\(separator) \"\(next)\" : true" - }).appending("}") - } - -} - -fileprivate extension String { - - func toBase64() -> String { - guard let data = self.data(using: String.Encoding.utf8) else { - return "" - } - return data.base64EncodedString() - } - -} diff --git a/Core/contentblocker.js b/Core/contentblocker.js index e42f9c58b0..f63680c25c 100644 --- a/Core/contentblocker.js +++ b/Core/contentblocker.js @@ -19,299 +19,447 @@ var duckduckgoContentBlocking = function() { - var parentEntityUrl = null - var topLevelUrl = null - - // private - function handleDetection(url, detectionMethod) { - if (isAssociatedFirstPartyDomain(url)) { - duckduckgoDebugMessaging.log("first party url: " + url) - return null - } + // tld.js + var tldjs = { - if (!duckduckgoBlockerData.blockingEnabled) { - return { - method: detectionMethod, - block: false, - reason: "protection disabled" - } - } + parse: function(url) { - if (currentDomainIsWhitelisted()) { - duckduckgoDebugMessaging.log("domain whitelisted: " + url) - return { - method: detectionMethod, - block: false, - reason: "domain whitelisted" - } - } + if (url.startsWith("//")) { + url = "http:" + url; + } - duckduckgoDebugMessaging.log("blocking: " + url) - return { - method: detectionMethod, - block: true, - reason: "tracker detected" - } - } + try { + var parsed = new URL(url); + return { + domain: parsed.hostname, + hostname: parsed.hostname + } + } catch { + return { + domain: "", + hostname: "" + } + } + } - // private - function toURL(url, protocol) { - try { - return new URL(url.startsWith("//") ? protocol + url : url) - } catch(error) { - return null - } - } + }; + // tld.js - // private - function getTopLevelURL() { - try { - // FROM: https://stackoverflow.com/a/7739035/73479 - // FIX: Better capturing of top level URL so that trackers in embedded documents are not considered first party - return new URL(window.location != window.parent.location ? document.referrer : document.location.href) - } catch(error) { - return new URL(location.href) - } - } + // util.js + var utils = { - // private - function currentDomainIsWhitelisted() { - return duckduckgoBlockerData.whitelist[topLevelUrl.host] - } + extractHostFromURL: function(url, shouldKeepWWW) { + if (!url) return '' - // private - function trackerWhitelisted(trackerUrl, type) { - return abpMatch(trackerUrl, type, "whitelist", duckduckgoBlockerData.easylistWhitelist) - } + let urlObj = tldjs.parse(url) + let hostname = urlObj.hostname || '' - // from https://stackoverflow.com/a/7616484/73479 - // private - function hashCode(string) { - var hash = 0, i, chr; - if (string.length === 0) return hash; - for (i = 0; i < string.length; i++) { - chr = string.charCodeAt(i); - hash = ((hash << 5) - hash) + chr; - hash |= 0; - } - return hash; - } + if (!shouldKeepWWW) { + hostname = hostname.replace(/^www\./, '') + } - // private - function getStatus(url) { - return statuses[hashCode(event.url)] - } + return hostname + } - // private - function domainsMatch(url1, url2) { - return duckduckgoTLDParser.extractDomain(url1) == duckduckgoTLDParser.extractDomain(url2) - } + }; + // util.js - // private - function isDuckDuckGo(url) { - return url.hostname.endsWith("duckduckgo.com") - } + // Buffer + class Buffer { + static from(string, type) { + return new Buffer(string); + } - // private - function urlBelongsToThisSite(urlToCheck) { - return domainsMatch(urlToCheck, topLevelUrl) - } + constructor(string) { + this.string = string; + } - // private - function urlBelongsToSiteParent(urlToCheck) { - return parentEntityUrl && domainsMatch(parentEntityUrl, urlToCheck) - } + toString(type) { + let string = this.string; + var aUTF16CodeUnits = new Uint16Array(string.length); + Array.prototype.forEach.call(aUTF16CodeUnits, function (el, idx, arr) { arr[idx] = string.charCodeAt(idx); }); + return btoa(String.fromCharCode.apply(null, new Uint8Array(aUTF16CodeUnits.buffer))); + } + } + // Buffer + + // trackers.js - https://raw.githubusercontent.com/duckduckgo/privacy-grade/298ddcbdd9d55808233643d90639578cd063a439/src/classes/trackers.js + (function () { + class Trackers { + constructor (ops) { + this.tldjs = ops.tldjs + this.utils = ops.utils + } - // private - function urlBelongsToRelatedSite(urlToCheck) { - if (!parentEntityUrl) { - return false - } + setLists (lists) { + lists.forEach(list => { + if (list.name === 'tds') { + this.entityList = this.processEntityList(list.data.entities) + this.trackerList = this.processTrackerList(list.data.trackers) + this.domains = list.data.domains + } else if (list.name === 'surrogates') { + this.surrogateList = this.processSurrogateList(list.data) + } + }) + } - var related = DisconnectMe.parentTracker(urlToCheck) - if (!related) { - return false - } + processTrackerList (data) { + for (let name in data) { + if (data[name].rules) { + for (let i in data[name].rules) { + data[name].rules[i].rule = new RegExp(data[name].rules[i].rule, 'ig') + } + } + } + return data + } - var relatedUrl = new URL(topLevelUrl.protocol + related.parent); - if (!domainsMatch(relatedUrl, parentEntityUrl)) { - return false - } + processEntityList (data) { + const processed = {} + for (let entity in data) { + data[entity].domains.forEach(domain => { + processed[domain] = entity + }) + } + return processed + } - return true - } + processSurrogateList (text) { + const b64dataheader = 'data:application/javascript;base64,' + const surrogateList = {} + const splitSurrogateList = text.trim().split('\n\n') + + splitSurrogateList.forEach(sur => { + // remove comment lines + const lines = sur.split('\n').filter((line) => { + return !(/^#.*/).test(line) + }) + + // remove first line, store it + const firstLine = lines.shift() + + // take identifier from first line + const pattern = firstLine.split(' ')[0].split('/')[1] + const b64surrogate = Buffer.from(lines.join('\n').toString(), 'binary').toString('base64') + surrogateList[pattern] = b64dataheader + b64surrogate + }) + return surrogateList + } - // private - function isAssociatedFirstPartyDomain(trackerUrl) { - var urlToCheck = toURL(trackerUrl, topLevelUrl.protocol) - if (urlToCheck == null) { - return false - } + getTrackerData (urlToCheck, siteUrl, request, ops) { + ops = ops || {} - if (urlBelongsToThisSite(urlToCheck)) { - return true - } + if (!this.entityList || !this.trackerList) { + throw new Error('tried to detect trackers before rules were loaded') + } - if (urlBelongsToSiteParent(urlToCheck)) { - return true - } + // single object with all of our requeest and site data split and + // processed into the correct format for the tracker set/get functions. + // This avoids repeat calls to split and util functions. + const requestData = { + ops: ops, + siteUrl: siteUrl, + request: request, + siteDomain: this.tldjs.parse(siteUrl).domain, + siteUrlSplit: this.utils.extractHostFromURL(siteUrl).split('.'), + urlToCheck: urlToCheck, + urlToCheckDomain: this.tldjs.parse(urlToCheck).domain, + urlToCheckSplit: this.utils.extractHostFromURL(urlToCheck).split('.') + } - if (urlBelongsToRelatedSite(urlToCheck)) { - return true - } + // finds a tracker definition by iterating over the whole trackerList and finding the matching tracker. + const tracker = this.findTracker(requestData) - return false - } + if (!tracker) { + return null + } - // private - function getParentEntityUrl() { - var parentEntity = DisconnectMe.parentTracker(topLevelUrl) - if (parentEntity) { - duckduckgoDebugMessaging.log("topLevelUrl: " + topLevelUrl.protocol + " parentEntity: " + JSON.stringify(parentEntity)) - return new URL(topLevelUrl.protocol + parentEntity.parent) - } - return null - } + // finds a matching rule by iterating over the rules in tracker.data and sets redirectUrl. + const matchedRule = this.findRule(tracker, requestData) - // private - function disconnectMeMatch(trackerUrl) { - var url = toURL(trackerUrl, topLevelUrl.protocol) - if (!url) { - return null - } + const redirectUrl = (matchedRule && matchedRule.surrogate) ? this.surrogateList[matchedRule.surrogate] : false - var result = DisconnectMe.parentTracker(url) - if (result && result.banned) { - return handleDetection(trackerUrl, "disconnectme") - } + // sets tracker.exception by looking at tracker.rule exceptions (if any) + const matchedRuleException = matchedRule ? this.matchesRuleDefinition(matchedRule, 'exceptions', requestData) : false - return null - } + const trackerOwner = this.findTrackerOwner(requestData.urlToCheckDomain) - // private - function abpMatch(trackerUrl, type, name, list) { - if (Object.keys(list).length == 0) { return } + const websiteOwner = this.findWebsiteOwner(requestData) - var typeMask = ABPFilterParser.elementTypes[type.toUpperCase()] + const firstParty = (trackerOwner && websiteOwner) ? trackerOwner === websiteOwner : false - var config = { - domain: document.location.hostname, - elementTypeMask: typeMask - } + const fullTrackerDomain = requestData.urlToCheckSplit.join('.') - var result = ABPFilterParser.matches(list, trackerUrl, config) - return result - } + const {action, reason} = this.getAction({ + firstParty, + matchedRule, + matchedRuleException, + defaultAction: tracker.default, + redirectUrl + }) - // private - function checkEasylist(trackerUrl, type, easylist, name) { - if (abpMatch(trackerUrl, type, name, easylist)) { - return handleDetection(trackerUrl, name) - } - return null - } + return { + action, + reason, + firstParty, + redirectUrl, + matchedRule, + matchedRuleException, + tracker, + fullTrackerDomain + } + } - // private - function easylistPrivacyMatch(trackerUrl, type) { - return checkEasylist(trackerUrl, type, duckduckgoBlockerData.easylistPrivacy, "easylist-privacy") - } + /* + * Pull subdomains off of the reqeust rule and look for a matching tracker object in our data + */ + findTracker (requestData) { + let urlList = Array.from(requestData.urlToCheckSplit) - // private - function easylistMatch(trackerUrl, type) { - return checkEasylist(trackerUrl, type, duckduckgoBlockerData.easylist, "easylist") - } + while (urlList.length > 1) { + let trackerDomain = urlList.join('.') + urlList.shift() - // public - function loadSurrogate(url) { - var withoutQueryString = url.split("?")[0] - duckduckgoDebugMessaging.log("looking for surrogate for " + withoutQueryString) - - var suggorateKeys = Object.keys(duckduckgoBlockerData.surrogates) - for (var i = 0; i < suggorateKeys.length; i++) { - var key = suggorateKeys[i] - if (withoutQueryString.endsWith(key)) { - var surrogate = duckduckgoBlockerData.surrogates[key] - var s = document.createElement("script") - s.type = "application/javascript" - s.async = true - s.src = surrogate - sp = document.getElementsByTagName("script")[0] - sp.parentNode.insertBefore(s, sp) - return true + const matchedTracker = this.trackerList[trackerDomain] + if (matchedTracker) { + return matchedTracker + } } } - return false - } + findTrackerOwner (trackerDomain) { + return this.entityList[trackerDomain] + } - // public - function shouldBlock(trackerUrl, type, blockFunc) { - var startTime = performance.now() + /* + * Set parent and first party values on tracker + */ + findWebsiteOwner (requestData) { + // find the site owner + let siteUrlList = Array.from(requestData.siteUrlSplit) + + while (siteUrlList.length > 1) { + let siteToCheck = siteUrlList.join('.') + siteUrlList.shift() + + if (this.entityList[siteToCheck]) { + return this.entityList[siteToCheck] + } + } + } + + /* + * Iterate through a tracker rule list and return the first matching rule, if any. + */ + findRule (tracker, requestData) { + let matchedRule = null + // Find a matching rule from this tracker + if (tracker.rules && tracker.rules.length) { + tracker.rules.some(ruleObj => { + if (this.requestMatchesRule(requestData, ruleObj)) { + matchedRule = ruleObj + return true + } + }) + } + return matchedRule + } + + requestMatchesRule (requestData, ruleObj) { + if (requestData.urlToCheck.match(ruleObj.rule)) { + if (ruleObj.options) { + return this.matchesRuleDefinition(ruleObj, 'options', requestData) + } else { + return true + } + } else { + return false + } + } + + /* Check the matched rule options against the request data + * return: true (all options matched) + */ + matchesRuleDefinition (rule, type, requestData) { + if (!rule[type]) { + return false + } + + const ruleDefinition = rule[type] + + const matchTypes = (ruleDefinition.types && ruleDefinition.types.length) + ? ruleDefinition.types.includes(requestData.request.type) : true + + const matchDomains = (ruleDefinition.domains && ruleDefinition.domains.length) + ? ruleDefinition.domains.some(domain => domain.match(requestData.siteDomain)) : true + + return (matchTypes && matchDomains) + } + + getAction (tracker) { + // Determine the blocking decision and reason. + let action, reason + if (tracker.firstParty) { + action = 'ignore' + reason = 'first party' + } else if (tracker.matchedRuleException) { + action = 'ignore' + reason = 'matched rule - exception' + } else if (!tracker.matchedRule && tracker.defaultAction === 'ignore') { + action = 'ignore' + reason = 'default ignore' + } else if (tracker.matchedRule && tracker.matchedRule.action === 'ignore') { + action = 'ignore' + reason = 'matched rule - ignore' + } else if (!tracker.matchedRule && tracker.defaultAction === 'block') { + action = 'block' + reason = 'default block' + } else if (tracker.matchedRule) { + if (tracker.redirectUrl) { + action = 'redirect' + reason = 'matched rule - surrogate' + } else { + action = 'block' + reason = 'matched rule - block' + } + } + + return {action, reason} + } + } - if (trackerWhitelisted(trackerUrl, type)) { - blockFunc(trackerUrl, false) + if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') + module.exports = Trackers + else + window.Trackers = Trackers + + })() + // trackers.js + + // surrogates + let surrogates = ` + ${surrogates} + ` + // surrogates + + // tracker data set + let trackerData = ${trackerData} + // tracker data set + + // overrides + Trackers.prototype.findTrackerOwner = function(domain) { + var parts = domain.split(".") + while (parts.length > 1) { + let entityName = trackerData.domains[parts.join(".")] + if (entityName) { + return entityName + } + parts = parts.slice(1) + } + return null; + } + + // create an instance to use + let trackers = new Trackers({ + tldjs: tldjs, + utils: utils + }); + + // update algorithm with the data it needs + trackers.setLists([{ + name: "tds", + data: trackerData + }, + { + name: "surrogates", + data: surrogates + } + ]); - duckduckgoDebugMessaging.signpostEvent({event: "Request Allowed", - url: trackerUrl, - time: performance.now() - startTime}) - return false - } + let topLevelUrl = getTopLevelURL(); - var detectors = [ - disconnectMeMatch, - easylistPrivacyMatch, - easylistMatch - ] - - var result = null - for (var i = 0; i < detectors.length; i++) { - result = detectors[i](trackerUrl, type) - if (result != null) { - break; - } + let whitelisted = ` + ${whitelist} + `.split("\n").filter(domain => domain.trim() == topLevelUrl.host).length > 0; + + // private + function getTopLevelURL() { + try { + // FROM: https://stackoverflow.com/a/7739035/73479 + // FIX: Better capturing of top level URL so that trackers in embedded documents are not considered first party + return new URL(window.location != window.parent.location ? document.referrer : document.location.href) + } catch(error) { + return new URL(location.href) } + } - if (result == null) { - blockFunc(trackerUrl, false) + // private + function loadSurrogate(surrogatePattern) { + var s = document.createElement("script") + s.type = "application/javascript" + s.async = true + s.src = trackers.surrogateList[surrogatePattern] + if (sp = document.getElementsByTagName("script")[0]) { + sp.parentNode.insertBefore(s, sp) + } + } + + // public + function shouldBlock(trackerUrl, type) { + let startTime = performance.now() + + let result = trackers.getTrackerData(trackerUrl.toString(), topLevelUrl.toString(), { + type: type + }, null); + if (result == null) { duckduckgoDebugMessaging.signpostEvent({event: "Request Allowed", url: trackerUrl, time: performance.now() - startTime}) return false; } - blockFunc(trackerUrl, result.block) + var blocked = false; + if (whitelisted) { + blocked = false; + result.reason = "whitelisted"; + } else if (result.action === 'block') { + blocked = true; + } else if (result.matchedRule && result.matchedRule.surrogate) { + blocked = true; + } duckduckgoMessaging.trackerDetected({ url: trackerUrl, - blocked: result.block, - method: result.method, - type: type + blocked: blocked, + reason: result.reason, }) - if (result.block) { - duckduckgoDebugMessaging.signpostEvent({event: "Request Blocked", + if (blocked) { + + if (result.matchedRule && result.matchedRule.surrogate) { + loadSurrogate(result.matchedRule.surrogate) + } + + duckduckgoDebugMessaging.signpostEvent({event: "Tracker Blocked", url: trackerUrl, time: performance.now() - startTime}) } else { - duckduckgoDebugMessaging.signpostEvent({event: "Request Allowed", + duckduckgoDebugMessaging.signpostEvent({event: "Tracker Allowed", url: trackerUrl, + reason: result.reason, time: performance.now() - startTime}) } - return result.block + return blocked; } // Init (function() { - topLevelUrl = getTopLevelURL() - parentEntityUrl = getParentEntityUrl() duckduckgoDebugMessaging.log("content blocking initialised") })() return { - loadSurrogate: loadSurrogate, shouldBlock: shouldBlock } }() - diff --git a/Core/detection.js b/Core/detection.js index 30a55148b4..943f2a4a67 100644 --- a/Core/detection.js +++ b/Core/detection.js @@ -20,7 +20,6 @@ (function() { duckduckgoDebugMessaging.log("installing beforeload detection") - document.addEventListener("beforeload", function(event) { if (event.target.nodeName == "LINK") { @@ -34,19 +33,14 @@ } duckduckgoDebugMessaging.log("checking " + event.url + " (" + type + ")"); - duckduckgoContentBlocking.shouldBlock(event.url, type, function(url, block) { - if (!block) { - duckduckgoDebugMessaging.log("don't block " + url); - return - } - + if (duckduckgoContentBlocking.shouldBlock(event.url, type)) { duckduckgoDebugMessaging.log("blocking beforeload") - if (duckduckgoContentBlocking.loadSurrogate(event.url)) { - duckduckgoDebugMessaging.log("surrogate loaded for " + event.url) - } event.preventDefault() event.stopPropagation() - }) + } else { + duckduckgoDebugMessaging.log("don't block " + event.url); + return + } }, true) @@ -62,14 +56,12 @@ set: function(value) { var instance = this - duckduckgoContentBlocking.shouldBlock(value, "image", function(url, block) { - if (block) { - duckduckgoDebugMessaging.log("blocking image src: " + url) - } else { - originalImageSrc.set.call(instance, value); - duckduckgoDebugMessaging.log("allowing image src: " + url) - } - }) + if (duckduckgoContentBlocking.shouldBlock(value, "image")) { + duckduckgoDebugMessaging.log("blocking image src: " + value) + } else { + originalImageSrc.set.call(instance, value); + duckduckgoDebugMessaging.log("allowing image src: " + value) + } } }) @@ -87,9 +79,9 @@ xhr.open = function() { var args = arguments var url = arguments[1] - duckduckgoContentBlocking.shouldBlock(url, "xmlhttprequest", function(url, block) { - args[1] = block ? "about:blank" : url - }) + if (duckduckgoContentBlocking.shouldBlock(url, "xmlhttprequest")) { + args[1] = "about:blank" + } duckduckgoDebugMessaging.log("sending xhr " + url + " to " + args[1]) return originalOpen.apply(this, args); } diff --git a/Core/disconnectme.js b/Core/disconnectme.js deleted file mode 100644 index a44e89c348..0000000000 --- a/Core/disconnectme.js +++ /dev/null @@ -1,48 +0,0 @@ -// -// disconnectme.js -// DuckDuckGo -// -// Copyright © 2017 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -var DisconnectMe = function() { - - // public - function parentTracker(url) { - - var splitHost = url.host.split(".") - while(splitHost.length >= 2) { - var domain = splitHost.join(".") - - var parentBlocked = duckduckgoBlockerData.disconnectmeBanned[domain] - if (parentBlocked) { - return { parent: parentBlocked, banned: true } - } - - var parentAllowed = duckduckgoBlockerData.disconnectmeAllowed[domain] - if (parentAllowed) { - return { parent: parentAllowed, banned: false } - } - - splitHost.shift() - } - - return null - } - - return { - parentTracker: parentTracker - } -}() diff --git a/Core/easylist-cached.js b/Core/easylist-cached.js deleted file mode 100644 index 94bf3fe117..0000000000 --- a/Core/easylist-cached.js +++ /dev/null @@ -1,68 +0,0 @@ -// -// easylist-cached.js -// DuckDuckGo -// -// Copyright © 2017 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -(function() { - - duckduckgoBlockerData.easylist = ${easylist_general_json} - duckduckgoBlockerData.easylistPrivacy = ${easylist_privacy_json} - duckduckgoBlockerData.easylistWhitelist = ${easylist_whitelist_json} - - function ddgFixSets(filters) { - for (var i = 0; i < filters.length; i++) { - - var options = filters[i].options - if (options == null) { continue } - if (options["binaryOptions"] == null) { continue } - - var ddgSet = options.binaryOptions["ddg_set"] - if (ddgSet == null) { continue } - - filters[i].options.binaryOptions = new Set(ddgSet) - } - } - - function ddgRepair(parserData) { - var startTime = performance.now() - parserData.bloomFilter = new BloomFilterModule.BloomFilter(parserData.bloomFilter) - parserData.exceptionBloomFilter = new BloomFilterModule.BloomFilter(parserData.exceptionBloomFilter) - - // find occurences of ddg_set and replace them with a set - ddgFixSets(parserData.filters) - ddgFixSets(parserData.exceptionFilters) - ddgFixSets(parserData.htmlRuleFilters) - ddgFixSets(parserData.noFingerprintFilters) - - duckduckgoDebugMessaging.signpostEvent({event: "Generic", - name: "ddgRepair", - time: performance.now() - startTime}) - } - - if (Object.keys(duckduckgoBlockerData.easylist).length > 0) { - ddgRepair(duckduckgoBlockerData.easylist) - } - - if (Object.keys(duckduckgoBlockerData.easylistPrivacy).length > 0) { - ddgRepair(duckduckgoBlockerData.easylistPrivacy) - } - - if (Object.keys(duckduckgoBlockerData.easylistWhitelist).length > 0) { - ddgRepair(duckduckgoBlockerData.easylistWhitelist) - } - -})() diff --git a/Core/easylist-parsing.js b/Core/easylist-parsing.js deleted file mode 100644 index 8c73c84b47..0000000000 --- a/Core/easylist-parsing.js +++ /dev/null @@ -1,57 +0,0 @@ -// -// easylist-parsing.js -// DuckDuckGo -// -// Copyright © 2017 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -(function (){ - - duckduckgoDebugMessaging.log("parsing adblock files") - - // from https://stackoverflow.com/a/46491780/73479 - function Set_toJSON(key, value) { - if (typeof value === 'object' && value instanceof Set) { - return { "ddg_set" : [...value] }; - } - return value; - } - - try { - - var easylistPrivacy = `${easylist_privacy}` - var easylistGeneral = `${easylist_general}` - var easylistWhitelist = `${easylist_whitelist}` - - if (easylistPrivacy != "") { - ABPFilterParser.parse(easylistPrivacy, duckduckgoBlockerData.easylistPrivacy) - } - duckduckgoMessaging.cache("easylist-privacy", JSON.stringify(duckduckgoBlockerData.easylistPrivacy, Set_toJSON)) - - if (easylistGeneral != "") { - ABPFilterParser.parse(easylistGeneral, duckduckgoBlockerData.easylist) - } - duckduckgoMessaging.cache("easylist", JSON.stringify(duckduckgoBlockerData.easylist, Set_toJSON)) - - if (easylistWhitelist != "") { - ABPFilterParser.parse(easylistWhitelist, duckduckgoBlockerData.easylistWhitelist) - } - duckduckgoMessaging.cache("easylist-whitelist", JSON.stringify(duckduckgoBlockerData.easylistWhitelist, Set_toJSON)) - - } catch (error) { - // no-op - } - -})() diff --git a/Core/messaging.js b/Core/messaging.js index 563e1dc449..c89a3385c7 100644 --- a/Core/messaging.js +++ b/Core/messaging.js @@ -19,17 +19,6 @@ var duckduckgoMessaging = function() { - function cache(name, value) { - try { - webkit.messageHandlers.cacheMessage.postMessage({ - name: name, - data: value - }); - } catch(error) { - // webkit might not be defined - } - } - function trackerDetected(data) { try { webkit.messageHandlers.trackerDetectedMessage.postMessage(data); @@ -40,7 +29,6 @@ var duckduckgoMessaging = function() { return { - cache: cache, trackerDetected: trackerDetected } diff --git a/Core/prevalence.json b/Core/prevalence.json deleted file mode 100644 index 06dc201a4e..0000000000 --- a/Core/prevalence.json +++ /dev/null @@ -1 +0,0 @@ -{"Google":83.513,"Taboola":3.027,"Facebook":16.092,"Twitter":6.715,"AddThis":3.77,"whos.amung.us":0.651,"Amazon.com":12.609,"Criteo":7.373,"nugg.ad":1.092,"AT Internet":0.855,"Médiamétrie-eStat":0.244,"PageFair":0.653,"Moat":0.569,"Horyzon Media":1.285,"AdSafe Media":0.529,"AppNexus":11.908,"Fox One Stop Media":4.833,"TripleLift":0.615,"Yandex":2.966,"PopAds":0.536,"Webtrends":0.363,"LiveInternet":2.273,"New Relic":8.7,"eXTReMe digital":0.174,"ADITION":0.628,"INFOnline":1.584,"WPP":1.029,"adscale":0.584,"ADTECH":1.372,"Casale Media":4.001,"OpenX":4.739,"Oath":6.054,"Adobe":9.557,"ValueClick":1.334,"Rhythm":1.311,"Federated Media":3.073,"CONTEXTWEB":2.829,"SiteScout":2.046,"EQ Ads":0.931,"ShareThis":0.676,"Rambler":0.386,"comScore":10.54,"Skimlinks":1.069,"Cross Pixel":0.191,"AK":1.391,"BlueKai":2.663,"MediaMath":4.619,"The Trade Desk":5.682,"Rocket Fuel":1.374,"Quantcast":6.814,"TNS":0.317,"AdFox":0.206,"Go Daddy":0.149,"unknown":4.092,"DoubleVerify":0.076,"SpotXchange":0.76,"DataXu":1.391,"Improve Digital":0.29,"Tapad":1.401,"Drawbridge":0.781,"Krux":2.945,"Rapleaf":1.796,"PubMatic":3.023,"StatCounter":1.363,"bigmir)net":0.059,"OptinMonster":0.386,"KISSmetrics":0.151,"Crazy Egg":1.796,"VigLink":0.916,"Weborama":0.378,"Chartbeat":3.176,"Wishabi":0.376,"Outbrain":1.344,"Lotame":3.413,"LinkedIn":0.823,"Histats":1.199,"Roxr":0.622,"Mixpanel":1.033,"Commission Junction":0.092,"Adverline":0.053,"m6d":1.559,"Meetrics":0.193,"Yieldlab":0.452,"xplosion interactive":0.498,"Gemius":0.878,"Demandbase":0.38,"ClickTale":0.349,"HP":0.034,"Ensighten":0.91,"Hotjar":2.867,"VKontakte":0.384,"reddit":0.193,"TubeMogul":0.523,"AdXpansion":0.05,"Adality":0.05,"Yieldmo":0.26,"media.net":0.83,"BuySellAds":0.334,"Simpli.fi":0.83,"GoStats":0.011,"Adform":1.433,"ExoClick":1.888,"Attracta":0.002,"EroAdvertising":0.376,"Soasta":0.781,"Exponential Interactive":0.466,"BloomReach":0.168,"eXelate":1.029,"Teads.tv":0.452,"LinkShare":0.229,"DC Storm":0.17,"cXense":0.519,"OwnerIQ":0.693,"Nativo":1.092,"Monetate":0.51,"Listrak":0.334,"Project Wonderful":0.032,"Gruner + Jahr":0.279,"Vibrant Media":0.258,"MarketGid":0.176,"Mouseflow":0.311,"Intercom":0.265,"sociomantic labs":0.099,"Specific Media":0.04,"ActiveConversion":0.002,"Nielsen":1.739,"Belstat":0.006,"Lockerz":0.336,"FriendFinder Networks":0.067,"Adverticum":0.057,"Flashtalking":0.204,"LiveIntent":0.271,"AdOcean":0.141,"Zemanta":0.355,"RadiumOne":0.235,"Netmining":0.281,"Microsoft":0.536,"AdRoll":1.021,"AWeber":0.256,"AdRiver":0.313,"RevContent":0.284,"AvantLink":0.08,"CPMStar":0.08,"JuicyAds":0.603,"BrightTag":0.536,"Datalogix":0.935,"Pictela":0.214,"Swoop":0.258,"Segment.io":0.626,"Inspectlet":0.284,"Bizo":0.206,"Oracle":0.624,"Certona":0.218,"Magnetic":0.403,"InvestingChannel":0.027,"Wysistat":0.021,"Amobee":0.042,"AdKernel":0.3,"AdGlare":0.023,"SessionCam":0.143,"SteelHouse":0.168,"Veeseo":0.013,"4shared.com":0.002,"OPT":0.021,"GetSiteControl":0.183,"HubSpot":0.559,"QuinStreet":0.055,"Adzerk":0.103,"FullStory":0.158,"Deep Intent":0.027,"FreeWheel":0.021,"Adotmob":0.143,"Admeta":0.076,"BLOOM Digital Platforms":0.166,"ZEDO":0.097,"plista":0.204,"Acxiom":0.034,"Technorati":0.027,"Evidon":0.481,"Think Realtime":0.008,"Undertone":0.053,"The Heron Partnership":0.323,"Intent Media":0.13,"Hurra.com":0.021,"IBM":0.3,"Adara Media":0.185,"DG":0.538,"etracker":0.246,"BrightEdge":0.032,"iPerceptions":0.21,"Tradedoubler":0.061,"adBrite":0.006,"Po.st":0.231,"Lakana":0.086,"Intergi":0.078,"Shareaholic":0.128,"AdTiger":0.011,"AdMedia":0.011,"eBay":0.187,"Marktest":0.029,"AT&T":0.027,"Marketo":0.611,"Polymorph":0.191,"FreakOut":0.078,"ClustrMaps":0.027,"Tinder":0.101,"Veoxa":0.008,"ReTargeter":0.019,"The Rimm-Kaufman Group":0.338,"Tremor Video":0.239,"iovation":0.002,"Intermarkets":0.019,"Qualaroo":0.704,"GSI Commerce":0.006,"ChannelAdvisor":0.143,"CNZZ":0.101,"SearchForce":0.006,"Cox Digital Solutions":0.263,"MailChimp":0.59,"Pardot":0.288,"Kantar Millward Brown":0.069,"NextPerformance":0.067,"orange.fr":0.019,"Webtrekk":0.162,"GumGum":0.92,"Renegade Internet":0.124,"Opera":0.011,"Spongecell":0.04,"Polar Mobile":0.265,"Silverpop":0.059,"Datonics":0.105,"Jumptap":0.017,"Acuity":0.16,"Mail.Ru":0.288,"Telstra":0.021,"AdServerPub":0.004,"Fairfax Media":0.013,"Evolve":0.046,"MaxPoint":0.059,"SupersonicAds":0.002,"agar.io":0.002,"zanox":0.092,"Neustar":0.111,"ad6media":0.046,"Selectable Media":0.048,"MerchantAdvantage":0.002,"BidVertiser":0.006,"Digital Target":0.168,"GetIntent":0.263,"Targetix":0.038,"LinkConnector":0.023,"AdReactor":0.061,"MetrixLab":0.021,"RichRelevance":0.208,"Monitus":0.017,"Internet Brands":0.12,"33Across":0.162,"Unruly":0.16,"NetSeer":0.137,"LifeStreet":0.086,"Adsty":0.13,"Effective Measure":0.103,"Switch":0.109,"AudienceScience":0.055,"SAS":0.061,"meinestadt.de":0.017,"I-Behavior":0.032,"ClickDistrict":0.155,"alibaba.com":0.004,"Underdog Media":0.05,"Automattic":0.155,"Chango":0.048,"Visible Measures":0.042,"CoinHive":0.101,"AdSpeed":0.04,"Kenshoo":0.086,"InfoStars":0.029,"Openstat":0.008,"RuTarget":0.078,"BlueCava":0.05,"eyeReturn Marketing":0.088,"engage:BDR":0.076,"Complex Media":0.069,"Infolinks":0.082,"Nexage":0.017,"BlogHer":0.078,"Jivox":0.036,"amarujala.com":0.002,"Navegg":0.092,"Caraytech":0.046,"Admotion":0.021,"Pressflex":0.008,"dianomi":0.053,"TruEffect":0.011,"ConversionRuler":0.002,"DynamicYield":0.153,"Adsupply":0.122,"ClickDimensions":0.034,"MicroAd":0.055,"anandabazar.com":0.002,"Emego":0.008,"Awio":0.008,"Forbes":0.004,"Instinctive":0.027,"ShinyStat":0.074,"GroovinAds":0.019,"Innity":0.032,"animenewsnetwork.com":0.002,"DirectAdvert":0.023,"Adiant":0.076,"AdSpirit":0.029,"Samurai Factory":0.013,"Salesforce.com":0.021,"GoDataFeed":0.013,"Fluct":0.034,"Glam Media":0.011,"Smowtion":0.002,"ara.cat":0.002,"StumbleUpon":0.05,"archiproducts.com":0.002,"argep.hu":0.002,"YuMe":0.034,"Research Now":0.071,"Adswizz":0.071,"nurago":0.011,"Woopra":0.044,"Developer Media":0.044,"Adventori":0.038,"ÖWA":0.155,"Adversal.com":0.008,"USI Technologies":0.053,"TouchCommerce":0.09,"Dataium":0.002,"Adnetik":0.015,"sophus3":0.053,"audi.co.uk":0.002,"Connexity":0.021,"Web.com":0.008,"Wirtualna Polska":0.053,"AdsTours":0.008,"Twyn Group":0.034,"avforums.com":0.002,"VisiStat":0.015,"Gannett":0.021,"ToneMedia":0.036,"Net-Results":0.008,"SocialTwist":0.004,"Mercent":0.038,"SAY":0.046,"TrafficHaus":0.084,"Rekko":0.004,"barclays.co.uk":0.002,"SmartLook":0.038,"Nanigans":0.057,"barstoolsports.com":0.002,"AdF.ly":0.015,"ADTELLIGENCE":0.013,"MarkMonitor":0.027,"VisualDNA":0.101,"User Local":0.032,"ShareASale":0.027,"Grapeshot":0.053,"C3 Metrics":0.032,"Performancing":0.011,"Net Applications":0.002,"mediaFORGE":0.044,"Dedicated Media":0.006,"Admicro":0.023,"Mirando":0.011,"Barilliance":0.008,"Fiksu":0.008,"nih.gov":0.006,"blizzardwatch.com":0.002,"Pinterest":0.029,"Delta Projects":0.011,"Shortest":0.013,"iPROM":0.011,"Ambient Digital":0.013,"FinancialContent":0.006,"Eulerian Technologies":0.008,"AdBrain":0.021,"Levexis":0.008,"britishairways.com":0.002,"brooksbrothers.com":0.002,"GitHub":0.044,"Opentracker":0.013,"adnologies":0.004,"ConvergeDirect":0.008,"AppsFlyer":0.025,"TradeTracker":0.008,"Streamray":0.008,"Public-Idées":0.006,"GoSquared":0.08,"Hearst":0.004,"Act-On":0.002,"nicovideo.jp":0.006,"sankakucomplex.com":0.004,"AdFrontiers":0.006,"chaturbate.com":0.004,"chatzy.com":0.002,"Amazing Counters":0.004,"uCoz":0.04,"cio.com":0.002,"Peer39":0.008,"clamav.net":0.002,"Collective":0.019,"Proclivity":0.032,"clubedohardware.com.br":0.002,"cnbc.com":0.002,"OneStat":0.021,"CPX Interactive":0.002,"wikimedia.org":0.008,"sosh.fr":0.002,"Optimizely":0.017,"LeadFormix":0.002,"computerworld.com":0.002,"Answers.com":0.002,"Chitika":0.015,"Httpool":0.013,"Spectate":0.008,"cqcounter.com":0.002,"Piximedia":0.017,"kohls.com":0.004,"adMarketplace":0.002,"Crowd Science":0.002,"dailycaller.com":0.002,"dailymail.co.uk":0.002,"AdvertiseSpace":0.002,"Match.com":0.008,"pornhub.com":0.021,"DoublePimp":0.029,"HealthPricer":0.006,"Kontera":0.019,"Feedjit":0.015,"HitsLink":0.015,"Unanimis":0.002,"NetShelter":0.008,"Digg":0.004,"Vizury":0.015,"Akamai":0.002,"LeadLander":0.011,"Digital River":0.013,"Begun":0.019,"MaxBounty":0.002,"Flite":0.008,"eProof.com":0.004,"Vdopia":0.013,"Monster":0.034,"xAd":0.013,"Augur":0.011,"eldiario.es":0.004,"Snoobi":0.008,"Kokteyl":0.004,"eurogamer.net":0.002,"eventhubs.com":0.002,"eventim.de":0.002,"Everyday Health":0.008,"Experian":0.004,"explosm.net":0.002,"fandango.com":0.004,"Affinity":0.004,"filmon.com":0.002,"Etarget":0.008,"financialexpress.com":0.002,"expedia.com":0.002,"flvto.biz":0.004,"Adworx":0.021,"I.UA":0.015,"HOTWords":0.002,"Ad Decisive":0.008,"bodybuilding.com":0.002,"AddFreeStats":0.013,"canoe.ca":0.002,"Ad4Game":0.011,"freewka.com":0.002,"Tisoomi":0.013,"gamecopyworld.com":0.002,"washingtonpost.com":0.006,"gamesgames.com":0.002,"Infectious Media":0.004,"AdReady":0.013,"RMBN":0.027,"Betgenius":0.004,"Accelia":0.004,"NetElixir":0.004,"walmart.com":0.006,"Adometry":0.006,"Meebo":0.002,"spiegel.de":0.006,"hd-porn.me":0.002,"ClearSaleing":0.004,"Addvantage Media":0.002,"hentai-foundry.com":0.002,"Syncapse":0.004,"ADP Dealer Services":0.008,"usps.com":0.004,"ibis.com":0.002,"craveonline.com":0.002,"Hi-media":0.002,"Adlucent":0.002,"Meteor":0.002,"infoworld.com":0.002,"Marchex":0.004,"ip-address.org":0.002,"itv.com":0.002,"Nokta":0.004,"AndBeyond":0.002,"jagranjosh.com":0.002,"Sparklit":0.002,"AdLantis":0.002,"The Numa Group":0.011,"Acquisio":0.006,"RadarURL":0.002,"kentucky.com":0.002,"ndtv.com":0.006,"Wingify":0.002,"365Media":0.013,"myThings":0.004,"Applovin":0.002,"AdPerfect":0.004,"lipsum.com":0.002,"mercadolivre.com.br":0.002,"apache.org":0.002,"Keyade":0.008,"BackBeat Media":0.004,"IgnitionOne":0.002,"KeyMetric":0.008,"4INFO":0.006,"rp-online.de":0.004,"seekingalpha.com":0.004,"StrikeAd":0.002,"Earnify":0.002,"BlogCatalog":0.002,"manga-news.com":0.002,"Wahoha":0.002,"medicare.gov":0.002,"mid-day.com":0.002,"milb.com":0.002,"perezhilton.com":0.004,"Mongoose Metrics":0.006,"Layer-Ad.org":0.002,"nationalcar.com":0.002,"affilinet":0.008,"necn.com":0.002,"networkworld.com":0.002,"next-episode.net":0.002,"Nextag":0.002,"TrackingSoft":0.002,"jimmyjohns.com":0.002,"Web Traxs":0.004,"overclock3d.net":0.002,"Brand.net":0.002,"pbskids.org":0.002,"Publishers Clearing House":0.002,"pep.ph":0.002,"Buysight":0.004,"VerticalResponse":0.002,"politiken.dk":0.002,"pornfun.com":0.002,"Web Stats":0.004,"AdEngage":0.002,"pornxs.com":0.002,"ti.com":0.002,"Web Tracking Services":0.006,"rakuten.co.jp":0.004,"ratemyprofessors.com":0.002,"Sapient":0.004,"redtube.com":0.002,"rockpapershotgun.com":0.002,"Triggit":0.002,"safeway.com":0.002,"sahibinden.com":0.002,"searchengineland.com":0.002,"CheckM8":0.004,"snapchat.com":0.002,"sonypictures.com":0.002,"southwest.com":0.002,"sparkylinux.org":0.002,"Communicator Corp":0.002,"dlink.com":0.002,"Terra":0.002,"texasroadhouse.com":0.002,"tf1.fr":0.002,"Clickdensity":0.002,"Ninua":0.004,"LiveRail":0.002,"thenextweb.com":0.002,"thesimsresource.com":0.002,"thetvdb.com":0.002,"Xrost DS":0.002,"Umbel":0.004,"tv5monde.com":0.002,"TellApart":0.004,"unrealengine.com":0.002,"vg247.com":0.002,"foxnews.com":0.002,"nationalgeographic.com":0.002,"vistek.ca":0.002,"vodafone.com.au":0.002,"walmartmoneycard.com":0.002,"Radiate Media":0.002,"dhlglobalmail.com":0.002,"NextSTAT":0.002,"Adforge":0.002,"Burst Media":0.002,"WordStream":0.002,"writing.com":0.002,"ynet.co.il":0.002,"zerozero.pt":0.002,"zorinos.com":0.002} \ No newline at end of file diff --git a/Core/tlds.js b/Core/tlds.js deleted file mode 100644 index 0ffb4e42fd..0000000000 --- a/Core/tlds.js +++ /dev/null @@ -1,36 +0,0 @@ - -// Based on https://github.com/131/node-tld -var duckduckgoTLDParser = function() { - - var tlds = ${tlds} - - // public - function extractDomain(url) { - var host = url.hostname - var parts = host.split(".") - var stack = "" - var tldLevel = 1 //unknown tld are 1st level - - for(var i = parts.length - 1, part; i >= 0; i--) { - part = parts[i] - stack = stack ? part + "." + stack : part - - if (!tlds[stack]) { - break - } - - tldLevel = tlds[stack] - } - - if (parts.length <= tldLevel) { - return parts[0] - } - - return parts.slice(-tldLevel - 1).join('.') - } - - return { - extractDomain: extractDomain - } - -}() diff --git a/Core/trackerData.json b/Core/trackerData.json new file mode 100644 index 0000000000..8c3c0d957f --- /dev/null +++ b/Core/trackerData.json @@ -0,0 +1,17034 @@ +{ + "trackers": { + "1dmp.io": { + "domain": "1dmp.io", + "default": "block", + "owner": { + "name": "CleverDATA LLC", + "displayName": "CleverDATA", + "privacyPolicy": "https://hermann.ai/privacy-en", + "url": "http://hermann.ai" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing" + ] + }, + "1rx.io": { + "domain": "1rx.io", + "default": "block", + "owner": { + "name": "RhythmOne", + "displayName": "RhythmOne", + "privacyPolicy": "https://www.rhythmone.com/privacy-policy", + "url": "http://rhythmone.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.055, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Third-Party Analytics Marketing" + ] + }, + "254a.com": { + "domain": "254a.com", + "default": "block", + "owner": { + "name": "Yieldr", + "displayName": "Yieldr", + "privacyPolicy": "https://www.yieldr.com/privacy/", + "url": "http://yieldr.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.011, + "fingerprinting": 0, + "cookies": 0.011, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing", + "Action Pixels", + "Obscure Ownership" + ] + }, + "2mdn.net": { + "domain": "2mdn.net", + "default": "block", + "owner": { + "name": "Google LLC", + "displayName": "Google", + "privacyPolicy": "https://policies.google.com/privacy?hl=en&gl=us", + "url": "http://google.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.09, + "fingerprinting": 1, + "cookies": 0.009, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud" + ], + "rules": [ + { + "rule": "2mdn\\.net\\/instream\\/html5\\/ima3\\.js", + "action": "ignore" + }, + { + "rule": "2mdn\\.net\\/instream\\/video\\/client\\.js", + "exceptions": { + "domains": [ + "surfline.com", + "dailywire.com" + ] + } + } + ] + }, + "2o7.net": { + "domain": "2o7.net", + "default": "block", + "owner": { + "name": "Adobe Inc.", + "displayName": "Adobe", + "privacyPolicy": "https://www.adobe.com/privacy/marketing-cloud.html", + "url": "http://adobe.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels" + ], + "rules": [ + { + "rule": "2o7\\.net", + "exceptions": { + "domains": [ + "tennislink.usta.com", + "kiplinger.com", + "dunesvillage.com" + ], + "types": [ + "image" + ] + } + } + ] + }, + "33across.com": { + "domain": "33across.com", + "default": "block", + "owner": { + "name": "33Across, Inc.", + "displayName": "33Across", + "privacyPolicy": "https://33across.com/privacy-policy/", + "url": "http://33across.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.028, + "fingerprinting": 0, + "cookies": 0.028, + "performance": { + "time": 1, + "size": 1, + "cpu": 3, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Action Pixels" + ] + }, + "360yield.com": { + "domain": "360yield.com", + "default": "block", + "owner": { + "name": "Improve Digital BV", + "displayName": "Improve Digital", + "privacyPolicy": "https://www.improvedigital.com/platform-privacy-policy/", + "url": "http://improvedigital.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.017, + "fingerprinting": 0, + "cookies": 0.017, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing" + ] + }, + "3lift.com": { + "domain": "3lift.com", + "default": "block", + "owner": { + "name": "TripleLift", + "displayName": "TripleLift", + "privacyPolicy": "https://triplelift.com/privacy/", + "url": "http://triplelift.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.039, + "fingerprinting": 0, + "cookies": 0.039, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "abtasty.com": { + "domain": "abtasty.com", + "default": "block", + "owner": { + "name": "Liwio", + "displayName": "Liwio", + "privacyPolicy": "https://www.abtasty.com/terms-of-use/", + "url": "http://abtasty.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 2, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 3 + }, + "categories": [ + "Analytics", + "Third-Party Analytics Marketing", + "Session Replay" + ] + }, + "acuityplatform.com": { + "domain": "acuityplatform.com", + "default": "block", + "owner": { + "name": "AcuityAds", + "displayName": "AcuityAds", + "privacyPolicy": "https://acuityscheduling.com/privacy.php", + "url": "http://acuityscheduling.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 0, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "addthis.com": { + "domain": "addthis.com", + "default": "block", + "owner": { + "name": "Oracle Corporation", + "displayName": "Oracle", + "privacyPolicy": "https://www.oracle.com/legal/privacy/privacy-policy.html", + "url": "http://oracle.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.113, + "fingerprinting": 1, + "cookies": 0.081, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Social - Share", + "Embedded Content" + ] + }, + "addtoany.com": { + "domain": "addtoany.com", + "default": "block", + "owner": { + "name": "AddToAny", + "displayName": "AddToAny", + "privacyPolicy": "https://www.addtoany.com/privacy", + "url": "http://addtoany.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.011, + "fingerprinting": 1, + "cookies": 0.011, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing", + "Social - Share", + "Embedded Content" + ] + }, + "adentifi.com": { + "domain": "adentifi.com", + "default": "block", + "owner": { + "name": "AdTheorent Inc", + "displayName": "AdTheorent", + "privacyPolicy": "https://www.adtheorent.com/privacy-policy", + "url": "http://adtheorent.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.016, + "fingerprinting": 0, + "cookies": 0.015, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud" + ] + }, + "adform.net": { + "domain": "adform.net", + "default": "block", + "owner": { + "name": "Adform A/S", + "displayName": "Adform", + "privacyPolicy": "https://site.adform.com/privacy-center/overview/", + "url": "http://adform.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.07, + "fingerprinting": 3, + "cookies": 0.062, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Audience Measurement" + ] + }, + "adfox.ru": { + "domain": "adfox.ru", + "default": "block", + "owner": { + "name": "Yandex LLC", + "displayName": "Yandex", + "privacyPolicy": "https://yandex.com/legal/privacy/", + "url": "http://yandex.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "adgrx.com": { + "domain": "adgrx.com", + "default": "block", + "owner": { + "name": "AdGear Technologies Inc.", + "displayName": "AdGear", + "privacyPolicy": "https://adgear.com/en/privacy/", + "url": "http://adgear.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.013, + "fingerprinting": 0, + "cookies": 0.013, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Third-Party Analytics Marketing" + ] + }, + "adhigh.net": { + "domain": "adhigh.net", + "default": "block", + "owner": { + "name": "GetIntent", + "displayName": "GetIntent", + "privacyPolicy": "https://getintent.com/privacy/", + "url": "http://getintent.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.015, + "fingerprinting": 0, + "cookies": 0.014, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "adition.com": { + "domain": "adition.com", + "default": "block", + "owner": { + "name": "Virtual Minds AG", + "displayName": "Virtual Minds", + "privacyPolicy": "https://www.virtualminds.de/en/", + "url": "http://virtualminds.de" + }, + "source": [ + "DDG" + ], + "prevalence": 0.015, + "fingerprinting": 0, + "cookies": 0.015, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "adkernel.com": { + "domain": "adkernel.com", + "default": "block", + "owner": { + "name": "Adkernel, LLC", + "displayName": "Adkernel", + "privacyPolicy": "https://adkernel.com/privacy-policy/", + "url": "http://adkernel.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 0, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing" + ] + }, + "adlightning.com": { + "domain": "adlightning.com", + "default": "block", + "owner": { + "name": "Ad Lightning, Inc.", + "displayName": "Ad Lightning", + "privacyPolicy": "https://www.adlightning.com/privacy", + "url": "http://adlightning.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "admedo.com": { + "domain": "admedo.com", + "default": "block", + "owner": { + "name": "Admedo", + "displayName": "Admedo", + "privacyPolicy": "https://www.admedo.com/privacy-policy", + "url": "http://admedo.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.007, + "fingerprinting": 0, + "cookies": 0.007, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "admixer.net": { + "domain": "admixer.net", + "default": "block", + "owner": { + "name": "Admixer Technologies", + "displayName": "Admixer", + "privacyPolicy": "https://admixer.net/privacy", + "url": "http://admixer.net" + }, + "source": [ + "DDG" + ], + "prevalence": 0.009, + "fingerprinting": 0, + "cookies": 0.009, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "adnium.com": { + "domain": "adnium.com", + "default": "block", + "owner": { + "name": "Adnium Inc", + "displayName": "Adnium", + "privacyPolicy": "https://adnium.com/privacy", + "url": "http://adnium.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 3, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 3, + "cpu": 3, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "adnxs.com": { + "domain": "adnxs.com", + "default": "block", + "owner": { + "name": "AppNexus, Inc.", + "displayName": "AppNexus", + "privacyPolicy": "https://www.appnexus.com/en/company/privacy-policy", + "url": "http://appnexus.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.223, + "fingerprinting": 0, + "cookies": 0.161, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ], + "rules": [ + { + "rule": "adnxs\\.com", + "exceptions": { + "domains": [ + "bild.de", + "aftonbladet.se", + "thechive.com", + "foxnews.com", + "foxbusiness.com" + ], + "types": [ + "script" + ] + } + } + ] + }, + "adobedtm.com": { + "domain": "adobedtm.com", + "default": "block", + "owner": { + "name": "Adobe Inc.", + "displayName": "Adobe", + "privacyPolicy": "https://www.adobe.com/privacy/marketing-cloud.html", + "url": "http://adobe.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.032, + "fingerprinting": 2, + "cookies": 0.014, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Audience Measurement", + "Third-Party Analytics Marketing" + ], + "rules": [ + { + "rule": "assets\\.adobedtm\\.com", + "action": "ignore" + } + ] + }, + "adotmob.com": { + "domain": "adotmob.com", + "default": "block", + "owner": { + "name": "A.Mob SAS", + "displayName": "A.Mob", + "privacyPolicy": "https://adotmob.com/privacy.html", + "url": "http://adotmob.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "adriver.ru": { + "domain": "adriver.ru", + "default": "block", + "owner": { + "name": "LLC \"Internest-holding\"", + "displayName": "Internest-holding", + "privacyPolicy": "https://www.adriver.ru/about/privacy-en/", + "url": "http://adriver.ru" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "adroll.com": { + "domain": "adroll.com", + "default": "block", + "owner": { + "name": "AdRoll, Inc.", + "displayName": "AdRoll", + "privacyPolicy": "https://www.adroll.com/about/privacy", + "url": "http://adroll.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.014, + "fingerprinting": 0, + "cookies": 0.013, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "ads-twitter.com": { + "domain": "ads-twitter.com", + "default": "block", + "owner": { + "name": "Twitter, Inc.", + "displayName": "Twitter", + "privacyPolicy": "https://twitter.com/en/privacy", + "url": "http://twitter.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.046, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "adsafeprotected.com": { + "domain": "adsafeprotected.com", + "default": "block", + "owner": { + "name": "Integral Ad Science, Inc.", + "displayName": "Integral Ad Science", + "privacyPolicy": "https://integralads.com/privacy-policy/", + "url": "http://integralads.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.044, + "fingerprinting": 3, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 3, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud" + ] + }, + "adscale.de": { + "domain": "adscale.de", + "default": "block", + "owner": { + "name": "Ströer Group", + "displayName": "Ströer Group", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 0, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "adsco.re": { + "domain": "adsco.re", + "default": "block", + "owner": { + "name": "Adscore Technologies DMCC", + "displayName": "Adscore", + "privacyPolicy": "https://www.adscore.com/privacy-policy", + "url": "http://adscore.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.007, + "fingerprinting": 3, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 3, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Ad Fraud", + "Analytics", + "Audience Measurement" + ] + }, + "adsrvr.org": { + "domain": "adsrvr.org", + "default": "block", + "owner": { + "name": "The Trade Desk Inc", + "displayName": "The Trade Desk", + "privacyPolicy": "https://www.thetradedesk.com/general/privacy-policy", + "url": "http://thetradedesk.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.191, + "fingerprinting": 1, + "cookies": 0.189, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "adstanding.com": { + "domain": "adstanding.com", + "default": "block", + "owner": { + "name": "AdStanding", + "displayName": "AdStanding", + "privacyPolicy": "https://standingroomonly.tv/privacy-policy/", + "url": "http://standingroomonly.tv" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 0, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "adsymptotic.com": { + "domain": "adsymptotic.com", + "default": "block", + "owner": { + "name": "Drawbridge Inc", + "displayName": "Drawbridge", + "privacyPolicy": "https://drawbridge.com/privacy/", + "url": "http://drawbridge.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.081, + "fingerprinting": 0, + "cookies": 0.08, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "adtech.de": { + "domain": "adtech.de", + "default": "block", + "owner": { + "name": "Verizon Media", + "displayName": "Verizon Media", + "privacyPolicy": "https://www.verizon.com/about/privacy/privacy-policy-summary", + "url": "http://verizon.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "adthrive.com": { + "domain": "adthrive.com", + "default": "block", + "owner": { + "name": "AdThrive, LLC", + "displayName": "AdThrive", + "privacyPolicy": "https://www.adthrive.com/privacy/", + "url": "http://adthrive.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 2, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Advertising" + ] + }, + "advertising.com": { + "domain": "advertising.com", + "default": "block", + "owner": { + "name": "Verizon Media", + "displayName": "Verizon Media", + "privacyPolicy": "https://www.verizon.com/about/privacy/privacy-policy-summary", + "url": "http://verizon.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.108, + "fingerprinting": 0, + "cookies": 0.095, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "agkn.com": { + "domain": "agkn.com", + "default": "block", + "owner": { + "name": "Neustar, Inc.", + "displayName": "Neustar", + "privacyPolicy": "https://www.home.neustar/privacy", + "url": "http://home.neustar" + }, + "source": [ + "DDG" + ], + "prevalence": 0.083, + "fingerprinting": 0, + "cookies": 0.079, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "aidata.io": { + "domain": "aidata.io", + "default": "block", + "owner": { + "name": "Aidata", + "displayName": "Aidata", + "privacyPolicy": "https://my.aidata.me/data/uploads/aidata.me-privacy-policy.pdf", + "url": "http://aidata.me" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "alcmpn.com": { + "domain": "alcmpn.com", + "default": "block", + "owner": { + "name": "ALC", + "displayName": "ALC", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Analytics", + "Audience Measurement" + ] + }, + "alexametrics.com": { + "domain": "alexametrics.com", + "default": "block", + "owner": { + "name": "Amazon Technologies, Inc.", + "displayName": "Amazon", + "privacyPolicy": "https://www.amazon.com/gp/help/customer/display.html?nodeId=468496", + "url": "http://amazon.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.017, + "fingerprinting": 1, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Audience Measurement" + ] + }, + "amazon-adsystem.com": { + "domain": "amazon-adsystem.com", + "default": "block", + "owner": { + "name": "Amazon Technologies, Inc.", + "displayName": "Amazon", + "privacyPolicy": "https://www.amazon.com/gp/help/customer/display.html?nodeId=468496", + "url": "http://amazon.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.094, + "fingerprinting": 1, + "cookies": 0.073, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ], + "rules": [ + { + "rule": "c\\.amazon-adsystem\\.com\\/aax2\\/apstag\\.js", + "exceptions": { + "domains": [ + "history.com", + "aetv.com", + "mylifetime.com", + "fyi.tv", + "sueddeutsche.de", + "foxnews.com", + "foxbusiness.com" + ] + } + }, + { + "rule": "amazon-adsystem\\.com\\/aax2/amzn_ads.js", + "surrogate": "amzn_ads.js" + } + ] + }, + "amazon.com": { + "domain": "amazon.com", + "default": "block", + "owner": { + "name": "Amazon Technologies, Inc.", + "displayName": "Amazon", + "privacyPolicy": "https://www.amazon.com/gp/help/customer/display.html?nodeId=468496", + "url": "http://amazon.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Analytics", + "SSO", + "Third-Party Analytics Marketing", + "Online Payment", + "Embedded Content" + ], + "rules": [ + { + "rule": "payments\\.amazon\\.com", + "action": "ignore" + }, + { + "rule": "apay-us\\.amazon\\.com", + "action": "ignore" + }, + { + "rule": "payments-uk\\.amazon\\.com", + "action": "ignore" + }, + { + "rule": "payments-de\\.amazon\\.com", + "action": "ignore" + }, + { + "rule": "api-cdn\\.amazon\\.com\\/sdk", + "exceptions": { + "types": [ + "subdocument", + "script" + ] + } + }, + { + "rule": "amazon\\.com\\/ap\\/oa", + "exceptions": { + "types": [ + "subdocument" + ] + } + }, + { + "rule": "api\\.amazon\\.com\\/auth", + "exceptions": { + "types": [ + "xmlhttprequest", + "script" + ] + } + } + ] + }, + "amplitude.com": { + "domain": "amplitude.com", + "default": "block", + "owner": { + "name": "Amplitude", + "displayName": "Amplitude", + "privacyPolicy": "https://amplitude.com/privacy", + "url": "http://amplitude.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 1, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics" + ] + }, + "amung.us": { + "domain": "amung.us", + "default": "block", + "owner": { + "name": "whos.amung.us Inc", + "displayName": "whos.amung.us", + "privacyPolicy": "https://whos.amung.us/legal/privacy/", + "url": "http://amung.us" + }, + "source": [ + "DDG" + ], + "prevalence": 0.007, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement" + ] + }, + "app.link": { + "domain": "app.link", + "default": "block", + "owner": { + "name": "Branch Metrics, Inc.", + "displayName": "Branch Metrics", + "privacyPolicy": "https://branch.io/policies/", + "url": "http://branch.io" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "appdynamics.com": { + "domain": "appdynamics.com", + "default": "block", + "owner": { + "name": "AppDynamics LLC", + "displayName": "AppDynamics", + "privacyPolicy": "https://www.appdynamics.com/privacy-policy/", + "url": "http://appdynamics.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 1, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Analytics" + ] + }, + "apxlv.com": { + "domain": "apxlv.com", + "default": "block", + "owner": { + "name": "Cogo Labs", + "displayName": "Cogo Labs", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.019, + "fingerprinting": 0, + "cookies": 0.019, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics" + ] + }, + "areyouahuman.com": { + "domain": "areyouahuman.com", + "default": "block", + "owner": { + "name": "Imperva Inc.", + "displayName": "Imperva", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 3, + "cookies": 0.006, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Ad Fraud" + ] + }, + "assoc-amazon.com": { + "domain": "assoc-amazon.com", + "default": "block", + "owner": { + "name": "Amazon Technologies, Inc.", + "displayName": "Amazon", + "privacyPolicy": "https://www.amazon.com/gp/help/customer/display.html?nodeId=468496", + "url": "http://amazon.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Action Pixels" + ] + }, + "atdmt.com": { + "domain": "atdmt.com", + "default": "block", + "owner": { + "name": "Facebook, Inc.", + "displayName": "Facebook", + "privacyPolicy": "https://www.facebook.com/privacy/explanation", + "url": "http://facebook.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.05, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "avocet.io": { + "domain": "avocet.io", + "default": "block", + "owner": { + "name": "Avocet Systems Ltd.", + "displayName": "Avocet Systems", + "privacyPolicy": "https://avocet.io/privacy-portal", + "url": "http://avocet.io" + }, + "source": [ + "DDG" + ], + "prevalence": 0.017, + "fingerprinting": 0, + "cookies": 0.017, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Audience Measurement" + ] + }, + "basis.net": { + "domain": "basis.net", + "default": "block", + "owner": { + "name": "Centro, Inc.", + "displayName": "Centro", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Action Pixels" + ] + }, + "bazaarvoice.com": { + "domain": "bazaarvoice.com", + "default": "block", + "owner": { + "name": "Bazaarvoice, Inc.", + "displayName": "Bazaarvoice", + "privacyPolicy": "https://www.bazaarvoice.com/legal/privacy-policy/", + "url": "http://bazaarvoice.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 2, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels", + "Embedded Content" + ], + "rules": [ + { + "rule": "ugc\\.bazaarvoice\\.com\\/static", + "exceptions": { + "types": [ + "script" + ] + } + }, + { + "rule": "ugc\\.bazaarvoice\\.com\\/.*\\/reviews\\.djs", + "exceptions": { + "types": [ + "script" + ] + } + }, + { + "rule": "ugc\\.bazaarvoice\\.com\\/module", + "exceptions": { + "types": [ + "script" + ] + } + }, + { + "rule": "ugc\\.bazaarvoice\\.com\\/answers", + "exceptions": { + "types": [ + "script" + ] + } + }, + { + "rule": "ugc\\.bazaarvoice\\.com\\/submit", + "exceptions": { + "types": [ + "script" + ] + } + }, + { + "rule": "api\\.bazaarvoice\\.com\\/data", + "exceptions": { + "types": [ + "xmlhttprequest", + "script", + "subdocument" + ] + } + }, + { + "rule": "bazaarvoice\\.com\\/photo", + "exceptions": { + "types": [ + "image" + ] + } + }, + { + "rule": "apps\\.bazaarvoice\\.com", + "exceptions": { + "types": [ + "script" + ] + } + }, + { + "rule": "ugc\\.bazaarvoice\\.com", + "exceptions": { + "types": [ + "subdocument", + "image", + "stylesheet" + ] + } + } + ] + }, + "betrad.com": { + "domain": "betrad.com", + "default": "block", + "owner": { + "name": "Crownpeak Technology", + "displayName": "Crownpeak", + "privacyPolicy": "https://www.crownpeak.com/privacy.aspx", + "url": "http://crownpeak.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Analytics", + "Third-Party Analytics Marketing" + ] + }, + "bfmio.com": { + "domain": "bfmio.com", + "default": "block", + "owner": { + "name": "Beachfront Media LLC", + "displayName": "Beachfront Media", + "privacyPolicy": "http://beachfront.com/privacy-policy/", + "url": "http://beachfront.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 0, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "bidr.io": { + "domain": "bidr.io", + "default": "block", + "owner": { + "name": "Beeswax", + "displayName": "Beeswax", + "privacyPolicy": "https://www.beeswax.com/privacy.html", + "url": "http://beeswax.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.058, + "fingerprinting": 0, + "cookies": 0.058, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "bidswitch.net": { + "domain": "bidswitch.net", + "default": "block", + "owner": { + "name": "IPONWEB GmbH", + "displayName": "IPONWEB", + "privacyPolicy": "https://www.iponweb.com/privacy-policy/", + "url": "http://iponweb.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.107, + "fingerprinting": 0, + "cookies": 0.105, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud" + ] + }, + "bizographics.com": { + "domain": "bizographics.com", + "default": "block", + "owner": { + "name": "LinkedIn Corporation", + "displayName": "LinkedIn", + "privacyPolicy": "https://www.linkedin.com/legal/privacy-policy", + "url": "http://linkedin.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.019, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Analytics", + "Audience Measurement", + "Action Pixels" + ] + }, + "bizrate.com": { + "domain": "bizrate.com", + "default": "block", + "owner": { + "name": "Synapse Group, Inc.", + "displayName": "Synapse Group", + "privacyPolicy": "http://about.bizrate.com/privacy-policy", + "url": "http://bizrate.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels", + "Embedded Content" + ] + }, + "bkrtx.com": { + "domain": "bkrtx.com", + "default": "block", + "owner": { + "name": "Oracle Corporation", + "displayName": "Oracle", + "privacyPolicy": "https://www.oracle.com/legal/privacy/privacy-policy.html", + "url": "http://oracle.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.011, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "blismedia.com": { + "domain": "blismedia.com", + "default": "block", + "owner": { + "name": "Blis Media Limited", + "displayName": "Blis Media", + "privacyPolicy": "http://www.blis.com/privacy/", + "url": "http://blis.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "bluecava.com": { + "domain": "bluecava.com", + "default": "block", + "owner": { + "name": "QBC Holdings, Inc.", + "displayName": "QBC Holdings", + "privacyPolicy": "https://www.alc.com/privacy-policy", + "url": "http://alc.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 2, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Ad Fraud", + "Analytics", + "Audience Measurement" + ] + }, + "blueconic.net": { + "domain": "blueconic.net", + "default": "block", + "owner": { + "name": "BlueConic, Inc.", + "displayName": "BlueConic", + "privacyPolicy": "https://www.blueconic.com/privacy-policy/", + "url": "http://blueconic.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 3, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing", + "Action Pixels" + ], + "rules": [ + { + "rule": "cdn\\.blueconic\\.net\\/salemmediagroup\\.js", + "exceptions": { + "domains": [ + "biblestudytools.com" + ], + "types": [ + "script" + ] + } + } + ] + }, + "bluekai.com": { + "domain": "bluekai.com", + "default": "block", + "owner": { + "name": "Oracle Corporation", + "displayName": "Oracle", + "privacyPolicy": "https://www.oracle.com/legal/privacy/privacy-policy.html", + "url": "http://oracle.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.092, + "fingerprinting": 0, + "cookies": 0.088, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement" + ] + }, + "bongacash.com": { + "domain": "bongacash.com", + "default": "block", + "owner": { + "name": "BongaCams", + "displayName": "BongaCams", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 2, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Advertising", + "Embedded Content" + ] + }, + "bounceexchange.com": { + "domain": "bounceexchange.com", + "default": "block", + "owner": { + "name": "Bounce Exchange", + "displayName": "Bounce Exchange", + "privacyPolicy": "https://www.bouncex.com/privacy/", + "url": "http://bouncex.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 3, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Action Pixels" + ] + }, + "branch.io": { + "domain": "branch.io", + "default": "block", + "owner": { + "name": "Branch Metrics, Inc.", + "displayName": "Branch Metrics", + "privacyPolicy": "https://branch.io/policies/", + "url": "http://branch.io" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 2, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "brealtime.com": { + "domain": "brealtime.com", + "default": "block", + "owner": { + "name": "ORC International", + "displayName": "ORC International", + "privacyPolicy": "https://orcinternational.com/privacy/", + "url": "http://orcinternational.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.024, + "fingerprinting": 0, + "cookies": 0.011, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement" + ] + }, + "bronto.com": { + "domain": "bronto.com", + "default": "block", + "owner": { + "name": "Oracle Corporation", + "displayName": "Oracle", + "privacyPolicy": "https://www.oracle.com/legal/privacy/privacy-policy.html", + "url": "http://oracle.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 1, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels", + "Embedded Content" + ] + }, + "browser-update.org": { + "domain": "browser-update.org", + "default": "block", + "owner": { + "name": "Browser Update", + "displayName": "Browser Update", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 1, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Badge", + "Embedded Content" + ] + }, + "btstatic.com": { + "domain": "btstatic.com", + "default": "block", + "owner": { + "name": "Signal Digital, Inc.", + "displayName": "Signal Digital", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.015, + "fingerprinting": 3, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "bttrack.com": { + "domain": "bttrack.com", + "default": "block", + "owner": { + "name": "Bidtellect, Inc", + "displayName": "Bidtellect", + "privacyPolicy": "https://bidtellect.com/privacy-policy/", + "url": "http://bidtellect.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.013, + "fingerprinting": 0, + "cookies": 0.009, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Analytics", + "Embedded Content" + ] + }, + "buysellads.com": { + "domain": "buysellads.com", + "default": "block", + "owner": { + "name": "BuySellAds", + "displayName": "BuySellAds", + "privacyPolicy": "https://www.buysellads.com/about/privacy", + "url": "http://buysellads.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Advertising" + ], + "rules": [ + { + "rule": "buysellads\\.com\\/ac\\/bsa\\.js", + "exceptions": { + "domains": [ + "whalesrule.tumblr.com" + ] + } + } + ] + }, + "casalemedia.com": { + "domain": "casalemedia.com", + "default": "block", + "owner": { + "name": "Index Exchange, Inc.", + "displayName": "Index Exchange", + "privacyPolicy": "http://casalemedia.com/", + "url": "http://casalemedia.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.132, + "fingerprinting": 0, + "cookies": 0.129, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "chartbeat.com": { + "domain": "chartbeat.com", + "default": "block", + "owner": { + "name": "Chartbeat", + "displayName": "Chartbeat", + "privacyPolicy": "https://chartbeat.com/privacy/", + "url": "http://chartbeat.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.021, + "fingerprinting": 2, + "cookies": 0.019, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement" + ], + "rules": [ + { + "rule": "chartbeat\\.com\\/chartbeat.js", + "surrogate": "chartbeat.js" + } + ] + }, + "chartbeat.net": { + "domain": "chartbeat.net", + "default": "block", + "owner": { + "name": "Chartbeat", + "displayName": "Chartbeat", + "privacyPolicy": "https://chartbeat.com/privacy/", + "url": "http://chartbeat.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.022, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement" + ] + }, + "chaturbate.com": { + "domain": "chaturbate.com", + "default": "block", + "owner": { + "name": "Chaturbate, LLC", + "displayName": "Chaturbate", + "privacyPolicy": "https://chaturbate.com/privacy/", + "url": "http://chaturbate.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Embedded Content" + ] + }, + "clickagy.com": { + "domain": "clickagy.com", + "default": "block", + "owner": { + "name": "Clickagy", + "displayName": "Clickagy", + "privacyPolicy": "https://www.clickagy.com/privacy/", + "url": "http://clickagy.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "clickcertain.com": { + "domain": "clickcertain.com", + "default": "block", + "owner": { + "name": "ClickCertain", + "displayName": "ClickCertain", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Audience Measurement" + ] + }, + "clicktale.net": { + "domain": "clicktale.net", + "default": "block", + "owner": { + "name": "ClickTale Ltd", + "displayName": "ClickTale", + "privacyPolicy": "https://www.clicktale.com/company/privacy-policy/", + "url": "http://clicktale.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 2, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 3 + }, + "categories": [ + "Analytics", + "Action Pixels", + "Session Replay" + ] + }, + "clrstm.com": { + "domain": "clrstm.com", + "default": "block", + "owner": { + "name": "ORC International", + "displayName": "ORC International", + "privacyPolicy": "https://orcinternational.com/privacy/", + "url": "http://orcinternational.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.011, + "fingerprinting": 0, + "cookies": 0.01, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "cogocast.net": { + "domain": "cogocast.net", + "default": "block", + "owner": { + "name": "Cogo Labs", + "displayName": "Cogo Labs", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.019, + "fingerprinting": 0, + "cookies": 0.019, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Audience Measurement" + ] + }, + "colossusssp.com": { + "domain": "colossusssp.com", + "default": "block", + "owner": { + "name": "Colossus Media, LLC", + "displayName": "Colossus Media", + "privacyPolicy": "https://colossus.media/privacy.php", + "url": "http://colossus.media" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking" + ] + }, + "commander1.com": { + "domain": "commander1.com", + "default": "block", + "owner": { + "name": "Fjord Technologies", + "displayName": "Fjord", + "privacyPolicy": "https://www.commandersact.com/en/privacy/", + "url": "http://commandersact.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "company-target.com": { + "domain": "company-target.com", + "default": "block", + "owner": { + "name": "Demandbase, Inc.", + "displayName": "Demandbase", + "privacyPolicy": "http://www.demandbase.com/privacy-policy/", + "url": "http://demandbase.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 0, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels", + "Obscure Ownership" + ] + }, + "connexity.net": { + "domain": "connexity.net", + "default": "block", + "owner": { + "name": "Connexity, Inc.", + "displayName": "Connexity", + "privacyPolicy": "https://connexity.com/privacy-policy/", + "url": "http://connexity.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "consensu.org": { + "domain": "consensu.org", + "default": "block", + "owner": { + "name": "IAB Europe", + "displayName": "IAB Europe", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.043, + "fingerprinting": 0, + "cookies": 0.015, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [] + }, + "contentabc.com": { + "domain": "contentabc.com", + "default": "block", + "owner": { + "name": "MindGeek", + "displayName": "MindGeek", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 0, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Advertising", + "Unknown High Risk Behavior", + "Obscure Ownership" + ] + }, + "contentsquare.net": { + "domain": "contentsquare.net", + "default": "block", + "owner": { + "name": "ContentSquare", + "displayName": "ContentSquare", + "privacyPolicy": "https://contentsquare.com/privacy-and-security/", + "url": "http://contentsquare.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 2, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels", + "Session Replay" + ] + }, + "contextweb.com": { + "domain": "contextweb.com", + "default": "block", + "owner": { + "name": "Pulsepoint, Inc.", + "displayName": "Pulsepoint", + "privacyPolicy": "https://www.pulsepoint.com/privacy-policy-platform", + "url": "http://pulsepoint.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.054, + "fingerprinting": 0, + "cookies": 0.054, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Audience Measurement" + ] + }, + "cpx.to": { + "domain": "cpx.to", + "default": "block", + "owner": { + "name": "Captify Technologies Ltd.", + "displayName": "Captify", + "privacyPolicy": "https://www.captify.co.uk/privacy-policy-opt/", + "url": "http://captify.co.uk" + }, + "source": [ + "DDG" + ], + "prevalence": 0.009, + "fingerprinting": 0, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Action Pixels", + "Obscure Ownership" + ] + }, + "cquotient.com": { + "domain": "cquotient.com", + "default": "block", + "owner": { + "name": "Salesforce.com, Inc.", + "displayName": "Salesforce.com", + "privacyPolicy": "https://www.salesforce.com/company/privacy/", + "url": "http://salesforce.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 0, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics" + ] + }, + "creative-serving.com": { + "domain": "creative-serving.com", + "default": "block", + "owner": { + "name": "Platform161", + "displayName": "Platform161", + "privacyPolicy": "https://platform161.com/cookie-and-privacy-policy/", + "url": "http://platform161.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.011, + "fingerprinting": 0, + "cookies": 0.01, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Action Pixels" + ] + }, + "criteo.com": { + "domain": "criteo.com", + "default": "block", + "owner": { + "name": "Criteo SA", + "displayName": "Criteo", + "privacyPolicy": "https://www.criteo.com/privacy/", + "url": "http://criteo.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.098, + "fingerprinting": 0, + "cookies": 0.038, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing" + ] + }, + "criteo.net": { + "domain": "criteo.net", + "default": "block", + "owner": { + "name": "Criteo SA", + "displayName": "Criteo", + "privacyPolicy": "https://www.criteo.com/privacy/", + "url": "http://criteo.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.073, + "fingerprinting": 0, + "cookies": 0.061, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "crsspxl.com": { + "domain": "crsspxl.com", + "default": "block", + "owner": { + "name": "Cross Pixel Media, Inc.", + "displayName": "Cross Pixel Media", + "privacyPolicy": "http://www.crosspixel.net/privacy-policy/", + "url": "http://crosspixel.net" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 0, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "crwdcntrl.net": { + "domain": "crwdcntrl.net", + "default": "block", + "owner": { + "name": "Lotame Solutions, Inc.", + "displayName": "Lotame Solutions", + "privacyPolicy": "https://www.lotame.com/about-lotame/privacy/lotame-corporate-websites-privacy-policy/", + "url": "http://lotame.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.079, + "fingerprinting": 0, + "cookies": 0.031, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Audience Measurement" + ], + "rules": [ + { + "rule": "ad\\.crwdcntrl\\.net\\/.*\\/callback=jsonp_callback", + "exceptions": { + "domains": [ + "weather.com" + ], + "types": [ + "script" + ] + } + } + ] + }, + "cxense.com": { + "domain": "cxense.com", + "default": "block", + "owner": { + "name": "Cxense ASA", + "displayName": "Cxense", + "privacyPolicy": "https://www.cxense.com/about-us/privacy-policy", + "url": "http://cxense.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 2, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing" + ] + }, + "dc-storm.com": { + "domain": "dc-storm.com", + "default": "block", + "owner": { + "name": "Rakuten, Inc.", + "displayName": "Rakuten", + "privacyPolicy": "https://rakutenmarketing.com/legal-notices/services-privacy-policy", + "url": "http://rakutenmarketing.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 0, + "cookies": 0.006, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "demandbase.com": { + "domain": "demandbase.com", + "default": "block", + "owner": { + "name": "Demandbase, Inc.", + "displayName": "Demandbase", + "privacyPolicy": "http://www.demandbase.com/privacy-policy/", + "url": "http://demandbase.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "demdex.net": { + "domain": "demdex.net", + "default": "block", + "owner": { + "name": "Adobe Inc.", + "displayName": "Adobe", + "privacyPolicy": "https://www.adobe.com/privacy/marketing-cloud.html", + "url": "http://adobe.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.109, + "fingerprinting": 0, + "cookies": 0.108, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing" + ] + }, + "deployads.com": { + "domain": "deployads.com", + "default": "block", + "owner": { + "name": "Snapsort Inc.", + "displayName": "Snapsort", + "privacyPolicy": "https://www.sortable.com/privacy-policy", + "url": "http://sortable.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 2, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "df-srv.de": { + "domain": "df-srv.de", + "default": "block", + "owner": { + "name": "Contact Impact GmbH", + "displayName": "Contact Impact", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "digitru.st": { + "domain": "digitru.st", + "default": "block", + "owner": { + "name": "Cookie Trust Working Group, Inc. DBA Cookie Trust", + "displayName": "Cookie Trust", + "privacyPolicy": "https://www.digitru.st/privacy-policy/", + "url": "http://digitru.st" + }, + "source": [ + "DDG" + ], + "prevalence": 0.021, + "fingerprinting": 0, + "cookies": 0.021, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking" + ] + }, + "disqus.com": { + "domain": "disqus.com", + "default": "ignore", + "owner": { + "name": "Disqus, Inc.", + "displayName": "Disqus", + "privacyPolicy": "https://help.disqus.com/terms-and-policies/disqus-privacy-policy", + "url": "http://disqus.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.02, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Federated Login", + "Social - Comment", + "Embedded Content" + ] + }, + "distiltag.com": { + "domain": "distiltag.com", + "default": "block", + "owner": { + "name": "Imperva Inc.", + "displayName": "Imperva", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 3, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Analytics", + "Third-Party Analytics Marketing" + ] + }, + "districtm.ca": { + "domain": "districtm.ca", + "default": "block", + "owner": { + "name": "District M Inc.", + "displayName": "District M", + "privacyPolicy": "https://districtm.net/en/page/platforms-data-and-privacy-policy/", + "url": "http://districtm.net" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 0, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "districtm.io": { + "domain": "districtm.io", + "default": "block", + "owner": { + "name": "District M Inc.", + "displayName": "District M", + "privacyPolicy": "https://districtm.net/en/page/platforms-data-and-privacy-policy/", + "url": "http://districtm.net" + }, + "source": [ + "DDG" + ], + "prevalence": 0.035, + "fingerprinting": 0, + "cookies": 0.034, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "dmxleo.com": { + "domain": "dmxleo.com", + "default": "block", + "owner": { + "name": "Dailymotion SA", + "displayName": "Dailymotion", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Analytics", + "Federated Login", + "Third-Party Analytics Marketing", + "Social - Share", + "Embedded Content" + ] + }, + "domdex.com": { + "domain": "domdex.com", + "default": "block", + "owner": { + "name": "Magnetic Media Online, Inc.", + "displayName": "Magnetic Media Online", + "privacyPolicy": "https://www.magnetic.com/policy-page/", + "url": "http://magnetic.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "dotomi.com": { + "domain": "dotomi.com", + "default": "block", + "owner": { + "name": "Conversant LLC", + "displayName": "Conversant", + "privacyPolicy": "https://www.conversantmedia.com/legal/privacy", + "url": "http://conversantmedia.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.046, + "fingerprinting": 0, + "cookies": 0.042, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "doubleclick.net": { + "domain": "doubleclick.net", + "default": "block", + "owner": { + "name": "Google LLC", + "displayName": "Google", + "privacyPolicy": "https://policies.google.com/privacy?hl=en&gl=us", + "url": "http://google.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.688, + "fingerprinting": 2, + "cookies": 0.5, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ], + "rules": [ + { + "rule": "doubleclick\\.net\\/instream\\/ad_status\\.js", + "exceptions": { + "domains": [ + "investing.com" + ], + "types": [ + "script" + ] + }, + "surrogate": "ad_status.js" + }, + { + "rule": "doubleclick\\.net\\/ddm\\/", + "exceptions": { + "types": [ + "image" + ] + } + }, + { + "rule": "pubads\\.g\\.doubleclick\\.net\\/gampad\\/ads", + "exceptions": { + "domains": [ + "surfline.com", + "dailywire.com", + "mlb.com" + ], + "types": [ + "xmlhttprequest" + ] + } + }, + { + "rule": "pubads\\.g\\.doubleclick\\.net\\/ssai\\/event", + "exceptions": { + "domains": [ + "cbsnews.com" + ], + "types": [ + "xmlhttprequest" + ] + } + }, + { + "rule": "pubads\\.g\\.doubleclick\\.net", + "exceptions": { + "domains": [ + "washingtonpost.com" + ], + "types": [ + "xmlhttprequest" + ] + } + } + ] + }, + "doubleverify.com": { + "domain": "doubleverify.com", + "default": "block", + "owner": { + "name": "DoubleVerify", + "displayName": "DoubleVerify", + "privacyPolicy": "https://www.doubleverify.com/privacy/", + "url": "http://doubleverify.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.024, + "fingerprinting": 2, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 3, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud" + ] + }, + "driftt.com": { + "domain": "driftt.com", + "default": "block", + "owner": { + "name": "Drift.com, Inc.", + "displayName": "Drift.com, Inc.", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 1, + "cookies": 0, + "performance": { + "time": 1, + "size": 2, + "cpu": 3, + "cache": 1 + }, + "categories": [ + "Analytics", + "Third-Party Analytics Marketing", + "Embedded Content" + ] + }, + "dtscout.com": { + "domain": "dtscout.com", + "default": "block", + "owner": { + "name": "DTS Technology", + "displayName": "DTS", + "privacyPolicy": "http://www.dtscout.com/techpolicy.html", + "url": "http://dtscout.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.02, + "fingerprinting": 0, + "cookies": 0.02, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "dwin1.com": { + "domain": "dwin1.com", + "default": "block", + "owner": { + "name": "Awin AG", + "displayName": "Awin", + "privacyPolicy": "https://www.awin.com/gb/legal/privacy-policy", + "url": "http://awin.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.009, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "dynamicyield.com": { + "domain": "dynamicyield.com", + "default": "block", + "owner": { + "name": "Dynamic Yield", + "displayName": "Dynamic Yield", + "privacyPolicy": "https://www.dynamicyield.com/platform-privacy-policy/", + "url": "http://dynamicyield.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 2, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 2, + "cpu": 3, + "cache": 3 + }, + "categories": [ + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Embedded Content" + ] + }, + "dyntrk.com": { + "domain": "dyntrk.com", + "default": "block", + "owner": { + "name": "DynAdmic", + "displayName": "DynAdmic", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.007, + "fingerprinting": 0, + "cookies": 0.007, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Audience Measurement" + ] + }, + "eloqua.com": { + "domain": "eloqua.com", + "default": "block", + "owner": { + "name": "Oracle Corporation", + "displayName": "Oracle", + "privacyPolicy": "https://www.oracle.com/legal/privacy/privacy-policy.html", + "url": "http://oracle.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 0, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Advertising", + "Analytics", + "Third-Party Analytics Marketing" + ] + }, + "emetriq.de": { + "domain": "emetriq.de", + "default": "block", + "owner": { + "name": "emetriq GmbH", + "displayName": "emetriq", + "privacyPolicy": "https://www.emetriq.com/datenschutz/", + "url": "http://emetriq.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 2, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Embedded Content" + ] + }, + "emxdgt.com": { + "domain": "emxdgt.com", + "default": "block", + "owner": { + "name": "Engine USA LLC", + "displayName": "Engine USA", + "privacyPolicy": "https://emxdigital.com/privacy/", + "url": "http://emxdigital.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.024, + "fingerprinting": 0, + "cookies": 0.022, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "en25.com": { + "domain": "en25.com", + "default": "block", + "owner": { + "name": "Oracle Corporation", + "displayName": "Oracle", + "privacyPolicy": "https://www.oracle.com/legal/privacy/privacy-policy.html", + "url": "http://oracle.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ], + "rules": [ + { + "rule": "img\\.en25\\.com\\/web", + "exceptions": { + "types": [ + "stylesheet", + "image", + "script" + ] + } + }, + { + "rule": "img\\.en25\\.com\\/eloquaimages", + "exceptions": { + "types": [ + "image" + ] + } + } + ] + }, + "ensighten.com": { + "domain": "ensighten.com", + "default": "block", + "owner": { + "name": "Ensighten, Inc", + "displayName": "Ensighten", + "privacyPolicy": "https://www.ensighten.com/privacy-policy/", + "url": "http://ensighten.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 2, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "ero-advertising.com": { + "domain": "ero-advertising.com", + "default": "block", + "owner": { + "name": "Interwebvertising B.V.", + "displayName": "Interwebvertising", + "privacyPolicy": "http://ero-advertising.com/#!/privacy", + "url": "http://ero-advertising.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 3, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Advertising" + ] + }, + "everestjs.net": { + "domain": "everestjs.net", + "default": "block", + "owner": { + "name": "Adobe Inc.", + "displayName": "Adobe", + "privacyPolicy": "https://www.adobe.com/privacy/marketing-cloud.html", + "url": "http://adobe.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking" + ] + }, + "everesttech.net": { + "domain": "everesttech.net", + "default": "block", + "owner": { + "name": "Adobe Inc.", + "displayName": "Adobe", + "privacyPolicy": "https://www.adobe.com/privacy/marketing-cloud.html", + "url": "http://adobe.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.133, + "fingerprinting": 0, + "cookies": 0.132, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Third-Party Analytics Marketing" + ] + }, + "evidon.com": { + "domain": "evidon.com", + "default": "block", + "owner": { + "name": "Crownpeak Technology", + "displayName": "Crownpeak", + "privacyPolicy": "https://www.crownpeak.com/privacy.aspx", + "url": "http://crownpeak.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.007, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Analytics", + "Third-Party Analytics Marketing" + ], + "rules": [ + { + "rule": "c\\.evidon\\.com", + "exceptions": { + "domains": [ + "cbsnews.com", + "etonline.com" + ], + "types": [ + "script" + ] + } + } + ] + }, + "exelator.com": { + "domain": "exelator.com", + "default": "block", + "owner": { + "name": "The Nielsen Company", + "displayName": "The Nielsen Company", + "privacyPolicy": "http://www.nielsen.com/us/en/privacy-statement/digital-measurement.html", + "url": "http://nielsen.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.09, + "fingerprinting": 0, + "cookies": 0.089, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Action Pixels" + ] + }, + "exoclick.com": { + "domain": "exoclick.com", + "default": "block", + "owner": { + "name": "ExoClick, S.L.", + "displayName": "ExoClick", + "privacyPolicy": "https://www.exoclick.com/privacy/", + "url": "http://exoclick.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.016, + "fingerprinting": 0, + "cookies": 0.011, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "exosrv.com": { + "domain": "exosrv.com", + "default": "block", + "owner": { + "name": "ExoClick, S.L.", + "displayName": "ExoClick", + "privacyPolicy": "https://www.exoclick.com/privacy/", + "url": "http://exoclick.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.029, + "fingerprinting": 1, + "cookies": 0.029, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "exponential.com": { + "domain": "exponential.com", + "default": "block", + "owner": { + "name": "Exponential Interactive Inc.", + "displayName": "Exponential Interactive", + "privacyPolicy": "http://exponential.com/privacy/", + "url": "http://exponential.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 3, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Analytics", + "Audience Measurement", + "Action Pixels", + "Session Replay" + ] + }, + "eyeota.net": { + "domain": "eyeota.net", + "default": "block", + "owner": { + "name": "eyeota Limited", + "displayName": "eyeota", + "privacyPolicy": "https://www.eyeota.com/privacy-policy", + "url": "http://eyeota.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.044, + "fingerprinting": 0, + "cookies": 0.042, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "eyereturn.com": { + "domain": "eyereturn.com", + "default": "block", + "owner": { + "name": "eyeReturn Marketing Inc.", + "displayName": "eyeReturn Marketing", + "privacyPolicy": "https://eyereturnmarketing.com/privacy/", + "url": "http://eyereturnmarketing.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.01, + "fingerprinting": 0, + "cookies": 0.01, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing", + "Action Pixels", + "Session Replay" + ] + }, + "eyeviewads.com": { + "domain": "eyeviewads.com", + "default": "block", + "owner": { + "name": "EyeView, Inc.", + "displayName": "EyeView", + "privacyPolicy": "https://www.eyeviewdigital.com/privacy-policy/", + "url": "http://eyeviewdigital.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 0, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Analytics", + "Audience Measurement", + "Action Pixels", + "Embedded Content" + ] + }, + "ezoic.net": { + "domain": "ezoic.net", + "default": "block", + "owner": { + "name": "Ezoic Inc.", + "displayName": "Ezoic", + "privacyPolicy": "https://www.ezoic.com/privacy-policy/", + "url": "http://ezoic.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 1, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels", + "Session Replay" + ] + }, + "facebook.com": { + "rules": [ + { + "rule": "facebook\\.com\\/connect\\/xd_arbiter\\.php" + }, + { + "rule": "facebook\\.com\\/tr\\/" + }, + { + "rule": "facebook\\.com\\/audiencenetwork\\/idsync" + }, + { + "rule": "facebook\\.com\\/fr\\/b\\.php" + }, + { + "rule": "facebook\\.com\\/common\\/cavalry_endpoint\\.php" + }, + { + "rule": "facebook\\.com\\/method\\/links\\.getStats" + }, + { + "rule": "facebook\\.com\\/ajax\\/bz" + }, + { + "rule": "facebook\\.com\\/brandlift\\.php" + }, + { + "rule": "facebook\\.com\\/pages\\/call_to_action\\/fetch_dialog_data\\/" + }, + { + "rule": "facebook\\.com\\/platform\\/plugin\\/tab\\/renderer\\/" + }, + { + "rule": "facebook\\.com\\/platform\\/plugin\\/page\\/logging\\/" + }, + { + "rule": "facebook\\.com\\/images\\/emoji\\.php" + }, + { + "rule": "facebook\\.com\\/ajax\\/bootloader-endpoint\\/" + }, + { + "rule": "facebook\\.com\\/connect\\/ping" + }, + { + "rule": "facebook\\.com\\/favicon\\.ico" + }, + { + "rule": "facebook\\.com\\/audiencenetwork\\/iframe\\/" + }, + { + "rule": "facebook\\.com\\/audiencenetwork\\/xhr\\/" + }, + { + "rule": "facebook\\.com\\/chat" + }, + { + "rule": "facebook\\.com\\/en_US\\/AudienceNetworkPrebid\\.js" + }, + { + "rule": "facebook\\.com\\/en_US\\/fbevents\\.js" + }, + { + "rule": "facebook\\.com\\/connect\\/xd_arbiter\\/r\\/lY4eZXm_YWu\\.js" + } + ], + "domain": "facebook.com", + "default": "ignore", + "owner": { + "name": "Facebook, Inc.", + "displayName": "Facebook", + "privacyPolicy": "https://www.facebook.com/privacy/explanation", + "url": "http://facebook.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.396, + "fingerprinting": 1, + "cookies": 0.279, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Analytics", + "Audience Measurement", + "Federated Login", + "Social - Comment", + "Social - Share", + "Action Pixels", + "Badge", + "Embedded Content", + "Social Network" + ] + }, + "facebook.net": { + "rules": [ + { + "rule": "facebook\\.net\\/.*\\/fbevents\\.js" + }, + { + "rule": "facebook\\.net\\/.*\\/all\\.js" + }, + { + "rule": "facebook\\.net\\/signals\\/config\\/" + }, + { + "rule": "facebook\\.net\\/.*\\/sdk\\/xfbml\\.customerchat\\.js" + }, + { + "rule": "facebook\\.net\\/en_US\\/fbadnw60-tag\\.js" + }, + { + "rule": "facebook\\.net\\/en_US\\/fbadnw60\\.js" + }, + { + "rule": "facebook\\.net\\/en_US\\/AudienceNetworkPrebid\\.js" + }, + { + "rule": "facebook\\.net\\/en_US\\/platform\\.Extensions\\.js" + } + ], + "domain": "facebook.net", + "default": "ignore", + "owner": { + "name": "Facebook, Inc.", + "displayName": "Facebook", + "privacyPolicy": "https://www.facebook.com/privacy/explanation", + "url": "http://facebook.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.345, + "fingerprinting": 1, + "cookies": 0.049, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Analytics", + "Audience Measurement", + "Federated Login", + "Social - Comment", + "Social - Share", + "Action Pixels", + "Badge", + "Embedded Content", + "Social Network" + ] + }, + "fg8dgt.com": { + "domain": "fg8dgt.com", + "default": "block", + "owner": { + "name": "FastG8", + "displayName": "FastG8", + "privacyPolicy": "http://www.fastg8.com/privacypolicy/", + "url": "http://fastg8.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "flashtalking.com": { + "domain": "flashtalking.com", + "default": "block", + "owner": { + "name": "Simplicity Marketing", + "displayName": "Simplicity Marketing", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.013, + "fingerprinting": 2, + "cookies": 0.01, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels", + "Embedded Content", + "Session Replay" + ] + }, + "foresee.com": { + "domain": "foresee.com", + "default": "block", + "owner": { + "name": "ForeSee Results, Inc.", + "displayName": "ForeSee Results", + "privacyPolicy": "https://www.foresee.com/privacy-policy/", + "url": "http://foresee.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 2, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Analytics", + "Audience Measurement", + "Action Pixels", + "Session Replay" + ] + }, + "fullstory.com": { + "domain": "fullstory.com", + "default": "block", + "owner": { + "name": "FullStory", + "displayName": "FullStory", + "privacyPolicy": "https://www.fullstory.com/legal/privacy/", + "url": "http://fullstory.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 2, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 3 + }, + "categories": [ + "Analytics", + "Session Replay" + ] + }, + "fwmrm.net": { + "domain": "fwmrm.net", + "default": "block", + "owner": { + "name": "FreeWheel", + "displayName": "FreeWheel", + "privacyPolicy": "http://freewheel.tv/privacy-policy/", + "url": "http://freewheel.tv" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing" + ] + }, + "gemius.pl": { + "domain": "gemius.pl", + "default": "block", + "owner": { + "name": "Gemius S.A.", + "displayName": "Gemius", + "privacyPolicy": "https://www.gemius.com/privacy-policy.html", + "url": "http://gemius.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.012, + "fingerprinting": 2, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Audience Measurement" + ] + }, + "getclicky.com": { + "domain": "getclicky.com", + "default": "block", + "owner": { + "name": "Roxr Software Ltd", + "displayName": "Roxr Software", + "privacyPolicy": "https://clicky.com/terms/privacy", + "url": "http://clicky.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 1, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Fraud", + "Analytics" + ] + }, + "getsitecontrol.com": { + "domain": "getsitecontrol.com", + "default": "block", + "owner": { + "name": "GetWebCraft Limited", + "displayName": "GetWebCraft", + "privacyPolicy": "https://getsitecontrol.com/privacy/", + "url": "http://getsitecontrol.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 2, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Embedded Content" + ] + }, + "go-mpulse.net": { + "domain": "go-mpulse.net", + "default": "block", + "owner": { + "name": "Akamai Technologies", + "displayName": "Akamai", + "privacyPolicy": "https://www.akamai.com/us/en/privacy-policies/", + "url": "http://akamai.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.015, + "fingerprinting": 2, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 3 + }, + "categories": [ + "Analytics" + ] + }, + "google-analytics.com": { + "domain": "google-analytics.com", + "default": "block", + "owner": { + "name": "Google LLC", + "displayName": "Google", + "privacyPolicy": "https://policies.google.com/privacy?hl=en&gl=us", + "url": "http://google.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.79, + "fingerprinting": 2, + "cookies": 0.551, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ], + "rules": [ + { + "rule": "google-analytics\\.com\\/analytics\\.js", + "exceptions": { + "domains": [ + "raspberrypi.org", + "localizahertz.com" + ] + }, + "surrogate": "analytics.js" + }, + { + "rule": "google-analytics\\.com\\/ga.js", + "surrogate": "ga.js" + }, + { + "rule": "google-analytics\\.com\\/inpage_linkid.js", + "surrogate": "inpage_linkid.js" + }, + { + "rule": "google-analytics\\.com\\/cx/api.js", + "surrogate": "api.js" + } + ] + }, + "googleadservices.com": { + "domain": "googleadservices.com", + "default": "block", + "owner": { + "name": "Google LLC", + "displayName": "Google", + "privacyPolicy": "https://policies.google.com/privacy?hl=en&gl=us", + "url": "http://google.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.182, + "fingerprinting": 2, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "googlesyndication.com": { + "domain": "googlesyndication.com", + "default": "block", + "owner": { + "name": "Google LLC", + "displayName": "Google", + "privacyPolicy": "https://policies.google.com/privacy?hl=en&gl=us", + "url": "http://google.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.282, + "fingerprinting": 3, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ], + "rules": [ + { + "rule": "googlesyndication\\.com\\/adsbygoogle.js", + "surrogate": "adsbygoogle.js" + } + ] + }, + "googletagmanager.com": { + "domain": "googletagmanager.com", + "default": "block", + "owner": { + "name": "Google LLC", + "displayName": "Google", + "privacyPolicy": "https://policies.google.com/privacy?hl=en&gl=us", + "url": "http://google.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.455, + "fingerprinting": 1, + "cookies": 0.098, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ], + "rules": [ + { + "rule": "googletagmanager\\.com\\/gtm\\.js", + "exceptions": { + "domains": [ + "bethesda.net", + "southbankresearch.com", + "redballoon.com.au", + "wrh.noaa.gov" + ] + }, + "surrogate": "gtm.js" + } + ] + }, + "googletagservices.com": { + "domain": "googletagservices.com", + "default": "block", + "owner": { + "name": "Google LLC", + "displayName": "Google", + "privacyPolicy": "https://policies.google.com/privacy?hl=en&gl=us", + "url": "http://google.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.285, + "fingerprinting": 1, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels" + ], + "rules": [ + { + "rule": "googletagservices\\.com\\/tag\\/js\\/gpt\\.js", + "exceptions": { + "domains": [ + "ijpr.org", + "buienradar.nl", + "theatlantic.com", + "avclub.com", + "deadspin.com", + "earther.com", + "gizmodo.com", + "jalopnik.com", + "jezebel.com", + "kotaku.com", + "lifehacker.com", + "splinternews.com", + "theroot.com", + "thetakeout.com" + ] + }, + "surrogate": "gpt.js" + }, + { + "rule": "googletagservices\\.com\\/gpt.js", + "surrogate": "gpt.js" + } + ] + }, + "gumgum.com": { + "domain": "gumgum.com", + "default": "block", + "owner": { + "name": "GumGum", + "displayName": "GumGum", + "privacyPolicy": "https://gumgum.com/privacy-policy", + "url": "http://gumgum.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.028, + "fingerprinting": 3, + "cookies": 0.026, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "gwallet.com": { + "domain": "gwallet.com", + "default": "block", + "owner": { + "name": "RhythmOne", + "displayName": "RhythmOne", + "privacyPolicy": "https://www.rhythmone.com/privacy-policy", + "url": "http://rhythmone.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.009, + "fingerprinting": 0, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing" + ] + }, + "heapanalytics.com": { + "domain": "heapanalytics.com", + "default": "block", + "owner": { + "name": "Heap", + "displayName": "Heap", + "privacyPolicy": "https://heap.io/privacy", + "url": "http://heap.io" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 1, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics", + "Action Pixels", + "Session Replay" + ] + }, + "hellobar.com": { + "domain": "hellobar.com", + "default": "block", + "owner": { + "name": "Crazy Egg, Inc.", + "displayName": "Crazy Egg", + "privacyPolicy": "https://www.crazyegg.com/privacy", + "url": "http://crazyegg.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Action Pixels", + "Session Replay" + ] + }, + "histats.com": { + "domain": "histats.com", + "default": "block", + "owner": { + "name": "wisecode s.r.l.", + "displayName": "wisecode", + "privacyPolicy": "https://www.histats.com/?act=102", + "url": "http://histats.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.018, + "fingerprinting": 2, + "cookies": 0.017, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics", + "Audience Measurement" + ] + }, + "hotjar.com": { + "domain": "hotjar.com", + "default": "block", + "owner": { + "name": "Hotjar Ltd", + "displayName": "Hotjar", + "privacyPolicy": "https://www.hotjar.com/legal/policies/privacy", + "url": "http://hotjar.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.082, + "fingerprinting": 2, + "cookies": 0.075, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Analytics", + "Audience Measurement", + "Session Replay" + ] + }, + "hs-analytics.net": { + "domain": "hs-analytics.net", + "default": "block", + "owner": { + "name": "HubSpot, Inc.", + "displayName": "HubSpot", + "privacyPolicy": "https://legal.hubspot.com/privacy-policy", + "url": "http://hubspot.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.011, + "fingerprinting": 2, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "hsadspixel.net": { + "domain": "hsadspixel.net", + "default": "block", + "owner": { + "name": "HubSpot, Inc.", + "displayName": "HubSpot", + "privacyPolicy": "https://legal.hubspot.com/privacy-policy", + "url": "http://hubspot.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "hsforms.com": { + "domain": "hsforms.com", + "default": "block", + "owner": { + "name": "HubSpot, Inc.", + "displayName": "HubSpot", + "privacyPolicy": "https://legal.hubspot.com/privacy-policy", + "url": "http://hubspot.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics", + "Embedded Content" + ], + "rules": [ + { + "rule": "hsforms\\.com", + "exceptions": { + "types": [ + "script", + "subdocument", + "xmlhttprequest" + ] + } + } + ] + }, + "hsleadflows.net": { + "domain": "hsleadflows.net", + "default": "block", + "owner": { + "name": "HubSpot, Inc.", + "displayName": "HubSpot", + "privacyPolicy": "https://legal.hubspot.com/privacy-policy", + "url": "http://hubspot.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "hubapi.com": { + "domain": "hubapi.com", + "default": "block", + "owner": { + "name": "HubSpot, Inc.", + "displayName": "HubSpot", + "privacyPolicy": "https://legal.hubspot.com/privacy-policy", + "url": "http://hubspot.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "hubspot.com": { + "domain": "hubspot.com", + "default": "block", + "owner": { + "name": "HubSpot, Inc.", + "displayName": "HubSpot", + "privacyPolicy": "https://legal.hubspot.com/privacy-policy", + "url": "http://hubspot.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.011, + "fingerprinting": 0, + "cookies": 0.01, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing", + "Embedded Content" + ], + "rules": [ + { + "rule": "forms\\.hubspot\\.com", + "exceptions": { + "types": [ + "xmlhttprequest" + ] + } + }, + { + "rule": "app\\.hubspot\\.com\\/.*\\/api", + "exceptions": { + "types": [ + "script" + ] + } + } + ] + }, + "hybrid.ai": { + "domain": "hybrid.ai", + "default": "block", + "owner": { + "name": "Hybrid Adtech, Inc.", + "displayName": "Hybrid Adtech", + "privacyPolicy": "https://hybrid.ai/privacy_policy", + "url": "http://hybrid.ai" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement" + ] + }, + "iasds01.com": { + "domain": "iasds01.com", + "default": "block", + "owner": { + "name": "Integral Ad Science, Inc.", + "displayName": "Integral Ad Science", + "privacyPolicy": "https://integralads.com/privacy-policy/", + "url": "http://integralads.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.032, + "fingerprinting": 0, + "cookies": 0.032, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud" + ] + }, + "ib-ibi.com": { + "domain": "ib-ibi.com", + "default": "block", + "owner": { + "name": "KBM Group LLC", + "displayName": "KBM Group", + "privacyPolicy": "https://www.kbmg.com/about-us/privacy/", + "url": "http://kbmg.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.016, + "fingerprinting": 0, + "cookies": 0.016, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "ibillboard.com": { + "domain": "ibillboard.com", + "default": "block", + "owner": { + "name": "Internet Billboard a.s.", + "displayName": "Internet Billboard", + "privacyPolicy": "http://www.ibillboard.com/en/privacy-information/", + "url": "http://ibillboard.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 1, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "id5-sync.com": { + "domain": "id5-sync.com", + "default": "block", + "owner": { + "name": "ID5 Technology Ltd", + "displayName": "ID5", + "privacyPolicy": "https://www.id5.io/privacy-policy", + "url": "http://id5.io" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 0, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "igodigital.com": { + "domain": "igodigital.com", + "default": "block", + "owner": { + "name": "ExactTarget, LLC", + "displayName": "ExactTarget", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.007, + "fingerprinting": 0, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "impactradius-event.com": { + "domain": "impactradius-event.com", + "default": "block", + "owner": { + "name": "Impact Radius", + "displayName": "Impact Radius", + "privacyPolicy": "https://impact.com/privacy-policy/", + "url": "http://impact.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 0, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "imrworldwide.com": { + "domain": "imrworldwide.com", + "default": "block", + "owner": { + "name": "The Nielsen Company", + "displayName": "The Nielsen Company", + "privacyPolicy": "http://www.nielsen.com/us/en/privacy-statement/digital-measurement.html", + "url": "http://nielsen.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.016, + "fingerprinting": 1, + "cookies": 0.016, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels" + ], + "rules": [ + { + "rule": "secure-drm\\.imrworldwide\\.com\\/.*\\/ggcmb400\\.js", + "exceptions": { + "types": [ + "script" + ] + } + }, + { + "rule": "imrworldwide\\.com\\/v60\\.js", + "exceptions": { + "domains": [ + "threenow.co.nz" + ], + "types": [ + "script" + ] + } + } + ] + }, + "indexww.com": { + "domain": "indexww.com", + "default": "block", + "owner": { + "name": "Index Exchange, Inc.", + "displayName": "Index Exchange", + "privacyPolicy": "http://casalemedia.com/", + "url": "http://casalemedia.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.018, + "fingerprinting": 1, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Advertising", + "Analytics" + ] + }, + "innovid.com": { + "domain": "innovid.com", + "default": "block", + "owner": { + "name": "Innovid Media", + "displayName": "Innovid Media", + "privacyPolicy": "https://www.innovid.com/privacy-policy/", + "url": "http://innovid.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 0, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "insightexpressai.com": { + "domain": "insightexpressai.com", + "default": "block", + "owner": { + "name": "Kantar Operations", + "displayName": "Kantar Operations", + "privacyPolicy": "https://www.millwardbrowndigital.com/about/privacy/", + "url": "http://millwardbrowndigital.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement" + ] + }, + "inspectlet.com": { + "domain": "inspectlet.com", + "default": "block", + "owner": { + "name": "Inspectlet", + "displayName": "Inspectlet", + "privacyPolicy": "https://www.inspectlet.com/legal", + "url": "http://inspectlet.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics", + "Third-Party Analytics Marketing", + "Session Replay" + ] + }, + "instagram.com": { + "rules": [ + { + "rule": "instagram\\.com\\/en_US\\/embeds\\.js" + }, + { + "rule": "instagram\\.com\\/embed\\.js" + }, + { + "rule": "instagram\\.com\\/static\\/bundles\\/es6\\/EmbedSDK\\.js\\/2fe3a16f6aeb\\.js" + }, + { + "rule": "instagram\\.com\\/ar15com" + }, + { + "rule": "instagram\\.com\\/de_de\\/embeds\\.js" + }, + { + "rule": "instagram\\.com\\/v1\\/users\\/self\\/media\\/recent\\/" + }, + { + "rule": "instagram\\.com\\/static\\/bundles\\/es6\\/en_US\\.js\\/83520ac22c7c\\.js" + }, + { + "rule": "instagram\\.com\\/static\\/bundles\\/es6\\/EmbedSimple\\.js\\/384bb7913821\\.js" + }, + { + "rule": "instagram\\.com\\/static\\/bundles\\/es6\\/EmbedSimpleBase\\.css\\/20a68d4f85ad\\.css" + }, + { + "rule": "instagram\\.com\\/static\\/bundles\\/es6\\/sprite_embed_d7bcbaf5d4c9\\.png\\/d7bcbaf5d4c9\\.png" + }, + { + "rule": "instagram\\.com\\/static\\/bundles\\/es6\\/EmbedAsyncLogger\\.js\\/b8467e949e08\\.js" + }, + { + "rule": "instagram\\.com\\/logging_client_events" + }, + { + "rule": "instagram\\.com\\/v1\\/users\\/self\\/media\\/recent" + }, + { + "rule": "instagram\\.com\\/static\\/images\\/ig-badge-view-24\\.png" + }, + { + "rule": "instagram\\.com\\/static\\/thirdparty\\/images\\/badges\\/ig-badge-view-24\\.png\\/41dd6fb5d8eb\\.png" + }, + { + "rule": "instagram\\.com\\/static\\/images\\/ig-badge-view-sprite-24\\.png" + }, + { + "rule": "instagram\\.com\\/static\\/thirdparty\\/images\\/badges\\/ig-badge-view-sprite-24\\.png\\/284161441bde\\.png" + }, + { + "rule": "instagram\\.com\\/static\\/images\\/ig-badge-16\\.png" + }, + { + "rule": "instagram\\.com\\/static\\/images\\/ig-badge-sprite-16\\.png" + }, + { + "rule": "instagram\\.com\\/static\\/thirdparty\\/images\\/badges\\/ig-badge-16\\.png\\/1f6a7ba1a929\\.png" + }, + { + "rule": "instagram\\.com\\/static\\/thirdparty\\/images\\/badges\\/ig-badge-sprite-16\\.png\\/fa7f5dc1affd\\.png" + }, + { + "rule": "instagram\\.com\\/static\\/bundles\\/es6\\/EmbedRichBase\\.css\\/9c6ecd8855b6\\.css" + }, + { + "rule": "instagram\\.com\\/static\\/bundles\\/es6\\/EmbedRich\\.js\\/39e7b75d4180\\.js" + }, + { + "rule": "instagram\\.com\\/static\\/bundles\\/es6\\/EmbedRich\\.css\\/9c6ecd8855b6\\.css" + }, + { + "rule": "instagram\\.com\\/fr_fr\\/embeds\\.js" + }, + { + "rule": "instagram\\.com\\/static\\/images\\/ig-badge-24\\.png" + }, + { + "rule": "instagram\\.com\\/static\\/images\\/ig-badge-sprite-24\\.png" + }, + { + "rule": "instagram\\.com\\/static\\/thirdparty\\/images\\/badges\\/ig-badge-24\\.png\\/e4bfeb5b807c\\.png" + }, + { + "rule": "instagram\\.com\\/static\\/thirdparty\\/images\\/badges\\/ig-badge-sprite-24\\.png\\/9b01fe0f0cc2\\.png" + }, + { + "rule": "instagram\\.com\\/v1\\/users\\/1562748433\\/media\\/recent" + }, + { + "rule": "instagram\\.com\\/static\\/images\\/ig-badge-32\\.png" + }, + { + "rule": "instagram\\.com\\/static\\/thirdparty\\/images\\/badges\\/ig-badge-32\\.png\\/71906700c669\\.png" + }, + { + "rule": "instagram\\.com\\/graphql\\/query\\/" + } + ], + "domain": "instagram.com", + "default": "ignore", + "owner": { + "name": "Facebook, Inc.", + "displayName": "Facebook", + "privacyPolicy": "https://help.instagram.com/402411646841720", + "url": "http://instagram.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 2, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Social - Share", + "Embedded Content", + "Social Network" + ] + }, + "intentiq.com": { + "domain": "intentiq.com", + "default": "block", + "owner": { + "name": "Intent IQ, LLC", + "displayName": "Intent IQ", + "privacyPolicy": "https://www.intentiq.com/technology-privacy-policy", + "url": "http://intentiq.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Analytics", + "Third-Party Analytics Marketing" + ] + }, + "ioam.de": { + "domain": "ioam.de", + "default": "block", + "owner": { + "name": "INFOnline GmbH", + "displayName": "INFOnline", + "privacyPolicy": "https://www.infonline.de/en/privacy-policy/", + "url": "http://infonline.de" + }, + "source": [ + "DDG" + ], + "prevalence": 0.013, + "fingerprinting": 2, + "cookies": 0.013, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Audience Measurement" + ], + "rules": [ + { + "rule": "script\\.ioam\\.de\\/iam\\.js", + "exceptions": { + "domains": [ + "ebay-kleinanzeigen.de" + ] + } + } + ] + }, + "iperceptions.com": { + "domain": "iperceptions.com", + "default": "block", + "owner": { + "name": "iPerceptions Inc.", + "displayName": "iPerceptions", + "privacyPolicy": "https://www.iperceptions.com/en/legal/privacy-policy", + "url": "http://iperceptions.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Analytics", + "Embedded Content" + ] + }, + "ipredictive.com": { + "domain": "ipredictive.com", + "default": "block", + "owner": { + "name": "Adelphic, Inc.", + "displayName": "Adelphic", + "privacyPolicy": "https://my.ipredictive.com/optout/", + "url": "http://ipredictive.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.021, + "fingerprinting": 0, + "cookies": 0.021, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "ispot.tv": { + "domain": "ispot.tv", + "default": "block", + "owner": { + "name": "iSpot.tv", + "displayName": "iSpot.tv", + "privacyPolicy": "https://www.ispot.tv/agreements", + "url": "http://ispot.tv" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Advertising", + "Analytics", + "Audience Measurement" + ] + }, + "itsup.com": { + "domain": "itsup.com", + "default": "block", + "owner": { + "name": "SCTR Services LLC", + "displayName": "SCTR Services LLC", + "privacyPolicy": "https://www.itsup.com/privacy/", + "url": "http://itsup.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.012, + "fingerprinting": 0, + "cookies": 0.012, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Action Pixels" + ] + }, + "ivitrack.com": { + "domain": "ivitrack.com", + "default": "block", + "owner": { + "name": "Ividence", + "displayName": "Ividence", + "privacyPolicy": "http://blog.ividence.com/privacy-policy/?lang=en", + "url": "http://ividence.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "ixiaa.com": { + "domain": "ixiaa.com", + "default": "block", + "owner": { + "name": "Equifax Inc.", + "displayName": "Equifax", + "privacyPolicy": "https://www.equifax.com/privacy/", + "url": "http://equifax.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Audience Measurement" + ] + }, + "juicyads.com": { + "domain": "juicyads.com", + "default": "block", + "owner": { + "name": "JuicyAds", + "displayName": "JuicyAds", + "privacyPolicy": "http://juicyads.com/privacy", + "url": "http://juicyads.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.009, + "fingerprinting": 0, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Malware" + ] + }, + "justpremium.com": { + "domain": "justpremium.com", + "default": "block", + "owner": { + "name": "JustPremium", + "displayName": "JustPremium", + "privacyPolicy": "https://justpremium.com/terms-conditions/", + "url": "http://justpremium.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 2, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "kampyle.com": { + "domain": "kampyle.com", + "default": "block", + "owner": { + "name": "Medallia Inc.", + "displayName": "Medallia", + "privacyPolicy": "https://www.medallia.com/privacy-policy/", + "url": "http://medallia.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 2, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Third-Party Analytics Marketing", + "Embedded Content" + ] + }, + "kargo.com": { + "domain": "kargo.com", + "default": "block", + "owner": { + "name": "Kargo Global, Inc.", + "displayName": "Kargo", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud" + ] + }, + "keywee.co": { + "domain": "keywee.co", + "default": "block", + "owner": { + "name": "Keywee", + "displayName": "Keywee", + "privacyPolicy": "https://keywee.co/privacy-policy/", + "url": "http://keywee.co" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 2, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing" + ] + }, + "klaviyo.com": { + "domain": "klaviyo.com", + "default": "block", + "owner": { + "name": "Klaviyo", + "displayName": "Klaviyo", + "privacyPolicy": "https://www.klaviyo.com/privacy", + "url": "http://klaviyo.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 2, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Embedded Content" + ] + }, + "korrelate.net": { + "domain": "korrelate.net", + "default": "block", + "owner": { + "name": "J.D. Power", + "displayName": "J.D. Power", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking" + ] + }, + "krxd.net": { + "domain": "krxd.net", + "default": "block", + "owner": { + "name": "Salesforce.com, Inc.", + "displayName": "Salesforce.com", + "privacyPolicy": "https://www.salesforce.com/company/privacy/", + "url": "http://salesforce.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.06, + "fingerprinting": 1, + "cookies": 0.059, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Audience Measurement" + ] + }, + "liadm.com": { + "domain": "liadm.com", + "default": "block", + "owner": { + "name": "LiveIntent Inc.", + "displayName": "LiveIntent", + "privacyPolicy": "https://liveintent.com/privacy-policy/", + "url": "http://liveintent.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.02, + "fingerprinting": 1, + "cookies": 0.02, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "lijit.com": { + "domain": "lijit.com", + "default": "block", + "owner": { + "name": "Sovrn Holdings", + "displayName": "Sovrn Holdings", + "privacyPolicy": "https://www.sovrn.com/sovrn-legal-policies/", + "url": "http://sovrn.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.05, + "fingerprinting": 2, + "cookies": 0.048, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "linkedin.com": { + "rules": [ + { + "rule": "linkedin\\.com\\/in\\.js" + }, + { + "rule": "linkedin\\.com\\/countserv\\/count\\/share" + }, + { + "rule": "linkedin\\.com\\/collect" + }, + { + "rule": "linkedin\\.com\\/px\\/li_sync" + }, + { + "rule": "linkedin\\.com\\/autofill\\/js\\/autofill\\.js" + }, + { + "rule": "linkedin\\.com\\/pages-extensions\\/FollowCompany\\.js" + }, + { + "rule": "linkedin\\.com\\/pages-extensions\\/FollowCompany" + }, + { + "rule": "linkedin\\.com\\/xdoor\\/scripts\\/in\\.js" + } + ], + "domain": "linkedin.com", + "default": "ignore", + "owner": { + "name": "LinkedIn Corporation", + "displayName": "LinkedIn", + "privacyPolicy": "https://www.linkedin.com/legal/privacy-policy", + "url": "http://linkedin.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.044, + "fingerprinting": 0, + "cookies": 0.042, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Social - Share", + "Action Pixels", + "Embedded Content", + "Social Network" + ] + }, + "linksynergy.com": { + "domain": "linksynergy.com", + "default": "block", + "owner": { + "name": "Rakuten, Inc.", + "displayName": "Rakuten", + "privacyPolicy": "https://rakutenmarketing.com/legal-notices/services-privacy-policy", + "url": "http://rakutenmarketing.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.034, + "fingerprinting": 0, + "cookies": 0.034, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "list-manage.com": { + "domain": "list-manage.com", + "default": "block", + "owner": { + "name": "The Rocket Science Group, LLC", + "displayName": "The Rocket Science Group", + "privacyPolicy": "https://mailchimp.com/legal/privacy/", + "url": "http://mailchimp.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Third-Party Analytics Marketing", + "Embedded Content" + ], + "rules": [ + { + "rule": "list-manage\\.com\\/subscribe", + "action": "ignore" + }, + { + "rule": "list-manage\\.com", + "exceptions": { + "types": [ + "image", + "stylesheet" + ] + } + } + ] + }, + "listrak.com": { + "domain": "listrak.com", + "default": "block", + "owner": { + "name": "Listrak", + "displayName": "Listrak", + "privacyPolicy": "https://www.listrak.com/privacy-and-terms/privacy", + "url": "http://listrak.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "listrakbi.com": { + "domain": "listrakbi.com", + "default": "block", + "owner": { + "name": "Listrak", + "displayName": "Listrak", + "privacyPolicy": "https://www.listrak.com/privacy-and-terms/privacy", + "url": "http://listrak.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 0, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "livechatinc.com": { + "domain": "livechatinc.com", + "default": "block", + "owner": { + "name": "LiveChat Inc", + "displayName": "LiveChat", + "privacyPolicy": "https://www.livechatinc.com/legal/privacy-policy/", + "url": "http://livechatinc.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 2, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 2, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Embedded Content" + ] + }, + "liveperson.net": { + "domain": "liveperson.net", + "default": "block", + "owner": { + "name": "LivePerson, Inc", + "displayName": "LivePerson", + "privacyPolicy": "https://www.liveperson.com/policies/privacy", + "url": "http://liveperson.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 2, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Analytics", + "Embedded Content" + ] + }, + "lkqd.net": { + "domain": "lkqd.net", + "default": "block", + "owner": { + "name": "Nexstar Media Group", + "displayName": "Nexstar Media Group", + "privacyPolicy": "https://www.nexstardigital.com/privacy", + "url": "http://nexstardigital.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 2, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cpu": 3, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "lockerdome.com": { + "domain": "lockerdome.com", + "default": "block", + "owner": { + "name": "LockerDome, LLC", + "displayName": "LockerDome", + "privacyPolicy": "https://lockerdome.com/privacy", + "url": "http://lockerdome.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "loopme.me": { + "domain": "loopme.me", + "default": "block", + "owner": { + "name": "LoopMe Ltd", + "displayName": "LoopMe", + "privacyPolicy": "https://loopme.com/privacy-policy/", + "url": "http://loopme.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "lpsnmedia.net": { + "domain": "lpsnmedia.net", + "default": "block", + "owner": { + "name": "LivePerson, Inc", + "displayName": "LivePerson", + "privacyPolicy": "https://www.liveperson.com/policies/privacy", + "url": "http://liveperson.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 1, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Analytics", + "Embedded Content" + ] + }, + "m6r.eu": { + "domain": "m6r.eu", + "default": "block", + "owner": { + "name": "Ströer Group", + "displayName": "Ströer Group", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "mail.ru": { + "domain": "mail.ru", + "default": "block", + "owner": { + "name": "LLC Mail.Ru", + "displayName": "Mail.Ru", + "privacyPolicy": "https://agent.mail.ru/legal/privacypolicy/en", + "url": "http://mail.ru" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 2, + "cookies": 0.007, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Social - Share", + "Action Pixels", + "Embedded Content" + ] + }, + "mailchimp.com": { + "domain": "mailchimp.com", + "default": "block", + "owner": { + "name": "The Rocket Science Group, LLC", + "displayName": "The Rocket Science Group", + "privacyPolicy": "https://mailchimp.com/legal/privacy/", + "url": "http://mailchimp.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.007, + "fingerprinting": 1, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Advertising", + "Third-Party Analytics Marketing", + "Embedded Content" + ], + "rules": [ + { + "rule": "gallery\\.mailchimp\\.com", + "exceptions": { + "types": [ + "image" + ] + } + }, + { + "rule": "cdn-images\\.mailchimp\\.com", + "exceptions": { + "types": [ + "image", + "stylesheet" + ] + } + } + ] + }, + "marinsm.com": { + "domain": "marinsm.com", + "default": "block", + "owner": { + "name": "Marin Software Inc.", + "displayName": "Marin Software", + "privacyPolicy": "http://www.marinsoftware.com/marin-software-privacy-policy", + "url": "http://marinsoftware.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing" + ] + }, + "marketo.com": { + "domain": "marketo.com", + "default": "block", + "owner": { + "name": "Marketo, Inc.", + "displayName": "Marketo", + "privacyPolicy": "https://documents.marketo.com/legal/privacy/", + "url": "http://marketo.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 1, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing", + "Embedded Content" + ], + "rules": [ + { + "rule": "marketo\\.com\\/index\\.php\\/form\\/getform", + "exceptions": { + "types": [ + "script" + ] + } + }, + { + "rule": "marketo\\.com\\/js\\/forms2", + "exceptions": { + "types": [ + "script", + "stylesheet" + ] + } + }, + { + "rule": "marketo\\.com\\/index\\.php\\/form\\/xdframe", + "exceptions": { + "types": [ + "subdocument" + ] + } + }, + { + "rule": "marketo\\.com\\/index\\.php\\/leadcapture\\/save2", + "exceptions": { + "types": [ + "xmlhttprequest" + ] + } + } + ] + }, + "marketo.net": { + "domain": "marketo.net", + "default": "block", + "owner": { + "name": "Marketo, Inc.", + "displayName": "Marketo", + "privacyPolicy": "https://documents.marketo.com/legal/privacy/", + "url": "http://marketo.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.009, + "fingerprinting": 1, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing", + "Embedded Content" + ] + }, + "mathtag.com": { + "domain": "mathtag.com", + "default": "block", + "owner": { + "name": "MediaMath, Inc.", + "displayName": "MediaMath", + "privacyPolicy": "http://www.mediamath.com/privacy-policy/", + "url": "http://mediamath.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.108, + "fingerprinting": 0, + "cookies": 0.108, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing" + ] + }, + "maxmind.com": { + "domain": "maxmind.com", + "default": "block", + "owner": { + "name": "MaxMind Inc.", + "displayName": "MaxMind", + "privacyPolicy": "https://www.maxmind.com/en/privacy-policy", + "url": "http://maxmind.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 3, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Ad Fraud", + "Analytics", + "Audience Measurement" + ] + }, + "maxymiser.net": { + "domain": "maxymiser.net", + "default": "block", + "owner": { + "name": "Oracle Corporation", + "displayName": "Oracle", + "privacyPolicy": "https://www.oracle.com/legal/privacy/privacy-policy.html", + "url": "http://oracle.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 1, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing" + ] + }, + "media.net": { + "domain": "media.net", + "default": "block", + "owner": { + "name": "Media.net Advertising FZ-LLC", + "displayName": "Media.net Advertising", + "privacyPolicy": "https://www.media.net/en/privacy-policy", + "url": "http://media.net" + }, + "source": [ + "DDG" + ], + "prevalence": 0.022, + "fingerprinting": 0, + "cookies": 0.02, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ], + "rules": [ + { + "rule": "contextual\\.media\\.net\\/bidexchange\\.js", + "exceptions": { + "domains": [ + "nytimes.com" + ] + } + } + ] + }, + "media6degrees.com": { + "domain": "media6degrees.com", + "default": "block", + "owner": { + "name": "Dstillery Inc.", + "displayName": "Dstillery", + "privacyPolicy": "https://dstillery.com/privacy-policy/", + "url": "http://dstillery.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 0, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement" + ] + }, + "mediaplex.com": { + "domain": "mediaplex.com", + "default": "block", + "owner": { + "name": "Conversant LLC", + "displayName": "Conversant", + "privacyPolicy": "https://www.conversantmedia.com/legal/privacy", + "url": "http://conversantmedia.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 0, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "mediavine.com": { + "domain": "mediavine.com", + "default": "block", + "owner": { + "name": "Mediavine, Inc.", + "displayName": "Mediavine", + "privacyPolicy": "https://www.mediavine.com/privacy-policy/", + "url": "http://mediavine.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 2, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cpu": 3, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Audience Measurement" + ] + }, + "mediawallahscript.com": { + "domain": "mediawallahscript.com", + "default": "block", + "owner": { + "name": "MediaWallah LLC", + "displayName": "MediaWallah", + "privacyPolicy": "https://www.mediawallah.com/privacy-policy", + "url": "http://mediawallah.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "meetrics.net": { + "domain": "meetrics.net", + "default": "block", + "owner": { + "name": "Meetrics GmbH", + "displayName": "Meetrics", + "privacyPolicy": "https://www.meetrics.com/en/data-privacy/", + "url": "http://meetrics.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Analytics", + "Audience Measurement" + ] + }, + "mfadsrvr.com": { + "domain": "mfadsrvr.com", + "default": "block", + "owner": { + "name": "IPONWEB GmbH", + "displayName": "IPONWEB", + "privacyPolicy": "https://www.iponweb.com/privacy-policy/", + "url": "http://iponweb.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.017, + "fingerprinting": 0, + "cookies": 0.017, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud" + ] + }, + "mgid.com": { + "domain": "mgid.com", + "default": "block", + "owner": { + "name": "MGID Inc", + "displayName": "MGID", + "privacyPolicy": "https://www.mgid.com/privacy-policy", + "url": "http://mgid.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 2, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "micpn.com": { + "domain": "micpn.com", + "default": "block", + "owner": { + "name": "Movable Ink", + "displayName": "Movable Ink", + "privacyPolicy": "https://movableink.com/legal/privacy", + "url": "http://movableink.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 1, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Audience Measurement" + ] + }, + "mixpanel.com": { + "domain": "mixpanel.com", + "default": "block", + "owner": { + "name": "Mixpanel, Inc.", + "displayName": "Mixpanel", + "privacyPolicy": "https://mixpanel.com/privacy/", + "url": "http://mixpanel.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels", + "Session Replay" + ] + }, + "mktoresp.com": { + "domain": "mktoresp.com", + "default": "block", + "owner": { + "name": "Marketo, Inc.", + "displayName": "Marketo", + "privacyPolicy": "https://documents.marketo.com/legal/privacy/", + "url": "http://marketo.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.009, + "fingerprinting": 0, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing", + "Embedded Content" + ] + }, + "ml314.com": { + "domain": "ml314.com", + "default": "block", + "owner": { + "name": "Bombora Inc.", + "displayName": "Bombora", + "privacyPolicy": "https://bombora.com/privacy/", + "url": "http://bombora.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.035, + "fingerprinting": 0, + "cookies": 0.035, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Action Pixels", + "Session Replay" + ] + }, + "moatads.com": { + "domain": "moatads.com", + "default": "block", + "owner": { + "name": "Oracle Corporation", + "displayName": "Oracle", + "privacyPolicy": "https://www.oracle.com/legal/privacy/privacy-policy.html", + "url": "http://oracle.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.03, + "fingerprinting": 3, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 3, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels" + ], + "rules": [ + { + "rule": "moatads\\.com\\/freewheel.*\\/moatfreewheeljspem\\.js", + "exceptions": { + "domains": [ + "tntdrama.com", + "nba.com" + ] + } + }, + { + "rule": "moatads\\.com\\/.*\\/moatwrapper\\.js", + "exceptions": { + "domains": [ + "cc.com", + "hulu.com" + ] + } + } + ] + }, + "mobileadtrading.com": { + "domain": "mobileadtrading.com", + "default": "block", + "owner": { + "name": "Somo Audience Corp", + "displayName": "Somo Audience", + "privacyPolicy": "https://somoaudience.com/legal/", + "url": "http://somoaudience.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.011, + "fingerprinting": 0, + "cookies": 0.011, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud" + ] + }, + "monetate.net": { + "domain": "monetate.net", + "default": "block", + "owner": { + "name": "Monetate, Inc.", + "displayName": "Monetate", + "privacyPolicy": "https://www.monetate.com/website-privacy-policy", + "url": "http://monetate.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 2, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels", + "Embedded Content" + ] + }, + "mookie1.com": { + "domain": "mookie1.com", + "default": "block", + "owner": { + "name": "Xaxis", + "displayName": "Xaxis", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.048, + "fingerprinting": 0, + "cookies": 0.047, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "mouseflow.com": { + "domain": "mouseflow.com", + "default": "block", + "owner": { + "name": "Mouseflow", + "displayName": "Mouseflow", + "privacyPolicy": "https://mouseflow.com/privacy/", + "url": "http://mouseflow.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 2, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Analytics", + "Third-Party Analytics Marketing", + "Action Pixels", + "Session Replay" + ] + }, + "mxpnl.com": { + "domain": "mxpnl.com", + "default": "block", + "owner": { + "name": "Mixpanel, Inc.", + "displayName": "Mixpanel", + "privacyPolicy": "https://mixpanel.com/privacy/", + "url": "http://mixpanel.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 2, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels", + "Session Replay" + ], + "rules": [ + { + "rule": "cdn\\.mxpnl\\.com\\/libs\\/mixpanel-2-latest\\.min\\.js", + "exceptions": { + "domains": [ + "7-eleven.ca" + ] + } + } + ] + }, + "mxptint.net": { + "domain": "mxptint.net", + "default": "block", + "owner": { + "name": "Valassis Digital", + "displayName": "Valassis Digital", + "privacyPolicy": "https://www.valassisdigital.com/legal/privacy-policy/", + "url": "http://valassisdigital.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.024, + "fingerprinting": 0, + "cookies": 0.023, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement" + ] + }, + "mynativeplatform.com": { + "domain": "mynativeplatform.com", + "default": "block", + "owner": { + "name": "My6sense Inc.", + "displayName": "My6sense", + "privacyPolicy": "https://my6sense.com/privacy-policy/", + "url": "http://my6sense.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "myvisualiq.net": { + "domain": "myvisualiq.net", + "default": "block", + "owner": { + "name": "The Nielsen Company", + "displayName": "The Nielsen Company", + "privacyPolicy": "http://www.nielsen.com/us/en/privacy-statement/digital-measurement.html", + "url": "http://nielsen.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.017, + "fingerprinting": 0, + "cookies": 0.017, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Audience Measurement" + ] + }, + "navdmp.com": { + "domain": "navdmp.com", + "default": "block", + "owner": { + "name": "Navegg S.A.", + "displayName": "Navegg", + "privacyPolicy": "https://www.navegg.com/en/privacy-policy/", + "url": "http://navegg.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 0, + "cookies": 0.006, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement" + ] + }, + "netmng.com": { + "domain": "netmng.com", + "default": "block", + "owner": { + "name": "IgnitionOne, LLC", + "displayName": "IgnitionOne", + "privacyPolicy": "https://ignitionone.com/privacy-policy/", + "url": "http://ignitionone.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.035, + "fingerprinting": 0, + "cookies": 0.034, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Action Pixels" + ] + }, + "newrelic.com": { + "domain": "newrelic.com", + "default": "block", + "owner": { + "name": "New Relic", + "displayName": "New Relic", + "privacyPolicy": "https://newrelic.com/privacy", + "url": "http://newrelic.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.084, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics" + ] + }, + "ninthdecimal.com": { + "domain": "ninthdecimal.com", + "default": "block", + "owner": { + "name": "NinthDecimal, Inc", + "displayName": "NinthDecimal", + "privacyPolicy": "https://www.ninthdecimal.com/privacy-policy-terms-of-service/", + "url": "http://ninthdecimal.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 0, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Analytics", + "Audience Measurement" + ] + }, + "nr-data.net": { + "domain": "nr-data.net", + "default": "block", + "owner": { + "name": "New Relic", + "displayName": "New Relic", + "privacyPolicy": "https://newrelic.com/privacy", + "url": "http://newrelic.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.084, + "fingerprinting": 0, + "cookies": 0.037, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics" + ] + }, + "ntv.io": { + "domain": "ntv.io", + "default": "block", + "owner": { + "name": "Nativo, Inc", + "displayName": "Nativo", + "privacyPolicy": "https://www.nativo.com/privacy-policy", + "url": "http://nativo.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.007, + "fingerprinting": 1, + "cookies": 0.007, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "nuggad.net": { + "domain": "nuggad.net", + "default": "block", + "owner": { + "name": "nugg.ad GmbH", + "displayName": "nugg.ad", + "privacyPolicy": "https://www.nugg.ad/en/privacy/general-information.html", + "url": "http://nugg.ad" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 0, + "cookies": 0.006, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "o333o.com": { + "domain": "o333o.com", + "default": "block", + "owner": { + "name": "AdSpyglass", + "displayName": "AdSpyglass", + "privacyPolicy": "https://www.adspyglass.com/privacy_policy", + "url": "http://adspyglass.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 1, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "olark.com": { + "domain": "olark.com", + "default": "block", + "owner": { + "name": "Olark", + "displayName": "Olark", + "privacyPolicy": "https://www.olark.com/privacy-policy", + "url": "http://olark.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 2, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 2, + "cpu": 3, + "cache": 3 + }, + "categories": [ + "Third-Party Analytics Marketing", + "Embedded Content" + ] + }, + "omnitagjs.com": { + "domain": "omnitagjs.com", + "default": "block", + "owner": { + "name": "Adyoulike", + "displayName": "Adyoulike", + "privacyPolicy": "https://www.adyoulike.com/privacy_policy.php", + "url": "http://adyoulike.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.009, + "fingerprinting": 0, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "omtrdc.net": { + "domain": "omtrdc.net", + "default": "block", + "owner": { + "name": "Adobe Inc.", + "displayName": "Adobe", + "privacyPolicy": "https://www.adobe.com/privacy/marketing-cloud.html", + "url": "http://adobe.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.035, + "fingerprinting": 0, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ], + "rules": [ + { + "rule": "omtrdc\\.net", + "exceptions": { + "domains": [ + "canadiantire.ca", + "delltechnologiesworld.com", + "disneyvacationclub.disney.go.com", + "mercuryinsurance.com" + ], + "types": [ + "script", + "image" + ] + } + } + ] + }, + "onesignal.com": { + "domain": "onesignal.com", + "default": "block", + "owner": { + "name": "OneSignal", + "displayName": "OneSignal", + "privacyPolicy": "https://onesignal.com/privacy_policy", + "url": "http://onesignal.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.023, + "fingerprinting": 2, + "cookies": 0.023, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Analytics", + "Third-Party Analytics Marketing", + "Embedded Content" + ] + }, + "onthe.io": { + "domain": "onthe.io", + "default": "block", + "owner": { + "name": "IO Technologies Inc.", + "displayName": "IO", + "privacyPolicy": "https://iotechnologies.com/pp", + "url": "http://iotechnologies.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 1, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 3 + }, + "categories": [ + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "openx.net": { + "domain": "openx.net", + "default": "block", + "owner": { + "name": "OpenX Technologies Inc", + "displayName": "OpenX", + "privacyPolicy": "https://www.openx.com/legal/privacy-policy/", + "url": "http://openx.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.141, + "fingerprinting": 0, + "cookies": 0.14, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Audience Measurement" + ] + }, + "opmnstr.com": { + "domain": "opmnstr.com", + "default": "block", + "owner": { + "name": "Retyp LLC", + "displayName": "Retyp", + "privacyPolicy": "https://optinmonster.com/privacy/", + "url": "http://optinmonster.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.013, + "fingerprinting": 1, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing", + "Action Pixels", + "Embedded Content" + ] + }, + "optmnstr.com": { + "domain": "optmnstr.com", + "default": "block", + "owner": { + "name": "Retyp LLC", + "displayName": "Retyp", + "privacyPolicy": "https://optinmonster.com/privacy/", + "url": "http://optinmonster.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 1, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing", + "Action Pixels", + "Embedded Content" + ] + }, + "outbrain.com": { + "domain": "outbrain.com", + "default": "block", + "owner": { + "name": "Outbrain", + "displayName": "Outbrain", + "privacyPolicy": "https://www.outbrain.com/legal/privacy", + "url": "http://outbrain.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.045, + "fingerprinting": 1, + "cookies": 0.036, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing" + ], + "rules": [ + { + "rule": "outbrain\\.com\\/outbrain.js", + "surrogate": "outbrain.js" + } + ] + }, + "owneriq.net": { + "domain": "owneriq.net", + "default": "block", + "owner": { + "name": "OwnerIQ Inc", + "displayName": "OwnerIQ", + "privacyPolicy": "http://www.owneriq.net/privacy-policy", + "url": "http://owneriq.net" + }, + "source": [ + "DDG" + ], + "prevalence": 0.025, + "fingerprinting": 0, + "cookies": 0.025, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Audience Measurement" + ] + }, + "pagefair.com": { + "domain": "pagefair.com", + "default": "block", + "owner": { + "name": "PageFair Limited", + "displayName": "PageFair", + "privacyPolicy": "https://pagefair.com/privacy/", + "url": "http://pagefair.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "pardot.com": { + "domain": "pardot.com", + "default": "block", + "owner": { + "name": "Salesforce.com, Inc.", + "displayName": "Salesforce.com", + "privacyPolicy": "https://www.salesforce.com/company/privacy/", + "url": "http://salesforce.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 0, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Embedded Content" + ] + }, + "parsely.com": { + "domain": "parsely.com", + "default": "block", + "owner": { + "name": "Parsely, Inc.", + "displayName": "Parsely", + "privacyPolicy": "https://www.parse.ly/privacy-policy/", + "url": "http://parse.ly" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 1, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "payments-amazon.com": { + "domain": "payments-amazon.com", + "default": "ignore", + "owner": { + "name": "Amazon Technologies, Inc.", + "displayName": "Amazon", + "privacyPolicy": "https://www.amazon.com/gp/help/customer/display.html?nodeId=468496", + "url": "http://amazon.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 0, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics", + "Audience Measurement", + "Federated Login", + "Online Payment", + "Badge", + "Embedded Content" + ] + }, + "paypal.com": { + "domain": "paypal.com", + "default": "ignore", + "owner": { + "name": "PayPal, Inc.", + "displayName": "PayPal", + "privacyPolicy": "https://www.paypal.com/us/webapps/mpp/ua/privacy-full", + "url": "http://paypal.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 3, + "cookies": 0.006, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics", + "Audience Measurement", + "SSO", + "Online Payment", + "Badge", + "Embedded Content" + ] + }, + "perfectmarket.com": { + "domain": "perfectmarket.com", + "default": "block", + "owner": { + "name": "Perfect Market, Inc.", + "displayName": "Perfect Market", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 1, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Session Replay" + ] + }, + "permutive.com": { + "domain": "permutive.com", + "default": "block", + "owner": { + "name": "Permutive, Inc.", + "displayName": "Permutive", + "privacyPolicy": "https://permutive.com/privacy/", + "url": "http://permutive.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 1, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Session Replay" + ] + }, + "pingdom.net": { + "domain": "pingdom.net", + "default": "block", + "owner": { + "name": "Pingdom AB", + "displayName": "Pingdom", + "privacyPolicy": "https://www.pingdom.com/legal/cookie-policy/", + "url": "http://pingdom.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.012, + "fingerprinting": 2, + "cookies": 0.012, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics", + "Audience Measurement", + "Action Pixels" + ] + }, + "pippio.com": { + "domain": "pippio.com", + "default": "block", + "owner": { + "name": "LiveRamp", + "displayName": "LiveRamp", + "privacyPolicy": "https://liveramp.com/privacy/", + "url": "http://liveramp.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.06, + "fingerprinting": 0, + "cookies": 0.06, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "playground.xyz": { + "domain": "playground.xyz", + "default": "block", + "owner": { + "name": "PLAYGROUND XYZ", + "displayName": "PLAYGROUND XYZ", + "privacyPolicy": "https://playground.xyz/privacy/", + "url": "http://playground.xyz" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 0, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing" + ] + }, + "po.st": { + "domain": "po.st", + "default": "block", + "owner": { + "name": "RhythmOne", + "displayName": "RhythmOne", + "privacyPolicy": "https://www.rhythmone.com/privacy-policy", + "url": "http://rhythmone.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 1, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Social - Comment", + "Social - Share", + "Badge", + "Embedded Content" + ] + }, + "popads.net": { + "domain": "popads.net", + "default": "block", + "owner": { + "name": "Tomksoft S.A.", + "displayName": "Tomksoft", + "privacyPolicy": "https://www.popads.net/privacy-policy.html", + "url": "http://popads.net" + }, + "source": [ + "DDG" + ], + "prevalence": 0.007, + "fingerprinting": 1, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Analytics", + "Third-Party Analytics Marketing" + ] + }, + "postrelease.com": { + "domain": "postrelease.com", + "default": "block", + "owner": { + "name": "Nativo, Inc", + "displayName": "Nativo", + "privacyPolicy": "https://www.nativo.com/privacy-policy", + "url": "http://nativo.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.01, + "fingerprinting": 0, + "cookies": 0.01, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing" + ] + }, + "powerlinks.com": { + "domain": "powerlinks.com", + "default": "block", + "owner": { + "name": "PowerLinks Media Limited", + "displayName": "PowerLinks Media", + "privacyPolicy": "https://www.powerlinks.com/privacy-policy/", + "url": "http://powerlinks.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.017, + "fingerprinting": 0, + "cookies": 0.016, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Analytics", + "Audience Measurement", + "Embedded Content" + ] + }, + "pro-market.net": { + "domain": "pro-market.net", + "default": "block", + "owner": { + "name": "Datonics LLC", + "displayName": "Datonics", + "privacyPolicy": "https://www.datonics.com/privacy/", + "url": "http://datonics.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.012, + "fingerprinting": 0, + "cookies": 0.011, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Audience Measurement" + ] + }, + "promo-bc.com": { + "domain": "promo-bc.com", + "default": "block", + "owner": { + "name": "BongaCams", + "displayName": "BongaCams", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Advertising", + "Embedded Content" + ] + }, + "pswec.com": { + "domain": "pswec.com", + "default": "block", + "owner": { + "name": "Proclivity Media, Inc.", + "displayName": "Proclivity Media", + "privacyPolicy": "https://www.proclivitysystems.com/", + "url": "http://proclivitysystems.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 0, + "cookies": 0.006, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement" + ] + }, + "pubmatic.com": { + "domain": "pubmatic.com", + "default": "block", + "owner": { + "name": "PubMatic, Inc.", + "displayName": "PubMatic", + "privacyPolicy": "https://pubmatic.com/legal/privacy-policy/", + "url": "http://pubmatic.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.121, + "fingerprinting": 0, + "cookies": 0.116, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "pushcrew.com": { + "domain": "pushcrew.com", + "default": "block", + "owner": { + "name": "Wingify", + "displayName": "Wingify", + "privacyPolicy": "https://wingify.com/privacy-policy", + "url": "http://wingify.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 1, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "qualaroo.com": { + "domain": "qualaroo.com", + "default": "block", + "owner": { + "name": "Qualaroo", + "displayName": "Qualaroo", + "privacyPolicy": "https://qualaroo.com/privacy-policy/", + "url": "http://qualaroo.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 1, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Embedded Content", + "Session Replay" + ] + }, + "qualtrics.com": { + "domain": "qualtrics.com", + "default": "block", + "owner": { + "name": "Qualtrics, LLC", + "displayName": "Qualtrics", + "privacyPolicy": "https://www.qualtrics.com/privacy-statement/", + "url": "http://qualtrics.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.009, + "fingerprinting": 1, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Embedded Content", + "Session Replay" + ] + }, + "quantserve.com": { + "domain": "quantserve.com", + "default": "block", + "owner": { + "name": "Quantcast Corporation", + "displayName": "Quantcast", + "privacyPolicy": "http://www.quantcast.com/privacy", + "url": "http://quantcast.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.11, + "fingerprinting": 1, + "cookies": 0.107, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "quora.com": { + "domain": "quora.com", + "default": "block", + "owner": { + "name": "Quora", + "displayName": "Quora", + "privacyPolicy": "https://www.quora.com/about/privacy", + "url": "http://quora.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 0, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Advertising", + "Analytics", + "Social - Comment", + "Social - Share", + "Embedded Content", + "Social Network" + ] + }, + "rambler.ru": { + "domain": "rambler.ru", + "default": "block", + "owner": { + "name": "Rambler Internet Holding, LLC", + "displayName": "Rambler Internet Holding", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 2, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing" + ], + "rules": [ + { + "rule": "palacesquare\\.rambler\\.ru", + "exceptions": { + "domains": [ + "championat.com", + "lenta.ru" + ], + "types": [ + "image", + "stylesheet" + ] + } + }, + { + "rule": "comments\\.rambler\\.ru", + "exceptions": { + "domains": [ + "championat.com", + "lenta.ru" + ], + "types": [ + "script" + ] + } + } + ] + }, + "ravenjs.com": { + "domain": "ravenjs.com", + "default": "block", + "owner": { + "name": "Functional Software, Inc.", + "displayName": "Functional Software", + "privacyPolicy": "https://sentry.io/privacy/", + "url": "http://sentry.io" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Analytics" + ], + "rules": [ + { + "rule": "cdn\\.ravenjs\\.com\\/.*\\/raven\\.min\\.js", + "exceptions": { + "types": [ + "script" + ] + } + } + ] + }, + "reddit.com": { + "domain": "reddit.com", + "default": "block", + "owner": { + "name": "Reddit Inc.", + "displayName": "Reddit", + "privacyPolicy": "https://www.redditinc.com/policies/privacy-policy", + "url": "http://redditinc.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.007, + "fingerprinting": 0, + "cookies": 0.007, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Social - Comment", + "Social - Share", + "Badge", + "Embedded Content", + "Social Network" + ], + "rules": [ + { + "rule": "reddit\\.com\\/r", + "exceptions": { + "types": [ + "script", + "xmlhttprequest" + ] + } + }, + { + "rule": "reddit\\.com\\/comments", + "exceptions": { + "types": [ + "script", + "xmlhttprequest" + ] + } + }, + { + "rule": "reddit\\.com\\/api", + "exceptions": { + "types": [ + "script", + "xmlhttprequest" + ] + } + }, + { + "rule": "oauth\\.reddit\\.com", + "action": "ignore" + } + ] + }, + "redditstatic.com": { + "domain": "redditstatic.com", + "default": "block", + "owner": { + "name": "Reddit Inc.", + "displayName": "Reddit", + "privacyPolicy": "https://www.redditinc.com/policies/privacy-policy", + "url": "http://redditinc.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 2, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Analytics", + "Action Pixels" + ] + }, + "reson8.com": { + "domain": "reson8.com", + "default": "block", + "owner": { + "name": "Resonate Networks", + "displayName": "Resonate Networks", + "privacyPolicy": "http://reson8.com/", + "url": "http://reson8.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.011, + "fingerprinting": 0, + "cookies": 0.011, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Analytics", + "Third-Party Analytics Marketing" + ] + }, + "revcontent.com": { + "domain": "revcontent.com", + "default": "block", + "owner": { + "name": "RevContent, LLC", + "displayName": "RevContent", + "privacyPolicy": "https://faq.revcontent.com/customer/en/portal/articles/2703838-revcontent-s-privacy-and-cookie-policy", + "url": "http://revcontent.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 0, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "revjet.com": { + "domain": "revjet.com", + "default": "block", + "owner": { + "name": "RevJet", + "displayName": "RevJet", + "privacyPolicy": "https://www.revjet.com/privacy", + "url": "http://revjet.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 1, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "rezync.com": { + "domain": "rezync.com", + "default": "block", + "owner": { + "name": "Zeta Global", + "displayName": "Zeta Global", + "privacyPolicy": "https://zetaglobal.com/privacy-policy/", + "url": "http://zetaglobal.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.017, + "fingerprinting": 0, + "cookies": 0.017, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking" + ] + }, + "rfihub.com": { + "domain": "rfihub.com", + "default": "block", + "owner": { + "name": "Rocket Fuel Inc.", + "displayName": "Rocket Fuel", + "privacyPolicy": "https://rocketfuel.com/privacy/", + "url": "http://rocketfuel.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.043, + "fingerprinting": 0, + "cookies": 0.042, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing" + ] + }, + "rfihub.net": { + "domain": "rfihub.net", + "default": "block", + "owner": { + "name": "Rocket Fuel Inc.", + "displayName": "Rocket Fuel", + "privacyPolicy": "https://rocketfuel.com/privacy/", + "url": "http://rocketfuel.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Advertising", + "Ad Fraud", + "Social - Comment", + "Social Network" + ] + }, + "rkdms.com": { + "domain": "rkdms.com", + "default": "block", + "owner": { + "name": "Merkle Inc", + "displayName": "Merkle", + "privacyPolicy": "https://www.merkleinc.com/privacy", + "url": "http://merkleinc.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.009, + "fingerprinting": 0, + "cookies": 0.009, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing" + ] + }, + "rmtag.com": { + "domain": "rmtag.com", + "default": "block", + "owner": { + "name": "Rakuten, Inc.", + "displayName": "Rakuten", + "privacyPolicy": "https://rakutenmarketing.com/legal-notices/services-privacy-policy", + "url": "http://rakutenmarketing.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 2, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing" + ] + }, + "rtmark.net": { + "domain": "rtmark.net", + "default": "block", + "owner": { + "name": "Propeller Ads", + "displayName": "Propeller Ads", + "privacyPolicy": "https://propellerads.com/privacy/", + "url": "http://propellerads.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 0, + "cookies": 0.006, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud" + ] + }, + "rubiconproject.com": { + "domain": "rubiconproject.com", + "default": "block", + "owner": { + "name": "The Rubicon Project, Inc.", + "displayName": "The Rubicon Project", + "privacyPolicy": "http://rubiconproject.com/rubicon-project-yield-optimization-privacy-policy/", + "url": "http://rubiconproject.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.161, + "fingerprinting": 1, + "cookies": 0.124, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "rundsp.com": { + "domain": "rundsp.com", + "default": "block", + "owner": { + "name": "RUN", + "displayName": "RUN", + "privacyPolicy": "http://www.runads.com/privacy-policy", + "url": "http://runads.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.02, + "fingerprinting": 0, + "cookies": 0.02, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "rutarget.ru": { + "domain": "rutarget.ru", + "default": "block", + "owner": { + "name": "RuTarget LLC", + "displayName": "RuTarget", + "privacyPolicy": "https://segmento.ru/privacy", + "url": "http://segmento.ru" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "s-onetag.com": { + "domain": "s-onetag.com", + "default": "block", + "owner": { + "name": "Sovrn Holdings", + "displayName": "Sovrn Holdings", + "privacyPolicy": "https://www.sovrn.com/sovrn-legal-policies/", + "url": "http://sovrn.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 1, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "sail-horizon.com": { + "domain": "sail-horizon.com", + "default": "block", + "owner": { + "name": "Sailthru, Inc", + "displayName": "Sailthru", + "privacyPolicy": "https://www.sailthru.com/legal/privacy-statement/", + "url": "http://sailthru.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 0, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing", + "Embedded Content" + ] + }, + "salesforceliveagent.com": { + "domain": "salesforceliveagent.com", + "default": "block", + "owner": { + "name": "Salesforce.com, Inc.", + "displayName": "Salesforce.com", + "privacyPolicy": "https://www.salesforce.com/company/privacy/", + "url": "http://salesforce.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 0, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Embedded Content" + ], + "rules": [ + { + "rule": "salesforceliveagent\\.com", + "exceptions": { + "types": [ + "script", + "subdocument", + "stylesheet", + "xmlhttprequest" + ] + } + } + ] + }, + "scarabresearch.com": { + "domain": "scarabresearch.com", + "default": "block", + "owner": { + "name": "Emarsys eMarketing Systems AG", + "displayName": "Emarsys eMarketing Systems", + "privacyPolicy": "https://www.emarsys.com/en/privacy-policy/", + "url": "http://emarsys.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 2, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Advertising", + "Analytics" + ] + }, + "scorecardresearch.com": { + "domain": "scorecardresearch.com", + "default": "block", + "owner": { + "name": "comScore, Inc", + "displayName": "comScore", + "privacyPolicy": "https://www.comscore.com/About/Privacy-Policy", + "url": "http://comscore.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.108, + "fingerprinting": 1, + "cookies": 0.107, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics", + "Audience Measurement" + ], + "rules": [ + { + "rule": "scorecardresearch\\.com\\/beacon.js", + "surrogate": "beacon.js" + } + ] + }, + "securedvisit.com": { + "domain": "securedvisit.com", + "default": "block", + "owner": { + "name": "4Cite Marketing", + "displayName": "4Cite Marketing", + "privacyPolicy": "https://www.4cite.com/4cite-marketing-llc-privacy-policy/", + "url": "http://4cite.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 2, + "cookies": 0.006, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "segment.com": { + "domain": "segment.com", + "default": "block", + "owner": { + "name": "Segment.io, Inc.", + "displayName": "Segment.io", + "privacyPolicy": "https://segment.io/privacy", + "url": "http://segment.io" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 0, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "segment.io": { + "domain": "segment.io", + "default": "block", + "owner": { + "name": "Segment.io, Inc.", + "displayName": "Segment.io", + "privacyPolicy": "https://segment.io/privacy", + "url": "http://segment.io" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "semasio.net": { + "domain": "semasio.net", + "default": "block", + "owner": { + "name": "Semasio GmbH", + "displayName": "Semasio", + "privacyPolicy": "https://www.semasio.com/privacy", + "url": "http://semasio.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.009, + "fingerprinting": 0, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "sessioncam.com": { + "domain": "sessioncam.com", + "default": "block", + "owner": { + "name": "SessionCam Ltd", + "displayName": "SessionCam", + "privacyPolicy": "https://sessioncam.com/privacy-policy-cookies/", + "url": "http://sessioncam.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 3, + "cache": 3 + }, + "categories": [ + "Analytics", + "Action Pixels", + "Session Replay" + ] + }, + "shareaholic.com": { + "domain": "shareaholic.com", + "default": "block", + "owner": { + "name": "Shareaholic Inc", + "displayName": "Shareaholic", + "privacyPolicy": "https://www.shareaholic.com/privacy/", + "url": "http://shareaholic.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing", + "Social - Share", + "Embedded Content" + ] + }, + "sharethis.com": { + "domain": "sharethis.com", + "default": "block", + "owner": { + "name": "ShareThis, Inc", + "displayName": "ShareThis", + "privacyPolicy": "https://www.sharethis.com/privacy/", + "url": "http://sharethis.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.046, + "fingerprinting": 0, + "cookies": 0.046, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing", + "Social - Share", + "Embedded Content" + ] + }, + "sharethrough.com": { + "domain": "sharethrough.com", + "default": "block", + "owner": { + "name": "Sharethrough, Inc.", + "displayName": "Sharethrough", + "privacyPolicy": "https://platform-cdn.sharethrough.com/privacy-policy", + "url": "http://sharethrough.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.014, + "fingerprinting": 0, + "cookies": 0.006, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "simpli.fi": { + "domain": "simpli.fi", + "default": "block", + "owner": { + "name": "Simplifi Holdings Inc.", + "displayName": "Simplifi Holdings", + "privacyPolicy": "https://www.simpli.fi/site-privacy-policy2/", + "url": "http://simpli.fi" + }, + "source": [ + "DDG" + ], + "prevalence": 0.043, + "fingerprinting": 0, + "cookies": 0.043, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "siteimproveanalytics.com": { + "domain": "siteimproveanalytics.com", + "default": "block", + "owner": { + "name": "Siteimprove A/S", + "displayName": "Siteimprove", + "privacyPolicy": "https://siteimprove.com/en/privacy/privacy-policy/", + "url": "http://siteimprove.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.009, + "fingerprinting": 1, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics" + ] + }, + "siteimproveanalytics.io": { + "domain": "siteimproveanalytics.io", + "default": "block", + "owner": { + "name": "Siteimprove A/S", + "displayName": "Siteimprove", + "privacyPolicy": "https://siteimprove.com/en/privacy/privacy-policy/", + "url": "http://siteimprove.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.009, + "fingerprinting": 0, + "cookies": 0.009, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Analytics" + ] + }, + "sitescout.com": { + "domain": "sitescout.com", + "default": "block", + "owner": { + "name": "Centro, Inc.", + "displayName": "Centro", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.065, + "fingerprinting": 0, + "cookies": 0.064, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Action Pixels" + ] + }, + "smaato.net": { + "domain": "smaato.net", + "default": "block", + "owner": { + "name": "Smaato Inc.", + "displayName": "Smaato", + "privacyPolicy": "https://www.smaato.com/privacy/", + "url": "http://smaato.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "smartadserver.com": { + "domain": "smartadserver.com", + "default": "block", + "owner": { + "name": "Smartadserver S.A.S", + "displayName": "Smartadserver", + "privacyPolicy": "http://smartadserver.com/company/privacy-policy/", + "url": "http://smartadserver.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.11, + "fingerprinting": 0, + "cookies": 0.109, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "snapchat.com": { + "domain": "snapchat.com", + "default": "block", + "owner": { + "name": "Snapchat, Inc.", + "displayName": "Snapchat", + "privacyPolicy": "https://www.snap.com/en-US/privacy/privacy-policy/", + "url": "http://snap.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.012, + "fingerprinting": 0, + "cookies": 0.012, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Action Pixels", + "Social Network" + ] + }, + "socdm.com": { + "domain": "socdm.com", + "default": "block", + "owner": { + "name": "Supership Inc", + "displayName": "Supership", + "privacyPolicy": "https://supership.jp/privacy/", + "url": "http://supership.jp" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "sojern.com": { + "domain": "sojern.com", + "default": "block", + "owner": { + "name": "Sojern, Inc.", + "displayName": "Sojern", + "privacyPolicy": "https://www.sojern.com/privacy/", + "url": "http://sojern.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "sonobi.com": { + "domain": "sonobi.com", + "default": "block", + "owner": { + "name": "Sonobi, Inc", + "displayName": "Sonobi", + "privacyPolicy": "https://sonobi.com/privacy-policy/", + "url": "http://sonobi.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.02, + "fingerprinting": 0, + "cookies": 0.019, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Advertising" + ] + }, + "spotxchange.com": { + "domain": "spotxchange.com", + "default": "block", + "owner": { + "name": "SpotX, Inc.", + "displayName": "SpotX", + "privacyPolicy": "https://www.spotx.tv/privacy-policy/", + "url": "http://spotx.tv" + }, + "source": [ + "DDG" + ], + "prevalence": 0.121, + "fingerprinting": 0, + "cookies": 0.118, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "springserve.com": { + "domain": "springserve.com", + "default": "block", + "owner": { + "name": "SpringServe, LLC", + "displayName": "SpringServe", + "privacyPolicy": "https://springserve.com/privacy-policy/", + "url": "http://springserve.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 1, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "stackadapt.com": { + "domain": "stackadapt.com", + "default": "block", + "owner": { + "name": "Collective Roll", + "displayName": "Collective Roll", + "privacyPolicy": "https://www.stackadapt.com/privacy", + "url": "http://stackadapt.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.03, + "fingerprinting": 0, + "cookies": 0.03, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Action Pixels" + ] + }, + "statcounter.com": { + "domain": "statcounter.com", + "default": "block", + "owner": { + "name": "StatCounter", + "displayName": "StatCounter", + "privacyPolicy": "https://statcounter.com/about/legal/", + "url": "http://statcounter.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.013, + "fingerprinting": 2, + "cookies": 0.012, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics" + ] + }, + "steelhousemedia.com": { + "domain": "steelhousemedia.com", + "default": "block", + "owner": { + "name": "Steel House, Inc", + "displayName": "Steel House", + "privacyPolicy": "https://steelhouse.com/privacy-policy/", + "url": "http://steelhouse.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing" + ] + }, + "stickyadstv.com": { + "domain": "stickyadstv.com", + "default": "block", + "owner": { + "name": "FreeWheel", + "displayName": "FreeWheel", + "privacyPolicy": "http://freewheel.tv/privacy-policy/", + "url": "http://freewheel.tv" + }, + "source": [ + "DDG" + ], + "prevalence": 0.054, + "fingerprinting": 0, + "cookies": 0.054, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "storygize.net": { + "domain": "storygize.net", + "default": "block", + "owner": { + "name": "Storygize", + "displayName": "Storygize", + "privacyPolicy": "https://www.storygize.com/service-privacy-policy/", + "url": "http://storygize.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 0, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "sumo.com": { + "domain": "sumo.com", + "default": "block", + "owner": { + "name": "Sumo Group", + "displayName": "Sumo Group", + "privacyPolicy": "https://help.sumo.com/hc/en-us/articles/218958727-Privacy-Policy", + "url": "http://sumo.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 2, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 2, + "cpu": 3, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing", + "Embedded Content" + ] + }, + "sundaysky.com": { + "domain": "sundaysky.com", + "default": "block", + "owner": { + "name": "SundaySky Ltd.", + "displayName": "SundaySky", + "privacyPolicy": "https://sundaysky.com/privacy-policy/", + "url": "http://sundaysky.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Audience Measurement", + "Embedded Content" + ] + }, + "survata.com": { + "domain": "survata.com", + "default": "block", + "owner": { + "name": "Survata, Inc.", + "displayName": "Survata, Inc.", + "privacyPolicy": "https://www.survata.com/respondent-privacy-policy/", + "url": "http://survata.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.01, + "fingerprinting": 0, + "cookies": 0.009, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "taboola.com": { + "domain": "taboola.com", + "default": "block", + "owner": { + "name": "Taboola.com LTD", + "displayName": "Taboola.com", + "privacyPolicy": "https://www.taboola.com/privacy-policy", + "url": "http://taboola.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.057, + "fingerprinting": 1, + "cookies": 0.042, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "tagcommander.com": { + "domain": "tagcommander.com", + "default": "block", + "owner": { + "name": "Fjord Technologies", + "displayName": "Fjord", + "privacyPolicy": "https://www.commandersact.com/en/privacy/", + "url": "http://commandersact.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 1, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels" + ] + }, + "tapad.com": { + "domain": "tapad.com", + "default": "block", + "owner": { + "name": "Tapad, Inc.", + "displayName": "Tapad", + "privacyPolicy": "https://www.tapad.com/privacy-policy", + "url": "http://tapad.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.09, + "fingerprinting": 0, + "cookies": 0.09, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing" + ] + }, + "teads.tv": { + "domain": "teads.tv", + "default": "block", + "owner": { + "name": "Teads ( Luxenbourg ) SA", + "displayName": "Teads", + "privacyPolicy": "https://teads.tv/privacy-policy/", + "url": "http://teads.tv" + }, + "source": [ + "DDG" + ], + "prevalence": 0.021, + "fingerprinting": 1, + "cookies": 0.019, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "tealiumiq.com": { + "domain": "tealiumiq.com", + "default": "block", + "owner": { + "name": "Tealium Inc.", + "displayName": "Tealium", + "privacyPolicy": "https://tealium.com/privacy/", + "url": "http://tealium.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Action Pixels", + "Session Replay" + ] + }, + "technoratimedia.com": { + "domain": "technoratimedia.com", + "default": "block", + "owner": { + "name": "Synacor, Inc.", + "displayName": "Synacor", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.031, + "fingerprinting": 0, + "cookies": 0.03, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud" + ] + }, + "theadex.com": { + "domain": "theadex.com", + "default": "block", + "owner": { + "name": "Virtual Minds AG", + "displayName": "Virtual Minds", + "privacyPolicy": "https://www.virtualminds.de/en/", + "url": "http://virtualminds.de" + }, + "source": [ + "DDG" + ], + "prevalence": 0.007, + "fingerprinting": 2, + "cookies": 0.007, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "thebrighttag.com": { + "domain": "thebrighttag.com", + "default": "block", + "owner": { + "name": "Signal Digital, Inc.", + "displayName": "Signal Digital", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.019, + "fingerprinting": 0, + "cookies": 0.016, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing" + ] + }, + "thrtle.com": { + "domain": "thrtle.com", + "default": "block", + "owner": { + "name": "Throtle", + "displayName": "Throtle", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.015, + "fingerprinting": 0, + "cookies": 0.014, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement" + ] + }, + "tidaltv.com": { + "domain": "tidaltv.com", + "default": "block", + "owner": { + "name": "Amobee, Inc", + "displayName": "Amobee", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.011, + "fingerprinting": 0, + "cookies": 0.011, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "tinypass.com": { + "domain": "tinypass.com", + "default": "ignore", + "owner": { + "name": "Piano Software", + "displayName": "Piano Software", + "privacyPolicy": "https://piano.io/privacy-policy/", + "url": "http://piano.io" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 2, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "SSO", + "Third-Party Analytics Marketing", + "Embedded Content" + ] + }, + "tiqcdn.com": { + "domain": "tiqcdn.com", + "default": "block", + "owner": { + "name": "Tealium Inc.", + "displayName": "Tealium", + "privacyPolicy": "https://tealium.com/privacy/", + "url": "http://tealium.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.023, + "fingerprinting": 3, + "cookies": 0.006, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ], + "rules": [ + { + "rule": "tags\\.tiqcdn\\.com\\/utag\\/.*\\/utag\\.js", + "exceptions": { + "types": [ + "script" + ] + } + } + ] + }, + "tns-counter.ru": { + "domain": "tns-counter.ru", + "default": "block", + "owner": { + "name": "JSC ADFACT", + "displayName": "ADFACT", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Analytics" + ] + }, + "trackcmp.net": { + "domain": "trackcmp.net", + "default": "block", + "owner": { + "name": "ActiveCampaign, Inc.", + "displayName": "ActiveCampaign", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Action Pixels" + ] + }, + "tremorhub.com": { + "domain": "tremorhub.com", + "default": "block", + "owner": { + "name": "Telaria", + "displayName": "Telaria", + "privacyPolicy": "https://telaria.com/privacy-policy/", + "url": "http://telaria.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.017, + "fingerprinting": 0, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "tribalfusion.com": { + "domain": "tribalfusion.com", + "default": "block", + "owner": { + "name": "Exponential Interactive Inc.", + "displayName": "Exponential Interactive", + "privacyPolicy": "http://exponential.com/privacy/", + "url": "http://exponential.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.02, + "fingerprinting": 3, + "cookies": 0.02, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "tru.am": { + "domain": "tru.am", + "default": "block", + "owner": { + "name": "trueAnthem Corp", + "displayName": "trueAnthem", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Third-Party Analytics Marketing" + ] + }, + "truefitcorp.com": { + "domain": "truefitcorp.com", + "default": "block", + "owner": { + "name": "True Fit Corporation", + "displayName": "True Fit Corporation", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Analytics", + "Third-Party Analytics Marketing" + ] + }, + "truoptik.com": { + "domain": "truoptik.com", + "default": "block", + "owner": { + "name": "21 Productions", + "displayName": "21 Productions", + "privacyPolicy": "https://www.truoptik.com/privacy-policy.php", + "url": "http://truoptik.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 0, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Third-Party Analytics Marketing" + ] + }, + "trustarc.com": { + "domain": "trustarc.com", + "default": "block", + "owner": { + "name": "TrustArc Inc.", + "displayName": "TrustArc", + "privacyPolicy": "https://www.trustarc.com/privacy-policy/", + "url": "http://trustarc.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.019, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Badge" + ] + }, + "truste.com": { + "domain": "truste.com", + "default": "block", + "owner": { + "name": "TrustArc Inc.", + "displayName": "TrustArc", + "privacyPolicy": "https://www.trustarc.com/privacy-policy/", + "url": "http://trustarc.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.019, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Badge" + ] + }, + "trustedshops.com": { + "domain": "trustedshops.com", + "default": "block", + "owner": { + "name": "Trusted Shops GmbH", + "displayName": "Trusted Shops", + "privacyPolicy": "https://www.trustedshops.eu/legal-notice-privacy.html", + "url": "http://trustedshops.eu" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Badge" + ] + }, + "trustpilot.com": { + "domain": "trustpilot.com", + "default": "block", + "owner": { + "name": "Trustpilot A/S", + "displayName": "Trustpilot", + "privacyPolicy": "https://uk.legal.trustpilot.com/end-user-privacy-terms", + "url": "http://trustpilot.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 1, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics", + "Embedded Content" + ] + }, + "trustx.org": { + "domain": "trustx.org", + "default": "block", + "owner": { + "name": "DCN", + "displayName": "DCN", + "privacyPolicy": "https://trustx.org/rules/", + "url": "http://trustx.org" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "tsyndicate.com": { + "domain": "tsyndicate.com", + "default": "block", + "owner": { + "name": "Traffic Stars", + "displayName": "Traffic Stars", + "privacyPolicy": "https://trafficstars.com/privacy-policy/", + "url": "http://trafficstars.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 1, + "cookies": 0.006, + "performance": { + "time": 1, + "size": 2, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Action Pixels" + ] + }, + "turn.com": { + "domain": "turn.com", + "default": "block", + "owner": { + "name": "Turn Inc.", + "displayName": "Turn", + "privacyPolicy": "https://www.amobee.com/trust/privacy-guidelines/", + "url": "http://amobee.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.1, + "fingerprinting": 0, + "cookies": 0.091, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "tvsquared.com": { + "domain": "tvsquared.com", + "default": "block", + "owner": { + "name": "TVSquared", + "displayName": "TVSquared", + "privacyPolicy": "https://tvsquared.com/privacy-policy/", + "url": "http://tvsquared.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 2, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement" + ] + }, + "twiago.com": { + "domain": "twiago.com", + "default": "block", + "owner": { + "name": "twiago GmbH", + "displayName": "twiago", + "privacyPolicy": "https://www.twiago.com/datenschutz/", + "url": "http://twiago.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "twitter.com": { + "rules": [ + { + "rule": "twitter\\.com\\/settings" + }, + { + "rule": "twitter\\.com\\/js\\/button\\.d941c9a422e2e3faf474b82a1f39e936\\.js" + }, + { + "rule": "twitter\\.com\\/i\\/jot" + }, + { + "rule": "twitter\\.com\\/i\\/adsct" + }, + { + "rule": "twitter\\.com\\/jot\\.html" + }, + { + "rule": "twitter\\.com\\/timeline\\/profile" + }, + { + "rule": "twitter\\.com\\/js\\/moment~timeline~tweet\\.ec04a6cb5ba879d0e0db41f211639fdf\\.js" + }, + { + "rule": "twitter\\.com\\/js\\/tweet\\.faba1cdc1d0b60d30bd0cf4ea438c3e8\\.js" + }, + { + "rule": "twitter\\.com\\/css\\/tweet\\.9bf5093a19cec463852b31b784bf047a\\.light\\.ltr\\.css" + }, + { + "rule": "twitter\\.com\\/js\\/timeline\\.0a7b4db67eacd23e35c5ce02e6ea3470\\.js" + }, + { + "rule": "twitter\\.com\\/css\\/timeline\\.9bf5093a19cec463852b31b784bf047a\\.dark\\.ltr\\.css" + }, + { + "rule": "twitter\\.com\\/css\\/timeline\\.9bf5093a19cec463852b31b784bf047a\\.light\\.ltr\\.css" + }, + { + "rule": "twitter\\.com\\/oct\\.js" + }, + { + "rule": "twitter\\.com\\/impressions\\.js" + }, + { + "rule": "twitter\\.com\\/1\\/statuses\\/user_timeline\\.json" + }, + { + "rule": "twitter\\.com\\/1\\/urls\\/count\\.json" + }, + { + "rule": "twitter\\.com\\/favicon\\.ico" + }, + { + "rule": "twitter\\.com\\/login" + }, + { + "rule": "twitter\\.com\\/1\\/users\\/show\\.json" + }, + { + "rule": "twitter\\.com\\/f\\.gif" + }, + { + "rule": "twitter\\.com\\/favicons\\/favicon\\.ico" + } + ], + "domain": "twitter.com", + "default": "ignore", + "owner": { + "name": "Twitter, Inc.", + "displayName": "Twitter", + "privacyPolicy": "https://twitter.com/en/privacy", + "url": "http://twitter.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.128, + "fingerprinting": 0, + "cookies": 0.065, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Federated Login", + "Social - Comment", + "Social - Share", + "Embedded Content", + "Social Network" + ] + }, + "tynt.com": { + "domain": "tynt.com", + "default": "block", + "owner": { + "name": "33Across, Inc.", + "displayName": "33Across", + "privacyPolicy": "https://33across.com/privacy-policy/", + "url": "http://33across.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.025, + "fingerprinting": 0, + "cookies": 0.025, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing", + "Social - Share", + "Action Pixels" + ] + }, + "ubembed.com": { + "domain": "ubembed.com", + "default": "block", + "owner": { + "name": "Unbounce", + "displayName": "Unbounce", + "privacyPolicy": "https://unbounce.com/privacy/", + "url": "http://unbounce.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 1, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Analytics", + "Action Pixels", + "Embedded Content" + ] + }, + "undertone.com": { + "domain": "undertone.com", + "default": "block", + "owner": { + "name": "Undertone Networks", + "displayName": "Undertone Networks", + "privacyPolicy": "https://www.undertone.com/privacy/", + "url": "http://undertone.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 1, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "unrulymedia.com": { + "domain": "unrulymedia.com", + "default": "block", + "owner": { + "name": "Unruly Group Limited", + "displayName": "Unruly Group", + "privacyPolicy": "https://unruly.co/privacy/", + "url": "http://unruly.co" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Audience Measurement", + "Third-Party Analytics Marketing", + "Embedded Content" + ] + }, + "usabilla.com": { + "domain": "usabilla.com", + "default": "block", + "owner": { + "name": "Usabilla B.V.", + "displayName": "Usabilla", + "privacyPolicy": "https://usabilla.com/privacy/", + "url": "http://usabilla.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Analytics", + "Third-Party Analytics Marketing" + ] + }, + "usemessages.com": { + "domain": "usemessages.com", + "default": "block", + "owner": { + "name": "HubSpot, Inc.", + "displayName": "HubSpot", + "privacyPolicy": "https://legal.hubspot.com/privacy-policy", + "url": "http://hubspot.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Embedded Content" + ] + }, + "userreport.com": { + "domain": "userreport.com", + "default": "block", + "owner": { + "name": "AudienceProject", + "displayName": "AudienceProject", + "privacyPolicy": "https://privacy.audienceproject.com/", + "url": "http://audienceproject.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.009, + "fingerprinting": 1, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Ad Fraud", + "Analytics", + "Audience Measurement" + ] + }, + "videohub.tv": { + "domain": "videohub.tv", + "default": "block", + "owner": { + "name": "Tremor Video DSP", + "displayName": "Tremor Video DSP", + "privacyPolicy": "https://www.tremorvideodsp.com/privacy-policy", + "url": "http://tremorvideodsp.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "vidible.tv": { + "domain": "vidible.tv", + "default": "block", + "owner": { + "name": "Verizon Media", + "displayName": "Verizon Media", + "privacyPolicy": "https://www.verizon.com/about/privacy/privacy-policy-summary", + "url": "http://verizon.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 1, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Embedded Content" + ] + }, + "viglink.com": { + "domain": "viglink.com", + "default": "block", + "owner": { + "name": "Sovrn Holdings", + "displayName": "Sovrn Holdings", + "privacyPolicy": "https://www.sovrn.com/sovrn-legal-policies/", + "url": "http://sovrn.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 0, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 3, + "cache": 3 + }, + "categories": [ + "Advertising" + ] + }, + "vindicosuite.com": { + "domain": "vindicosuite.com", + "default": "block", + "owner": { + "name": "Vindico LLC", + "displayName": "Vindico", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "visualwebsiteoptimizer.com": { + "domain": "visualwebsiteoptimizer.com", + "default": "block", + "owner": { + "name": "Wingify", + "displayName": "Wingify", + "privacyPolicy": "https://wingify.com/privacy-policy", + "url": "http://wingify.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.015, + "fingerprinting": 1, + "cookies": 0.011, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Analytics", + "Third-Party Analytics Marketing", + "Session Replay" + ], + "rules": [ + { + "rule": "dev\\.visualwebsiteoptimizer\\.com", + "exceptions": { + "domains": [ + "adoramapix.com" + ], + "types": [ + "script" + ] + } + } + ] + }, + "vk.com": { + "domain": "vk.com", + "default": "block", + "owner": { + "name": "V Kontakte LLC", + "displayName": "V Kontakte", + "privacyPolicy": "https://vk.com/privacy/cookies", + "url": "http://vk.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 1, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Federated Login", + "Social - Comment", + "Social - Share", + "Embedded Content", + "Social Network" + ] + }, + "w55c.net": { + "domain": "w55c.net", + "default": "block", + "owner": { + "name": "DataXu", + "displayName": "DataXu", + "privacyPolicy": "https://www.dataxu.com/about-us/privacy/data-collection-platform/", + "url": "http://dataxu.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.05, + "fingerprinting": 0, + "cookies": 0.05, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "walmart.com": { + "domain": "walmart.com", + "default": "block", + "owner": { + "name": "Wal-Mart Stores, Inc.", + "displayName": "Wal-Mart Stores", + "privacyPolicy": "https://corporate.walmart.com/privacy-security/walmart-privacy-policy", + "url": "http://walmart.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 0, + "cookies": 0.006, + "performance": { + "time": 1, + "size": 1, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "wbtrk.net": { + "domain": "wbtrk.net", + "default": "block", + "owner": { + "name": "Webtrekk GmbH", + "displayName": "Webtrekk", + "privacyPolicy": "https://www.webtrekk.com/en/why-webtrekk/data-protection/", + "url": "http://webtrekk.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Embedded Content" + ] + }, + "wcfbc.net": { + "domain": "wcfbc.net", + "default": "block", + "owner": { + "name": "Webtrekk GmbH", + "displayName": "Webtrekk", + "privacyPolicy": "https://www.webtrekk.com/en/why-webtrekk/data-protection/", + "url": "http://webtrekk.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.003, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "weborama.com": { + "domain": "weborama.com", + "default": "block", + "owner": { + "name": "Weborama", + "displayName": "Weborama", + "privacyPolicy": "https://weborama.com/en/privacy_en/", + "url": "http://weborama.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.008, + "fingerprinting": 0, + "cookies": 0.008, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "weborama.fr": { + "domain": "weborama.fr", + "default": "block", + "owner": { + "name": "Weborama", + "displayName": "Weborama", + "privacyPolicy": "https://weborama.com/en/privacy_en/", + "url": "http://weborama.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.012, + "fingerprinting": 0, + "cookies": 0.011, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "webvisor.org": { + "domain": "webvisor.org", + "default": "block", + "owner": { + "name": "Yandex LLC", + "displayName": "Yandex", + "privacyPolicy": "https://yandex.com/legal/privacy/", + "url": "http://yandex.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 0, + "cookies": 0.005, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Session Replay" + ] + }, + "wt-eu02.net": { + "domain": "wt-eu02.net", + "default": "block", + "owner": { + "name": "Webtrekk GmbH", + "displayName": "Webtrekk", + "privacyPolicy": "https://www.webtrekk.com/en/why-webtrekk/data-protection/", + "url": "http://webtrekk.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Embedded Content" + ] + }, + "wt-safetag.com": { + "domain": "wt-safetag.com", + "default": "block", + "owner": { + "name": "Webtrekk GmbH", + "displayName": "Webtrekk", + "privacyPolicy": "https://www.webtrekk.com/en/why-webtrekk/data-protection/", + "url": "http://webtrekk.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 1, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Advertising", + "Third-Party Analytics Marketing" + ] + }, + "xg4ken.com": { + "domain": "xg4ken.com", + "default": "block", + "owner": { + "name": "Kenshoo TLD", + "displayName": "Kenshoo TLD", + "privacyPolicy": "https://kenshoo.com/privacy-policy/", + "url": "http://kenshoo.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.007, + "fingerprinting": 0, + "cookies": 0.001, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking" + ] + }, + "xiti.com": { + "domain": "xiti.com", + "default": "block", + "owner": { + "name": "AT Internet", + "displayName": "AT Internet", + "privacyPolicy": "https://www.atinternet.com/en/company/data-protection/data-collection-on-at-internets-sites/", + "url": "http://atinternet.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 0, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Analytics" + ] + }, + "xplosion.de": { + "domain": "xplosion.de", + "default": "block", + "owner": { + "name": "emetriq GmbH", + "displayName": "emetriq", + "privacyPolicy": "https://www.emetriq.com/datenschutz/", + "url": "http://emetriq.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.006, + "fingerprinting": 0, + "cookies": 0.006, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "yadro.ru": { + "domain": "yadro.ru", + "default": "block", + "owner": { + "name": "OOO \"ECO PC - Complex Solutions\"", + "displayName": "ECO PC - Complex Solutions", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.022, + "fingerprinting": 0, + "cookies": 0.02, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Analytics", + "Malware" + ] + }, + "yahoo.co.jp": { + "domain": "yahoo.co.jp", + "default": "block", + "owner": { + "name": "Yahoo Japan Corporation", + "displayName": "Yahoo Japan", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 1, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "yahoo.com": { + "domain": "yahoo.com", + "default": "block", + "owner": { + "name": "Verizon Media", + "displayName": "Verizon Media", + "privacyPolicy": "https://www.verizon.com/about/privacy/privacy-policy-summary", + "url": "http://verizon.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.155, + "fingerprinting": 0, + "cookies": 0.148, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "Federated Login" + ] + }, + "yandex.ru": { + "domain": "yandex.ru", + "default": "block", + "owner": { + "name": "Yandex LLC", + "displayName": "Yandex", + "privacyPolicy": "https://yandex.com/legal/privacy/", + "url": "http://yandex.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.035, + "fingerprinting": 2, + "cookies": 0.034, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Audience Measurement", + "SSO", + "Online Payment", + "Action Pixels", + "Embedded Content", + "Session Replay" + ], + "rules": [ + { + "rule": "api-maps\\.yandex\\.ru", + "exceptions": { + "types": [ + "script", + "image" + ] + } + }, + { + "rule": "static-maps\\.yandex\\.ru", + "exceptions": { + "types": [ + "image" + ] + } + }, + { + "rule": "money\\.yandex\\.ru", + "action": "ignore" + }, + { + "rule": "img-fotki\\.yandex\\.ru", + "exceptions": { + "types": [ + "image" + ] + } + } + ] + }, + "yieldlab.net": { + "domain": "yieldlab.net", + "default": "block", + "owner": { + "name": "Virtual Minds AG", + "displayName": "Virtual Minds", + "privacyPolicy": "https://www.virtualminds.de/en/", + "url": "http://virtualminds.de" + }, + "source": [ + "DDG" + ], + "prevalence": 0.014, + "fingerprinting": 0, + "cookies": 0.011, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "yieldlove-ad-serving.net": { + "domain": "yieldlove-ad-serving.net", + "default": "block", + "owner": { + "name": "Yieldlove GmbH", + "displayName": "Yieldlove", + "privacyPolicy": "https://www.yieldlove.com/privacy", + "url": "http://yieldlove.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "yieldlove.com": { + "domain": "yieldlove.com", + "default": "block", + "owner": { + "name": "Yieldlove GmbH", + "displayName": "Yieldlove", + "privacyPolicy": "https://www.yieldlove.com/privacy", + "url": "http://yieldlove.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 2, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 3 + }, + "categories": [ + "Advertising" + ] + }, + "yieldmo.com": { + "domain": "yieldmo.com", + "default": "block", + "owner": { + "name": "YieldMo, Inc.", + "displayName": "YieldMo", + "privacyPolicy": "http://www.yieldmo.com/privacy/", + "url": "http://yieldmo.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.015, + "fingerprinting": 3, + "cookies": 0.015, + "performance": { + "time": 1, + "size": 1, + "cpu": 3, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising" + ] + }, + "yieldoptimizer.com": { + "domain": "yieldoptimizer.com", + "default": "block", + "owner": { + "name": "AppNexus, Inc.", + "displayName": "AppNexus", + "privacyPolicy": "https://www.appnexus.com/en/company/privacy-policy", + "url": "http://appnexus.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.005, + "fingerprinting": 0, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics" + ] + }, + "yotpo.com": { + "domain": "yotpo.com", + "default": "block", + "owner": { + "name": "Yotpo Ltd", + "displayName": "Yotpo", + "privacyPolicy": "https://www.yotpo.com/privacy-policy/", + "url": "http://yotpo.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.004, + "fingerprinting": 2, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 1 + }, + "categories": [ + "Advertising", + "Third-Party Analytics Marketing", + "Embedded Content" + ] + }, + "ywxi.net": { + "domain": "ywxi.net", + "default": "block", + "owner": { + "name": "PathDefender", + "displayName": "PathDefender", + "url": "", + "privacyPolicy": "" + }, + "source": [ + "DDG" + ], + "prevalence": 0.003, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Badge" + ] + }, + "zemanta.com": { + "domain": "zemanta.com", + "default": "block", + "owner": { + "name": "Outbrain", + "displayName": "Outbrain", + "privacyPolicy": "https://www.outbrain.com/legal/privacy", + "url": "http://outbrain.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.025, + "fingerprinting": 0, + "cookies": 0.024, + "performance": { + "time": 1, + "size": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing" + ] + }, + "zopim.com": { + "domain": "zopim.com", + "default": "block", + "owner": { + "name": "Zendesk, Inc.", + "displayName": "Zendesk", + "privacyPolicy": "https://www.zendesk.com/company/customers-partners/privacy-policy/", + "url": "http://zendesk.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.011, + "fingerprinting": 2, + "cookies": 0.004, + "performance": { + "time": 1, + "size": 1, + "cpu": 2, + "cache": 1 + }, + "categories": [ + "Embedded Content" + ] + }, + "zorosrv.com": { + "domain": "zorosrv.com", + "default": "block", + "owner": { + "name": "Taboola.com LTD", + "displayName": "Taboola.com", + "privacyPolicy": "https://www.taboola.com/privacy-policy", + "url": "http://taboola.com" + }, + "source": [ + "DDG" + ], + "prevalence": 0.011, + "fingerprinting": 0, + "cookies": 0.011, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Third-Party Analytics Marketing" + ] + } + }, + "entities": { + "21 Productions": { + "domains": [ + "truoptik.com" + ], + "displayName": "21 Productions", + "prevalence": 0.348 + }, + "33Across, Inc.": { + "domains": [ + "33across.com", + "tynt.com" + ], + "displayName": "33Across", + "prevalence": 3.592 + }, + "4Cite Marketing": { + "domains": [ + "securedvisit.com" + ], + "displayName": "4Cite Marketing", + "prevalence": 0.341 + }, + "A.Mob SAS": { + "domains": [ + "adotmob.com" + ], + "displayName": "A.Mob", + "prevalence": 0.291 + }, + "ALC": { + "domains": [ + "alcmpn.com", + "idify.com", + "alc.com" + ], + "prevalence": 0.168 + }, + "AT Internet": { + "domains": [ + "xiti.com", + "aticdn.net", + "ati-host.net" + ], + "displayName": "AT Internet", + "prevalence": 0.718 + }, + "ActiveCampaign, Inc.": { + "domains": [ + "activehosted.com", + "img-us3.com", + "activecampaign.com", + "trackcmp.net" + ], + "displayName": "ActiveCampaign", + "prevalence": 0.35 + }, + "AcuityAds": { + "domains": [ + "acuityads.com", + "acuityplatform.com" + ], + "displayName": "AcuityAds", + "prevalence": 3.539 + }, + "Ad Lightning, Inc.": { + "domains": [ + "adlightning.com" + ], + "displayName": "Ad Lightning", + "prevalence": 0.327 + }, + "AdGear Technologies Inc.": { + "domains": [ + "adgrx.com", + "adgear.com" + ], + "displayName": "AdGear", + "prevalence": 2.019 + }, + "AdRoll, Inc.": { + "domains": [ + "adroll.com" + ], + "displayName": "AdRoll", + "prevalence": 1.487 + }, + "AdSpyglass": { + "domains": [ + "adspyglass.com", + "o333o.com" + ], + "displayName": "AdSpyglass", + "prevalence": 0.264 + }, + "AdStanding": { + "domains": [ + "adstanding.com", + "atedra.com" + ], + "displayName": "AdStanding", + "prevalence": 0.427 + }, + "AdTheorent Inc": { + "domains": [ + "adentifi.com" + ], + "displayName": "AdTheorent", + "prevalence": 2.132 + }, + "AdThrive, LLC": { + "domains": [ + "adthrive.com" + ], + "displayName": "AdThrive", + "prevalence": 0.614 + }, + "AddToAny": { + "domains": [ + "addtoany.com" + ], + "displayName": "AddToAny", + "prevalence": 1.089 + }, + "Adelphic, Inc.": { + "domains": [ + "ipredictive.com" + ], + "displayName": "Adelphic", + "prevalence": 3.948 + }, + "Adform A/S": { + "domains": [ + "adform.net", + "adformdsp.net" + ], + "displayName": "Adform", + "prevalence": 8.897 + }, + "Adkernel, LLC": { + "domains": [ + "adkernel.com" + ], + "displayName": "Adkernel", + "prevalence": 1.007 + }, + "Admedo": { + "domains": [ + "admedo.com", + "adizio.com", + "a8723.com" + ], + "displayName": "Admedo", + "prevalence": 0.948 + }, + "Admixer Technologies": { + "domains": [ + "admixer.net" + ], + "displayName": "Admixer", + "prevalence": 0.812 + }, + "Adnium Inc": { + "domains": [ + "adnium.com" + ], + "displayName": "Adnium", + "prevalence": 0.371 + }, + "Adobe Inc.": { + "domains": [ + "everesttech.net", + "everestjs.net", + "everestads.net", + "sitestat.com", + "adobetag.com", + "demdex.net", + "omtrdc.net", + "typekit.com", + "typekit.net", + "edgefonts.net", + "2o7.net", + "adobe.com", + "adobedtm.com", + "adobelogin.com", + "assetsadobe.com", + "fyre.co", + "livefyre.com", + "scene7.com", + "tubemogul.com", + "storify.com", + "atomz.com", + "ftcdn.net", + "adobecqms.net", + "assetsadobe2.com", + "fotolia.net", + "businesscatalyst.com", + "adobeccstatic.com", + "adobe.io", + "creativecloud.com", + "photoshop.com", + "worldsecuresystems.com", + "assetsadobe3.com", + "acrobatusers.com", + "omniture.com", + "ss-omtrdc.net", + "nedstat.net", + "hitbox.com", + "behance.net", + "fotolia.com", + "auditude.com" + ], + "displayName": "Adobe", + "prevalence": 24.864 + }, + "Adscore Technologies DMCC": { + "domains": [ + "adsco.re", + "adscore.com", + "ad-score.com" + ], + "displayName": "Adscore", + "prevalence": 0.827 + }, + "Adyoulike": { + "domains": [ + "omnitagjs.com", + "adyoulike.com" + ], + "displayName": "Adyoulike", + "prevalence": 1.573 + }, + "Aidata": { + "domains": [ + "aidata.io", + "aidata.me", + "advombat.ru" + ], + "displayName": "Aidata", + "prevalence": 0.232 + }, + "Akamai Technologies": { + "domains": [ + "akamaihd.net", + "akamaized.net", + "akamai.net", + "go-mpulse.net", + "abmr.net", + "edgekey.net", + "edgesuite.net", + "akamai.com", + "gw-ec.com", + "securetve.com" + ], + "displayName": "Akamai", + "prevalence": 4.683 + }, + "Amazon Technologies, Inc.": { + "domains": [ + "amazon-adsystem.com", + "ssl-images-amazon.com", + "amazon.com", + "amazon.ca", + "payments-amazon.com", + "amazonpay.com", + "media-amazon.com", + "assoc-amazon.com", + "images-amazon.com", + "awsstatic.com", + "amazonadsystem.com", + "graphiq.com", + "img-dpreview.com", + "elasticbeanstalk.com", + "amazonwebservices.com", + "dpreview.com", + "amazon.in", + "amazon.fr", + "amazon.it", + "amazon.de", + "amazon.co.jp", + "amazon.co.uk", + "assoc-amazon.de", + "assoc-amazon.jp", + "assoc-amazon.co.uk", + "amazon.com.au", + "amazon.com.br", + "primevideo.com", + "amazon.jobs", + "amazonforum.com", + "amazon.com.mx", + "mturk.com", + "awsevents.com", + "ring.com", + "cloudfront.net", + "amazonaws.com", + "zappos.com", + "twitch.tv", + "jtvnw.net", + "ttvnw.net", + "twitchsvc.net", + "forgecdn.net", + "twitchcdn.net", + "audible.com", + "audible.de", + "audible.co.uk", + "alexametrics.com", + "alexa.com", + "serving-sys.com", + "peer39.net", + "peer39.com", + "sizmek.com" + ], + "displayName": "Amazon", + "prevalence": 28.323 + }, + "Amobee, Inc": { + "domains": [ + "tidaltv.com", + "amgdgt.com" + ], + "displayName": "Amobee", + "prevalence": 1.757 + }, + "Amplitude": { + "domains": [ + "amplitude.com" + ], + "displayName": "Amplitude", + "prevalence": 0.502 + }, + "AppDynamics LLC": { + "domains": [ + "appdynamics.com", + "eum-appdynamics.com" + ], + "displayName": "AppDynamics", + "prevalence": 0.571 + }, + "AppNexus, Inc.": { + "domains": [ + "adnxs.com", + "247realmedia.com", + "yieldoptimizer.com", + "ml-attr.com", + "realmedia.com" + ], + "displayName": "AppNexus", + "prevalence": 25.977 + }, + "AudienceProject": { + "domains": [ + "userreport.com", + "audienceproject.com" + ], + "displayName": "AudienceProject", + "prevalence": 0.527 + }, + "Avocet Systems Ltd.": { + "domains": [ + "avocet.io" + ], + "displayName": "Avocet Systems", + "prevalence": 2.164 + }, + "Awin AG": { + "domains": [ + "webmasterplan.com", + "html-links.com", + "reussissonsensemble.fr", + "successfultogether.co.uk", + "contentfeed.net", + "digitalwindow.com", + "dwin1.com", + "dwin2.com", + "zanox.com", + "awin.com", + "zanox-affiliate.de" + ], + "displayName": "Awin", + "prevalence": 1.066 + }, + "Bazaarvoice, Inc.": { + "domains": [ + "bazaarvoice.com" + ], + "displayName": "Bazaarvoice", + "prevalence": 0.552 + }, + "Beachfront Media LLC": { + "domains": [ + "bfmio.com" + ], + "displayName": "Beachfront Media", + "prevalence": 1.284 + }, + "Beeswax": { + "domains": [ + "bidr.io", + "beeswax.com" + ], + "displayName": "Beeswax", + "prevalence": 6.626 + }, + "Bidtellect, Inc": { + "domains": [ + "bttrack.com", + "bidtellect.com" + ], + "displayName": "Bidtellect", + "prevalence": 3.401 + }, + "Blis Media Limited": { + "domains": [ + "blismedia.com" + ], + "displayName": "Blis Media", + "prevalence": 0.302 + }, + "BlueConic, Inc.": { + "domains": [ + "blueconic.net", + "blueconic.com" + ], + "displayName": "BlueConic", + "prevalence": 0.293 + }, + "Bombora Inc.": { + "domains": [ + "ml314.com", + "bombora.com" + ], + "displayName": "Bombora", + "prevalence": 5.369 + }, + "BongaCams": { + "domains": [ + "bongacams.org", + "bongacams.com", + "bongacams.dk", + "bongacams2.com", + "bongacash.com", + "redcams.su", + "promo-bc.com" + ], + "displayName": "BongaCams" + }, + "Bounce Exchange": { + "domains": [ + "bounceexchange.com", + "bouncex.net", + "cdnbasket.net" + ], + "displayName": "Bounce Exchange", + "prevalence": 0.618 + }, + "Branch Metrics, Inc.": { + "domains": [ + "branch.io", + "app.link" + ], + "displayName": "Branch Metrics", + "prevalence": 0.389 + }, + "Browser Update": { + "domains": [ + "browser-update.org" + ], + "displayName": "Browser Update", + "prevalence": 0.38 + }, + "BuySellAds": { + "domains": [ + "buysellads.net", + "buysellads.com", + "servedby-buysellads.com", + "carbonads.com", + "carbonads.net" + ], + "displayName": "BuySellAds", + "prevalence": 0.4 + }, + "Captify Technologies Ltd.": { + "domains": [ + "cpx.to", + "captify.co.uk" + ], + "displayName": "Captify", + "prevalence": 1.255 + }, + "Centro, Inc.": { + "domains": [ + "sitescout.com", + "adbrite.com", + "basis.net", + "centro.net" + ], + "displayName": "Centro", + "prevalence": 8.277 + }, + "Chartbeat": { + "domains": [ + "chartbeat.com", + "chartbeat.net" + ], + "displayName": "Chartbeat", + "prevalence": 2.428 + }, + "Chaturbate, LLC": { + "domains": [ + "chaturbate.com", + "highwebmedia.com", + "oncam.xxx" + ], + "displayName": "Chaturbate", + "prevalence": 0.448 + }, + "CleverDATA LLC": { + "domains": [ + "1dmp.io" + ], + "displayName": "CleverDATA", + "prevalence": 0.207 + }, + "ClickCertain": { + "domains": [ + "clickcertain.com" + ], + "prevalence": 0.22 + }, + "ClickTale Ltd": { + "domains": [ + "clicktale.net" + ], + "displayName": "ClickTale", + "prevalence": 0.543 + }, + "Clickagy": { + "domains": [ + "clickagy.com" + ], + "displayName": "Clickagy", + "prevalence": 0.461 + }, + "Cogo Labs": { + "domains": [ + "cogocast.net", + "cogocast.com", + "apxlv.com" + ], + "displayName": "Cogo Labs", + "prevalence": 2.014 + }, + "Collective Roll": { + "domains": [ + "stackadapt.com" + ], + "displayName": "Collective Roll", + "prevalence": 4.324 + }, + "Colossus Media, LLC": { + "domains": [ + "colossusssp.com" + ], + "displayName": "Colossus Media", + "prevalence": 0.271 + }, + "Connexity, Inc.": { + "domains": [ + "cnnx.io", + "connexity.net", + "bizrate-images.com", + "beso-images.com", + "mammothshopper.com", + "beso.com", + "prixmoinscher.com" + ], + "displayName": "Connexity", + "prevalence": 0.341 + }, + "Contact Impact GmbH": { + "domains": [ + "df-srv.de" + ], + "displayName": "Contact Impact", + "prevalence": 0.271 + }, + "ContentSquare": { + "domains": [ + "contentsquare.com", + "contentsquare.net" + ], + "displayName": "ContentSquare", + "prevalence": 0.391 + }, + "Conversant LLC": { + "domains": [ + "dotomi.com", + "dtmpub.com", + "fastclick.net", + "anrdoezrs.net", + "mplxtms.com", + "mediaplex.com", + "lduhtrp.net", + "tqlkg.com", + "ftjcfx.com", + "awltovhc.com", + "yceml.net", + "emjcd.com", + "jdoqocy.com", + "tkqlhce.com", + "kqzyfj.com", + "qksrv.net", + "greystripe.com" + ], + "displayName": "Conversant", + "prevalence": 7.056 + }, + "Cookie Trust Working Group, Inc. DBA Cookie Trust": { + "domains": [ + "digitru.st" + ], + "displayName": "Cookie Trust", + "prevalence": 1.587 + }, + "Crazy Egg, Inc.": { + "domains": [ + "crazyegg.com", + "hellobar.com" + ], + "displayName": "Crazy Egg", + "prevalence": 3.251 + }, + "Criteo SA": { + "domains": [ + "criteo.net", + "criteo.com", + "hlserve.com", + "emailretargeting.com" + ], + "displayName": "Criteo", + "prevalence": 12.027 + }, + "Cross Pixel Media, Inc.": { + "domains": [ + "crsspxl.com" + ], + "displayName": "Cross Pixel Media", + "prevalence": 0.35 + }, + "Crownpeak Technology": { + "domains": [ + "crownpeak.com", + "crownpeak.net", + "betrad.com", + "evidon.com" + ], + "displayName": "Crownpeak", + "prevalence": 0.016 + }, + "Cxense ASA": { + "domains": [ + "cxense.com", + "emediate.dk", + "emediate.eu" + ], + "displayName": "Cxense", + "prevalence": 0.441 + }, + "DCN": { + "domains": [ + "trustx.org" + ], + "displayName": "DCN", + "prevalence": 0.361 + }, + "DTS Technology": { + "domains": [ + "dtscout.com" + ], + "displayName": "DTS", + "prevalence": 2.248 + }, + "Dailymotion SA": { + "domains": [ + "dmcdn.net", + "dailymotion.com", + "dm-event.net", + "dmxleo.com", + "pxlad.io" + ], + "displayName": "Dailymotion", + "prevalence": 0.482 + }, + "DataXu": { + "domains": [ + "w55c.net" + ], + "displayName": "DataXu", + "prevalence": 7.61 + }, + "Datonics LLC": { + "domains": [ + "pro-market.net" + ], + "displayName": "Datonics", + "prevalence": 0.8 + }, + "Demandbase, Inc.": { + "domains": [ + "demandbase.com", + "company-target.com" + ], + "displayName": "Demandbase", + "prevalence": 0.673 + }, + "Disqus, Inc.": { + "domains": [ + "disqus.com", + "disquscdn.com" + ], + "displayName": "Disqus", + "prevalence": 2.221 + }, + "District M Inc.": { + "domains": [ + "districtm.io", + "districtm.ca", + "districtm.net" + ], + "displayName": "District M", + "prevalence": 5.026 + }, + "DoubleVerify": { + "domains": [ + "doubleverify.com" + ], + "displayName": "DoubleVerify", + "prevalence": 1.591 + }, + "Drawbridge Inc": { + "domains": [ + "adsymptotic.com" + ], + "displayName": "Drawbridge", + "prevalence": 9.104 + }, + "Drift.com, Inc.": { + "domains": [ + "drift.com", + "driftt.com" + ], + "prevalence": 0.225 + }, + "Dstillery Inc.": { + "domains": [ + "dstillery.com", + "media6degrees.com" + ], + "displayName": "Dstillery", + "prevalence": 1 + }, + "DynAdmic": { + "domains": [ + "dyntrk.com" + ], + "displayName": "DynAdmic", + "prevalence": 1.962 + }, + "Dynamic Yield": { + "domains": [ + "dynamicyield.com" + ], + "displayName": "Dynamic Yield", + "prevalence": 0.268 + }, + "Emarsys eMarketing Systems AG": { + "domains": [ + "emarsys.com", + "scarabresearch.com" + ], + "displayName": "Emarsys eMarketing Systems", + "prevalence": 0.393 + }, + "Engine USA LLC": { + "domains": [ + "emxdgt.com" + ], + "displayName": "Engine USA", + "prevalence": 2.196 + }, + "Ensighten, Inc": { + "domains": [ + "ensighten.com", + "ebayadvertising.com", + "nc0.co" + ], + "displayName": "Ensighten", + "prevalence": 0.859 + }, + "Equifax Inc.": { + "domains": [ + "ixiaa.com", + "equifax.com", + "trustedid.com", + "optimahub.com" + ], + "displayName": "Equifax", + "prevalence": 0.564 + }, + "ExactTarget, LLC": { + "domains": [ + "igodigital.com", + "fuelcdn.com" + ], + "displayName": "ExactTarget", + "prevalence": 0.65 + }, + "ExoClick, S.L.": { + "domains": [ + "exoclick.com", + "exosrv.com", + "exdynsrv.com", + "dynsrvtyu.com", + "realsrv.com", + "dynsrvtbg.com", + "notifysrv.com", + "dynsrvazh.com", + "dynsrvazg.com", + "wpncdn.com" + ], + "displayName": "ExoClick", + "prevalence": 3.86 + }, + "Exponential Interactive Inc.": { + "domains": [ + "exponential.com", + "tribalfusion.com" + ], + "displayName": "Exponential Interactive", + "prevalence": 2.844 + }, + "EyeView, Inc.": { + "domains": [ + "eyeviewads.com" + ], + "displayName": "EyeView", + "prevalence": 1.257 + }, + "Ezoic Inc.": { + "domains": [ + "ezoic.net", + "usconstitution.net" + ], + "displayName": "Ezoic", + "prevalence": 0.668 + }, + "Facebook, Inc.": { + "domains": [ + "facebook.net", + "facebook.com", + "fbcdn.net", + "cdninstagram.com", + "instagram.com", + "instagr.com", + "instagr.am", + "atdmt.com", + "atdmt2.com", + "atlassolutions.com", + "atlassbx.com", + "fbsbx.com", + "accountkit.com", + "fb.me", + "fb.com", + "whatsapp.com", + "whatsapp.net", + "thefind.com", + "liverail.com", + "reactjs.org", + "messenger.com", + "m.me", + "oculus.com", + "graphql.org", + "flow.org", + "flowtype.org" + ], + "displayName": "Facebook", + "prevalence": 41.874 + }, + "FastG8": { + "domains": [ + "fastg8.com", + "fg8dgt.com" + ], + "displayName": "FastG8", + "prevalence": 0.414 + }, + "Fjord Technologies": { + "domains": [ + "commander1.com", + "tagcommander.com" + ], + "displayName": "Fjord", + "prevalence": 0.436 + }, + "ForeSee Results, Inc.": { + "domains": [ + "foresee.com", + "4seeresults.com", + "foreseeresults.com" + ], + "displayName": "ForeSee Results", + "prevalence": 0.546 + }, + "FreeWheel": { + "domains": [ + "fwmrm.net", + "freewheel.tv", + "stickyadstv.com" + ], + "displayName": "FreeWheel", + "prevalence": 5.787 + }, + "FullStory": { + "domains": [ + "fullstory.com" + ], + "displayName": "FullStory", + "prevalence": 0.425 + }, + "Functional Software, Inc.": { + "domains": [ + "sentry.io", + "getsentry.com", + "ravenjs.com", + "sentry-cdn.com" + ], + "displayName": "Functional Software", + "prevalence": 0.859 + }, + "Gemius S.A.": { + "domains": [ + "gemius.pl" + ], + "displayName": "Gemius", + "prevalence": 4.608 + }, + "GetIntent": { + "domains": [ + "adhigh.net", + "getintent.com" + ], + "displayName": "GetIntent", + "prevalence": 3.482 + }, + "GetWebCraft Limited": { + "domains": [ + "getsitecontrol.com" + ], + "displayName": "GetWebCraft", + "prevalence": 0.307 + }, + "Google LLC": { + "domains": [ + "googleapis.com", + "googleapis.co", + "google-analytics.com", + "gstatic.com", + "googletagmanager.com", + "google.com", + "googletagservices.com", + "doubleclick.net", + "googlesyndication.com", + "googleweblight.com", + "translate.goog", + "unfiltered.news", + "admeld.com", + "adgoogle.net", + "ytimg.com", + "youtube.com", + "googleadservices.com", + "googleusercontent.com", + "2mdn.net", + "blogspot.com", + "googledrive.com", + "googlemail.com", + "googlecommerce.com", + "ggpht.com", + "doubleclickusercontent.com", + "blogger.com", + "blogblog.com", + "feedburner.com", + "ampproject.org", + "googlevideo.com", + "appspot.com", + "google.de", + "1e100cdn.net", + "google.fr", + "anvato.net", + "google.ch", + "youtube-nocookie.com", + "google.ca", + "google.ru", + "ampproject.net", + "google.co.uk", + "google.es", + "googlecode.com", + "google.se", + "youtu.be", + "android.com", + "google.nl", + "google.it", + "goo.gl", + "gmodules.com", + "google.com.vn", + "firebase.com", + "google.co.in", + "1emn.com", + "getmdl.io", + "google.com.au", + "2enm.com", + "google.co.jp", + "google.pl", + "google.at", + "google.be", + "google.com.ua", + "cc-dt.com", + "doubleclick.com", + "g.co", + "gvt2.com", + "ggpht.cn", + "google.ac", + "google.ad", + "google.af", + "google.ag", + "google.ai", + "google.al", + "google.am", + "google.as", + "google.az", + "google.ba", + "google.bf", + "google.bi", + "google.bj", + "google.bs", + "google.bt", + "google.by", + "google.cat", + "google.cc", + "google.cd", + "google.cf", + "google.cg", + "google.ci", + "google.cl", + "google.cm", + "google.co.ao", + "google.co.bw", + "google.co.ck", + "google.co.cr", + "google.co.hu", + "google.co.im", + "google.co.je", + "google.co.ke", + "google.co.ls", + "google.co.ma", + "google.co.mz", + "google.co.nz", + "google.co.th", + "google.co.tz", + "google.co.ug", + "google.co.uz", + "google.co.ve", + "google.co.vi", + "google.co.za", + "google.co.zm", + "google.co.zw", + "google.com.af", + "google.com.ag", + "google.com.ai", + "google.com.bd", + "google.com.bh", + "google.com.bn", + "google.com.bo", + "google.com.by", + "google.com.bz", + "google.com.cn", + "google.com.co", + "google.com.cu", + "google.com.cy", + "google.com.do", + "google.com.ec", + "google.com.eg", + "google.com.et", + "google.com.fj", + "google.com.ge", + "google.com.gh", + "google.com.gi", + "google.com.gr", + "google.com.gt", + "google.com.iq", + "google.com.jo", + "google.com.kh", + "google.com.kw", + "google.com.lb", + "google.com.ly", + "google.com.mm", + "google.com.mt", + "google.com.na", + "google.com.nf", + "google.com.ng", + "google.com.ni", + "google.com.np", + "google.com.nr", + "google.com.om", + "google.com.pa", + "google.com.pe", + "google.com.pg", + "google.com.ph", + "google.com.pl", + "google.com.pr", + "google.com.py", + "google.com.qa", + "google.com.sb", + "google.com.sl", + "google.com.sv", + "google.com.tj", + "google.com.tn", + "google.com.uy", + "google.com.vc", + "google.com.ve", + "google.cv", + "google.dj", + "google.dm", + "google.dz", + "google.ee", + "google.eus", + "google.fm", + "google.frl", + "google.ga", + "google.gal", + "google.ge", + "google.gl", + "google.gm", + "google.gp", + "google.gy", + "google.hk", + "google.hn", + "google.hr", + "google.ht", + "google.im", + "google.in", + "google.info", + "google.iq", + "google.ir", + "google.is", + "google.it.ao", + "google.je", + "google.jo", + "google.jobs", + "google.jp", + "google.kg", + "google.ki", + "google.kz", + "google.la", + "google.li", + "google.lk", + "google.lu", + "google.lv", + "google.md", + "google.me", + "google.mg", + "google.mk", + "google.ml", + "google.mn", + "google.ms", + "google.mu", + "google.mv", + "google.mw", + "google.ne", + "google.ne.jp", + "google.net", + "google.ng", + "google.nr", + "google.nu", + "google.off.ai", + "google.pk", + "google.pn", + "google.ps", + "google.ro", + "google.rw", + "google.sc", + "google.sh", + "google.si", + "google.sm", + "google.so", + "google.sr", + "google.st", + "google.td", + "google.tel", + "google.tg", + "google.tk", + "google.tl", + "google.tm", + "google.tn", + "google.to", + "google.tt", + "google.ua", + "google.us", + "google.uz", + "google.vg", + "google.vu", + "google.ws", + "googleadapis.com", + "googleadsserving.cn", + "googleapis.cn", + "googleusercontent.cn", + "gstaticcnapps.cn", + "youtubeeducation.com", + "youtubekids.com", + "yt.be", + "emn0.com", + "cloudfunctions.net", + "firebaseapp.com", + "google.com.br", + "recaptcha.net", + "invitemedia.com", + "accurateshooter.net", + "google.cn", + "relaymedia.com", + "golang.org", + "googlesource.com", + "em0n.com", + "dmtry.com", + "meebo.com", + "firebaseio.com", + "dialogflow.com", + "chrome.com", + "1enm.com", + "google.co.id", + "google.com.mx", + "google.fi", + "google.hu", + "google.no", + "google.pt", + "8d1f.com", + "e0mn.com", + "mn0e.com", + "chromium.org", + "advertisercommunity.com", + "advertiserscommunity.com", + "apigee.net", + "blog.google", + "nest.com", + "google.ae", + "google.com.jm", + "gvt1.com", + "google.com.ar", + "chromeexperiments.com", + "goooglesyndication.com", + "markerly.com", + "0m66lx69dx.com", + "ai.google", + "google.gr", + "google.sn", + "googlefiber.net", + "googleblog.com", + "bazel.build", + "fastlane.tools", + "fabric.io", + "urchin.com", + "googleapps.com", + "google.bg", + "gstatic.cn", + "google.co.kr", + "google.com.tr", + "google.com.tw", + "google.cz", + "google.dk", + "google.lt", + "google.sk", + "google.com.pk", + "google.com.sg", + "googlegroups.com", + "google.co.il", + "socratic.org", + "tensorflow.org", + "material.io", + "gmail.com", + "waze.com", + "kaggle.com", + "flutter.io", + "domains.google", + "google.com.sa", + "godoc.org", + "google.com.my", + "itasoftware.com", + "elections.google", + "google.ie", + "dartlang.org", + "withgoogle.com", + "google.com.hk", + "adsense.com", + "grpc.io", + "listentoyoutube.com", + "admob.com", + "google.rs", + "shoppingil.co.il", + "google.gg", + "on2.com", + "oneworldmanystories.com", + "pagespeedmobilizer.com", + "pageview.mobi", + "partylikeits1986.org", + "paxlicense.org", + "pittpatt.com", + "polymerproject.org", + "postini.com", + "projectara.com", + "projectbaseline.com", + "questvisual.com", + "quiksee.com", + "beatthatquote.com", + "revolv.com", + "ridepenguin.com", + "bandpage.com", + "saynow.com", + "schemer.com", + "screenwisetrends.com", + "screenwisetrendspanel.com", + "snapseed.com", + "solveforx.com", + "studywatchbyverily.com", + "studywatchbyverily.org", + "thecleversense.com", + "thinkquarterly.co.uk", + "thinkquarterly.com", + "txcloud.net", + "txvia.com", + "useplannr.com", + "v8project.org", + "verily.com", + "verilylifesciences.com", + "verilystudyhub.com", + "verilystudywatch.com", + "verilystudywatch.org", + "wallet.com", + "waymo.com", + "webappfieldguide.com", + "weltweitwachsen.de", + "whatbrowser.org", + "womenwill.com", + "womenwill.com.br", + "womenwill.id", + "womenwill.in", + "womenwill.mx", + "cookiechoices.org", + "x.company", + "x.team", + "xn--9trs65b.com", + "youtubemobilesupport.com", + "zukunftswerkstatt.de", + "dartsearch.net", + "googleads.com", + "cloudburstresearch.com", + "cloudrobotics.com", + "conscrypt.com", + "conscrypt.org", + "coova.com", + "coova.net", + "coova.org", + "crr.com", + "registry.google", + "cs4hs.com", + "debug.com", + "debugproject.com", + "design.google", + "environment.google", + "episodic.com", + "fflick.com", + "financeleadsonline.com", + "flutterapp.com", + "g-tun.com", + "gerritcodereview.com", + "getbumptop.com", + "gipscorp.com", + "globaledu.org", + "gonglchuangl.net", + "google.berlin", + "google.org", + "google.ventures", + "googlecompare.co.uk", + "googledanmark.com", + "googlefinland.com", + "googlemaps.com", + "googlephotos.com", + "googleplay.com", + "googleplus.com", + "googlesverige.com", + "googletraveladservices.com", + "googleventures.com", + "gsrc.io", + "gsuite.com", + "hdrplusdata.org", + "hindiweb.com", + "howtogetmo.co.uk", + "html5rocks.com", + "hwgo.com", + "impermium.com", + "j2objc.org", + "keytransparency.com", + "keytransparency.foo", + "keytransparency.org", + "mdialog.com", + "mfg-inspector.com", + "mobileview.page", + "moodstocks.com", + "asp-cc.com", + "near.by", + "oauthz.com", + "on.here", + "adwords-community.com", + "adwordsexpress.com", + "angulardart.org", + "api.ai", + "baselinestudy.com", + "baselinestudy.org", + "blink.org", + "brotli.org", + "bumpshare.com", + "bumptop.ca", + "bumptop.com", + "bumptop.net", + "bumptop.org", + "bumptunes.com", + "campuslondon.com", + "certificate-transparency.org", + "chromecast.com", + "quickoffice.com", + "widevine.com", + "appbridge.ca", + "appbridge.io", + "appbridge.it", + "apture.com", + "area120.com" + ], + "displayName": "Google", + "prevalence": 92.974 + }, + "GumGum": { + "domains": [ + "gumgum.com" + ], + "displayName": "GumGum", + "prevalence": 4.048 + }, + "Heap": { + "domains": [ + "heapanalytics.com", + "heap.io" + ], + "displayName": "Heap", + "prevalence": 0.314 + }, + "Hotjar Ltd": { + "domains": [ + "hotjar.com" + ], + "displayName": "Hotjar", + "prevalence": 7.872 + }, + "HubSpot, Inc.": { + "domains": [ + "hs-analytics.net", + "hs-scripts.com", + "hubspot.com", + "hsforms.net", + "hsleadflows.net", + "hsforms.com", + "hubspot.net", + "usemessages.com", + "hscollectedforms.net", + "hscta.net", + "hsadspixel.net", + "hubapi.com", + "hsappstatic.net", + "gettally.com", + "leadin.com", + "hubspotfeedback.com", + "minitab.com", + "li.me" + ], + "displayName": "HubSpot", + "prevalence": 1.107 + }, + "Hybrid Adtech, Inc.": { + "domains": [ + "hybrid.ai" + ], + "displayName": "Hybrid Adtech" + }, + "IAB Europe": { + "domains": [ + "consensu.org" + ], + "displayName": "IAB Europe", + "prevalence": 4.192 + }, + "ID5 Technology Ltd": { + "domains": [ + "id5-sync.com" + ], + "displayName": "ID5", + "prevalence": 0.459 + }, + "INFOnline GmbH": { + "domains": [ + "ioam.de", + "iocnt.net" + ], + "displayName": "INFOnline", + "prevalence": 1.443 + }, + "IO Technologies Inc.": { + "domains": [ + "onthe.io" + ], + "displayName": "IO", + "prevalence": 0.264 + }, + "IPONWEB GmbH": { + "domains": [ + "bidswitch.net", + "mfadsrvr.com", + "bidswitch.com", + "iponweb.com", + "iponweb.net" + ], + "displayName": "IPONWEB", + "prevalence": 11.882 + }, + "IgnitionOne, LLC": { + "domains": [ + "netmng.com", + "apxprogrammatic.com" + ], + "displayName": "IgnitionOne", + "prevalence": 4.874 + }, + "Impact Radius": { + "domains": [ + "ojrq.net", + "sjv.io", + "evyy.net", + "r7ls.net", + "pxf.io", + "impactradius.com", + "impactradius-event.com" + ], + "displayName": "Impact Radius", + "prevalence": 0.664 + }, + "Imperva Inc.": { + "domains": [ + "incapdns.net", + "incapsula.com", + "distilnetworks.com", + "distil.us", + "distiltag.com", + "areyouahuman.com" + ], + "displayName": "Imperva", + "prevalence": 0.002 + }, + "Improve Digital BV": { + "domains": [ + "improvedigital.com", + "360yield.com" + ], + "displayName": "Improve Digital", + "prevalence": 2.262 + }, + "Index Exchange, Inc.": { + "domains": [ + "casalemedia.com", + "indexww.com" + ], + "displayName": "Index Exchange", + "prevalence": 16.708 + }, + "Innovid Media": { + "domains": [ + "innovid.com" + ], + "displayName": "Innovid Media", + "prevalence": 3.628 + }, + "Inspectlet": { + "domains": [ + "inspectlet.com" + ], + "displayName": "Inspectlet", + "prevalence": 0.305 + }, + "Integral Ad Science, Inc.": { + "domains": [ + "adsafeprotected.com", + "iasds01.com" + ], + "displayName": "Integral Ad Science", + "prevalence": 3.078 + }, + "Intent IQ, LLC": { + "domains": [ + "intentiq.com" + ], + "displayName": "Intent IQ", + "prevalence": 0.366 + }, + "Internet Billboard a.s.": { + "domains": [ + "ibillboard.com", + "bbelements.com" + ], + "displayName": "Internet Billboard", + "prevalence": 0.5 + }, + "Interwebvertising B.V.": { + "domains": [ + "ero-advertising.com" + ], + "displayName": "Interwebvertising", + "prevalence": 0.418 + }, + "Ividence": { + "domains": [ + "ivitrack.com" + ], + "displayName": "Ividence", + "prevalence": 0.257 + }, + "J.D. Power": { + "domains": [ + "korrelate.net", + "jdpower.com", + "nadaguides.com", + "nada.com" + ], + "prevalence": 0.098 + }, + "JSC ADFACT": { + "domains": [ + "tns-counter.ru" + ], + "displayName": "ADFACT", + "prevalence": 0.255 + }, + "JuicyAds": { + "domains": [ + "juicyads.com" + ], + "displayName": "JuicyAds", + "prevalence": 0.902 + }, + "JustPremium": { + "domains": [ + "justpremium.com", + "justpremium.nl" + ], + "displayName": "JustPremium", + "prevalence": 0.53 + }, + "KBM Group LLC": { + "domains": [ + "ib-ibi.com" + ], + "displayName": "KBM Group", + "prevalence": 1.571 + }, + "Kantar Operations": { + "domains": [ + "insightexpressai.com" + ], + "displayName": "Kantar Operations", + "prevalence": 0.441 + }, + "Kargo Global, Inc.": { + "domains": [ + "kargo.com" + ], + "displayName": "Kargo", + "prevalence": 0.1 + }, + "Kenshoo TLD": { + "domains": [ + "xg4ken.com" + ], + "displayName": "Kenshoo TLD", + "prevalence": 0.727 + }, + "Keywee": { + "domains": [ + "keywee.co" + ], + "displayName": "Keywee", + "prevalence": 0.243 + }, + "Klaviyo": { + "domains": [ + "klaviyo.com" + ], + "displayName": "Klaviyo", + "prevalence": 0.432 + }, + "LLC Internest-holding": { + "domains": [ + "adriver.ru", + "soloway.ru" + ], + "displayName": "Internest-holding", + "prevalence": 1.187 + }, + "LLC Mail.Ru": { + "domains": [ + "mail.ru", + "list.ru", + "ok.ru", + "mycdn.me", + "imgsmail.ru", + "odnoklassniki.ru", + "mradx.net", + "gmru.net", + "youla.ru" + ], + "displayName": "Mail.Ru", + "prevalence": 0.245 + }, + "LinkedIn Corporation": { + "domains": [ + "linkedin.com", + "licdn.com", + "bizographics.com", + "slidesharecdn.com", + "slideshare.net", + "lynda.com", + "video2brain.com" + ], + "displayName": "LinkedIn", + "prevalence": 4.212 + }, + "Listrak": { + "domains": [ + "listrak.com", + "listrakbi.com" + ], + "displayName": "Listrak", + "prevalence": 0.486 + }, + "LiveChat Inc": { + "domains": [ + "livechatinc.com", + "livechatinc.net", + "helpdesk.com", + "chatbot.com", + "knowledgebase.ai", + "chat.io", + "botengine.ai" + ], + "displayName": "LiveChat", + "prevalence": 0.589 + }, + "LiveIntent Inc.": { + "domains": [ + "liadm.com", + "liveintent.com" + ], + "displayName": "LiveIntent", + "prevalence": 1.803 + }, + "LivePerson, Inc": { + "domains": [ + "liveperson.net", + "lpsnmedia.net" + ], + "displayName": "LivePerson", + "prevalence": 0.791 + }, + "LiveRamp": { + "domains": [ + "liveramp.com", + "pippio.com", + "arbor.io", + "circulate.com", + "faktor.io" + ], + "displayName": "LiveRamp", + "prevalence": 6.488 + }, + "Liwio": { + "domains": [ + "abtasty.com" + ], + "displayName": "Liwio", + "prevalence": 0.473 + }, + "LockerDome, LLC": { + "domains": [ + "lockerdome.com", + "lockerdomecdn.com" + ], + "displayName": "LockerDome", + "prevalence": 0.35 + }, + "LoopMe Ltd": { + "domains": [ + "loopme.com", + "loopme.me" + ], + "displayName": "LoopMe", + "prevalence": 0.55 + }, + "Lotame Solutions, Inc.": { + "domains": [ + "crwdcntrl.net", + "lotame.com" + ], + "displayName": "Lotame Solutions", + "prevalence": 9.006 + }, + "MGID Inc": { + "domains": [ + "mgid.com" + ], + "displayName": "MGID", + "prevalence": 0.459 + }, + "Magnetic Media Online, Inc.": { + "domains": [ + "domdex.com" + ], + "displayName": "Magnetic Media Online", + "prevalence": 1.666 + }, + "Marin Software Inc.": { + "domains": [ + "marinsm.com", + "prfct.co", + "mysocialpixel.com", + "perfectaudience.com" + ], + "displayName": "Marin Software", + "prevalence": 0.461 + }, + "Marketo, Inc.": { + "domains": [ + "marketo.net", + "marketo.com", + "mktoresp.com" + ], + "displayName": "Marketo", + "prevalence": 0.996 + }, + "MaxMind Inc.": { + "domains": [ + "maxmind.com" + ], + "displayName": "MaxMind", + "prevalence": 0.321 + }, + "Medallia Inc.": { + "domains": [ + "medallia.com", + "medallia.eu", + "kampyle.com" + ], + "displayName": "Medallia", + "prevalence": 0.352 + }, + "Media.net Advertising FZ-LLC": { + "domains": [ + "media.net" + ], + "displayName": "Media.net Advertising", + "prevalence": 4.06 + }, + "MediaMath, Inc.": { + "domains": [ + "mathtag.com" + ], + "displayName": "MediaMath", + "prevalence": 12.755 + }, + "MediaWallah LLC": { + "domains": [ + "mediawallahscript.com" + ], + "displayName": "MediaWallah", + "prevalence": 0.477 + }, + "Mediavine, Inc.": { + "domains": [ + "mediavine.com", + "thehollywoodgossip.com", + "tvfanatic.com", + "moviefanatic.com" + ], + "displayName": "Mediavine", + "prevalence": 0.677 + }, + "Meetrics GmbH": { + "domains": [ + "mxcdn.net", + "meetrics.de", + "meetrics.com", + "meetrics.net", + "research.de.com" + ], + "displayName": "Meetrics", + "prevalence": 0.452 + }, + "Merkle Inc": { + "domains": [ + "rkdms.com", + "merklesearch.com" + ], + "displayName": "Merkle", + "prevalence": 1.096 + }, + "MindGeek": { + "domains": [ + "seancodycontent.com", + "seancody.com", + "dplaygroundcontent.com", + "digitalplayground.com", + "realitykingscontent.com", + "realitykings.com", + "redtube.com", + "men.com", + "mencontent.com", + "fakehub.com", + "transangels.com", + "momxxx.com", + "faketaxi.com", + "phncdn.com", + "pornhub.com", + "ypncdn.com", + "youporn.com", + "t8cdn.com", + "rdtcdn.com", + "tube8.com", + "xtube.com", + "youporngay.com", + "gaytube.com", + "redtube.com.br", + "pornmd.com", + "hubtraffic.com", + "thumbzilla.com", + "pornhubselect.com", + "pornhubpremium.com", + "modelhub.com", + "contentabc.com", + "etahub.com", + "brazzerscontent.com", + "brazzers.com", + "mofos.com", + "mofoscontent.com", + "babescontent.com", + "twistyscontent.com", + "babes.com", + "twistys.com", + "trafficjunky.net", + "trafficjunky.com", + "adultforce.com" + ], + "displayName": "MindGeek", + "prevalence": 0.511 + }, + "Mixpanel, Inc.": { + "domains": [ + "mxpnl.com", + "mxpnl.net", + "mixpanel.com" + ], + "displayName": "Mixpanel", + "prevalence": 0.9 + }, + "Monetate, Inc.": { + "domains": [ + "monetate.net" + ], + "displayName": "Monetate", + "prevalence": 0.482 + }, + "Mouseflow": { + "domains": [ + "mouseflow.com" + ], + "displayName": "Mouseflow", + "prevalence": 0.596 + }, + "Movable Ink": { + "domains": [ + "movableink.com", + "micpn.com" + ], + "displayName": "Movable Ink", + "prevalence": 0.448 + }, + "My6sense Inc.": { + "domains": [ + "my6sense.com", + "mynativeplatform.com" + ], + "displayName": "My6sense", + "prevalence": 0.709 + }, + "Nativo, Inc": { + "domains": [ + "postrelease.com", + "ntv.io", + "nativo.net" + ], + "displayName": "Nativo", + "prevalence": 1.107 + }, + "Navegg S.A.": { + "domains": [ + "navdmp.com" + ], + "displayName": "Navegg", + "prevalence": 0.83 + }, + "Neustar, Inc.": { + "domains": [ + "agkn.com", + "neustar.biz", + "comal.tx.us", + "contra-costa.ca.us", + "ultratools.com", + "berks.pa.us", + "washington.mn.us", + "forsyth.nc.us" + ], + "displayName": "Neustar", + "prevalence": 8.836 + }, + "New Relic": { + "domains": [ + "newrelic.com", + "nr-data.net" + ], + "displayName": "New Relic", + "prevalence": 8.474 + }, + "Nexstar Media Group": { + "domains": [ + "kark.com", + "fox16.com", + "nwahomepage.com", + "yashi.com", + "channel4000.com", + "cbs17.com", + "lasvegasnow.com", + "localsyr.com", + "rochesterfirst.com", + "lakana.com", + "lkqd.net" + ], + "displayName": "Nexstar Media Group", + "prevalence": 0.443 + }, + "NinthDecimal, Inc": { + "domains": [ + "ninthdecimal.com" + ], + "displayName": "NinthDecimal", + "prevalence": 0.416 + }, + "OOO ECO PC - Complex Solutions": { + "domains": [ + "yadro.ru", + "mediametrics.ru" + ], + "displayName": "ECO PC - Complex Solutions", + "prevalence": 2.2 + }, + "ORC International": { + "domains": [ + "brealtime.com", + "clrstm.com" + ], + "displayName": "ORC International", + "prevalence": 3.56 + }, + "Olark": { + "domains": [ + "olark.com" + ], + "displayName": "Olark", + "prevalence": 0.296 + }, + "OneSignal": { + "domains": [ + "onesignal.com", + "os.tc" + ], + "displayName": "OneSignal", + "prevalence": 2.412 + }, + "OpenX Technologies Inc": { + "domains": [ + "openx.net", + "openx.com", + "openx.org", + "openxadexchange.com", + "servedbyopenx.com", + "jump-time.net", + "deliverimp.com", + "mezzobit.com", + "pixfuture.net", + "godengo.com", + "pubnation.com" + ], + "displayName": "OpenX", + "prevalence": 15.366 + }, + "Oracle Corporation": { + "domains": [ + "addthis.com", + "addthisedge.com", + "bluekai.com", + "nexac.com", + "bkrtx.com", + "moatads.com", + "moat.com", + "moatpixel.com", + "eloqua.com", + "en25.com", + "maxymiser.net", + "bronto.com", + "univide.com", + "bm23.com", + "custhelp.com", + "atgsvcs.com", + "rightnowtech.com", + "oraclecloud.com", + "responsys.net", + "adrsp.net", + "oracleoutsourcing.com", + "estara.com", + "oracleimg.com", + "oracle.com", + "addthiscdn.com", + "mysql.com", + "netsuite.com", + "q-go.net", + "virtualbox.org", + "clearspring.com", + "livelook.com", + "compendium.com", + "compendiumblog.com", + "java.net", + "java.com", + "netbeans.org", + "homeip.net", + "grapeshot.co.uk" + ], + "displayName": "Oracle", + "prevalence": 22.331 + }, + "Outbrain": { + "domains": [ + "outbrain.com", + "zemanta.com" + ], + "displayName": "Outbrain", + "prevalence": 5.819 + }, + "OwnerIQ Inc": { + "domains": [ + "owneriq.net", + "manualsonline.com" + ], + "displayName": "OwnerIQ", + "prevalence": 4.578 + }, + "PLAYGROUND XYZ": { + "domains": [ + "playground.xyz" + ], + "displayName": "PLAYGROUND XYZ", + "prevalence": 1.625 + }, + "PageFair Limited": { + "domains": [ + "pagefair.com", + "pagefair.net" + ], + "displayName": "PageFair", + "prevalence": 0.402 + }, + "Parsely, Inc.": { + "domains": [ + "parsely.com", + "parse.ly" + ], + "displayName": "Parsely", + "prevalence": 0.912 + }, + "PathDefender": { + "domains": [ + "ywxi.net", + "trustedsite.com" + ], + "displayName": "PathDefender", + "prevalence": 0.302 + }, + "PayPal, Inc.": { + "domains": [ + "paypalobjects.com", + "paypal.com", + "braintreegateway.com", + "where.com", + "braintree-api.com", + "venmo.com", + "s-xoom.com", + "paypal-community.com", + "xoom.com", + "paypal-prepaid.com", + "paypal-brasil.com.br", + "paypal.co.uk", + "paypal.at", + "paypal.be", + "paypal.ca", + "paypal.ch", + "paypal.cl", + "paypal.cn", + "paypal.co", + "paypal.co.id", + "paypal.co.il", + "paypal.co.in", + "paypal.co.kr", + "paypal.co.nz", + "paypal.co.th", + "paypal.co.za", + "paypal.com.ar", + "paypal.com.au", + "paypal.com.br", + "paypal.com.hk", + "paypal.com.mx", + "paypal.com.my", + "paypal.com.pe", + "paypal.com.pt", + "paypal.com.sa", + "paypal.com.sg", + "paypal.com.tr", + "paypal.com.tw", + "paypal.com.ve", + "paypal.de", + "paypal.dk", + "paypal.es", + "paypal.eu", + "paypal.fi", + "paypal.fr", + "paypal.ie", + "paypal.it", + "paypal.jp", + "paypal.lu", + "paypal.nl", + "paypal.no", + "paypal.ph", + "paypal.pl", + "paypal.pt", + "paypal.ru", + "paypal.se", + "paypal.vn", + "paypal-deutschland.de", + "paypal-forward.com", + "paypal-france.fr", + "paypal-latam.com", + "paypal-marketing.pl", + "paypal-mena.com", + "paypal-nakit.com", + "paypal-prepagata.com", + "thepaypalblog.com", + "paypal.me", + "paypal-information.com", + "paypal-apps.com", + "paypalbenefits.com", + "paypal-knowledge.com", + "paypal-knowledge-test.com" + ], + "displayName": "PayPal", + "prevalence": 1.098 + }, + "Perfect Market, Inc.": { + "domains": [ + "perfectmarket.com" + ], + "displayName": "Perfect Market", + "prevalence": 0.284 + }, + "Permutive, Inc.": { + "domains": [ + "permutive.com" + ], + "displayName": "Permutive", + "prevalence": 0.443 + }, + "Piano Software": { + "domains": [ + "npttech.com", + "piano.io", + "tinypass.com" + ], + "displayName": "Piano Software", + "prevalence": 0.389 + }, + "Pingdom AB": { + "domains": [ + "pingdom.net", + "pingdom.com" + ], + "displayName": "Pingdom", + "prevalence": 1.205 + }, + "Platform161": { + "domains": [ + "creative-serving.com", + "platform161.com", + "p161.net" + ], + "displayName": "Platform161", + "prevalence": 1.521 + }, + "PowerLinks Media Limited": { + "domains": [ + "powerlinks.com" + ], + "displayName": "PowerLinks Media", + "prevalence": 1.591 + }, + "Proclivity Media, Inc.": { + "domains": [ + "pswec.com", + "proclivitysystems.com" + ], + "displayName": "Proclivity Media", + "prevalence": 0.73 + }, + "Propeller Ads": { + "domains": [ + "rtmark.net", + "propellerads.com", + "propellerclick.com" + ], + "displayName": "Propeller Ads", + "prevalence": 0.743 + }, + "PubMatic, Inc.": { + "domains": [ + "pubmatic.com" + ], + "displayName": "PubMatic", + "prevalence": 15.569 + }, + "Pulsepoint, Inc.": { + "domains": [ + "contextweb.com" + ], + "displayName": "Pulsepoint", + "prevalence": 6.879 + }, + "QBC Holdings, Inc.": { + "domains": [ + "bluecava.com" + ], + "displayName": "QBC Holdings", + "prevalence": 0.216 + }, + "Qualaroo": { + "domains": [ + "qualaroo.com" + ], + "displayName": "Qualaroo", + "prevalence": 0.982 + }, + "Qualtrics, LLC": { + "domains": [ + "qualtrics.com" + ], + "displayName": "Qualtrics", + "prevalence": 0.852 + }, + "Quantcast Corporation": { + "domains": [ + "quantserve.com", + "quantcount.com", + "quantcast.com", + "apextag.com" + ], + "displayName": "Quantcast", + "prevalence": 13.725 + }, + "Quora": { + "domains": [ + "quora.com", + "quoracdn.net" + ], + "displayName": "Quora", + "prevalence": 0.652 + }, + "RUN": { + "domains": [ + "rundsp.com", + "runadtag.com" + ], + "displayName": "RUN", + "prevalence": 2.653 + }, + "Rakuten, Inc.": { + "domains": [ + "rakuten.co.jp", + "r10s.jp", + "rakuten-static.com", + "rakuten.com", + "fril.jp", + "infoseek.co.jp", + "rpaas.net", + "r10s.com", + "rakuten.fr", + "rakuten.ne.jp", + "rakuten-card.co.jp", + "kobo.com", + "linksynergy.com", + "nxtck.com", + "mediaforge.com", + "rmtag.com", + "dc-storm.com", + "jrs5.com", + "rakutenmarketing.com" + ], + "displayName": "Rakuten", + "prevalence": 3.646 + }, + "Rambler Internet Holding, LLC": { + "domains": [ + "rambler.ru", + "top100.ru", + "rnet.plus", + "rl0.ru", + "rambler.su", + "dsp-rambler.ru", + "rambler-co.ru" + ], + "displayName": "Rambler Internet Holding", + "prevalence": 0.323 + }, + "Reddit Inc.": { + "domains": [ + "reddit.com", + "redditstatic.com", + "redditmedia.com", + "redd.it", + "redditinc.com" + ], + "displayName": "Reddit", + "prevalence": 0.689 + }, + "Resonate Networks": { + "domains": [ + "reson8.com", + "resonate.com" + ], + "displayName": "Resonate Networks", + "prevalence": 1.048 + }, + "Retyp LLC": { + "domains": [ + "optinmonster.com", + "optnmstr.com", + "opmnstr.com", + "optmnstr.com", + "optmstr.com" + ], + "displayName": "Retyp", + "prevalence": 1.3 + }, + "RevContent, LLC": { + "domains": [ + "revcontent.com" + ], + "displayName": "RevContent", + "prevalence": 0.189 + }, + "RevJet": { + "domains": [ + "revjet.com" + ], + "displayName": "RevJet", + "prevalence": 0.28 + }, + "RhythmOne": { + "domains": [ + "1rx.io", + "burstnet.com", + "allmusic.com", + "sidereel.com", + "allmovie.com", + "rhythmone.com", + "yumenetworks.com", + "yume.com", + "po.st", + "gwallet.com" + ], + "displayName": "RhythmOne", + "prevalence": 7.276 + }, + "Rocket Fuel Inc.": { + "domains": [ + "rfihub.com", + "rfihub.net", + "ru4.com" + ], + "displayName": "Rocket Fuel", + "prevalence": 6.36 + }, + "Roxr Software Ltd": { + "domains": [ + "getclicky.com" + ], + "displayName": "Roxr Software", + "prevalence": 0.755 + }, + "RuTarget LLC": { + "domains": [ + "rutarget.ru" + ], + "displayName": "RuTarget", + "prevalence": 0.298 + }, + "SCTR Services LLC": { + "domains": [ + "itsup.com" + ], + "prevalence": 0.034 + }, + "Sailthru, Inc": { + "domains": [ + "sail-horizon.com", + "sail-personalize.com", + "sailthru.com", + "sail-track.com" + ], + "displayName": "Sailthru", + "prevalence": 0.482 + }, + "Salesforce.com, Inc.": { + "domains": [ + "krxd.net", + "cquotient.com", + "salesforceliveagent.com", + "pardot.com", + "force.com", + "salesforce.com", + "desk.com", + "exacttarget.com", + "exct.net", + "brighteroption.com", + "semver.io", + "cloudforce.com", + "database.com", + "lightning.com", + "salesforce-communities.com", + "visualforce.com", + "documentforce.com", + "forceusercontent.com", + "sfdcstatic.com", + "chatter.com", + "data.com", + "site.com", + "dreamforce.com", + "quotable.com", + "einstein.com", + "heywire.com", + "beyondcore.com", + "twinprime.com", + "gravitytank.com", + "krux.com", + "sequence.com", + "metamind.io", + "salesforceiq.com", + "relateiq.com", + "marketingcloud.com", + "steelbrick.com", + "radian6.com", + "buddymedia.com", + "social.com", + "demandware.com", + "cotweet.com", + "salesforcemarketingcloud.com", + "weinvoiceit.com", + "cloudcraze.com", + "attic.io", + "sforce.com", + "govforce.com", + "appexchange.com", + "appcloud.com" + ], + "displayName": "Salesforce.com", + "prevalence": 8.336 + }, + "Segment.io, Inc.": { + "domains": [ + "segment.com", + "segment.io" + ], + "displayName": "Segment.io", + "prevalence": 0.852 + }, + "Semasio GmbH": { + "domains": [ + "semasio.com", + "semasio.net" + ], + "displayName": "Semasio", + "prevalence": 1.275 + }, + "SessionCam Ltd": { + "domains": [ + "sessioncam.com" + ], + "displayName": "SessionCam", + "prevalence": 0.357 + }, + "ShareThis, Inc": { + "domains": [ + "sharethis.com" + ], + "displayName": "ShareThis", + "prevalence": 4.494 + }, + "Shareaholic Inc": { + "domains": [ + "shareaholic.com" + ], + "displayName": "Shareaholic", + "prevalence": 0.35 + }, + "Sharethrough, Inc.": { + "domains": [ + "sharethrough.com", + "shareth.ru" + ], + "displayName": "Sharethrough", + "prevalence": 2.475 + }, + "Signal Digital, Inc.": { + "domains": [ + "btstatic.com", + "yjtag.jp", + "thebrighttag.com" + ], + "displayName": "Signal Digital", + "prevalence": 2.098 + }, + "Simplicity Marketing": { + "domains": [ + "flashtalking.com" + ], + "displayName": "Simplicity Marketing", + "prevalence": 1.093 + }, + "Simplifi Holdings Inc.": { + "domains": [ + "simpli.fi" + ], + "displayName": "Simplifi Holdings", + "prevalence": 6.628 + }, + "Siteimprove A/S": { + "domains": [ + "siteimprove.com", + "siteimproveanalytics.com", + "siteimproveanalytics.io", + "siteimprove.net" + ], + "displayName": "Siteimprove", + "prevalence": 0.934 + }, + "Smaato Inc.": { + "domains": [ + "smaato.net" + ], + "displayName": "Smaato", + "prevalence": 0.525 + }, + "Smartadserver S.A.S": { + "domains": [ + "smartadserver.com", + "sascdn.com" + ], + "displayName": "Smartadserver", + "prevalence": 13.934 + }, + "Snapchat, Inc.": { + "domains": [ + "sc-static.net", + "snapchat.com", + "bitmoji.com" + ], + "displayName": "Snapchat", + "prevalence": 1.08 + }, + "Snapsort Inc.": { + "domains": [ + "deployads.com", + "cpuboss.com", + "gpuboss.com", + "snapsort.com", + "carsort.com" + ], + "displayName": "Snapsort", + "prevalence": 0.518 + }, + "Sojern, Inc.": { + "domains": [ + "sojern.com" + ], + "displayName": "Sojern", + "prevalence": 0.521 + }, + "Somo Audience Corp": { + "domains": [ + "mobileadtrading.com" + ], + "displayName": "Somo Audience", + "prevalence": 1.093 + }, + "Sonobi, Inc": { + "domains": [ + "sonobi.com" + ], + "displayName": "Sonobi", + "prevalence": 1.655 + }, + "Sovrn Holdings": { + "domains": [ + "sovrnlabs.net", + "sovrn.com", + "lijit.com", + "viglink.com", + "s-onetag.com" + ], + "displayName": "Sovrn Holdings", + "prevalence": 7.322 + }, + "SpotX, Inc.": { + "domains": [ + "spotx.tv", + "spotxcdn.com", + "spotxchange.com" + ], + "displayName": "SpotX", + "prevalence": 12.625 + }, + "SpringServe, LLC": { + "domains": [ + "springserve.com", + "springserve.net" + ], + "displayName": "SpringServe", + "prevalence": 0.766 + }, + "StatCounter": { + "domains": [ + "statcounter.com" + ], + "displayName": "StatCounter", + "prevalence": 1.253 + }, + "Steel House, Inc": { + "domains": [ + "steelhousemedia.com" + ], + "displayName": "Steel House", + "prevalence": 0.355 + }, + "Storygize": { + "domains": [ + "storygize.com", + "storygize.net" + ], + "displayName": "Storygize", + "prevalence": 0.673 + }, + "Ströer Group": { + "domains": [ + "adscale.de", + "m6r.eu", + "stroeerdigitalgroup.de", + "stroeerdigitalmedia.de", + "interactivemedia.net", + "stroeerdp.de", + "stroeermediabrands.de" + ], + "displayName": "Ströer Group", + "prevalence": 1.364 + }, + "Sumo Group": { + "domains": [ + "sumo.com" + ], + "displayName": "Sumo Group", + "prevalence": 0.877 + }, + "SundaySky Ltd.": { + "domains": [ + "sundaysky.com" + ], + "displayName": "SundaySky", + "prevalence": 1.03 + }, + "Supership Inc": { + "domains": [ + "socdm.com" + ], + "displayName": "Supership", + "prevalence": 1.073 + }, + "Survata, Inc.": { + "domains": [ + "survata.com" + ], + "prevalence": 1.596 + }, + "Synacor, Inc.": { + "domains": [ + "technoratimedia.com", + "synacor.com", + "syn-api.com", + "syn-cdn.com", + "zimbra.org", + "technorati.com" + ], + "displayName": "Synacor", + "prevalence": 0.148 + }, + "Synapse Group, Inc.": { + "domains": [ + "bizrate.com", + "bizrateinsights.com" + ], + "displayName": "Synapse Group", + "prevalence": 0.368 + }, + "TVSquared": { + "domains": [ + "tvsquared.com" + ], + "displayName": "TVSquared", + "prevalence": 0.273 + }, + "Taboola.com LTD": { + "domains": [ + "taboola.com", + "taboolasyndication.com", + "zorosrv.com", + "admailtiser.com", + "basebanner.com", + "vidfuture.com", + "cmbestsrv.com", + "convertmedia.com" + ], + "displayName": "Taboola.com", + "prevalence": 7.724 + }, + "Tapad, Inc.": { + "domains": [ + "tapad.com" + ], + "displayName": "Tapad", + "prevalence": 11.288 + }, + "Teads ( Luxenbourg ) SA": { + "domains": [ + "teads.tv", + "ebz.io" + ], + "displayName": "Teads", + "prevalence": 2.182 + }, + "Tealium Inc.": { + "domains": [ + "tiqcdn.com", + "tealium.com", + "tealiumiq.com" + ], + "displayName": "Tealium", + "prevalence": 2.339 + }, + "Telaria": { + "domains": [ + "tremorhub.com" + ], + "displayName": "Telaria", + "prevalence": 2.066 + }, + "The Nielsen Company": { + "domains": [ + "imrworldwide.com", + "nielsen.com", + "exelator.com", + "exelate.com", + "visualdna.com", + "vdna-assets.com", + "myvisualiq.net", + "visualiq.com", + "visualiq.de", + "visualiq.fr" + ], + "displayName": "The Nielsen Company", + "prevalence": 13.643 + }, + "The Rocket Science Group, LLC": { + "domains": [ + "chimpstatic.com", + "mailchimp.com", + "mailchi.mp", + "list-manage.com", + "mailchimpapp.com", + "eep.io" + ], + "displayName": "The Rocket Science Group", + "prevalence": 1.278 + }, + "The Rubicon Project, Inc.": { + "domains": [ + "rubiconproject.com", + "chango.com" + ], + "displayName": "The Rubicon Project", + "prevalence": 16.828 + }, + "The Trade Desk Inc": { + "domains": [ + "adsrvr.org" + ], + "displayName": "The Trade Desk", + "prevalence": 19.938 + }, + "Throtle": { + "domains": [ + "thrtle.com" + ], + "displayName": "Throtle", + "prevalence": 2.471 + }, + "Tomksoft S.A.": { + "domains": [ + "popads.net" + ], + "displayName": "Tomksoft", + "prevalence": 0.718 + }, + "Traffic Stars": { + "domains": [ + "trafficstars.com", + "tsyndicate.com" + ], + "displayName": "Traffic Stars", + "prevalence": 0.586 + }, + "Tremor Video DSP": { + "domains": [ + "videohub.tv", + "scanscout.com", + "tremormedia.com" + ], + "displayName": "Tremor Video DSP", + "prevalence": 0.821 + }, + "TripleLift": { + "domains": [ + "triplelift.com", + "3lift.com" + ], + "displayName": "TripleLift", + "prevalence": 5.294 + }, + "True Fit Corporation": { + "domains": [ + "truefitcorp.com", + "truefit.com" + ], + "prevalence": 0.014 + }, + "TrustArc Inc.": { + "domains": [ + "truste.com", + "trustarc.com" + ], + "displayName": "TrustArc", + "prevalence": 1.778 + }, + "Trusted Shops GmbH": { + "domains": [ + "trustedshops.com", + "trustedshops.de" + ], + "displayName": "Trusted Shops", + "prevalence": 0.355 + }, + "Trustpilot A/S": { + "domains": [ + "trustpilot.net", + "trustpilot.com" + ], + "displayName": "Trustpilot", + "prevalence": 0.775 + }, + "Turn Inc.": { + "domains": [ + "turn.com" + ], + "displayName": "Turn", + "prevalence": 10.772 + }, + "Twitter, Inc.": { + "domains": [ + "twitter.com", + "twimg.com", + "t.co", + "twttr.net", + "twttr.com", + "ads-twitter.com", + "vine.co", + "pscp.tv", + "cms-twdigitalassets.com", + "periscope.tv", + "twittercommunity.com", + "twitter.fr" + ], + "displayName": "Twitter", + "prevalence": 13.896 + }, + "Unbounce": { + "domains": [ + "unbounce.com", + "ubembed.com" + ], + "displayName": "Unbounce", + "prevalence": 0.325 + }, + "Undertone Networks": { + "domains": [ + "undertone.com" + ], + "displayName": "Undertone Networks", + "prevalence": 0.568 + }, + "Unruly Group Limited": { + "domains": [ + "unrulymedia.com" + ], + "displayName": "Unruly Group", + "prevalence": 0.625 + }, + "Usabilla B.V.": { + "domains": [ + "usabilla.com" + ], + "displayName": "Usabilla", + "prevalence": 0.584 + }, + "V Kontakte LLC": { + "domains": [ + "vk.com", + "userapi.com", + "vk.me", + "vkontakte.com", + "vkontakte.ru", + "vk.cc" + ], + "displayName": "V Kontakte", + "prevalence": 0.655 + }, + "Valassis Digital": { + "domains": [ + "brand.net", + "mxptint.net", + "valassisdigital.com", + "valassis.eu", + "valassis.com" + ], + "displayName": "Valassis Digital", + "prevalence": 3.987 + }, + "Verizon Media": { + "domains": [ + "yahoo.com", + "yimg.com", + "adtechus.com", + "adtechjp.com", + "oath.com", + "yahooapis.com", + "btrll.com", + "adtech.de", + "aolcdn.com", + "atwola.com", + "convertro.com", + "bluelithium.com", + "brightroll.com", + "yieldmanager.com", + "yahoodns.net", + "rivals.com", + "mapquestapi.com", + "mapquest.com", + "hostingprod.com", + "5min.com", + "techcrunch.com", + "techcrunch.cn", + "huffingtonpost.de", + "huffingtonpost.fr", + "huffingtonpost.it", + "huffingtonpost.jp", + "huffingtonpost.kr", + "huffingtonpost.es", + "huffingtonpost.co.za", + "huffingtonpost.com.au", + "huffingtonpost.com.mx", + "huffingtonpost.gr", + "pictela.net", + "tumblr.com", + "pulsemgr.com", + "huffpost.com", + "huffpo.com", + "huffpost.co.uk", + "huffpost.de", + "huffpost.gr", + "huffpost.kr", + "huffingtonpost.com", + "aolp.jp", + "advertising.com", + "blogsmithmedia.com", + "nexage.com", + "adap.tv", + "aol.com", + "mqcdn.com", + "aol.co.uk", + "aol.jp", + "pollster.com", + "teamaol.com", + "aol.ca", + "ryot.org", + "ryotlab.com", + "ryotstudio.com", + "ryotstudio.co.uk", + "adsonar.com", + "stylelist.com", + "autoblog.com", + "sre-perim.com", + "vidible.tv", + "lexity.com", + "yahoo.net", + "netscape.com", + "huffingtonpost.ca", + "tecnoactual.net", + "engadget.com", + "huffingtonpost.co.uk", + "geocities.com", + "yahoosmallbusiness.com", + "luminate.com", + "tastefullyoffensive.com", + "zenfs.com", + "videovore.com", + "aol.de", + "aol.fr", + "golocal.guru", + "aabacosmallbusiness.com", + "wow.com", + "24-7.pet", + "247.vacations", + "anyprice.com", + "autos24-7.com", + "autos.parts", + "baby.guide", + "chowist.com", + "citypedia.com", + "couponbear.com", + "diylife.com", + "fashion.life", + "fast.rentals", + "find.furniture", + "foodbegood.com", + "furniture.deals", + "gamer.site", + "glamorbank.com", + "going.com", + "greendaily.com", + "health247.com", + "health.zone", + "homesessive.com", + "shelterpop.com", + "parentdish.ca", + "alephd.com", + "yho.com", + "housingwatch.com", + "insurance24-7.com", + "job-sift.com", + "jsyk.com", + "kitchepedia.com", + "know-legal.com", + "learn-247.com", + "luxist.com", + "money-a2z.com", + "mydaily.com", + "netdeals.com", + "pets.world", + "see-it.live", + "shopfone.com", + "streampad.com", + "joystiq.com", + "sport-king.com", + "tech247.co", + "thatsfit.ca", + "tech24.deals", + "thegifts.co", + "wmconnect.com", + "think24-7.com", + "viral.site", + "intoautos.com", + "netfind.com", + "when.com", + "enow.com", + "aolsearch.com", + "searchjam.com" + ], + "displayName": "Verizon Media", + "prevalence": 18.922 + }, + "Vindico LLC": { + "domains": [ + "vindicosuite.com" + ], + "displayName": "Vindico", + "prevalence": 0.268 + }, + "Virtual Minds AG": { + "domains": [ + "adition.com", + "movad.net", + "adclear.net", + "theadex.com", + "t4ft.de", + "batch.ba", + "yieldlab.net", + "yieldlab.com", + "yieldlab.de", + "virtualminds.de", + "vm.de" + ], + "displayName": "Virtual Minds", + "prevalence": 2.444 + }, + "Wal-Mart Stores, Inc.": { + "domains": [ + "walmart.com", + "wal.co", + "walmartimages.com", + "asda.com", + "assets-asda.com", + "samsclub.com", + "walmartone.com", + "walmartimages.ca", + "wmobjects.com.br", + "samsclubresources.com", + "walmart.ca", + "vudu.com", + "walmartcanada.ca", + "walmartmoneycard.com", + "walmart.com.mx" + ], + "displayName": "Wal-Mart Stores", + "prevalence": 3.826 + }, + "Weborama": { + "domains": [ + "weborama.fr", + "weborama.com", + "weborama.io" + ], + "displayName": "Weborama", + "prevalence": 1.123 + }, + "Webtrekk GmbH": { + "domains": [ + "wbtrk.net", + "wt-safetag.com", + "wt-eu02.net", + "wcfbc.net", + "webtrekk.net", + "mateti.net", + "cbtrk.net", + "webtrekk.com" + ], + "displayName": "Webtrekk", + "prevalence": 0.602 + }, + "Wingify": { + "domains": [ + "wingify.com", + "vwo.com", + "pushcrew.com", + "visualwebsiteoptimizer.com" + ], + "displayName": "Wingify", + "prevalence": 1.755 + }, + "Xaxis": { + "domains": [ + "mookie1.com", + "mookie1.cn" + ], + "displayName": "Xaxis", + "prevalence": 7.415 + }, + "Yahoo Japan Corporation": { + "domains": [ + "yahoo.co.jp", + "yimg.jp", + "storage-yahoo.jp", + "yahooapis.jp", + "geocities.jp" + ], + "displayName": "Yahoo Japan", + "prevalence": 0.286 + }, + "Yandex LLC": { + "domains": [ + "yandex.ru", + "yastatic.net", + "webvisor.org", + "yandex.net", + "adfox.ru", + "adfox.me", + "yandex.st", + "ymetrica1.com", + "yandex.com", + "metrika-informer.com", + "ya.ru", + "loginza.ru", + "yandex.sx", + "kinopoisk.ru", + "auto.ru", + "yandex.ua", + "yandex.by", + "yandex.com.tr" + ], + "displayName": "Yandex", + "prevalence": 3.582 + }, + "YieldMo, Inc.": { + "domains": [ + "yieldmo.com" + ], + "displayName": "YieldMo", + "prevalence": 2.76 + }, + "Yieldlove GmbH": { + "domains": [ + "yieldlove.com", + "yieldlove-ad-serving.net" + ], + "displayName": "Yieldlove", + "prevalence": 0.443 + }, + "Yieldr": { + "domains": [ + "yieldr.com", + "254a.com" + ], + "displayName": "Yieldr", + "prevalence": 1.541 + }, + "Yotpo Ltd": { + "domains": [ + "yotpo.com" + ], + "displayName": "Yotpo", + "prevalence": 0.418 + }, + "Zendesk, Inc.": { + "domains": [ + "zopim.com", + "zendesk.com", + "zdassets.com", + "zopim.io", + "zendesk.tv", + "outbound.io", + "zndsk.com" + ], + "displayName": "Zendesk", + "prevalence": 1.457 + }, + "Zeta Global": { + "domains": [ + "rezync.com", + "zetaglobal.com", + "zetazync.com" + ], + "displayName": "Zeta Global", + "prevalence": 1.3 + }, + "comScore, Inc": { + "domains": [ + "zqtk.net", + "comscore.com", + "mdotlabs.com", + "scorecardresearch.com", + "e.cl" + ], + "displayName": "comScore", + "prevalence": 12.482 + }, + "emetriq GmbH": { + "domains": [ + "emetriq.de", + "emetriq.com", + "xplosion.de" + ], + "displayName": "emetriq", + "prevalence": 0.591 + }, + "eyeReturn Marketing Inc.": { + "domains": [ + "eyereturn.com" + ], + "displayName": "eyeReturn Marketing", + "prevalence": 2.205 + }, + "eyeota Limited": { + "domains": [ + "eyeota.net" + ], + "displayName": "eyeota", + "prevalence": 6.744 + }, + "iPerceptions Inc.": { + "domains": [ + "iper2.com", + "iperceptions.com" + ], + "displayName": "iPerceptions", + "prevalence": 0.259 + }, + "iSpot.tv": { + "domains": [ + "ispot.tv" + ], + "displayName": "iSpot.tv", + "prevalence": 0.277 + }, + "nugg.ad GmbH": { + "domains": [ + "nuggad.net" + ], + "displayName": "nugg.ad", + "prevalence": 0.884 + }, + "trueAnthem Corp": { + "domains": [ + "tru.am" + ], + "displayName": "trueAnthem", + "prevalence": 0.189 + }, + "twiago GmbH": { + "domains": [ + "twiago.com" + ], + "displayName": "twiago", + "prevalence": 0.277 + }, + "whos.amung.us Inc": { + "domains": [ + "amung.us", + "waust.at" + ], + "displayName": "whos.amung.us", + "prevalence": 0.741 + }, + "wisecode s.r.l.": { + "domains": [ + "histats.com" + ], + "displayName": "wisecode", + "prevalence": 1.959 + } + }, + "domains": { + "truoptik.com": "21 Productions", + "33across.com": "33Across, Inc.", + "tynt.com": "33Across, Inc.", + "securedvisit.com": "4Cite Marketing", + "adotmob.com": "A.Mob SAS", + "alcmpn.com": "ALC", + "idify.com": "ALC", + "alc.com": "ALC", + "xiti.com": "AT Internet", + "aticdn.net": "AT Internet", + "ati-host.net": "AT Internet", + "activehosted.com": "ActiveCampaign, Inc.", + "img-us3.com": "ActiveCampaign, Inc.", + "activecampaign.com": "ActiveCampaign, Inc.", + "trackcmp.net": "ActiveCampaign, Inc.", + "acuityads.com": "AcuityAds", + "acuityplatform.com": "AcuityAds", + "adlightning.com": "Ad Lightning, Inc.", + "adgrx.com": "AdGear Technologies Inc.", + "adgear.com": "AdGear Technologies Inc.", + "adroll.com": "AdRoll, Inc.", + "adspyglass.com": "AdSpyglass", + "o333o.com": "AdSpyglass", + "adstanding.com": "AdStanding", + "atedra.com": "AdStanding", + "adentifi.com": "AdTheorent Inc", + "adthrive.com": "AdThrive, LLC", + "addtoany.com": "AddToAny", + "ipredictive.com": "Adelphic, Inc.", + "adform.net": "Adform A/S", + "adformdsp.net": "Adform A/S", + "adkernel.com": "Adkernel, LLC", + "admedo.com": "Admedo", + "adizio.com": "Admedo", + "a8723.com": "Admedo", + "admixer.net": "Admixer Technologies", + "adnium.com": "Adnium Inc", + "everesttech.net": "Adobe Inc.", + "everestjs.net": "Adobe Inc.", + "everestads.net": "Adobe Inc.", + "sitestat.com": "Adobe Inc.", + "adobetag.com": "Adobe Inc.", + "demdex.net": "Adobe Inc.", + "omtrdc.net": "Adobe Inc.", + "typekit.com": "Adobe Inc.", + "typekit.net": "Adobe Inc.", + "edgefonts.net": "Adobe Inc.", + "2o7.net": "Adobe Inc.", + "adobe.com": "Adobe Inc.", + "adobedtm.com": "Adobe Inc.", + "adobelogin.com": "Adobe Inc.", + "assetsadobe.com": "Adobe Inc.", + "fyre.co": "Adobe Inc.", + "livefyre.com": "Adobe Inc.", + "scene7.com": "Adobe Inc.", + "tubemogul.com": "Adobe Inc.", + "storify.com": "Adobe Inc.", + "atomz.com": "Adobe Inc.", + "ftcdn.net": "Adobe Inc.", + "adobecqms.net": "Adobe Inc.", + "assetsadobe2.com": "Adobe Inc.", + "fotolia.net": "Adobe Inc.", + "businesscatalyst.com": "Adobe Inc.", + "adobeccstatic.com": "Adobe Inc.", + "adobe.io": "Adobe Inc.", + "creativecloud.com": "Adobe Inc.", + "photoshop.com": "Adobe Inc.", + "worldsecuresystems.com": "Adobe Inc.", + "assetsadobe3.com": "Adobe Inc.", + "acrobatusers.com": "Adobe Inc.", + "omniture.com": "Adobe Inc.", + "ss-omtrdc.net": "Adobe Inc.", + "nedstat.net": "Adobe Inc.", + "hitbox.com": "Adobe Inc.", + "behance.net": "Adobe Inc.", + "fotolia.com": "Adobe Inc.", + "auditude.com": "Adobe Inc.", + "adsco.re": "Adscore Technologies DMCC", + "adscore.com": "Adscore Technologies DMCC", + "ad-score.com": "Adscore Technologies DMCC", + "omnitagjs.com": "Adyoulike", + "adyoulike.com": "Adyoulike", + "aidata.io": "Aidata", + "aidata.me": "Aidata", + "advombat.ru": "Aidata", + "akamaihd.net": "Akamai Technologies", + "akamaized.net": "Akamai Technologies", + "akamai.net": "Akamai Technologies", + "go-mpulse.net": "Akamai Technologies", + "abmr.net": "Akamai Technologies", + "edgekey.net": "Akamai Technologies", + "edgesuite.net": "Akamai Technologies", + "akamai.com": "Akamai Technologies", + "gw-ec.com": "Akamai Technologies", + "securetve.com": "Akamai Technologies", + "amazon-adsystem.com": "Amazon Technologies, Inc.", + "ssl-images-amazon.com": "Amazon Technologies, Inc.", + "amazon.com": "Amazon Technologies, Inc.", + "amazon.ca": "Amazon Technologies, Inc.", + "payments-amazon.com": "Amazon Technologies, Inc.", + "amazonpay.com": "Amazon Technologies, Inc.", + "media-amazon.com": "Amazon Technologies, Inc.", + "assoc-amazon.com": "Amazon Technologies, Inc.", + "images-amazon.com": "Amazon Technologies, Inc.", + "awsstatic.com": "Amazon Technologies, Inc.", + "amazonadsystem.com": "Amazon Technologies, Inc.", + "graphiq.com": "Amazon Technologies, Inc.", + "img-dpreview.com": "Amazon Technologies, Inc.", + "elasticbeanstalk.com": "Amazon Technologies, Inc.", + "amazonwebservices.com": "Amazon Technologies, Inc.", + "dpreview.com": "Amazon Technologies, Inc.", + "amazon.in": "Amazon Technologies, Inc.", + "amazon.fr": "Amazon Technologies, Inc.", + "amazon.it": "Amazon Technologies, Inc.", + "amazon.de": "Amazon Technologies, Inc.", + "amazon.co.jp": "Amazon Technologies, Inc.", + "amazon.co.uk": "Amazon Technologies, Inc.", + "assoc-amazon.de": "Amazon Technologies, Inc.", + "assoc-amazon.jp": "Amazon Technologies, Inc.", + "assoc-amazon.co.uk": "Amazon Technologies, Inc.", + "amazon.com.au": "Amazon Technologies, Inc.", + "amazon.com.br": "Amazon Technologies, Inc.", + "primevideo.com": "Amazon Technologies, Inc.", + "amazon.jobs": "Amazon Technologies, Inc.", + "amazonforum.com": "Amazon Technologies, Inc.", + "amazon.com.mx": "Amazon Technologies, Inc.", + "mturk.com": "Amazon Technologies, Inc.", + "awsevents.com": "Amazon Technologies, Inc.", + "ring.com": "Amazon Technologies, Inc.", + "cloudfront.net": "Amazon Technologies, Inc.", + "amazonaws.com": "Amazon Technologies, Inc.", + "zappos.com": "Amazon Technologies, Inc.", + "twitch.tv": "Amazon Technologies, Inc.", + "jtvnw.net": "Amazon Technologies, Inc.", + "ttvnw.net": "Amazon Technologies, Inc.", + "twitchsvc.net": "Amazon Technologies, Inc.", + "forgecdn.net": "Amazon Technologies, Inc.", + "twitchcdn.net": "Amazon Technologies, Inc.", + "audible.com": "Amazon Technologies, Inc.", + "audible.de": "Amazon Technologies, Inc.", + "audible.co.uk": "Amazon Technologies, Inc.", + "alexametrics.com": "Amazon Technologies, Inc.", + "alexa.com": "Amazon Technologies, Inc.", + "serving-sys.com": "Amazon Technologies, Inc.", + "peer39.net": "Amazon Technologies, Inc.", + "peer39.com": "Amazon Technologies, Inc.", + "sizmek.com": "Amazon Technologies, Inc.", + "tidaltv.com": "Amobee, Inc", + "amgdgt.com": "Amobee, Inc", + "amplitude.com": "Amplitude", + "appdynamics.com": "AppDynamics LLC", + "eum-appdynamics.com": "AppDynamics LLC", + "adnxs.com": "AppNexus, Inc.", + "247realmedia.com": "AppNexus, Inc.", + "yieldoptimizer.com": "AppNexus, Inc.", + "ml-attr.com": "AppNexus, Inc.", + "realmedia.com": "AppNexus, Inc.", + "userreport.com": "AudienceProject", + "audienceproject.com": "AudienceProject", + "avocet.io": "Avocet Systems Ltd.", + "webmasterplan.com": "Awin AG", + "html-links.com": "Awin AG", + "reussissonsensemble.fr": "Awin AG", + "successfultogether.co.uk": "Awin AG", + "contentfeed.net": "Awin AG", + "digitalwindow.com": "Awin AG", + "dwin1.com": "Awin AG", + "dwin2.com": "Awin AG", + "zanox.com": "Awin AG", + "awin.com": "Awin AG", + "zanox-affiliate.de": "Awin AG", + "bazaarvoice.com": "Bazaarvoice, Inc.", + "bfmio.com": "Beachfront Media LLC", + "bidr.io": "Beeswax", + "beeswax.com": "Beeswax", + "bttrack.com": "Bidtellect, Inc", + "bidtellect.com": "Bidtellect, Inc", + "blismedia.com": "Blis Media Limited", + "blueconic.net": "BlueConic, Inc.", + "blueconic.com": "BlueConic, Inc.", + "ml314.com": "Bombora Inc.", + "bombora.com": "Bombora Inc.", + "bongacams.org": "BongaCams", + "bongacams.com": "BongaCams", + "bongacams.dk": "BongaCams", + "bongacams2.com": "BongaCams", + "bongacash.com": "BongaCams", + "redcams.su": "BongaCams", + "promo-bc.com": "BongaCams", + "bounceexchange.com": "Bounce Exchange", + "bouncex.net": "Bounce Exchange", + "cdnbasket.net": "Bounce Exchange", + "branch.io": "Branch Metrics, Inc.", + "app.link": "Branch Metrics, Inc.", + "browser-update.org": "Browser Update", + "buysellads.net": "BuySellAds", + "buysellads.com": "BuySellAds", + "servedby-buysellads.com": "BuySellAds", + "carbonads.com": "BuySellAds", + "carbonads.net": "BuySellAds", + "cpx.to": "Captify Technologies Ltd.", + "captify.co.uk": "Captify Technologies Ltd.", + "sitescout.com": "Centro, Inc.", + "adbrite.com": "Centro, Inc.", + "basis.net": "Centro, Inc.", + "centro.net": "Centro, Inc.", + "chartbeat.com": "Chartbeat", + "chartbeat.net": "Chartbeat", + "chaturbate.com": "Chaturbate, LLC", + "highwebmedia.com": "Chaturbate, LLC", + "oncam.xxx": "Chaturbate, LLC", + "1dmp.io": "CleverDATA LLC", + "clickcertain.com": "ClickCertain", + "clicktale.net": "ClickTale Ltd", + "clickagy.com": "Clickagy", + "cogocast.net": "Cogo Labs", + "cogocast.com": "Cogo Labs", + "apxlv.com": "Cogo Labs", + "stackadapt.com": "Collective Roll", + "colossusssp.com": "Colossus Media, LLC", + "cnnx.io": "Connexity, Inc.", + "connexity.net": "Connexity, Inc.", + "bizrate-images.com": "Connexity, Inc.", + "beso-images.com": "Connexity, Inc.", + "mammothshopper.com": "Connexity, Inc.", + "beso.com": "Connexity, Inc.", + "prixmoinscher.com": "Connexity, Inc.", + "df-srv.de": "Contact Impact GmbH", + "contentsquare.com": "ContentSquare", + "contentsquare.net": "ContentSquare", + "dotomi.com": "Conversant LLC", + "dtmpub.com": "Conversant LLC", + "fastclick.net": "Conversant LLC", + "anrdoezrs.net": "Conversant LLC", + "mplxtms.com": "Conversant LLC", + "mediaplex.com": "Conversant LLC", + "lduhtrp.net": "Conversant LLC", + "tqlkg.com": "Conversant LLC", + "ftjcfx.com": "Conversant LLC", + "awltovhc.com": "Conversant LLC", + "yceml.net": "Conversant LLC", + "emjcd.com": "Conversant LLC", + "jdoqocy.com": "Conversant LLC", + "tkqlhce.com": "Conversant LLC", + "kqzyfj.com": "Conversant LLC", + "qksrv.net": "Conversant LLC", + "greystripe.com": "Conversant LLC", + "digitru.st": "Cookie Trust Working Group, Inc. DBA Cookie Trust", + "crazyegg.com": "Crazy Egg, Inc.", + "hellobar.com": "Crazy Egg, Inc.", + "criteo.net": "Criteo SA", + "criteo.com": "Criteo SA", + "hlserve.com": "Criteo SA", + "emailretargeting.com": "Criteo SA", + "crsspxl.com": "Cross Pixel Media, Inc.", + "crownpeak.com": "Crownpeak Technology", + "crownpeak.net": "Crownpeak Technology", + "betrad.com": "Crownpeak Technology", + "evidon.com": "Crownpeak Technology", + "cxense.com": "Cxense ASA", + "emediate.dk": "Cxense ASA", + "emediate.eu": "Cxense ASA", + "trustx.org": "DCN", + "dtscout.com": "DTS Technology", + "dmcdn.net": "Dailymotion SA", + "dailymotion.com": "Dailymotion SA", + "dm-event.net": "Dailymotion SA", + "dmxleo.com": "Dailymotion SA", + "pxlad.io": "Dailymotion SA", + "w55c.net": "DataXu", + "pro-market.net": "Datonics LLC", + "demandbase.com": "Demandbase, Inc.", + "company-target.com": "Demandbase, Inc.", + "disqus.com": "Disqus, Inc.", + "disquscdn.com": "Disqus, Inc.", + "districtm.io": "District M Inc.", + "districtm.ca": "District M Inc.", + "districtm.net": "District M Inc.", + "doubleverify.com": "DoubleVerify", + "adsymptotic.com": "Drawbridge Inc", + "drift.com": "Drift.com, Inc.", + "driftt.com": "Drift.com, Inc.", + "dstillery.com": "Dstillery Inc.", + "media6degrees.com": "Dstillery Inc.", + "dyntrk.com": "DynAdmic", + "dynamicyield.com": "Dynamic Yield", + "emarsys.com": "Emarsys eMarketing Systems AG", + "scarabresearch.com": "Emarsys eMarketing Systems AG", + "emxdgt.com": "Engine USA LLC", + "ensighten.com": "Ensighten, Inc", + "ebayadvertising.com": "Ensighten, Inc", + "nc0.co": "Ensighten, Inc", + "ixiaa.com": "Equifax Inc.", + "equifax.com": "Equifax Inc.", + "trustedid.com": "Equifax Inc.", + "optimahub.com": "Equifax Inc.", + "igodigital.com": "ExactTarget, LLC", + "fuelcdn.com": "ExactTarget, LLC", + "exoclick.com": "ExoClick, S.L.", + "exosrv.com": "ExoClick, S.L.", + "exdynsrv.com": "ExoClick, S.L.", + "dynsrvtyu.com": "ExoClick, S.L.", + "realsrv.com": "ExoClick, S.L.", + "dynsrvtbg.com": "ExoClick, S.L.", + "notifysrv.com": "ExoClick, S.L.", + "dynsrvazh.com": "ExoClick, S.L.", + "dynsrvazg.com": "ExoClick, S.L.", + "wpncdn.com": "ExoClick, S.L.", + "exponential.com": "Exponential Interactive Inc.", + "tribalfusion.com": "Exponential Interactive Inc.", + "eyeviewads.com": "EyeView, Inc.", + "ezoic.net": "Ezoic Inc.", + "usconstitution.net": "Ezoic Inc.", + "facebook.net": "Facebook, Inc.", + "facebook.com": "Facebook, Inc.", + "fbcdn.net": "Facebook, Inc.", + "cdninstagram.com": "Facebook, Inc.", + "instagram.com": "Facebook, Inc.", + "instagr.com": "Facebook, Inc.", + "instagr.am": "Facebook, Inc.", + "atdmt.com": "Facebook, Inc.", + "atdmt2.com": "Facebook, Inc.", + "atlassolutions.com": "Facebook, Inc.", + "atlassbx.com": "Facebook, Inc.", + "fbsbx.com": "Facebook, Inc.", + "accountkit.com": "Facebook, Inc.", + "fb.me": "Facebook, Inc.", + "fb.com": "Facebook, Inc.", + "whatsapp.com": "Facebook, Inc.", + "whatsapp.net": "Facebook, Inc.", + "thefind.com": "Facebook, Inc.", + "liverail.com": "Facebook, Inc.", + "reactjs.org": "Facebook, Inc.", + "messenger.com": "Facebook, Inc.", + "m.me": "Facebook, Inc.", + "oculus.com": "Facebook, Inc.", + "graphql.org": "Facebook, Inc.", + "flow.org": "Facebook, Inc.", + "flowtype.org": "Facebook, Inc.", + "fastg8.com": "FastG8", + "fg8dgt.com": "FastG8", + "commander1.com": "Fjord Technologies", + "tagcommander.com": "Fjord Technologies", + "foresee.com": "ForeSee Results, Inc.", + "4seeresults.com": "ForeSee Results, Inc.", + "foreseeresults.com": "ForeSee Results, Inc.", + "fwmrm.net": "FreeWheel", + "freewheel.tv": "FreeWheel", + "stickyadstv.com": "FreeWheel", + "fullstory.com": "FullStory", + "sentry.io": "Functional Software, Inc.", + "getsentry.com": "Functional Software, Inc.", + "ravenjs.com": "Functional Software, Inc.", + "sentry-cdn.com": "Functional Software, Inc.", + "gemius.pl": "Gemius S.A.", + "adhigh.net": "GetIntent", + "getintent.com": "GetIntent", + "getsitecontrol.com": "GetWebCraft Limited", + "googleapis.com": "Google LLC", + "googleapis.co": "Google LLC", + "google-analytics.com": "Google LLC", + "gstatic.com": "Google LLC", + "googletagmanager.com": "Google LLC", + "google.com": "Google LLC", + "googletagservices.com": "Google LLC", + "doubleclick.net": "Google LLC", + "googlesyndication.com": "Google LLC", + "googleweblight.com": "Google LLC", + "translate.goog": "Google LLC", + "unfiltered.news": "Google LLC", + "admeld.com": "Google LLC", + "adgoogle.net": "Google LLC", + "ytimg.com": "Google LLC", + "youtube.com": "Google LLC", + "googleadservices.com": "Google LLC", + "googleusercontent.com": "Google LLC", + "2mdn.net": "Google LLC", + "blogspot.com": "Google LLC", + "googledrive.com": "Google LLC", + "googlemail.com": "Google LLC", + "googlecommerce.com": "Google LLC", + "ggpht.com": "Google LLC", + "doubleclickusercontent.com": "Google LLC", + "blogger.com": "Google LLC", + "blogblog.com": "Google LLC", + "feedburner.com": "Google LLC", + "ampproject.org": "Google LLC", + "googlevideo.com": "Google LLC", + "appspot.com": "Google LLC", + "google.de": "Google LLC", + "1e100cdn.net": "Google LLC", + "google.fr": "Google LLC", + "anvato.net": "Google LLC", + "google.ch": "Google LLC", + "youtube-nocookie.com": "Google LLC", + "google.ca": "Google LLC", + "google.ru": "Google LLC", + "ampproject.net": "Google LLC", + "google.co.uk": "Google LLC", + "google.es": "Google LLC", + "googlecode.com": "Google LLC", + "google.se": "Google LLC", + "youtu.be": "Google LLC", + "android.com": "Google LLC", + "google.nl": "Google LLC", + "google.it": "Google LLC", + "goo.gl": "Google LLC", + "gmodules.com": "Google LLC", + "google.com.vn": "Google LLC", + "firebase.com": "Google LLC", + "google.co.in": "Google LLC", + "1emn.com": "Google LLC", + "getmdl.io": "Google LLC", + "google.com.au": "Google LLC", + "2enm.com": "Google LLC", + "google.co.jp": "Google LLC", + "google.pl": "Google LLC", + "google.at": "Google LLC", + "google.be": "Google LLC", + "google.com.ua": "Google LLC", + "cc-dt.com": "Google LLC", + "doubleclick.com": "Google LLC", + "g.co": "Google LLC", + "gvt2.com": "Google LLC", + "ggpht.cn": "Google LLC", + "google.ac": "Google LLC", + "google.ad": "Google LLC", + "google.af": "Google LLC", + "google.ag": "Google LLC", + "google.ai": "Google LLC", + "google.al": "Google LLC", + "google.am": "Google LLC", + "google.as": "Google LLC", + "google.az": "Google LLC", + "google.ba": "Google LLC", + "google.bf": "Google LLC", + "google.bi": "Google LLC", + "google.bj": "Google LLC", + "google.bs": "Google LLC", + "google.bt": "Google LLC", + "google.by": "Google LLC", + "google.cat": "Google LLC", + "google.cc": "Google LLC", + "google.cd": "Google LLC", + "google.cf": "Google LLC", + "google.cg": "Google LLC", + "google.ci": "Google LLC", + "google.cl": "Google LLC", + "google.cm": "Google LLC", + "google.co.ao": "Google LLC", + "google.co.bw": "Google LLC", + "google.co.ck": "Google LLC", + "google.co.cr": "Google LLC", + "google.co.hu": "Google LLC", + "google.co.im": "Google LLC", + "google.co.je": "Google LLC", + "google.co.ke": "Google LLC", + "google.co.ls": "Google LLC", + "google.co.ma": "Google LLC", + "google.co.mz": "Google LLC", + "google.co.nz": "Google LLC", + "google.co.th": "Google LLC", + "google.co.tz": "Google LLC", + "google.co.ug": "Google LLC", + "google.co.uz": "Google LLC", + "google.co.ve": "Google LLC", + "google.co.vi": "Google LLC", + "google.co.za": "Google LLC", + "google.co.zm": "Google LLC", + "google.co.zw": "Google LLC", + "google.com.af": "Google LLC", + "google.com.ag": "Google LLC", + "google.com.ai": "Google LLC", + "google.com.bd": "Google LLC", + "google.com.bh": "Google LLC", + "google.com.bn": "Google LLC", + "google.com.bo": "Google LLC", + "google.com.by": "Google LLC", + "google.com.bz": "Google LLC", + "google.com.cn": "Google LLC", + "google.com.co": "Google LLC", + "google.com.cu": "Google LLC", + "google.com.cy": "Google LLC", + "google.com.do": "Google LLC", + "google.com.ec": "Google LLC", + "google.com.eg": "Google LLC", + "google.com.et": "Google LLC", + "google.com.fj": "Google LLC", + "google.com.ge": "Google LLC", + "google.com.gh": "Google LLC", + "google.com.gi": "Google LLC", + "google.com.gr": "Google LLC", + "google.com.gt": "Google LLC", + "google.com.iq": "Google LLC", + "google.com.jo": "Google LLC", + "google.com.kh": "Google LLC", + "google.com.kw": "Google LLC", + "google.com.lb": "Google LLC", + "google.com.ly": "Google LLC", + "google.com.mm": "Google LLC", + "google.com.mt": "Google LLC", + "google.com.na": "Google LLC", + "google.com.nf": "Google LLC", + "google.com.ng": "Google LLC", + "google.com.ni": "Google LLC", + "google.com.np": "Google LLC", + "google.com.nr": "Google LLC", + "google.com.om": "Google LLC", + "google.com.pa": "Google LLC", + "google.com.pe": "Google LLC", + "google.com.pg": "Google LLC", + "google.com.ph": "Google LLC", + "google.com.pl": "Google LLC", + "google.com.pr": "Google LLC", + "google.com.py": "Google LLC", + "google.com.qa": "Google LLC", + "google.com.sb": "Google LLC", + "google.com.sl": "Google LLC", + "google.com.sv": "Google LLC", + "google.com.tj": "Google LLC", + "google.com.tn": "Google LLC", + "google.com.uy": "Google LLC", + "google.com.vc": "Google LLC", + "google.com.ve": "Google LLC", + "google.cv": "Google LLC", + "google.dj": "Google LLC", + "google.dm": "Google LLC", + "google.dz": "Google LLC", + "google.ee": "Google LLC", + "google.eus": "Google LLC", + "google.fm": "Google LLC", + "google.frl": "Google LLC", + "google.ga": "Google LLC", + "google.gal": "Google LLC", + "google.ge": "Google LLC", + "google.gl": "Google LLC", + "google.gm": "Google LLC", + "google.gp": "Google LLC", + "google.gy": "Google LLC", + "google.hk": "Google LLC", + "google.hn": "Google LLC", + "google.hr": "Google LLC", + "google.ht": "Google LLC", + "google.im": "Google LLC", + "google.in": "Google LLC", + "google.info": "Google LLC", + "google.iq": "Google LLC", + "google.ir": "Google LLC", + "google.is": "Google LLC", + "google.it.ao": "Google LLC", + "google.je": "Google LLC", + "google.jo": "Google LLC", + "google.jobs": "Google LLC", + "google.jp": "Google LLC", + "google.kg": "Google LLC", + "google.ki": "Google LLC", + "google.kz": "Google LLC", + "google.la": "Google LLC", + "google.li": "Google LLC", + "google.lk": "Google LLC", + "google.lu": "Google LLC", + "google.lv": "Google LLC", + "google.md": "Google LLC", + "google.me": "Google LLC", + "google.mg": "Google LLC", + "google.mk": "Google LLC", + "google.ml": "Google LLC", + "google.mn": "Google LLC", + "google.ms": "Google LLC", + "google.mu": "Google LLC", + "google.mv": "Google LLC", + "google.mw": "Google LLC", + "google.ne": "Google LLC", + "google.ne.jp": "Google LLC", + "google.net": "Google LLC", + "google.ng": "Google LLC", + "google.nr": "Google LLC", + "google.nu": "Google LLC", + "google.off.ai": "Google LLC", + "google.pk": "Google LLC", + "google.pn": "Google LLC", + "google.ps": "Google LLC", + "google.ro": "Google LLC", + "google.rw": "Google LLC", + "google.sc": "Google LLC", + "google.sh": "Google LLC", + "google.si": "Google LLC", + "google.sm": "Google LLC", + "google.so": "Google LLC", + "google.sr": "Google LLC", + "google.st": "Google LLC", + "google.td": "Google LLC", + "google.tel": "Google LLC", + "google.tg": "Google LLC", + "google.tk": "Google LLC", + "google.tl": "Google LLC", + "google.tm": "Google LLC", + "google.tn": "Google LLC", + "google.to": "Google LLC", + "google.tt": "Google LLC", + "google.ua": "Google LLC", + "google.us": "Google LLC", + "google.uz": "Google LLC", + "google.vg": "Google LLC", + "google.vu": "Google LLC", + "google.ws": "Google LLC", + "googleadapis.com": "Google LLC", + "googleadsserving.cn": "Google LLC", + "googleapis.cn": "Google LLC", + "googleusercontent.cn": "Google LLC", + "gstaticcnapps.cn": "Google LLC", + "youtubeeducation.com": "Google LLC", + "youtubekids.com": "Google LLC", + "yt.be": "Google LLC", + "emn0.com": "Google LLC", + "cloudfunctions.net": "Google LLC", + "firebaseapp.com": "Google LLC", + "google.com.br": "Google LLC", + "recaptcha.net": "Google LLC", + "invitemedia.com": "Google LLC", + "accurateshooter.net": "Google LLC", + "google.cn": "Google LLC", + "relaymedia.com": "Google LLC", + "golang.org": "Google LLC", + "googlesource.com": "Google LLC", + "em0n.com": "Google LLC", + "dmtry.com": "Google LLC", + "meebo.com": "Google LLC", + "firebaseio.com": "Google LLC", + "dialogflow.com": "Google LLC", + "chrome.com": "Google LLC", + "1enm.com": "Google LLC", + "google.co.id": "Google LLC", + "google.com.mx": "Google LLC", + "google.fi": "Google LLC", + "google.hu": "Google LLC", + "google.no": "Google LLC", + "google.pt": "Google LLC", + "8d1f.com": "Google LLC", + "e0mn.com": "Google LLC", + "mn0e.com": "Google LLC", + "chromium.org": "Google LLC", + "advertisercommunity.com": "Google LLC", + "advertiserscommunity.com": "Google LLC", + "apigee.net": "Google LLC", + "blog.google": "Google LLC", + "nest.com": "Google LLC", + "google.ae": "Google LLC", + "google.com.jm": "Google LLC", + "gvt1.com": "Google LLC", + "google.com.ar": "Google LLC", + "chromeexperiments.com": "Google LLC", + "goooglesyndication.com": "Google LLC", + "markerly.com": "Google LLC", + "0m66lx69dx.com": "Google LLC", + "ai.google": "Google LLC", + "google.gr": "Google LLC", + "google.sn": "Google LLC", + "googlefiber.net": "Google LLC", + "googleblog.com": "Google LLC", + "bazel.build": "Google LLC", + "fastlane.tools": "Google LLC", + "fabric.io": "Google LLC", + "urchin.com": "Google LLC", + "googleapps.com": "Google LLC", + "google.bg": "Google LLC", + "gstatic.cn": "Google LLC", + "google.co.kr": "Google LLC", + "google.com.tr": "Google LLC", + "google.com.tw": "Google LLC", + "google.cz": "Google LLC", + "google.dk": "Google LLC", + "google.lt": "Google LLC", + "google.sk": "Google LLC", + "google.com.pk": "Google LLC", + "google.com.sg": "Google LLC", + "googlegroups.com": "Google LLC", + "google.co.il": "Google LLC", + "socratic.org": "Google LLC", + "tensorflow.org": "Google LLC", + "material.io": "Google LLC", + "gmail.com": "Google LLC", + "waze.com": "Google LLC", + "kaggle.com": "Google LLC", + "flutter.io": "Google LLC", + "domains.google": "Google LLC", + "google.com.sa": "Google LLC", + "godoc.org": "Google LLC", + "google.com.my": "Google LLC", + "itasoftware.com": "Google LLC", + "elections.google": "Google LLC", + "google.ie": "Google LLC", + "dartlang.org": "Google LLC", + "withgoogle.com": "Google LLC", + "google.com.hk": "Google LLC", + "adsense.com": "Google LLC", + "grpc.io": "Google LLC", + "listentoyoutube.com": "Google LLC", + "admob.com": "Google LLC", + "google.rs": "Google LLC", + "shoppingil.co.il": "Google LLC", + "google.gg": "Google LLC", + "on2.com": "Google LLC", + "oneworldmanystories.com": "Google LLC", + "pagespeedmobilizer.com": "Google LLC", + "pageview.mobi": "Google LLC", + "partylikeits1986.org": "Google LLC", + "paxlicense.org": "Google LLC", + "pittpatt.com": "Google LLC", + "polymerproject.org": "Google LLC", + "postini.com": "Google LLC", + "projectara.com": "Google LLC", + "projectbaseline.com": "Google LLC", + "questvisual.com": "Google LLC", + "quiksee.com": "Google LLC", + "beatthatquote.com": "Google LLC", + "revolv.com": "Google LLC", + "ridepenguin.com": "Google LLC", + "bandpage.com": "Google LLC", + "saynow.com": "Google LLC", + "schemer.com": "Google LLC", + "screenwisetrends.com": "Google LLC", + "screenwisetrendspanel.com": "Google LLC", + "snapseed.com": "Google LLC", + "solveforx.com": "Google LLC", + "studywatchbyverily.com": "Google LLC", + "studywatchbyverily.org": "Google LLC", + "thecleversense.com": "Google LLC", + "thinkquarterly.co.uk": "Google LLC", + "thinkquarterly.com": "Google LLC", + "txcloud.net": "Google LLC", + "txvia.com": "Google LLC", + "useplannr.com": "Google LLC", + "v8project.org": "Google LLC", + "verily.com": "Google LLC", + "verilylifesciences.com": "Google LLC", + "verilystudyhub.com": "Google LLC", + "verilystudywatch.com": "Google LLC", + "verilystudywatch.org": "Google LLC", + "wallet.com": "Google LLC", + "waymo.com": "Google LLC", + "webappfieldguide.com": "Google LLC", + "weltweitwachsen.de": "Google LLC", + "whatbrowser.org": "Google LLC", + "womenwill.com": "Google LLC", + "womenwill.com.br": "Google LLC", + "womenwill.id": "Google LLC", + "womenwill.in": "Google LLC", + "womenwill.mx": "Google LLC", + "cookiechoices.org": "Google LLC", + "x.company": "Google LLC", + "x.team": "Google LLC", + "xn--9trs65b.com": "Google LLC", + "youtubemobilesupport.com": "Google LLC", + "zukunftswerkstatt.de": "Google LLC", + "dartsearch.net": "Google LLC", + "googleads.com": "Google LLC", + "cloudburstresearch.com": "Google LLC", + "cloudrobotics.com": "Google LLC", + "conscrypt.com": "Google LLC", + "conscrypt.org": "Google LLC", + "coova.com": "Google LLC", + "coova.net": "Google LLC", + "coova.org": "Google LLC", + "crr.com": "Google LLC", + "registry.google": "Google LLC", + "cs4hs.com": "Google LLC", + "debug.com": "Google LLC", + "debugproject.com": "Google LLC", + "design.google": "Google LLC", + "environment.google": "Google LLC", + "episodic.com": "Google LLC", + "fflick.com": "Google LLC", + "financeleadsonline.com": "Google LLC", + "flutterapp.com": "Google LLC", + "g-tun.com": "Google LLC", + "gerritcodereview.com": "Google LLC", + "getbumptop.com": "Google LLC", + "gipscorp.com": "Google LLC", + "globaledu.org": "Google LLC", + "gonglchuangl.net": "Google LLC", + "google.berlin": "Google LLC", + "google.org": "Google LLC", + "google.ventures": "Google LLC", + "googlecompare.co.uk": "Google LLC", + "googledanmark.com": "Google LLC", + "googlefinland.com": "Google LLC", + "googlemaps.com": "Google LLC", + "googlephotos.com": "Google LLC", + "googleplay.com": "Google LLC", + "googleplus.com": "Google LLC", + "googlesverige.com": "Google LLC", + "googletraveladservices.com": "Google LLC", + "googleventures.com": "Google LLC", + "gsrc.io": "Google LLC", + "gsuite.com": "Google LLC", + "hdrplusdata.org": "Google LLC", + "hindiweb.com": "Google LLC", + "howtogetmo.co.uk": "Google LLC", + "html5rocks.com": "Google LLC", + "hwgo.com": "Google LLC", + "impermium.com": "Google LLC", + "j2objc.org": "Google LLC", + "keytransparency.com": "Google LLC", + "keytransparency.foo": "Google LLC", + "keytransparency.org": "Google LLC", + "mdialog.com": "Google LLC", + "mfg-inspector.com": "Google LLC", + "mobileview.page": "Google LLC", + "moodstocks.com": "Google LLC", + "asp-cc.com": "Google LLC", + "near.by": "Google LLC", + "oauthz.com": "Google LLC", + "on.here": "Google LLC", + "adwords-community.com": "Google LLC", + "adwordsexpress.com": "Google LLC", + "angulardart.org": "Google LLC", + "api.ai": "Google LLC", + "baselinestudy.com": "Google LLC", + "baselinestudy.org": "Google LLC", + "blink.org": "Google LLC", + "brotli.org": "Google LLC", + "bumpshare.com": "Google LLC", + "bumptop.ca": "Google LLC", + "bumptop.com": "Google LLC", + "bumptop.net": "Google LLC", + "bumptop.org": "Google LLC", + "bumptunes.com": "Google LLC", + "campuslondon.com": "Google LLC", + "certificate-transparency.org": "Google LLC", + "chromecast.com": "Google LLC", + "quickoffice.com": "Google LLC", + "widevine.com": "Google LLC", + "appbridge.ca": "Google LLC", + "appbridge.io": "Google LLC", + "appbridge.it": "Google LLC", + "apture.com": "Google LLC", + "area120.com": "Google LLC", + "gumgum.com": "GumGum", + "heapanalytics.com": "Heap", + "heap.io": "Heap", + "hotjar.com": "Hotjar Ltd", + "hs-analytics.net": "HubSpot, Inc.", + "hs-scripts.com": "HubSpot, Inc.", + "hubspot.com": "HubSpot, Inc.", + "hsforms.net": "HubSpot, Inc.", + "hsleadflows.net": "HubSpot, Inc.", + "hsforms.com": "HubSpot, Inc.", + "hubspot.net": "HubSpot, Inc.", + "usemessages.com": "HubSpot, Inc.", + "hscollectedforms.net": "HubSpot, Inc.", + "hscta.net": "HubSpot, Inc.", + "hsadspixel.net": "HubSpot, Inc.", + "hubapi.com": "HubSpot, Inc.", + "hsappstatic.net": "HubSpot, Inc.", + "gettally.com": "HubSpot, Inc.", + "leadin.com": "HubSpot, Inc.", + "hubspotfeedback.com": "HubSpot, Inc.", + "minitab.com": "HubSpot, Inc.", + "li.me": "HubSpot, Inc.", + "hybrid.ai": "Hybrid Adtech, Inc.", + "consensu.org": "IAB Europe", + "id5-sync.com": "ID5 Technology Ltd", + "ioam.de": "INFOnline GmbH", + "iocnt.net": "INFOnline GmbH", + "onthe.io": "IO Technologies Inc.", + "bidswitch.net": "IPONWEB GmbH", + "mfadsrvr.com": "IPONWEB GmbH", + "bidswitch.com": "IPONWEB GmbH", + "iponweb.com": "IPONWEB GmbH", + "iponweb.net": "IPONWEB GmbH", + "netmng.com": "IgnitionOne, LLC", + "apxprogrammatic.com": "IgnitionOne, LLC", + "ojrq.net": "Impact Radius", + "sjv.io": "Impact Radius", + "evyy.net": "Impact Radius", + "r7ls.net": "Impact Radius", + "pxf.io": "Impact Radius", + "impactradius.com": "Impact Radius", + "impactradius-event.com": "Impact Radius", + "incapdns.net": "Imperva Inc.", + "incapsula.com": "Imperva Inc.", + "distilnetworks.com": "Imperva Inc.", + "distil.us": "Imperva Inc.", + "distiltag.com": "Imperva Inc.", + "areyouahuman.com": "Imperva Inc.", + "improvedigital.com": "Improve Digital BV", + "360yield.com": "Improve Digital BV", + "casalemedia.com": "Index Exchange, Inc.", + "indexww.com": "Index Exchange, Inc.", + "innovid.com": "Innovid Media", + "inspectlet.com": "Inspectlet", + "adsafeprotected.com": "Integral Ad Science, Inc.", + "iasds01.com": "Integral Ad Science, Inc.", + "intentiq.com": "Intent IQ, LLC", + "ibillboard.com": "Internet Billboard a.s.", + "bbelements.com": "Internet Billboard a.s.", + "ero-advertising.com": "Interwebvertising B.V.", + "ivitrack.com": "Ividence", + "korrelate.net": "J.D. Power", + "jdpower.com": "J.D. Power", + "nadaguides.com": "J.D. Power", + "nada.com": "J.D. Power", + "tns-counter.ru": "JSC ADFACT", + "juicyads.com": "JuicyAds", + "justpremium.com": "JustPremium", + "justpremium.nl": "JustPremium", + "ib-ibi.com": "KBM Group LLC", + "insightexpressai.com": "Kantar Operations", + "kargo.com": "Kargo Global, Inc.", + "xg4ken.com": "Kenshoo TLD", + "keywee.co": "Keywee", + "klaviyo.com": "Klaviyo", + "adriver.ru": "LLC Internest-holding", + "soloway.ru": "LLC Internest-holding", + "mail.ru": "LLC Mail.Ru", + "list.ru": "LLC Mail.Ru", + "ok.ru": "LLC Mail.Ru", + "mycdn.me": "LLC Mail.Ru", + "imgsmail.ru": "LLC Mail.Ru", + "odnoklassniki.ru": "LLC Mail.Ru", + "mradx.net": "LLC Mail.Ru", + "gmru.net": "LLC Mail.Ru", + "youla.ru": "LLC Mail.Ru", + "linkedin.com": "LinkedIn Corporation", + "licdn.com": "LinkedIn Corporation", + "bizographics.com": "LinkedIn Corporation", + "slidesharecdn.com": "LinkedIn Corporation", + "slideshare.net": "LinkedIn Corporation", + "lynda.com": "LinkedIn Corporation", + "video2brain.com": "LinkedIn Corporation", + "listrak.com": "Listrak", + "listrakbi.com": "Listrak", + "livechatinc.com": "LiveChat Inc", + "livechatinc.net": "LiveChat Inc", + "helpdesk.com": "LiveChat Inc", + "chatbot.com": "LiveChat Inc", + "knowledgebase.ai": "LiveChat Inc", + "chat.io": "LiveChat Inc", + "botengine.ai": "LiveChat Inc", + "liadm.com": "LiveIntent Inc.", + "liveintent.com": "LiveIntent Inc.", + "liveperson.net": "LivePerson, Inc", + "lpsnmedia.net": "LivePerson, Inc", + "liveramp.com": "LiveRamp", + "pippio.com": "LiveRamp", + "arbor.io": "LiveRamp", + "circulate.com": "LiveRamp", + "faktor.io": "LiveRamp", + "abtasty.com": "Liwio", + "lockerdome.com": "LockerDome, LLC", + "lockerdomecdn.com": "LockerDome, LLC", + "loopme.com": "LoopMe Ltd", + "loopme.me": "LoopMe Ltd", + "crwdcntrl.net": "Lotame Solutions, Inc.", + "lotame.com": "Lotame Solutions, Inc.", + "mgid.com": "MGID Inc", + "domdex.com": "Magnetic Media Online, Inc.", + "marinsm.com": "Marin Software Inc.", + "prfct.co": "Marin Software Inc.", + "mysocialpixel.com": "Marin Software Inc.", + "perfectaudience.com": "Marin Software Inc.", + "marketo.net": "Marketo, Inc.", + "marketo.com": "Marketo, Inc.", + "mktoresp.com": "Marketo, Inc.", + "maxmind.com": "MaxMind Inc.", + "medallia.com": "Medallia Inc.", + "medallia.eu": "Medallia Inc.", + "kampyle.com": "Medallia Inc.", + "media.net": "Media.net Advertising FZ-LLC", + "mathtag.com": "MediaMath, Inc.", + "mediawallahscript.com": "MediaWallah LLC", + "mediavine.com": "Mediavine, Inc.", + "thehollywoodgossip.com": "Mediavine, Inc.", + "tvfanatic.com": "Mediavine, Inc.", + "moviefanatic.com": "Mediavine, Inc.", + "mxcdn.net": "Meetrics GmbH", + "meetrics.de": "Meetrics GmbH", + "meetrics.com": "Meetrics GmbH", + "meetrics.net": "Meetrics GmbH", + "research.de.com": "Meetrics GmbH", + "rkdms.com": "Merkle Inc", + "merklesearch.com": "Merkle Inc", + "seancodycontent.com": "MindGeek", + "seancody.com": "MindGeek", + "dplaygroundcontent.com": "MindGeek", + "digitalplayground.com": "MindGeek", + "realitykingscontent.com": "MindGeek", + "realitykings.com": "MindGeek", + "redtube.com": "MindGeek", + "men.com": "MindGeek", + "mencontent.com": "MindGeek", + "fakehub.com": "MindGeek", + "transangels.com": "MindGeek", + "momxxx.com": "MindGeek", + "faketaxi.com": "MindGeek", + "phncdn.com": "MindGeek", + "pornhub.com": "MindGeek", + "ypncdn.com": "MindGeek", + "youporn.com": "MindGeek", + "t8cdn.com": "MindGeek", + "rdtcdn.com": "MindGeek", + "tube8.com": "MindGeek", + "xtube.com": "MindGeek", + "youporngay.com": "MindGeek", + "gaytube.com": "MindGeek", + "redtube.com.br": "MindGeek", + "pornmd.com": "MindGeek", + "hubtraffic.com": "MindGeek", + "thumbzilla.com": "MindGeek", + "pornhubselect.com": "MindGeek", + "pornhubpremium.com": "MindGeek", + "modelhub.com": "MindGeek", + "contentabc.com": "MindGeek", + "etahub.com": "MindGeek", + "brazzerscontent.com": "MindGeek", + "brazzers.com": "MindGeek", + "mofos.com": "MindGeek", + "mofoscontent.com": "MindGeek", + "babescontent.com": "MindGeek", + "twistyscontent.com": "MindGeek", + "babes.com": "MindGeek", + "twistys.com": "MindGeek", + "trafficjunky.net": "MindGeek", + "trafficjunky.com": "MindGeek", + "adultforce.com": "MindGeek", + "mxpnl.com": "Mixpanel, Inc.", + "mxpnl.net": "Mixpanel, Inc.", + "mixpanel.com": "Mixpanel, Inc.", + "monetate.net": "Monetate, Inc.", + "mouseflow.com": "Mouseflow", + "movableink.com": "Movable Ink", + "micpn.com": "Movable Ink", + "my6sense.com": "My6sense Inc.", + "mynativeplatform.com": "My6sense Inc.", + "postrelease.com": "Nativo, Inc", + "ntv.io": "Nativo, Inc", + "nativo.net": "Nativo, Inc", + "navdmp.com": "Navegg S.A.", + "agkn.com": "Neustar, Inc.", + "neustar.biz": "Neustar, Inc.", + "comal.tx.us": "Neustar, Inc.", + "contra-costa.ca.us": "Neustar, Inc.", + "ultratools.com": "Neustar, Inc.", + "berks.pa.us": "Neustar, Inc.", + "washington.mn.us": "Neustar, Inc.", + "forsyth.nc.us": "Neustar, Inc.", + "newrelic.com": "New Relic", + "nr-data.net": "New Relic", + "kark.com": "Nexstar Media Group", + "fox16.com": "Nexstar Media Group", + "nwahomepage.com": "Nexstar Media Group", + "yashi.com": "Nexstar Media Group", + "channel4000.com": "Nexstar Media Group", + "cbs17.com": "Nexstar Media Group", + "lasvegasnow.com": "Nexstar Media Group", + "localsyr.com": "Nexstar Media Group", + "rochesterfirst.com": "Nexstar Media Group", + "lakana.com": "Nexstar Media Group", + "lkqd.net": "Nexstar Media Group", + "ninthdecimal.com": "NinthDecimal, Inc", + "yadro.ru": "OOO ECO PC - Complex Solutions", + "mediametrics.ru": "OOO ECO PC - Complex Solutions", + "brealtime.com": "ORC International", + "clrstm.com": "ORC International", + "olark.com": "Olark", + "onesignal.com": "OneSignal", + "os.tc": "OneSignal", + "openx.net": "OpenX Technologies Inc", + "openx.com": "OpenX Technologies Inc", + "openx.org": "OpenX Technologies Inc", + "openxadexchange.com": "OpenX Technologies Inc", + "servedbyopenx.com": "OpenX Technologies Inc", + "jump-time.net": "OpenX Technologies Inc", + "deliverimp.com": "OpenX Technologies Inc", + "mezzobit.com": "OpenX Technologies Inc", + "pixfuture.net": "OpenX Technologies Inc", + "godengo.com": "OpenX Technologies Inc", + "pubnation.com": "OpenX Technologies Inc", + "addthis.com": "Oracle Corporation", + "addthisedge.com": "Oracle Corporation", + "bluekai.com": "Oracle Corporation", + "nexac.com": "Oracle Corporation", + "bkrtx.com": "Oracle Corporation", + "moatads.com": "Oracle Corporation", + "moat.com": "Oracle Corporation", + "moatpixel.com": "Oracle Corporation", + "eloqua.com": "Oracle Corporation", + "en25.com": "Oracle Corporation", + "maxymiser.net": "Oracle Corporation", + "bronto.com": "Oracle Corporation", + "univide.com": "Oracle Corporation", + "bm23.com": "Oracle Corporation", + "custhelp.com": "Oracle Corporation", + "atgsvcs.com": "Oracle Corporation", + "rightnowtech.com": "Oracle Corporation", + "oraclecloud.com": "Oracle Corporation", + "responsys.net": "Oracle Corporation", + "adrsp.net": "Oracle Corporation", + "oracleoutsourcing.com": "Oracle Corporation", + "estara.com": "Oracle Corporation", + "oracleimg.com": "Oracle Corporation", + "oracle.com": "Oracle Corporation", + "addthiscdn.com": "Oracle Corporation", + "mysql.com": "Oracle Corporation", + "netsuite.com": "Oracle Corporation", + "q-go.net": "Oracle Corporation", + "virtualbox.org": "Oracle Corporation", + "clearspring.com": "Oracle Corporation", + "livelook.com": "Oracle Corporation", + "compendium.com": "Oracle Corporation", + "compendiumblog.com": "Oracle Corporation", + "java.net": "Oracle Corporation", + "java.com": "Oracle Corporation", + "netbeans.org": "Oracle Corporation", + "homeip.net": "Oracle Corporation", + "grapeshot.co.uk": "Oracle Corporation", + "outbrain.com": "Outbrain", + "zemanta.com": "Outbrain", + "owneriq.net": "OwnerIQ Inc", + "manualsonline.com": "OwnerIQ Inc", + "playground.xyz": "PLAYGROUND XYZ", + "pagefair.com": "PageFair Limited", + "pagefair.net": "PageFair Limited", + "parsely.com": "Parsely, Inc.", + "parse.ly": "Parsely, Inc.", + "ywxi.net": "PathDefender", + "trustedsite.com": "PathDefender", + "paypalobjects.com": "PayPal, Inc.", + "paypal.com": "PayPal, Inc.", + "braintreegateway.com": "PayPal, Inc.", + "where.com": "PayPal, Inc.", + "braintree-api.com": "PayPal, Inc.", + "venmo.com": "PayPal, Inc.", + "s-xoom.com": "PayPal, Inc.", + "paypal-community.com": "PayPal, Inc.", + "xoom.com": "PayPal, Inc.", + "paypal-prepaid.com": "PayPal, Inc.", + "paypal-brasil.com.br": "PayPal, Inc.", + "paypal.co.uk": "PayPal, Inc.", + "paypal.at": "PayPal, Inc.", + "paypal.be": "PayPal, Inc.", + "paypal.ca": "PayPal, Inc.", + "paypal.ch": "PayPal, Inc.", + "paypal.cl": "PayPal, Inc.", + "paypal.cn": "PayPal, Inc.", + "paypal.co": "PayPal, Inc.", + "paypal.co.id": "PayPal, Inc.", + "paypal.co.il": "PayPal, Inc.", + "paypal.co.in": "PayPal, Inc.", + "paypal.co.kr": "PayPal, Inc.", + "paypal.co.nz": "PayPal, Inc.", + "paypal.co.th": "PayPal, Inc.", + "paypal.co.za": "PayPal, Inc.", + "paypal.com.ar": "PayPal, Inc.", + "paypal.com.au": "PayPal, Inc.", + "paypal.com.br": "PayPal, Inc.", + "paypal.com.hk": "PayPal, Inc.", + "paypal.com.mx": "PayPal, Inc.", + "paypal.com.my": "PayPal, Inc.", + "paypal.com.pe": "PayPal, Inc.", + "paypal.com.pt": "PayPal, Inc.", + "paypal.com.sa": "PayPal, Inc.", + "paypal.com.sg": "PayPal, Inc.", + "paypal.com.tr": "PayPal, Inc.", + "paypal.com.tw": "PayPal, Inc.", + "paypal.com.ve": "PayPal, Inc.", + "paypal.de": "PayPal, Inc.", + "paypal.dk": "PayPal, Inc.", + "paypal.es": "PayPal, Inc.", + "paypal.eu": "PayPal, Inc.", + "paypal.fi": "PayPal, Inc.", + "paypal.fr": "PayPal, Inc.", + "paypal.ie": "PayPal, Inc.", + "paypal.it": "PayPal, Inc.", + "paypal.jp": "PayPal, Inc.", + "paypal.lu": "PayPal, Inc.", + "paypal.nl": "PayPal, Inc.", + "paypal.no": "PayPal, Inc.", + "paypal.ph": "PayPal, Inc.", + "paypal.pl": "PayPal, Inc.", + "paypal.pt": "PayPal, Inc.", + "paypal.ru": "PayPal, Inc.", + "paypal.se": "PayPal, Inc.", + "paypal.vn": "PayPal, Inc.", + "paypal-deutschland.de": "PayPal, Inc.", + "paypal-forward.com": "PayPal, Inc.", + "paypal-france.fr": "PayPal, Inc.", + "paypal-latam.com": "PayPal, Inc.", + "paypal-marketing.pl": "PayPal, Inc.", + "paypal-mena.com": "PayPal, Inc.", + "paypal-nakit.com": "PayPal, Inc.", + "paypal-prepagata.com": "PayPal, Inc.", + "thepaypalblog.com": "PayPal, Inc.", + "paypal.me": "PayPal, Inc.", + "paypal-information.com": "PayPal, Inc.", + "paypal-apps.com": "PayPal, Inc.", + "paypalbenefits.com": "PayPal, Inc.", + "paypal-knowledge.com": "PayPal, Inc.", + "paypal-knowledge-test.com": "PayPal, Inc.", + "perfectmarket.com": "Perfect Market, Inc.", + "permutive.com": "Permutive, Inc.", + "npttech.com": "Piano Software", + "piano.io": "Piano Software", + "tinypass.com": "Piano Software", + "pingdom.net": "Pingdom AB", + "pingdom.com": "Pingdom AB", + "creative-serving.com": "Platform161", + "platform161.com": "Platform161", + "p161.net": "Platform161", + "powerlinks.com": "PowerLinks Media Limited", + "pswec.com": "Proclivity Media, Inc.", + "proclivitysystems.com": "Proclivity Media, Inc.", + "rtmark.net": "Propeller Ads", + "propellerads.com": "Propeller Ads", + "propellerclick.com": "Propeller Ads", + "pubmatic.com": "PubMatic, Inc.", + "contextweb.com": "Pulsepoint, Inc.", + "bluecava.com": "QBC Holdings, Inc.", + "qualaroo.com": "Qualaroo", + "qualtrics.com": "Qualtrics, LLC", + "quantserve.com": "Quantcast Corporation", + "quantcount.com": "Quantcast Corporation", + "quantcast.com": "Quantcast Corporation", + "apextag.com": "Quantcast Corporation", + "quora.com": "Quora", + "quoracdn.net": "Quora", + "rundsp.com": "RUN", + "runadtag.com": "RUN", + "rakuten.co.jp": "Rakuten, Inc.", + "r10s.jp": "Rakuten, Inc.", + "rakuten-static.com": "Rakuten, Inc.", + "rakuten.com": "Rakuten, Inc.", + "fril.jp": "Rakuten, Inc.", + "infoseek.co.jp": "Rakuten, Inc.", + "rpaas.net": "Rakuten, Inc.", + "r10s.com": "Rakuten, Inc.", + "rakuten.fr": "Rakuten, Inc.", + "rakuten.ne.jp": "Rakuten, Inc.", + "rakuten-card.co.jp": "Rakuten, Inc.", + "kobo.com": "Rakuten, Inc.", + "linksynergy.com": "Rakuten, Inc.", + "nxtck.com": "Rakuten, Inc.", + "mediaforge.com": "Rakuten, Inc.", + "rmtag.com": "Rakuten, Inc.", + "dc-storm.com": "Rakuten, Inc.", + "jrs5.com": "Rakuten, Inc.", + "rakutenmarketing.com": "Rakuten, Inc.", + "rambler.ru": "Rambler Internet Holding, LLC", + "top100.ru": "Rambler Internet Holding, LLC", + "rnet.plus": "Rambler Internet Holding, LLC", + "rl0.ru": "Rambler Internet Holding, LLC", + "rambler.su": "Rambler Internet Holding, LLC", + "dsp-rambler.ru": "Rambler Internet Holding, LLC", + "rambler-co.ru": "Rambler Internet Holding, LLC", + "reddit.com": "Reddit Inc.", + "redditstatic.com": "Reddit Inc.", + "redditmedia.com": "Reddit Inc.", + "redd.it": "Reddit Inc.", + "redditinc.com": "Reddit Inc.", + "reson8.com": "Resonate Networks", + "resonate.com": "Resonate Networks", + "optinmonster.com": "Retyp LLC", + "optnmstr.com": "Retyp LLC", + "opmnstr.com": "Retyp LLC", + "optmnstr.com": "Retyp LLC", + "optmstr.com": "Retyp LLC", + "revcontent.com": "RevContent, LLC", + "revjet.com": "RevJet", + "1rx.io": "RhythmOne", + "burstnet.com": "RhythmOne", + "allmusic.com": "RhythmOne", + "sidereel.com": "RhythmOne", + "allmovie.com": "RhythmOne", + "rhythmone.com": "RhythmOne", + "yumenetworks.com": "RhythmOne", + "yume.com": "RhythmOne", + "po.st": "RhythmOne", + "gwallet.com": "RhythmOne", + "rfihub.com": "Rocket Fuel Inc.", + "rfihub.net": "Rocket Fuel Inc.", + "ru4.com": "Rocket Fuel Inc.", + "getclicky.com": "Roxr Software Ltd", + "rutarget.ru": "RuTarget LLC", + "itsup.com": "SCTR Services LLC", + "sail-horizon.com": "Sailthru, Inc", + "sail-personalize.com": "Sailthru, Inc", + "sailthru.com": "Sailthru, Inc", + "sail-track.com": "Sailthru, Inc", + "krxd.net": "Salesforce.com, Inc.", + "cquotient.com": "Salesforce.com, Inc.", + "salesforceliveagent.com": "Salesforce.com, Inc.", + "pardot.com": "Salesforce.com, Inc.", + "force.com": "Salesforce.com, Inc.", + "salesforce.com": "Salesforce.com, Inc.", + "desk.com": "Salesforce.com, Inc.", + "exacttarget.com": "Salesforce.com, Inc.", + "exct.net": "Salesforce.com, Inc.", + "brighteroption.com": "Salesforce.com, Inc.", + "semver.io": "Salesforce.com, Inc.", + "cloudforce.com": "Salesforce.com, Inc.", + "database.com": "Salesforce.com, Inc.", + "lightning.com": "Salesforce.com, Inc.", + "salesforce-communities.com": "Salesforce.com, Inc.", + "visualforce.com": "Salesforce.com, Inc.", + "documentforce.com": "Salesforce.com, Inc.", + "forceusercontent.com": "Salesforce.com, Inc.", + "sfdcstatic.com": "Salesforce.com, Inc.", + "chatter.com": "Salesforce.com, Inc.", + "data.com": "Salesforce.com, Inc.", + "site.com": "Salesforce.com, Inc.", + "dreamforce.com": "Salesforce.com, Inc.", + "quotable.com": "Salesforce.com, Inc.", + "einstein.com": "Salesforce.com, Inc.", + "heywire.com": "Salesforce.com, Inc.", + "beyondcore.com": "Salesforce.com, Inc.", + "twinprime.com": "Salesforce.com, Inc.", + "gravitytank.com": "Salesforce.com, Inc.", + "krux.com": "Salesforce.com, Inc.", + "sequence.com": "Salesforce.com, Inc.", + "metamind.io": "Salesforce.com, Inc.", + "salesforceiq.com": "Salesforce.com, Inc.", + "relateiq.com": "Salesforce.com, Inc.", + "marketingcloud.com": "Salesforce.com, Inc.", + "steelbrick.com": "Salesforce.com, Inc.", + "radian6.com": "Salesforce.com, Inc.", + "buddymedia.com": "Salesforce.com, Inc.", + "social.com": "Salesforce.com, Inc.", + "demandware.com": "Salesforce.com, Inc.", + "cotweet.com": "Salesforce.com, Inc.", + "salesforcemarketingcloud.com": "Salesforce.com, Inc.", + "weinvoiceit.com": "Salesforce.com, Inc.", + "cloudcraze.com": "Salesforce.com, Inc.", + "attic.io": "Salesforce.com, Inc.", + "sforce.com": "Salesforce.com, Inc.", + "govforce.com": "Salesforce.com, Inc.", + "appexchange.com": "Salesforce.com, Inc.", + "appcloud.com": "Salesforce.com, Inc.", + "segment.com": "Segment.io, Inc.", + "segment.io": "Segment.io, Inc.", + "semasio.com": "Semasio GmbH", + "semasio.net": "Semasio GmbH", + "sessioncam.com": "SessionCam Ltd", + "sharethis.com": "ShareThis, Inc", + "shareaholic.com": "Shareaholic Inc", + "sharethrough.com": "Sharethrough, Inc.", + "shareth.ru": "Sharethrough, Inc.", + "btstatic.com": "Signal Digital, Inc.", + "yjtag.jp": "Signal Digital, Inc.", + "thebrighttag.com": "Signal Digital, Inc.", + "flashtalking.com": "Simplicity Marketing", + "simpli.fi": "Simplifi Holdings Inc.", + "siteimprove.com": "Siteimprove A/S", + "siteimproveanalytics.com": "Siteimprove A/S", + "siteimproveanalytics.io": "Siteimprove A/S", + "siteimprove.net": "Siteimprove A/S", + "smaato.net": "Smaato Inc.", + "smartadserver.com": "Smartadserver S.A.S", + "sascdn.com": "Smartadserver S.A.S", + "sc-static.net": "Snapchat, Inc.", + "snapchat.com": "Snapchat, Inc.", + "bitmoji.com": "Snapchat, Inc.", + "deployads.com": "Snapsort Inc.", + "cpuboss.com": "Snapsort Inc.", + "gpuboss.com": "Snapsort Inc.", + "snapsort.com": "Snapsort Inc.", + "carsort.com": "Snapsort Inc.", + "sojern.com": "Sojern, Inc.", + "mobileadtrading.com": "Somo Audience Corp", + "sonobi.com": "Sonobi, Inc", + "sovrnlabs.net": "Sovrn Holdings", + "sovrn.com": "Sovrn Holdings", + "lijit.com": "Sovrn Holdings", + "viglink.com": "Sovrn Holdings", + "s-onetag.com": "Sovrn Holdings", + "spotx.tv": "SpotX, Inc.", + "spotxcdn.com": "SpotX, Inc.", + "spotxchange.com": "SpotX, Inc.", + "springserve.com": "SpringServe, LLC", + "springserve.net": "SpringServe, LLC", + "statcounter.com": "StatCounter", + "steelhousemedia.com": "Steel House, Inc", + "storygize.com": "Storygize", + "storygize.net": "Storygize", + "adscale.de": "Ströer Group", + "m6r.eu": "Ströer Group", + "stroeerdigitalgroup.de": "Ströer Group", + "stroeerdigitalmedia.de": "Ströer Group", + "interactivemedia.net": "Ströer Group", + "stroeerdp.de": "Ströer Group", + "stroeermediabrands.de": "Ströer Group", + "sumo.com": "Sumo Group", + "sundaysky.com": "SundaySky Ltd.", + "socdm.com": "Supership Inc", + "survata.com": "Survata, Inc.", + "technoratimedia.com": "Synacor, Inc.", + "synacor.com": "Synacor, Inc.", + "syn-api.com": "Synacor, Inc.", + "syn-cdn.com": "Synacor, Inc.", + "zimbra.org": "Synacor, Inc.", + "technorati.com": "Synacor, Inc.", + "bizrate.com": "Synapse Group, Inc.", + "bizrateinsights.com": "Synapse Group, Inc.", + "tvsquared.com": "TVSquared", + "taboola.com": "Taboola.com LTD", + "taboolasyndication.com": "Taboola.com LTD", + "zorosrv.com": "Taboola.com LTD", + "admailtiser.com": "Taboola.com LTD", + "basebanner.com": "Taboola.com LTD", + "vidfuture.com": "Taboola.com LTD", + "cmbestsrv.com": "Taboola.com LTD", + "convertmedia.com": "Taboola.com LTD", + "tapad.com": "Tapad, Inc.", + "teads.tv": "Teads ( Luxenbourg ) SA", + "ebz.io": "Teads ( Luxenbourg ) SA", + "tiqcdn.com": "Tealium Inc.", + "tealium.com": "Tealium Inc.", + "tealiumiq.com": "Tealium Inc.", + "tremorhub.com": "Telaria", + "imrworldwide.com": "The Nielsen Company", + "nielsen.com": "The Nielsen Company", + "exelator.com": "The Nielsen Company", + "exelate.com": "The Nielsen Company", + "visualdna.com": "The Nielsen Company", + "vdna-assets.com": "The Nielsen Company", + "myvisualiq.net": "The Nielsen Company", + "visualiq.com": "The Nielsen Company", + "visualiq.de": "The Nielsen Company", + "visualiq.fr": "The Nielsen Company", + "chimpstatic.com": "The Rocket Science Group, LLC", + "mailchimp.com": "The Rocket Science Group, LLC", + "mailchi.mp": "The Rocket Science Group, LLC", + "list-manage.com": "The Rocket Science Group, LLC", + "mailchimpapp.com": "The Rocket Science Group, LLC", + "eep.io": "The Rocket Science Group, LLC", + "rubiconproject.com": "The Rubicon Project, Inc.", + "chango.com": "The Rubicon Project, Inc.", + "adsrvr.org": "The Trade Desk Inc", + "thrtle.com": "Throtle", + "popads.net": "Tomksoft S.A.", + "trafficstars.com": "Traffic Stars", + "tsyndicate.com": "Traffic Stars", + "videohub.tv": "Tremor Video DSP", + "scanscout.com": "Tremor Video DSP", + "tremormedia.com": "Tremor Video DSP", + "triplelift.com": "TripleLift", + "3lift.com": "TripleLift", + "truefitcorp.com": "True Fit Corporation", + "truefit.com": "True Fit Corporation", + "truste.com": "TrustArc Inc.", + "trustarc.com": "TrustArc Inc.", + "trustedshops.com": "Trusted Shops GmbH", + "trustedshops.de": "Trusted Shops GmbH", + "trustpilot.net": "Trustpilot A/S", + "trustpilot.com": "Trustpilot A/S", + "turn.com": "Turn Inc.", + "twitter.com": "Twitter, Inc.", + "twimg.com": "Twitter, Inc.", + "t.co": "Twitter, Inc.", + "twttr.net": "Twitter, Inc.", + "twttr.com": "Twitter, Inc.", + "ads-twitter.com": "Twitter, Inc.", + "vine.co": "Twitter, Inc.", + "pscp.tv": "Twitter, Inc.", + "cms-twdigitalassets.com": "Twitter, Inc.", + "periscope.tv": "Twitter, Inc.", + "twittercommunity.com": "Twitter, Inc.", + "twitter.fr": "Twitter, Inc.", + "unbounce.com": "Unbounce", + "ubembed.com": "Unbounce", + "undertone.com": "Undertone Networks", + "unrulymedia.com": "Unruly Group Limited", + "usabilla.com": "Usabilla B.V.", + "vk.com": "V Kontakte LLC", + "userapi.com": "V Kontakte LLC", + "vk.me": "V Kontakte LLC", + "vkontakte.com": "V Kontakte LLC", + "vkontakte.ru": "V Kontakte LLC", + "vk.cc": "V Kontakte LLC", + "brand.net": "Valassis Digital", + "mxptint.net": "Valassis Digital", + "valassisdigital.com": "Valassis Digital", + "valassis.eu": "Valassis Digital", + "valassis.com": "Valassis Digital", + "yahoo.com": "Verizon Media", + "yimg.com": "Verizon Media", + "adtechus.com": "Verizon Media", + "adtechjp.com": "Verizon Media", + "oath.com": "Verizon Media", + "yahooapis.com": "Verizon Media", + "btrll.com": "Verizon Media", + "adtech.de": "Verizon Media", + "aolcdn.com": "Verizon Media", + "atwola.com": "Verizon Media", + "convertro.com": "Verizon Media", + "bluelithium.com": "Verizon Media", + "brightroll.com": "Verizon Media", + "yieldmanager.com": "Verizon Media", + "yahoodns.net": "Verizon Media", + "rivals.com": "Verizon Media", + "mapquestapi.com": "Verizon Media", + "mapquest.com": "Verizon Media", + "hostingprod.com": "Verizon Media", + "5min.com": "Verizon Media", + "techcrunch.com": "Verizon Media", + "techcrunch.cn": "Verizon Media", + "huffingtonpost.de": "Verizon Media", + "huffingtonpost.fr": "Verizon Media", + "huffingtonpost.it": "Verizon Media", + "huffingtonpost.jp": "Verizon Media", + "huffingtonpost.kr": "Verizon Media", + "huffingtonpost.es": "Verizon Media", + "huffingtonpost.co.za": "Verizon Media", + "huffingtonpost.com.au": "Verizon Media", + "huffingtonpost.com.mx": "Verizon Media", + "huffingtonpost.gr": "Verizon Media", + "pictela.net": "Verizon Media", + "tumblr.com": "Verizon Media", + "pulsemgr.com": "Verizon Media", + "huffpost.com": "Verizon Media", + "huffpo.com": "Verizon Media", + "huffpost.co.uk": "Verizon Media", + "huffpost.de": "Verizon Media", + "huffpost.gr": "Verizon Media", + "huffpost.kr": "Verizon Media", + "huffingtonpost.com": "Verizon Media", + "aolp.jp": "Verizon Media", + "advertising.com": "Verizon Media", + "blogsmithmedia.com": "Verizon Media", + "nexage.com": "Verizon Media", + "adap.tv": "Verizon Media", + "aol.com": "Verizon Media", + "mqcdn.com": "Verizon Media", + "aol.co.uk": "Verizon Media", + "aol.jp": "Verizon Media", + "pollster.com": "Verizon Media", + "teamaol.com": "Verizon Media", + "aol.ca": "Verizon Media", + "ryot.org": "Verizon Media", + "ryotlab.com": "Verizon Media", + "ryotstudio.com": "Verizon Media", + "ryotstudio.co.uk": "Verizon Media", + "adsonar.com": "Verizon Media", + "stylelist.com": "Verizon Media", + "autoblog.com": "Verizon Media", + "sre-perim.com": "Verizon Media", + "vidible.tv": "Verizon Media", + "lexity.com": "Verizon Media", + "yahoo.net": "Verizon Media", + "netscape.com": "Verizon Media", + "huffingtonpost.ca": "Verizon Media", + "tecnoactual.net": "Verizon Media", + "engadget.com": "Verizon Media", + "huffingtonpost.co.uk": "Verizon Media", + "geocities.com": "Verizon Media", + "yahoosmallbusiness.com": "Verizon Media", + "luminate.com": "Verizon Media", + "tastefullyoffensive.com": "Verizon Media", + "zenfs.com": "Verizon Media", + "videovore.com": "Verizon Media", + "aol.de": "Verizon Media", + "aol.fr": "Verizon Media", + "golocal.guru": "Verizon Media", + "aabacosmallbusiness.com": "Verizon Media", + "wow.com": "Verizon Media", + "24-7.pet": "Verizon Media", + "247.vacations": "Verizon Media", + "anyprice.com": "Verizon Media", + "autos24-7.com": "Verizon Media", + "autos.parts": "Verizon Media", + "baby.guide": "Verizon Media", + "chowist.com": "Verizon Media", + "citypedia.com": "Verizon Media", + "couponbear.com": "Verizon Media", + "diylife.com": "Verizon Media", + "fashion.life": "Verizon Media", + "fast.rentals": "Verizon Media", + "find.furniture": "Verizon Media", + "foodbegood.com": "Verizon Media", + "furniture.deals": "Verizon Media", + "gamer.site": "Verizon Media", + "glamorbank.com": "Verizon Media", + "going.com": "Verizon Media", + "greendaily.com": "Verizon Media", + "health247.com": "Verizon Media", + "health.zone": "Verizon Media", + "homesessive.com": "Verizon Media", + "shelterpop.com": "Verizon Media", + "parentdish.ca": "Verizon Media", + "alephd.com": "Verizon Media", + "yho.com": "Verizon Media", + "housingwatch.com": "Verizon Media", + "insurance24-7.com": "Verizon Media", + "job-sift.com": "Verizon Media", + "jsyk.com": "Verizon Media", + "kitchepedia.com": "Verizon Media", + "know-legal.com": "Verizon Media", + "learn-247.com": "Verizon Media", + "luxist.com": "Verizon Media", + "money-a2z.com": "Verizon Media", + "mydaily.com": "Verizon Media", + "netdeals.com": "Verizon Media", + "pets.world": "Verizon Media", + "see-it.live": "Verizon Media", + "shopfone.com": "Verizon Media", + "streampad.com": "Verizon Media", + "joystiq.com": "Verizon Media", + "sport-king.com": "Verizon Media", + "tech247.co": "Verizon Media", + "thatsfit.ca": "Verizon Media", + "tech24.deals": "Verizon Media", + "thegifts.co": "Verizon Media", + "wmconnect.com": "Verizon Media", + "think24-7.com": "Verizon Media", + "viral.site": "Verizon Media", + "intoautos.com": "Verizon Media", + "netfind.com": "Verizon Media", + "when.com": "Verizon Media", + "enow.com": "Verizon Media", + "aolsearch.com": "Verizon Media", + "searchjam.com": "Verizon Media", + "vindicosuite.com": "Vindico LLC", + "adition.com": "Virtual Minds AG", + "movad.net": "Virtual Minds AG", + "adclear.net": "Virtual Minds AG", + "theadex.com": "Virtual Minds AG", + "t4ft.de": "Virtual Minds AG", + "batch.ba": "Virtual Minds AG", + "yieldlab.net": "Virtual Minds AG", + "yieldlab.com": "Virtual Minds AG", + "yieldlab.de": "Virtual Minds AG", + "virtualminds.de": "Virtual Minds AG", + "vm.de": "Virtual Minds AG", + "walmart.com": "Wal-Mart Stores, Inc.", + "wal.co": "Wal-Mart Stores, Inc.", + "walmartimages.com": "Wal-Mart Stores, Inc.", + "asda.com": "Wal-Mart Stores, Inc.", + "assets-asda.com": "Wal-Mart Stores, Inc.", + "samsclub.com": "Wal-Mart Stores, Inc.", + "walmartone.com": "Wal-Mart Stores, Inc.", + "walmartimages.ca": "Wal-Mart Stores, Inc.", + "wmobjects.com.br": "Wal-Mart Stores, Inc.", + "samsclubresources.com": "Wal-Mart Stores, Inc.", + "walmart.ca": "Wal-Mart Stores, Inc.", + "vudu.com": "Wal-Mart Stores, Inc.", + "walmartcanada.ca": "Wal-Mart Stores, Inc.", + "walmartmoneycard.com": "Wal-Mart Stores, Inc.", + "walmart.com.mx": "Wal-Mart Stores, Inc.", + "weborama.fr": "Weborama", + "weborama.com": "Weborama", + "weborama.io": "Weborama", + "wbtrk.net": "Webtrekk GmbH", + "wt-safetag.com": "Webtrekk GmbH", + "wt-eu02.net": "Webtrekk GmbH", + "wcfbc.net": "Webtrekk GmbH", + "webtrekk.net": "Webtrekk GmbH", + "mateti.net": "Webtrekk GmbH", + "cbtrk.net": "Webtrekk GmbH", + "webtrekk.com": "Webtrekk GmbH", + "wingify.com": "Wingify", + "vwo.com": "Wingify", + "pushcrew.com": "Wingify", + "visualwebsiteoptimizer.com": "Wingify", + "mookie1.com": "Xaxis", + "mookie1.cn": "Xaxis", + "yahoo.co.jp": "Yahoo Japan Corporation", + "yimg.jp": "Yahoo Japan Corporation", + "storage-yahoo.jp": "Yahoo Japan Corporation", + "yahooapis.jp": "Yahoo Japan Corporation", + "geocities.jp": "Yahoo Japan Corporation", + "yandex.ru": "Yandex LLC", + "yastatic.net": "Yandex LLC", + "webvisor.org": "Yandex LLC", + "yandex.net": "Yandex LLC", + "adfox.ru": "Yandex LLC", + "adfox.me": "Yandex LLC", + "yandex.st": "Yandex LLC", + "ymetrica1.com": "Yandex LLC", + "yandex.com": "Yandex LLC", + "metrika-informer.com": "Yandex LLC", + "ya.ru": "Yandex LLC", + "loginza.ru": "Yandex LLC", + "yandex.sx": "Yandex LLC", + "kinopoisk.ru": "Yandex LLC", + "auto.ru": "Yandex LLC", + "yandex.ua": "Yandex LLC", + "yandex.by": "Yandex LLC", + "yandex.com.tr": "Yandex LLC", + "yieldmo.com": "YieldMo, Inc.", + "yieldlove.com": "Yieldlove GmbH", + "yieldlove-ad-serving.net": "Yieldlove GmbH", + "yieldr.com": "Yieldr", + "254a.com": "Yieldr", + "yotpo.com": "Yotpo Ltd", + "zopim.com": "Zendesk, Inc.", + "zendesk.com": "Zendesk, Inc.", + "zdassets.com": "Zendesk, Inc.", + "zopim.io": "Zendesk, Inc.", + "zendesk.tv": "Zendesk, Inc.", + "outbound.io": "Zendesk, Inc.", + "zndsk.com": "Zendesk, Inc.", + "rezync.com": "Zeta Global", + "zetaglobal.com": "Zeta Global", + "zetazync.com": "Zeta Global", + "zqtk.net": "comScore, Inc", + "comscore.com": "comScore, Inc", + "mdotlabs.com": "comScore, Inc", + "scorecardresearch.com": "comScore, Inc", + "e.cl": "comScore, Inc", + "emetriq.de": "emetriq GmbH", + "emetriq.com": "emetriq GmbH", + "xplosion.de": "emetriq GmbH", + "eyereturn.com": "eyeReturn Marketing Inc.", + "eyeota.net": "eyeota Limited", + "iper2.com": "iPerceptions Inc.", + "iperceptions.com": "iPerceptions Inc.", + "ispot.tv": "iSpot.tv", + "nuggad.net": "nugg.ad GmbH", + "tru.am": "trueAnthem Corp", + "twiago.com": "twiago GmbH", + "amung.us": "whos.amung.us Inc", + "waust.at": "whos.amung.us Inc", + "histats.com": "wisecode s.r.l." + } +} \ No newline at end of file diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 5d26bc7c38..17b61418c6 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -42,8 +42,6 @@ 8349119D217F4D6E00610F35 /* proximanova-light.otf in Resources */ = {isa = PBXBuildFile; fileRef = F18642031E949E1900B2A911 /* proximanova-light.otf */; }; 8349119E217F4D6E00610F35 /* proximanova-regular.otf in Resources */ = {isa = PBXBuildFile; fileRef = F18642041E949E1900B2A911 /* proximanova-regular.otf */; }; 8349119F217F4D6E00610F35 /* proximanova-semibold.otf in Resources */ = {isa = PBXBuildFile; fileRef = F18642051E949E1900B2A911 /* proximanova-semibold.otf */; }; - 8364F7101F95FA7600562989 /* DisconnectMeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8364F70F1F95FA7600562989 /* DisconnectMeStore.swift */; }; - 8364F7131F961E5E00562989 /* DisconnectMeStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8364F7111F961DDB00562989 /* DisconnectMeStoreTests.swift */; }; 836B6B6A1F67F11E0061ECFB /* ContentBlockerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 836B6B691F67F11E0061ECFB /* ContentBlockerTests.swift */; }; 837094302142C4C200A58838 /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F1AA54601E48D90700223211 /* NotificationCenter.framework */; }; 837094332142C4C200A58838 /* TodayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 837094322142C4C200A58838 /* TodayViewController.swift */; }; @@ -75,9 +73,7 @@ 84E3419B1E2F7EFB00BDBA6F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 84E341991E2F7EFB00BDBA6F /* Main.storyboard */; }; 84E341A01E2F7EFB00BDBA6F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 84E3419E1E2F7EFB00BDBA6F /* LaunchScreen.storyboard */; }; 850250B520D80419002199C7 /* AtbAndVariantCleanupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850250B420D80419002199C7 /* AtbAndVariantCleanupTests.swift */; }; - 85047B821F6827AD002A95D8 /* blockerdata.js in Resources */ = {isa = PBXBuildFile; fileRef = 85047B811F6827AD002A95D8 /* blockerdata.js */; }; 85047B861F6887D2002A95D8 /* ContentBlockerLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85047B851F6887D2002A95D8 /* ContentBlockerLoader.swift */; }; - 85047B881F6966ED002A95D8 /* disconnectme.js in Resources */ = {isa = PBXBuildFile; fileRef = 85047B871F6966ED002A95D8 /* disconnectme.js */; }; 85047B8A1F69692C002A95D8 /* contentblocker.js in Resources */ = {isa = PBXBuildFile; fileRef = 85047B891F69692C002A95D8 /* contentblocker.js */; }; 85058366219AE9EA00ED4EDB /* HomePageConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85058365219AE9EA00ED4EDB /* HomePageConfiguration.swift */; }; 85058368219C49E000ED4EDB /* HomeViewSectionRenderers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85058367219C49E000ED4EDB /* HomeViewSectionRenderers.swift */; }; @@ -94,7 +90,6 @@ 850819DD1FDDE1A8006561FD /* SettingsTutorialAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 850819DC1FDDE1A8006561FD /* SettingsTutorialAssets.xcassets */; }; 85081A031FE03154006561FD /* SiteRatingPrivacyProtectionExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85081A021FE03154006561FD /* SiteRatingPrivacyProtectionExtensionTests.swift */; }; 85081A051FE05D40006561FD /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = 85081A041FE05D40006561FD /* Localizable.stringsdict */; }; - 85124C13201F5B4900288070 /* SurrogateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85124C12201F5B4900288070 /* SurrogateTests.swift */; }; 8512BCB620612E500085E862 /* UITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8512BCB520612E500085E862 /* UITests.swift */; }; 8512BCBE20615EB40085E862 /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8512BCBD20615EB30085E862 /* SnapshotHelper.swift */; }; 8517D98B221783A0006A8DD0 /* FindInPage.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8517D98A221783A0006A8DD0 /* FindInPage.xcassets */; }; @@ -126,11 +121,16 @@ 85200FA41FBC607E001AF290 /* NetworkLeaderboard.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 85200FA21FBC607E001AF290 /* NetworkLeaderboard.xcdatamodeld */; }; 85200FA61FBCCD58001AF290 /* PrivacyProtectionNetworkLeaderboardController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85200FA51FBCCD58001AF290 /* PrivacyProtectionNetworkLeaderboardController.swift */; }; 85200FA81FBDE472001AF290 /* UIColorPrivacyProtectionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85200FA71FBDE472001AF290 /* UIColorPrivacyProtectionExtension.swift */; }; + 8521FDE3238C0D0D00A44CC3 /* TrackerData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8521FDE2238C0D0D00A44CC3 /* TrackerData.swift */; }; + 8521FDE6238D414B00A44CC3 /* FileStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8521FDE4238D411400A44CC3 /* FileStoreTests.swift */; }; + 8521FDEA238DA9D800A44CC3 /* trackerData.json in Resources */ = {isa = PBXBuildFile; fileRef = 8521FDE9238DA9D800A44CC3 /* trackerData.json */; }; + 8521FDEC238DA9E600A44CC3 /* TrackerDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8521FDEB238DA9E600A44CC3 /* TrackerDataManager.swift */; }; + 8521FDEE238DD1B500A44CC3 /* MockEntityMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8521FDED238DD1B500A44CC3 /* MockEntityMapping.swift */; }; + 8521FDF1238DD82700A44CC3 /* TrackerDataManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8521FDEF238DD78D00A44CC3 /* TrackerDataManagerTests.swift */; }; + 8521FDF3238DF4BB00A44CC3 /* KnownTrackerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8521FDF2238DF4BB00A44CC3 /* KnownTrackerTests.swift */; }; 85267D232151221C00A0CE28 /* PrivacyPractices.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85267D222151221C00A0CE28 /* PrivacyPractices.swift */; }; 85267D2521513FC700A0CE28 /* EntityMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85267D2421513FC700A0CE28 /* EntityMapping.swift */; }; 85267D2721526E2F00A0CE28 /* EntityMappingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85267D2621526E2F00A0CE28 /* EntityMappingTests.swift */; }; - 85267D29215273F500A0CE28 /* EntityMappingStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85267D28215273F500A0CE28 /* EntityMappingStore.swift */; }; - 85267D2B215274F700A0CE28 /* DownloadedEntityMappingStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85267D2A215274F700A0CE28 /* DownloadedEntityMappingStoreTests.swift */; }; 85267D2D215286C500A0CE28 /* PrivacyPracticesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85267D2C215286C500A0CE28 /* PrivacyPracticesTests.swift */; }; 8528AE7C212EF4A200D0BD74 /* AppRatingPrompt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8528AE7B212EF4A200D0BD74 /* AppRatingPrompt.swift */; }; 8528AE7E212EF5FF00D0BD74 /* AppRatingPromptTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8528AE7D212EF5FF00D0BD74 /* AppRatingPromptTests.swift */; }; @@ -176,10 +176,8 @@ 8565A34D1FC8DFE400239327 /* LaunchTabNotificationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8565A34C1FC8DFE400239327 /* LaunchTabNotificationTests.swift */; }; 856D57C5206568BE000170B5 /* tlds.json in Resources */ = {isa = PBXBuildFile; fileRef = 856D57C4206568BE000170B5 /* tlds.json */; }; 856D57C720656A28000170B5 /* TLDTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 856D57C620656A28000170B5 /* TLDTests.swift */; }; - 857065681F6AADC40044DCB1 /* DisconnectMeTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 857065671F6AADC40044DCB1 /* DisconnectMeTracker.swift */; }; 8570656E1F6AAE270044DCB1 /* DetectedTrackerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8570656A1F6AADDF0044DCB1 /* DetectedTrackerTests.swift */; }; 857065701F6ABFA40044DCB1 /* APIRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8570656F1F6ABFA40044DCB1 /* APIRequest.swift */; }; - 857065761F6ADCF70044DCB1 /* EasylistStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 857065751F6ADCF70044DCB1 /* EasylistStore.swift */; }; 8577B0AC2225C778009CCC71 /* Onboarding-iPad.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8577B0AB2225C778009CCC71 /* Onboarding-iPad.storyboard */; }; 8577B0AE2225CA4F009CCC71 /* OnboardingPadViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8577B0AD2225CA4F009CCC71 /* OnboardingPadViewController.swift */; }; 857D1DC9223A9E7D00833940 /* MockAppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 857D1DC8223A9E7D00833940 /* MockAppSettings.swift */; }; @@ -194,7 +192,7 @@ 85A1B3B220C6CD9900C18F15 /* CookieStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85A1B3B120C6CD9900C18F15 /* CookieStorage.swift */; }; 85A1B3B420C6D07100C18F15 /* CookieStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85A1B3B320C6D07100C18F15 /* CookieStorageTests.swift */; }; 85A313972028E78A00327D00 /* release_notes.txt in Resources */ = {isa = PBXBuildFile; fileRef = 85A313962028E78A00327D00 /* release_notes.txt */; }; - 85A53ECA200D1FA20010D13F /* SurrogateStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85A53EC9200D1FA20010D13F /* SurrogateStore.swift */; }; + 85A53ECA200D1FA20010D13F /* FileStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85A53EC9200D1FA20010D13F /* FileStore.swift */; }; 85A9C37720DD2DC200073340 /* AddToHomeRowCTAViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85A9C37620DD2DC200073340 /* AddToHomeRowCTAViewController.swift */; }; 85A9C37920E0E00C00073340 /* HomeRow.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 85A9C37820E0E00C00073340 /* HomeRow.xcassets */; }; 85AB24A81FA7449D00896A5F /* PrivacyProtection.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85AB24A71FA7449D00896A5F /* PrivacyProtection.storyboard */; }; @@ -202,10 +200,6 @@ 85AE668E2097206E0014CF04 /* NotificationView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 85AE668D2097206E0014CF04 /* NotificationView.xib */; }; 85AE6690209724120014CF04 /* NotificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AE668F209724120014CF04 /* NotificationView.swift */; }; 85AE66942098BAC10014CF04 /* home-row-instructions.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 85AE66932098BAC10014CF04 /* home-row-instructions.mp4 */; }; - 85B5470A1FE852CE0008E1E5 /* bloom-filter-packed.js in Resources */ = {isa = PBXBuildFile; fileRef = 85B547061FE852CE0008E1E5 /* bloom-filter-packed.js */; }; - 85B5470B1FE852CE0008E1E5 /* abp-filter-parser-packed.js in Resources */ = {isa = PBXBuildFile; fileRef = 85B547071FE852CE0008E1E5 /* abp-filter-parser-packed.js */; }; - 85B5470C1FE852CE0008E1E5 /* abp-filter-parser-packed-es2015.js in Resources */ = {isa = PBXBuildFile; fileRef = 85B547081FE852CE0008E1E5 /* abp-filter-parser-packed-es2015.js */; }; - 85B5470D1FE852CE0008E1E5 /* bloom-filter-packed-es2015.js in Resources */ = {isa = PBXBuildFile; fileRef = 85B547091FE852CE0008E1E5 /* bloom-filter-packed-es2015.js */; }; 85B718F51FD071E50031A14F /* HTTPSUpgrade.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 85B718F31FD071E50031A14F /* HTTPSUpgrade.xcdatamodeld */; }; 85B9CB8521AEBD92009001F1 /* NavigationSearchHomeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B9CB8421AEBD92009001F1 /* NavigationSearchHomeCell.swift */; }; 85B9CB8721AEBDB7009001F1 /* CenteredSearchHomeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B9CB8621AEBDB7009001F1 /* CenteredSearchHomeCell.swift */; }; @@ -215,7 +209,6 @@ 85BA58581F34F72F00C6E8CA /* AppUserDefaultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85BA58561F34F61C00C6E8CA /* AppUserDefaultsTests.swift */; }; 85BA585A1F3506AE00C6E8CA /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85BA58591F3506AE00C6E8CA /* AppSettings.swift */; }; 85BA79911F6FF75000F59015 /* ContentBlockerStoreConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85BA79901F6FF75000F59015 /* ContentBlockerStoreConstants.swift */; }; - 85BA79951F71B37100F59015 /* tlds.js in Resources */ = {isa = PBXBuildFile; fileRef = 85BA79941F71B37100F59015 /* tlds.js */; }; 85BD869D1FAA08C400252411 /* PrivacyProtectionEncryptionDetailController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85BD869C1FAA08C400252411 /* PrivacyProtectionEncryptionDetailController.swift */; }; 85BD869F1FAA0E1100252411 /* PrivacyProtectionInfoDisplaying.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85BD869E1FAA0E1100252411 /* PrivacyProtectionInfoDisplaying.swift */; }; 85C11E3E20904B8400BFFEB4 /* VariantManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85C11E3D20904B8400BFFEB4 /* VariantManager.swift */; }; @@ -242,12 +235,9 @@ 85EE7F55224667DD000FE757 /* WebContainer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85EE7F54224667DD000FE757 /* WebContainer.storyboard */; }; 85EE7F572246685B000FE757 /* WebContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85EE7F562246685B000FE757 /* WebContainerViewController.swift */; }; 85EE7F59224673C5000FE757 /* WebContainerNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85EE7F58224673C5000FE757 /* WebContainerNavigationController.swift */; }; - 85F1C3C61F7A4C7500161346 /* easylist-cached.js in Resources */ = {isa = PBXBuildFile; fileRef = 85F1C3C31F7A4C7500161346 /* easylist-cached.js */; }; - 85F1C3C71F7A4C7500161346 /* easylist-parsing.js in Resources */ = {isa = PBXBuildFile; fileRef = 85F1C3C41F7A4C7500161346 /* easylist-parsing.js */; }; 85F1C3C81F7A4C7500161346 /* messaging.js in Resources */ = {isa = PBXBuildFile; fileRef = 85F1C3C51F7A4C7500161346 /* messaging.js */; }; 85F1C3CA1F7A4E8000161346 /* ContentBlockerStringCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85F1C3C91F7A4E8000161346 /* ContentBlockerStringCache.swift */; }; 85F1C3CD1F7A76EF00161346 /* ContentBlockerStringCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85F1C3CB1F7A76B900161346 /* ContentBlockerStringCacheTests.swift */; }; - 85F1C3D11F7BF30B00161346 /* EasylistStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85F1C3D01F7BF30B00161346 /* EasylistStoreTests.swift */; }; 85F1E9AC1FB49C0F00A75AC1 /* DisplayableCertificateBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85F1E9AB1FB49C0F00A75AC1 /* DisplayableCertificateBuilder.swift */; }; 85F1E9AE1FB49EA200A75AC1 /* ServerTrustCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85F1E9AD1FB49EA200A75AC1 /* ServerTrustCache.swift */; }; 85F1E9B01FB7BF3900A75AC1 /* NativeDisplayableCertificateBuilderDriver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85F1E9AF1FB7BF3900A75AC1 /* NativeDisplayableCertificateBuilderDriver.swift */; }; @@ -270,11 +260,7 @@ 85F2FFCF2211F8E5006BB258 /* TabSwitcherViewController+KeyCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85F2FFCE2211F8E5006BB258 /* TabSwitcherViewController+KeyCommands.swift */; }; 85F2FFFD2215C020006BB258 /* findinpage.js in Resources */ = {isa = PBXBuildFile; fileRef = 85F2FFFC2215C020006BB258 /* findinpage.js */; }; 85F45B2A1F875DFF00DB1978 /* ContentBlockerRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85F45B291F875DFF00DB1978 /* ContentBlockerRequest.swift */; }; - 85F591251FD1BFAA00746C77 /* DisconnectMeTrackerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85F591241FD1BFAA00746C77 /* DisconnectMeTrackerTests.swift */; }; 85F6191B20D293AC003904D1 /* HomeRowCTA.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85F6191A20D293AC003904D1 /* HomeRowCTA.swift */; }; - 85FAAE61214EFD21006AD5DA /* prevalence.json in Resources */ = {isa = PBXBuildFile; fileRef = 85FAAE60214EFD20006AD5DA /* prevalence.json */; }; - 85FAAE63214EFD37006AD5DA /* PrevalenceStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85FAAE62214EFD37006AD5DA /* PrevalenceStore.swift */; }; - 85FAAE66214EFE59006AD5DA /* EmbeddedPrevalenceStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85FAAE65214EFE59006AD5DA /* EmbeddedPrevalenceStoreTests.swift */; }; 8C4724502217A14B004C9B2D /* TabViewControllerLongPressBookmarkExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C47244F2217A14B004C9B2D /* TabViewControllerLongPressBookmarkExtension.swift */; }; 8C4838B5221C8F7F008A6739 /* GestureToolbarButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C4838B4221C8F7F008A6739 /* GestureToolbarButton.swift */; }; 980891A222369ADB00313A70 /* FeedbackUserText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 980891A122369ADB00313A70 /* FeedbackUserText.swift */; }; @@ -372,7 +358,6 @@ F114C5611E67599C0018F95F /* UseDuckDuckGoInSafariViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F114C5601E67599C0018F95F /* UseDuckDuckGoInSafariViewController.swift */; }; F11E22F61ED31CB600523BC9 /* JsonError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11E22F51ED31CB600523BC9 /* JsonError.swift */; }; F11E22F91ED33D8600523BC9 /* ApiRequestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11E22F81ED33D8600523BC9 /* ApiRequestError.swift */; }; - F11E23091ED3559700523BC9 /* DisconnectMeTrackersParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11E23081ED3559700523BC9 /* DisconnectMeTrackersParserTests.swift */; }; F11E23211ED3B7A600523BC9 /* MockBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = F17669A71E412A12003D3222 /* MockBundle.swift */; }; F130D73A1E5776C500C45811 /* OmniBarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F130D7391E5776C500C45811 /* OmniBarDelegate.swift */; }; F1386BA41E6846C40062FC3C /* TabDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1386BA31E6846C40062FC3C /* TabDelegate.swift */; }; @@ -408,7 +393,6 @@ F16390821E648B7A005B4550 /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F16390811E648B7A005B4550 /* HomeViewController.swift */; }; F16393F51ECCA85900DDD653 /* ContentBlockerUserDefaultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F16393F41ECCA85900DDD653 /* ContentBlockerUserDefaultsTests.swift */; }; F16393FF1ECCB9CC00DDD653 /* FileLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = F16393FE1ECCB9CC00DDD653 /* FileLoader.swift */; }; - F16394071ECE01E400DDD653 /* DisconnectMeTrackersParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F16394061ECE01E400DDD653 /* DisconnectMeTrackersParser.swift */; }; F164DDBF1F2A509200010DD2 /* FireAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F164DDBE1F2A509200010DD2 /* FireAnimation.swift */; }; F1668BCE1E798081008CBA04 /* BookmarksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1668BCD1E798081008CBA04 /* BookmarksViewController.swift */; }; F16956401ECC9C66009C35C9 /* ContentBlockerConfigurationStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F169563F1ECC9C66009C35C9 /* ContentBlockerConfigurationStore.swift */; }; @@ -618,8 +602,6 @@ 8349118F217F491200610F35 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 83491196217F49AF00610F35 /* BookmarkCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkCell.swift; sourceTree = ""; }; 83491198217F4AA300610F35 /* BookmarksTodayExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = BookmarksTodayExtension.entitlements; sourceTree = ""; }; - 8364F70F1F95FA7600562989 /* DisconnectMeStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisconnectMeStore.swift; sourceTree = ""; }; - 8364F7111F961DDB00562989 /* DisconnectMeStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisconnectMeStoreTests.swift; sourceTree = ""; }; 836B6B671F67F11E0061ECFB /* IntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = IntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 836B6B691F67F11E0061ECFB /* ContentBlockerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentBlockerTests.swift; sourceTree = ""; }; 836B6B6B1F67F11E0061ECFB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -669,9 +651,7 @@ 84E341AC1E2F7EFB00BDBA6F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 850250B220D803F4002199C7 /* AtbAndVariantCleanup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AtbAndVariantCleanup.swift; path = ../Core/AtbAndVariantCleanup.swift; sourceTree = ""; }; 850250B420D80419002199C7 /* AtbAndVariantCleanupTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AtbAndVariantCleanupTests.swift; sourceTree = ""; }; - 85047B811F6827AD002A95D8 /* blockerdata.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = blockerdata.js; sourceTree = ""; }; 85047B851F6887D2002A95D8 /* ContentBlockerLoader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBlockerLoader.swift; sourceTree = ""; }; - 85047B871F6966ED002A95D8 /* disconnectme.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = disconnectme.js; sourceTree = ""; }; 85047B891F69692C002A95D8 /* contentblocker.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = contentblocker.js; sourceTree = ""; }; 85058365219AE9EA00ED4EDB /* HomePageConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePageConfiguration.swift; sourceTree = ""; }; 85058367219C49E000ED4EDB /* HomeViewSectionRenderers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewSectionRenderers.swift; sourceTree = ""; }; @@ -680,7 +660,6 @@ 850819DC1FDDE1A8006561FD /* SettingsTutorialAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = SettingsTutorialAssets.xcassets; sourceTree = ""; }; 85081A021FE03154006561FD /* SiteRatingPrivacyProtectionExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteRatingPrivacyProtectionExtensionTests.swift; sourceTree = ""; }; 85081A041FE05D40006561FD /* Localizable.stringsdict */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; path = Localizable.stringsdict; sourceTree = ""; }; - 85124C12201F5B4900288070 /* SurrogateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurrogateTests.swift; sourceTree = ""; }; 8512BCB320612E500085E862 /* UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 8512BCB520612E500085E862 /* UITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITests.swift; sourceTree = ""; }; 8512BCB720612E500085E862 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -715,11 +694,16 @@ 85200FA31FBC607E001AF290 /* NetworkLeaderboard.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = NetworkLeaderboard.xcdatamodel; sourceTree = ""; }; 85200FA51FBCCD58001AF290 /* PrivacyProtectionNetworkLeaderboardController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyProtectionNetworkLeaderboardController.swift; sourceTree = ""; }; 85200FA71FBDE472001AF290 /* UIColorPrivacyProtectionExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColorPrivacyProtectionExtension.swift; sourceTree = ""; }; + 8521FDE2238C0D0D00A44CC3 /* TrackerData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerData.swift; sourceTree = ""; }; + 8521FDE4238D411400A44CC3 /* FileStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileStoreTests.swift; sourceTree = ""; }; + 8521FDE9238DA9D800A44CC3 /* trackerData.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = trackerData.json; sourceTree = ""; }; + 8521FDEB238DA9E600A44CC3 /* TrackerDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerDataManager.swift; sourceTree = ""; }; + 8521FDED238DD1B500A44CC3 /* MockEntityMapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockEntityMapping.swift; sourceTree = ""; }; + 8521FDEF238DD78D00A44CC3 /* TrackerDataManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerDataManagerTests.swift; sourceTree = ""; }; + 8521FDF2238DF4BB00A44CC3 /* KnownTrackerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KnownTrackerTests.swift; sourceTree = ""; }; 85267D222151221C00A0CE28 /* PrivacyPractices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyPractices.swift; sourceTree = ""; }; 85267D2421513FC700A0CE28 /* EntityMapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityMapping.swift; sourceTree = ""; }; 85267D2621526E2F00A0CE28 /* EntityMappingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityMappingTests.swift; sourceTree = ""; }; - 85267D28215273F500A0CE28 /* EntityMappingStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityMappingStore.swift; sourceTree = ""; }; - 85267D2A215274F700A0CE28 /* DownloadedEntityMappingStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadedEntityMappingStoreTests.swift; sourceTree = ""; }; 85267D2C215286C500A0CE28 /* PrivacyPracticesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyPracticesTests.swift; sourceTree = ""; }; 8528AE7B212EF4A200D0BD74 /* AppRatingPrompt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRatingPrompt.swift; sourceTree = ""; }; 8528AE7D212EF5FF00D0BD74 /* AppRatingPromptTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRatingPromptTests.swift; sourceTree = ""; }; @@ -764,10 +748,8 @@ 8565A34C1FC8DFE400239327 /* LaunchTabNotificationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchTabNotificationTests.swift; sourceTree = ""; }; 856D57C4206568BE000170B5 /* tlds.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tlds.json; sourceTree = ""; }; 856D57C620656A28000170B5 /* TLDTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TLDTests.swift; sourceTree = ""; }; - 857065671F6AADC40044DCB1 /* DisconnectMeTracker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisconnectMeTracker.swift; sourceTree = ""; }; 8570656A1F6AADDF0044DCB1 /* DetectedTrackerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetectedTrackerTests.swift; sourceTree = ""; }; 8570656F1F6ABFA40044DCB1 /* APIRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APIRequest.swift; sourceTree = ""; }; - 857065751F6ADCF70044DCB1 /* EasylistStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EasylistStore.swift; sourceTree = ""; }; 8577B0AB2225C778009CCC71 /* Onboarding-iPad.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = "Onboarding-iPad.storyboard"; sourceTree = ""; }; 8577B0AD2225CA4F009CCC71 /* OnboardingPadViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingPadViewController.swift; sourceTree = ""; }; 857A07A81F3AE3800035EFF4 /* SwiftRichString.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftRichString.framework; path = Carthage/Build/iOS/SwiftRichString.framework; sourceTree = ""; }; @@ -784,7 +766,7 @@ 85A1B3B120C6CD9900C18F15 /* CookieStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CookieStorage.swift; sourceTree = ""; }; 85A1B3B320C6D07100C18F15 /* CookieStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CookieStorageTests.swift; sourceTree = ""; }; 85A313962028E78A00327D00 /* release_notes.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = release_notes.txt; path = fastlane/metadata/default/release_notes.txt; sourceTree = ""; }; - 85A53EC9200D1FA20010D13F /* SurrogateStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurrogateStore.swift; sourceTree = ""; }; + 85A53EC9200D1FA20010D13F /* FileStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileStore.swift; sourceTree = ""; }; 85A9C37620DD2DC200073340 /* AddToHomeRowCTAViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddToHomeRowCTAViewController.swift; sourceTree = ""; }; 85A9C37820E0E00C00073340 /* HomeRow.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = HomeRow.xcassets; sourceTree = ""; }; 85AB24A71FA7449D00896A5F /* PrivacyProtection.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = PrivacyProtection.storyboard; sourceTree = ""; }; @@ -835,12 +817,9 @@ 85EE7F54224667DD000FE757 /* WebContainer.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = WebContainer.storyboard; sourceTree = ""; }; 85EE7F562246685B000FE757 /* WebContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebContainerViewController.swift; sourceTree = ""; }; 85EE7F58224673C5000FE757 /* WebContainerNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebContainerNavigationController.swift; sourceTree = ""; }; - 85F1C3C31F7A4C7500161346 /* easylist-cached.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "easylist-cached.js"; sourceTree = ""; }; - 85F1C3C41F7A4C7500161346 /* easylist-parsing.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "easylist-parsing.js"; sourceTree = ""; }; 85F1C3C51F7A4C7500161346 /* messaging.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = messaging.js; sourceTree = ""; }; 85F1C3C91F7A4E8000161346 /* ContentBlockerStringCache.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBlockerStringCache.swift; sourceTree = ""; }; 85F1C3CB1F7A76B900161346 /* ContentBlockerStringCacheTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBlockerStringCacheTests.swift; sourceTree = ""; }; - 85F1C3D01F7BF30B00161346 /* EasylistStoreTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EasylistStoreTests.swift; sourceTree = ""; }; 85F1E9AB1FB49C0F00A75AC1 /* DisplayableCertificateBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayableCertificateBuilder.swift; sourceTree = ""; }; 85F1E9AD1FB49EA200A75AC1 /* ServerTrustCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerTrustCache.swift; sourceTree = ""; }; 85F1E9AF1FB7BF3900A75AC1 /* NativeDisplayableCertificateBuilderDriver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeDisplayableCertificateBuilderDriver.swift; sourceTree = ""; }; @@ -861,11 +840,7 @@ 85F2FFFC2215C020006BB258 /* findinpage.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = findinpage.js; path = "submodules/ios-js-support/src/findinpage.js"; sourceTree = SOURCE_ROOT; }; 85F2FFFF2215C17B006BB258 /* FindInPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindInPage.swift; sourceTree = ""; }; 85F45B291F875DFF00DB1978 /* ContentBlockerRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBlockerRequest.swift; sourceTree = ""; }; - 85F591241FD1BFAA00746C77 /* DisconnectMeTrackerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisconnectMeTrackerTests.swift; sourceTree = ""; }; 85F6191A20D293AC003904D1 /* HomeRowCTA.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeRowCTA.swift; sourceTree = ""; }; - 85FAAE60214EFD20006AD5DA /* prevalence.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = prevalence.json; sourceTree = ""; }; - 85FAAE62214EFD37006AD5DA /* PrevalenceStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrevalenceStore.swift; sourceTree = ""; }; - 85FAAE65214EFE59006AD5DA /* EmbeddedPrevalenceStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmbeddedPrevalenceStoreTests.swift; sourceTree = ""; }; 8C47244F2217A14B004C9B2D /* TabViewControllerLongPressBookmarkExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabViewControllerLongPressBookmarkExtension.swift; sourceTree = ""; }; 8C4838B4221C8F7F008A6739 /* GestureToolbarButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GestureToolbarButton.swift; sourceTree = ""; }; 980891A122369ADB00313A70 /* FeedbackUserText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbackUserText.swift; sourceTree = ""; }; @@ -965,7 +940,6 @@ F114C5601E67599C0018F95F /* UseDuckDuckGoInSafariViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UseDuckDuckGoInSafariViewController.swift; sourceTree = ""; }; F11E22F51ED31CB600523BC9 /* JsonError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JsonError.swift; sourceTree = ""; }; F11E22F81ED33D8600523BC9 /* ApiRequestError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiRequestError.swift; sourceTree = ""; }; - F11E23081ED3559700523BC9 /* DisconnectMeTrackersParserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisconnectMeTrackersParserTests.swift; sourceTree = ""; }; F130D7391E5776C500C45811 /* OmniBarDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OmniBarDelegate.swift; sourceTree = ""; }; F1386BA31E6846C40062FC3C /* TabDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabDelegate.swift; sourceTree = ""; }; F13B4BBF1F180D8A00814661 /* TabsModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabsModel.swift; sourceTree = ""; }; @@ -1004,7 +978,6 @@ F16390811E648B7A005B4550 /* HomeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = ""; }; F16393F41ECCA85900DDD653 /* ContentBlockerUserDefaultsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBlockerUserDefaultsTests.swift; sourceTree = ""; }; F16393FE1ECCB9CC00DDD653 /* FileLoader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileLoader.swift; sourceTree = ""; }; - F16394061ECE01E400DDD653 /* DisconnectMeTrackersParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DisconnectMeTrackersParser.swift; sourceTree = ""; }; F164DDBE1F2A509200010DD2 /* FireAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FireAnimation.swift; sourceTree = ""; }; F1668BCD1E798081008CBA04 /* BookmarksViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksViewController.swift; sourceTree = ""; }; F169563F1ECC9C66009C35C9 /* ContentBlockerConfigurationStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBlockerConfigurationStore.swift; sourceTree = ""; }; @@ -1199,7 +1172,6 @@ isa = PBXGroup; children = ( 85C271E31FD04ACD007216B4 /* HTTPSUpgradeParserTests.swift */, - F11E23081ED3559700523BC9 /* DisconnectMeTrackersParserTests.swift */, ); name = Converters; sourceTree = ""; @@ -1208,8 +1180,9 @@ isa = PBXGroup; children = ( 8570656A1F6AADDF0044DCB1 /* DetectedTrackerTests.swift */, - 85F591241FD1BFAA00746C77 /* DisconnectMeTrackerTests.swift */, 8341D808212D6159000514C2 /* HTTPSBloomFilterSpecificationTest.swift */, + 8521FDEF238DD78D00A44CC3 /* TrackerDataManagerTests.swift */, + 8521FDF2238DF4BB00A44CC3 /* KnownTrackerTests.swift */, ); name = Domain; sourceTree = ""; @@ -1217,11 +1190,9 @@ 830FA79E1F8E82DB00FCE105 /* Store */ = { isa = PBXGroup; children = ( - F16393F41ECCA85900DDD653 /* ContentBlockerUserDefaultsTests.swift */, 85F1C3CB1F7A76B900161346 /* ContentBlockerStringCacheTests.swift */, - 85F1C3D01F7BF30B00161346 /* EasylistStoreTests.swift */, - 8364F7111F961DDB00562989 /* DisconnectMeStoreTests.swift */, - 85124C12201F5B4900288070 /* SurrogateTests.swift */, + F16393F41ECCA85900DDD653 /* ContentBlockerUserDefaultsTests.swift */, + 8521FDE4238D411400A44CC3 /* FileStoreTests.swift */, 85C271E51FD065D8007216B4 /* HTTPSUpgradePersistenceTests.swift */, ); name = Store; @@ -1270,11 +1241,7 @@ isa = PBXGroup; children = ( 8595BF491FE847EB00F692EC /* ios-js-support */, - 85047B811F6827AD002A95D8 /* blockerdata.js */, 85047B891F69692C002A95D8 /* contentblocker.js */, - 85047B871F6966ED002A95D8 /* disconnectme.js */, - 85F1C3C31F7A4C7500161346 /* easylist-cached.js */, - 85F1C3C41F7A4C7500161346 /* easylist-parsing.js */, 85F1C3C51F7A4C7500161346 /* messaging.js */, 987AFB6B22AE83C2001B84CF /* debug-messaging-enabled.js */, 9833FD7122BCE44C00E95CD8 /* debug-messaging-disabled.js */, @@ -1322,9 +1289,11 @@ 8377744B1F8E1F8000E17A29 /* Domain */ = { isa = PBXGroup; children = ( - 857065671F6AADC40044DCB1 /* DisconnectMeTracker.swift */, 85C271D01FCF33C8007216B4 /* DetectedTracker.swift */, 8328AABD212AF70B00293140 /* HTTPSBloomFilterSpecification.swift */, + 8521FDE2238C0D0D00A44CC3 /* TrackerData.swift */, + 8521FDE9238DA9D800A44CC3 /* trackerData.json */, + 8521FDEB238DA9E600A44CC3 /* TrackerDataManager.swift */, ); name = Domain; sourceTree = ""; @@ -1336,10 +1305,8 @@ 85BA79901F6FF75000F59015 /* ContentBlockerStoreConstants.swift */, F16956411ECCA3D4009C35C9 /* ContentBlockerConfigurationUserDefaults.swift */, F169563F1ECC9C66009C35C9 /* ContentBlockerConfigurationStore.swift */, - 8364F70F1F95FA7600562989 /* DisconnectMeStore.swift */, - 857065751F6ADCF70044DCB1 /* EasylistStore.swift */, 85F1C3C91F7A4E8000161346 /* ContentBlockerStringCache.swift */, - 85A53EC9200D1FA20010D13F /* SurrogateStore.swift */, + 85A53EC9200D1FA20010D13F /* FileStore.swift */, 85C271DE1FD044D7007216B4 /* HTTPSUpgradeStore.swift */, 85B718F31FD071E50031A14F /* HTTPSUpgrade.xcdatamodeld */, ); @@ -1350,7 +1317,6 @@ isa = PBXGroup; children = ( 85C271D71FD0311A007216B4 /* HTTPSUpgradeParser.swift */, - F16394061ECE01E400DDD653 /* DisconnectMeTrackersParser.swift */, ); name = Converter; sourceTree = ""; @@ -1814,8 +1780,6 @@ 85FAAE64214EFE40006AD5DA /* Store */ = { isa = PBXGroup; children = ( - 85267D2A215274F700A0CE28 /* DownloadedEntityMappingStoreTests.swift */, - 85FAAE65214EFE59006AD5DA /* EmbeddedPrevalenceStoreTests.swift */, 85C7438D2152A66D005CF998 /* EmbeddedTermsOfServiceStoreTests.swift */, ); name = Store; @@ -2280,11 +2244,8 @@ F16956331ECC9C4A009C35C9 /* Store */ = { isa = PBXGroup; children = ( - 85FAAE60214EFD20006AD5DA /* prevalence.json */, - 85FAAE62214EFD37006AD5DA /* PrevalenceStore.swift */, 830C375D1F6C3A3B00E317A7 /* TermsOfServiceStore.swift */, 83D306A31F6D500B00ED7CE2 /* tosdr.json */, - 85267D28215273F500A0CE28 /* EntityMappingStore.swift */, ); name = Store; sourceTree = ""; @@ -2309,6 +2270,7 @@ 98B3128F218CCB2200E54DE1 /* MockDependencyProvider.swift */, 851B12A922259DAF004781BC /* MockContextualTipsStorage.swift */, 857D1DC8223A9E7D00833940 /* MockAppSettings.swift */, + 8521FDED238DD1B500A44CC3 /* MockEntityMapping.swift */, ); name = Mocks; sourceTree = ""; @@ -3173,24 +3135,15 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 85BA79951F71B37100F59015 /* tlds.js in Resources */, - 85047B821F6827AD002A95D8 /* blockerdata.js in Resources */, - 85B5470C1FE852CE0008E1E5 /* abp-filter-parser-packed-es2015.js in Resources */, 987AFB6C22AE83C2001B84CF /* debug-messaging-enabled.js in Resources */, F18608E01E5E649400361C30 /* document.js in Resources */, - 85B5470B1FE852CE0008E1E5 /* abp-filter-parser-packed.js in Resources */, 85F2FFFD2215C020006BB258 /* findinpage.js in Resources */, - 85B5470A1FE852CE0008E1E5 /* bloom-filter-packed.js in Resources */, - 85F1C3C71F7A4C7500161346 /* easylist-parsing.js in Resources */, 85325A8D2024BB7C003EB195 /* detection.js in Resources */, 856D57C5206568BE000170B5 /* tlds.json in Resources */, 85047B8A1F69692C002A95D8 /* contentblocker.js in Resources */, - 85B5470D1FE852CE0008E1E5 /* bloom-filter-packed-es2015.js in Resources */, - 85047B881F6966ED002A95D8 /* disconnectme.js in Resources */, - 85FAAE61214EFD21006AD5DA /* prevalence.json in Resources */, 83D306A41F6D500B00ED7CE2 /* tosdr.json in Resources */, - 85F1C3C61F7A4C7500161346 /* easylist-cached.js in Resources */, 9833FD7222BCE44C00E95CD8 /* debug-messaging-disabled.js in Resources */, + 8521FDEA238DA9D800A44CC3 /* trackerData.json in Resources */, 85F1C3C81F7A4C7500161346 /* messaging.js in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3610,6 +3563,7 @@ F11E23211ED3B7A600523BC9 /* MockBundle.swift in Sources */, F13B4BF91F18CA0600814661 /* TabsModelTests.swift in Sources */, 98B31290218CCB2200E54DE1 /* MockDependencyProvider.swift in Sources */, + 8521FDF1238DD82700A44CC3 /* TrackerDataManagerTests.swift in Sources */, 83C05D06212C77D80068712A /* BloomFilterWrapperTest.swift in Sources */, 8536A1C8209AF2410050739E /* MockVariantManager.swift in Sources */, 85267D2721526E2F00A0CE28 /* EntityMappingTests.swift in Sources */, @@ -3645,8 +3599,6 @@ 851B1283221FE65E004781BC /* ImproveOnboardingExperiment1Tests.swift in Sources */, F14513591F4664E900710C46 /* SiteRatingTests.swift in Sources */, 857EEB7120934BB0008A005C /* UserDefaultsHomeRowCTAStorageTests.swift in Sources */, - 85267D2B215274F700A0CE28 /* DownloadedEntityMappingStoreTests.swift in Sources */, - 85124C13201F5B4900288070 /* SurrogateTests.swift in Sources */, F194FAFB1F14E622009B4DF8 /* UIFontExtensionTests.swift in Sources */, 981FED7422046017008488D7 /* AutoClearTests.swift in Sources */, 98DDF9F322C4029D00DE38DB /* InitHelpers.swift in Sources */, @@ -3654,20 +3606,17 @@ 853C5F6521C3E697001F7A05 /* MockBookmarkStore.swift in Sources */, 85C7438E2152A66D005CF998 /* EmbeddedTermsOfServiceStoreTests.swift in Sources */, 851B12AF2226B1C2004781BC /* DefaultContextualTipsStorageTests.swift in Sources */, - 85F1C3D11F7BF30B00161346 /* EasylistStoreTests.swift in Sources */, F1D477C91F2139410031ED49 /* OmniBarStateTests.swift in Sources */, - 85FAAE66214EFE59006AD5DA /* EmbeddedPrevalenceStoreTests.swift in Sources */, 85F1E9B71FB7C81C00A75AC1 /* NativeDisplayableCertificateBuilderDriverTests.swift in Sources */, F198D7981E3A45D90088DA8A /* WKWebViewConfigurationExtensionTests.swift in Sources */, + 8521FDE6238D414B00A44CC3 /* FileStoreTests.swift in Sources */, 851B12AB22259DDE004781BC /* MockContextualTipsStorage.swift in Sources */, - 8364F7131F961E5E00562989 /* DisconnectMeStoreTests.swift in Sources */, F14E491F1E391CE900DC037C /* URLExtensionTests.swift in Sources */, 22CB1ED8203DDD2C00D2C724 /* AppDeepLinksTests.swift in Sources */, 830C37671F6C4A6200E317A7 /* TermsOfServiceParserTests.swift in Sources */, 8341D80A212D616F000514C2 /* HTTPSBloomFilterSpecificationTest.swift in Sources */, F1134ED21F40EF3A00B73467 /* JsonTestDataLoader.swift in Sources */, 85A1B3B420C6D07100C18F15 /* CookieStorageTests.swift in Sources */, - 85F591251FD1BFAA00746C77 /* DisconnectMeTrackerTests.swift in Sources */, 850250B520D80419002199C7 /* AtbAndVariantCleanupTests.swift in Sources */, F17D72391E8B35C6003E8B0E /* AppUrlsTests.swift in Sources */, F1134ED61F40F29F00B73467 /* StatisticsUserDefaultsTests.swift in Sources */, @@ -3676,8 +3625,8 @@ 83B8BDAF20AB14A70076D6A1 /* APIHeadersTests.swift in Sources */, 8536A1CA209AF6490050739E /* HomeRowReminderTests.swift in Sources */, 851DFD8A212C5EE800D95F20 /* TabSwitcherButtonTests.swift in Sources */, + 8521FDF3238DF4BB00A44CC3 /* KnownTrackerTests.swift in Sources */, 85F200072217032E006BB258 /* OmniBarTests.swift in Sources */, - F11E23091ED3559700523BC9 /* DisconnectMeTrackersParserTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3735,24 +3684,20 @@ 85267D232151221C00A0CE28 /* PrivacyPractices.swift in Sources */, F18326871E60542100240060 /* JavascriptLoader.swift in Sources */, 8328AABE212AF70B00293140 /* HTTPSBloomFilterSpecification.swift in Sources */, - 857065761F6ADCF70044DCB1 /* EasylistStore.swift in Sources */, 85C271DD1FD04459007216B4 /* HTTPSUpgrade.swift in Sources */, 83004E802193BB8200DA013C /* WKNavigationExtension.swift in Sources */, 853A717620F62FE800FE60BC /* Pixel.swift in Sources */, 9876B75E2232B36900D81D9F /* TabInstrumentation.swift in Sources */, - F16394071ECE01E400DDD653 /* DisconnectMeTrackersParser.swift in Sources */, - 85A53ECA200D1FA20010D13F /* SurrogateStore.swift in Sources */, + 85A53ECA200D1FA20010D13F /* FileStore.swift in Sources */, F1134EB31F40AD2500B73467 /* Atb.swift in Sources */, 8328AAB9212A3DF200293140 /* BloomFilterWrapper.mm in Sources */, 98982B3422F8D8E400578AC9 /* Debounce.swift in Sources */, F143C3291E4A9A0E00CFDE3A /* URLExtension.swift in Sources */, F143C3271E4A9A0E00CFDE3A /* Logger.swift in Sources */, - 85267D29215273F500A0CE28 /* EntityMappingStore.swift in Sources */, 855D914F206563A000C4B448 /* TLD.swift in Sources */, 85267D2521513FC700A0CE28 /* EntityMapping.swift in Sources */, F143C33D1E4A9A9200CFDE3A /* WKWebViewExtension.swift in Sources */, F11E22F61ED31CB600523BC9 /* JsonError.swift in Sources */, - 85FAAE63214EFD37006AD5DA /* PrevalenceStore.swift in Sources */, 85372447220DD103009D09CD /* UIKeyCommandExtension.swift in Sources */, 85A1B3B220C6CD9900C18F15 /* CookieStorage.swift in Sources */, 9813F79822BA71AA00A80EDB /* StorageCache.swift in Sources */, @@ -3766,21 +3711,22 @@ 8331AEDE20AA37560024962B /* APIHeaders.swift in Sources */, 9896632422C56716007BE4FE /* EtagStorage.swift in Sources */, F1134EB01F40AC6300B73467 /* AtbParser.swift in Sources */, + 8521FDEE238DD1B500A44CC3 /* MockEntityMapping.swift in Sources */, 830C375E1F6C3A3B00E317A7 /* TermsOfServiceStore.swift in Sources */, 85F1E9AE1FB49EA200A75AC1 /* ServerTrustCache.swift in Sources */, 857065701F6ABFA40044DCB1 /* APIRequest.swift in Sources */, F14513571F46388F00710C46 /* SiteRating.swift in Sources */, F159BDA71F0C073D00B4A01D /* WebCacheSummary.swift in Sources */, + 8521FDEC238DA9E600A44CC3 /* TrackerDataManager.swift in Sources */, 85C743902154E981005CF998 /* GradeCache.swift in Sources */, 85BA79911F6FF75000F59015 /* ContentBlockerStoreConstants.swift in Sources */, 85047B861F6887D2002A95D8 /* ContentBlockerLoader.swift in Sources */, F1134EDA1F40FC3F00B73467 /* InfoBundle.swift in Sources */, 85F21DC621145DD5002631A6 /* global.swift in Sources */, F143C3181E4A99D200CFDE3A /* Link.swift in Sources */, - 8364F7101F95FA7600562989 /* DisconnectMeStore.swift in Sources */, F1CE42A61ECA0A460074A8DF /* BookmarkUserDefaults.swift in Sources */, 830C375C1F6C06BF00E317A7 /* TermsOfService.swift in Sources */, - 857065681F6AADC40044DCB1 /* DisconnectMeTracker.swift in Sources */, + 8521FDE3238C0D0D00A44CC3 /* TrackerData.swift in Sources */, 85F1C3CA1F7A4E8000161346 /* ContentBlockerStringCache.swift in Sources */, F16956421ECCA3D4009C35C9 /* ContentBlockerConfigurationUserDefaults.swift in Sources */, 85200FA11FBC5BB5001AF290 /* DDGPersistenceContainer.swift in Sources */, diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 012b36c0ea..052c695b03 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -49,6 +49,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } + DispatchQueue.global(qos: .background).async { + FileStore().removeLegacyData() + ContentBlockerStringCache.removeLegacyData() + } + Database.shared.loadStore { context in DatabaseMigration.migrate(to: context) } diff --git a/DuckDuckGo/Grade.swift b/DuckDuckGo/Grade.swift index f42d812dff..41b5d81cd5 100644 --- a/DuckDuckGo/Grade.swift +++ b/DuckDuckGo/Grade.swift @@ -31,6 +31,10 @@ public class Grade { case d = "D" case dMinus = "D-" + public func normalize() -> Grading { + self == .dMinus ? .d : self + } + } public struct Score: Codable, Equatable { diff --git a/DuckDuckGo/NetworkLeaderboard.swift b/DuckDuckGo/NetworkLeaderboard.swift index 318bbfdbec..323c70987d 100644 --- a/DuckDuckGo/NetworkLeaderboard.swift +++ b/DuckDuckGo/NetworkLeaderboard.swift @@ -25,6 +25,14 @@ class NetworkLeaderboard { public static let shared = NetworkLeaderboard() + struct Constants { + + // Increment this to cause a reset on startup (e.g. if we know the TDS has changed significantly) + static let dataVersion = 1 + static let dataVersionKey = "com.duckduckgo.mobile.ios.networkleaderboard.dataversion" + + } + struct EntityNames { static let pageStats = "PPPageStats" @@ -33,18 +41,24 @@ class NetworkLeaderboard { } private lazy var context = Database.shared.makeContext(concurrencyType: .mainQueueConcurrencyType, name: "NetworkLeaderboard") - + private var userDefaults: UserDefaults + var startDate: Date? { return pageStats?.startDate } + var needsDataReset: Bool { + return userDefaults.integer(forKey: Constants.dataVersionKey) < Constants.dataVersion + } + private var pageStats: PPPageStats? { let request: NSFetchRequest = PPPageStats.fetchRequest() return try? context.fetch(request).first } - init() { - if pageStats == nil { + init(userDefaults: UserDefaults = UserDefaults.standard) { + self.userDefaults = userDefaults + if pageStats == nil || needsDataReset { reset() } } @@ -54,6 +68,7 @@ class NetworkLeaderboard { context.deleteAll(matching: PPPageStats.fetchRequest()) createNewPageStatsEntity() try? context.save() + userDefaults.set(Constants.dataVersion, forKey: Constants.dataVersionKey) } func incrementPagesLoaded() { diff --git a/DuckDuckGo/PrivacyProtectionController.swift b/DuckDuckGo/PrivacyProtectionController.swift index 318ab23bf8..7a07f273a5 100644 --- a/DuckDuckGo/PrivacyProtectionController.swift +++ b/DuckDuckGo/PrivacyProtectionController.swift @@ -49,9 +49,7 @@ class PrivacyProtectionController: ThemableNavigationController { navigationBar.isHidden = isPad popoverPresentationController?.backgroundColor = UIColor.nearlyWhite - if !storageCache.hasData { - showBlockerListError() - } else if let errorText = errorText { + if let errorText = errorText { showError(withText: errorText) } else if siteRating == nil { showError(withText: UserText.unknownErrorOccurred) @@ -67,13 +65,6 @@ class PrivacyProtectionController: ThemableNavigationController { pushViewController(controller, animated: true) } - private func showBlockerListError() { - guard let controller = storyboard?.instantiateViewController(withIdentifier: "Error") as? PrivacyProtectionErrorController else { return } - controller.errorText = UserText.privacyProtectionReloadBlockerLists - controller.delegate = self - pushViewController(controller, animated: true) - } - private func showInitialScreen() { guard let controller = storyboard?.instantiateViewController(withIdentifier: "InitialScreen") as? PrivacyProtectionOverviewController else { return } diff --git a/DuckDuckGo/PrivacyProtectionFooterController.swift b/DuckDuckGo/PrivacyProtectionFooterController.swift index 67841bc03a..d2939718d2 100644 --- a/DuckDuckGo/PrivacyProtectionFooterController.swift +++ b/DuckDuckGo/PrivacyProtectionFooterController.swift @@ -145,7 +145,8 @@ class TrackerNetworkPillView: UIView { fileprivate extension PPTrackerNetwork { var image: UIImage { - let imageName = "PP Pill \(name!.lowercased())" + let name = TrackerDataManager.shared.findEntity(byName: self.name ?? "")?.displayName ?? "" + let imageName = "PP Pill \(name.lowercased())" return UIImage(named: imageName) ?? #imageLiteral(resourceName: "PP Pill Generic") } diff --git a/DuckDuckGo/PrivacyProtectionHeaderController.swift b/DuckDuckGo/PrivacyProtectionHeaderController.swift index f1d2e529f6..0306b05ddf 100644 --- a/DuckDuckGo/PrivacyProtectionHeaderController.swift +++ b/DuckDuckGo/PrivacyProtectionHeaderController.swift @@ -76,7 +76,7 @@ class PrivacyProtectionHeaderController: UIViewController { if contentBlockerConfiguration.domainWhitelist.contains(siteRating.domain ?? "") { stackView.addArrangedSubview(protectionDisabledLabel) - } else if siteRating.scores.enhanced != siteRating.scores.site { + } else if differentGrades() { protectionUpgraded.update(with: siteRating) stackView.addArrangedSubview(protectionUpgraded) } else { @@ -84,6 +84,12 @@ class PrivacyProtectionHeaderController: UIViewController { } } + + private func differentGrades() -> Bool { + let siteGrade = siteRating.scores.site.grade.normalize() + let enhancedGrade = siteRating.scores.enhanced.grade.normalize() + return siteGrade != enhancedGrade + } } diff --git a/DuckDuckGo/PrivacyProtectionNetworkLeaderboardController.swift b/DuckDuckGo/PrivacyProtectionNetworkLeaderboardController.swift index 6d2ca6e429..dc62a95917 100644 --- a/DuckDuckGo/PrivacyProtectionNetworkLeaderboardController.swift +++ b/DuckDuckGo/PrivacyProtectionNetworkLeaderboardController.swift @@ -185,7 +185,9 @@ extension PrivacyProtectionNetworkLeaderboardController: UITableViewDataSource { guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as? PrivacyProtectionNetworkLeaderboardCell else { fatalError("Failed to dequeue cell as PrivacyProtectionNetworkLeaderboardCell") } - cell.update(network: network.name!, percent: percent) + + let networkName = TrackerDataManager.shared.findEntity(byName: network.name!)?.displayName ?? network.name! + cell.update(network: networkName, percent: percent) return cell } diff --git a/DuckDuckGo/PrivacyProtectionOverviewController.swift b/DuckDuckGo/PrivacyProtectionOverviewController.swift index fc210bb01f..55df984414 100644 --- a/DuckDuckGo/PrivacyProtectionOverviewController.swift +++ b/DuckDuckGo/PrivacyProtectionOverviewController.swift @@ -112,7 +112,7 @@ class PrivacyProtectionOverviewController: UITableViewController { private func updateTrackers() { trackersCell.summaryLabel.text = siteRating.networksText(configuration: contentBlockerConfiguration) - if protecting() || siteRating.uniqueTrackersDetected == 0 { + if protecting() || siteRating.trackersDetected.count == 0 { trackersCell.summaryImage.image = #imageLiteral(resourceName: "PP Icon Major Networks On") } else { trackersCell.summaryImage.image = #imageLiteral(resourceName: "PP Icon Major Networks Bad") diff --git a/DuckDuckGo/PrivacyProtectionTrackerNetworksController.swift b/DuckDuckGo/PrivacyProtectionTrackerNetworksController.swift index 7bb02da00e..1e46e0cd4e 100644 --- a/DuckDuckGo/PrivacyProtectionTrackerNetworksController.swift +++ b/DuckDuckGo/PrivacyProtectionTrackerNetworksController.swift @@ -30,7 +30,6 @@ class PrivacyProtectionTrackerNetworksController: UIViewController { private var siteRating: SiteRating! private var contentBlockerConfiguration = AppDependencyProvider.shared.storageCache.current.configuration - private var prevalenceStore: PrevalenceStore = AppDependencyProvider.shared.storageCache.current.prevalenceStore struct Section { @@ -70,16 +69,16 @@ class PrivacyProtectionTrackerNetworksController: UIViewController { func update() { guard isViewLoaded else { return } - sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers(), prevalenceStore: prevalenceStore).build() + sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers()).build() updateDomain() updateSubtitle() updateIcon() tableView.reloadData() } - private func trackers() -> [DetectedTracker: Int] { + private func trackers() -> [DetectedTracker] { let protecting = siteRating.protecting(contentBlockerConfiguration) - return protecting ? siteRating.trackersBlocked : siteRating.trackersDetected + return [DetectedTracker](protecting ? siteRating.trackersBlocked : siteRating.trackersDetected) } private func updateDomain() { @@ -92,7 +91,7 @@ class PrivacyProtectionTrackerNetworksController: UIViewController { private func updateIcon() { - if protecting() || siteRating.uniqueTrackerNetworksDetected == 0 { + if protecting() || siteRating.trackerNetworksDetected == 0 { iconImage.image = #imageLiteral(resourceName: "PP Hero Major On") } else { iconImage.image = #imageLiteral(resourceName: "PP Hero Major Bad") @@ -157,40 +156,24 @@ extension PrivacyProtectionTrackerNetworksController: PrivacyProtectionInfoDispl } -class SiteRatingTrackerNetworkSectionBuilder { +struct SiteRatingTrackerNetworkSectionBuilder { - let prevalenceStore: PrevalenceStore - let trackers: [DetectedTracker: Int] - - init(trackers: [DetectedTracker: Int], prevalenceStore: PrevalenceStore) { - self.trackers = trackers - self.prevalenceStore = prevalenceStore - } + let trackers: [DetectedTracker] func build() -> [PrivacyProtectionTrackerNetworksController.Section] { - return toSections(trackers: trackers) + return toSections() } - private func toSections(trackers: [DetectedTracker: Int]) -> [PrivacyProtectionTrackerNetworksController.Section] { + private func toSections() -> [PrivacyProtectionTrackerNetworksController.Section] { var sections = [PrivacyProtectionTrackerNetworksController.Section]() - // work around bug in first party detection - everything *should* have a URL with host - let trackers = trackers.compactMap({ $0.key }).filter({ $0.domain != nil }).sorted(by: { $0.domain! < $1.domain! }) - - // group by tracker types, sorted appropriately - let majorTrackers = trackers.filter({ prevalenceStore.isMajorNetwork(named: $0.networkName) }) - .sorted(by: compareTrackersByPrevalence) - - let nonMajorKnownTrackers = trackers.filter({ $0.networkName != nil && !prevalenceStore.isMajorNetwork(named: $0.networkName) }) - .sorted(by: { $0.networkName! < $1.networkName! }) - - let unknownTrackers = trackers.filter({ $0.networkName == nil }) - - for tracker in majorTrackers + nonMajorKnownTrackers + unknownTrackers { + let sortedTrackers = trackers.sorted(by: compareTrackersByHostName).sorted(by: compareTrackersByPrevalence) + for tracker in sortedTrackers { guard let domain = tracker.domain else { continue } let networkName = tracker.networkNameForDisplay - let row = PrivacyProtectionTrackerNetworksController.Row(name: domain, value: tracker.category ?? "") + let row = PrivacyProtectionTrackerNetworksController.Row(name: domain.dropPrefix(prefix: "www."), + value: tracker.knownTracker?.category ?? "") if let sectionIndex = sections.firstIndex(where: { $0.name == networkName }) { if row.name != networkName { @@ -207,9 +190,11 @@ class SiteRatingTrackerNetworkSectionBuilder { } func compareTrackersByPrevalence(tracker1: DetectedTracker, tracker2: DetectedTracker) -> Bool { - let prevalence1 = prevalenceStore.prevalences[tracker1.networkName ?? ""] ?? 0.0 - let prevalence2 = prevalenceStore.prevalences[tracker2.networkName ?? ""] ?? 0.0 - return prevalence1 > prevalence2 + return tracker1.entity?.prevalence ?? 0 > tracker2.entity?.prevalence ?? 0 + } + + func compareTrackersByHostName(tracker1: DetectedTracker, tracker2: DetectedTracker) -> Bool { + return tracker1.domain ?? "" < tracker2.domain ?? "" } } diff --git a/DuckDuckGo/SiteRatingPrivacyProtectionExtension.swift b/DuckDuckGo/SiteRatingPrivacyProtectionExtension.swift index b23597d410..9f21bf8125 100644 --- a/DuckDuckGo/SiteRatingPrivacyProtectionExtension.swift +++ b/DuckDuckGo/SiteRatingPrivacyProtectionExtension.swift @@ -64,15 +64,15 @@ extension SiteRating { } func majorNetworksSuccess(configuration: ContentBlockerConfigurationStore) -> Bool { - return (protecting(configuration) ? uniqueMajorTrackerNetworksBlocked : uniqueMajorTrackerNetworksDetected) <= 0 + return (protecting(configuration) ? majorTrackerNetworksBlocked : majorTrackerNetworksDetected) <= 0 } func majorNetworksBlockedText() -> String { - return String(format: UserText.privacyProtectionMajorTrackersBlocked, uniqueMajorTrackerNetworksBlocked) + return String(format: UserText.privacyProtectionMajorTrackersBlocked, majorTrackerNetworksBlocked) } func majorNetworksDetectedText() -> String { - return String(format: UserText.privacyProtectionMajorTrackersFound, uniqueMajorTrackerNetworksDetected) + return String(format: UserText.privacyProtectionMajorTrackersFound, majorTrackerNetworksDetected) } func networksText(configuration: ContentBlockerConfigurationStore) -> String { @@ -80,15 +80,15 @@ extension SiteRating { } func networksSuccess(configuration: ContentBlockerConfigurationStore) -> Bool { - return (protecting(configuration) ? uniqueTrackersBlocked : uniqueTrackersDetected) <= 0 + return (protecting(configuration) ? trackersBlocked.count : trackersDetected.count) <= 0 } func networksBlockedText() -> String { - return String(format: UserText.privacyProtectionTrackersBlocked, uniqueTrackersBlocked) + return String(format: UserText.privacyProtectionTrackersBlocked, trackersBlocked.count) } func networksDetectedText() -> String { - return String(format: UserText.privacyProtectionTrackersFound, uniqueTrackersDetected) + return String(format: UserText.privacyProtectionTrackersFound, trackersDetected.count) } func protecting(_ contentBlocker: ContentBlockerConfigurationStore) -> Bool { diff --git a/DuckDuckGo/SiteRatingView.swift b/DuckDuckGo/SiteRatingView.swift index a5e262b64d..294d43116c 100644 --- a/DuckDuckGo/SiteRatingView.swift +++ b/DuckDuckGo/SiteRatingView.swift @@ -55,7 +55,7 @@ public class SiteRatingView: UIView { public func refresh(with storageCache: StorageCache?) { circleIndicator.image = #imageLiteral(resourceName: "PP Indicator Unknown") - guard let storageCache = storageCache, storageCache.hasData else { return } + guard let storageCache = storageCache else { return } guard let siteRating = siteRating else { return } let grades = siteRating.scores diff --git a/DuckDuckGo/TabViewController.swift b/DuckDuckGo/TabViewController.swift index f45e86d7f4..7d28b2bc85 100644 --- a/DuckDuckGo/TabViewController.swift +++ b/DuckDuckGo/TabViewController.swift @@ -190,7 +190,6 @@ class TabViewController: UIViewController { let controller = webView.configuration.userContentController controller.add(self, name: MessageHandlerNames.trackerDetected) controller.add(self, name: MessageHandlerNames.signpost) - controller.add(self, name: MessageHandlerNames.cache) controller.add(self, name: MessageHandlerNames.log) controller.add(self, name: MessageHandlerNames.findInPageHandler) reloadScripts() @@ -453,7 +452,7 @@ class TabViewController: UIViewController { } private func makeSiteRating(url: URL) -> SiteRating { - let entityMapping = storageCache.entityMapping + let entityMapping = EntityMapping() let privacyPractices = PrivacyPractices(tld: storageCache.tld, termsOfServiceStore: storageCache.termsOfServiceStore, entityMapping: entityMapping) @@ -461,8 +460,7 @@ class TabViewController: UIViewController { return SiteRating(url: url, httpsForced: httpsForced, entityMapping: entityMapping, - privacyPractices: privacyPractices, - prevalenceStore: storageCache.prevalenceStore) + privacyPractices: privacyPractices) } private func updateSiteRating() { @@ -557,7 +555,6 @@ class TabViewController: UIViewController { let controller = webView.configuration.userContentController controller.removeScriptMessageHandler(forName: MessageHandlerNames.trackerDetected) controller.removeScriptMessageHandler(forName: MessageHandlerNames.signpost) - controller.removeScriptMessageHandler(forName: MessageHandlerNames.cache) controller.removeScriptMessageHandler(forName: MessageHandlerNames.log) controller.removeScriptMessageHandler(forName: MessageHandlerNames.findInPageHandler) } @@ -574,7 +571,7 @@ class TabViewController: UIViewController { dismiss() tearDown() } -} +} extension TabViewController: WKScriptMessageHandler { @@ -588,7 +585,6 @@ extension TabViewController: WKScriptMessageHandler { private struct MessageHandlerNames { static let trackerDetected = "trackerDetectedMessage" static let signpost = "signpostMessage" - static let cache = "cacheMessage" static let log = "log" static let findInPageHandler = "findInPageHandler" } @@ -597,9 +593,6 @@ extension TabViewController: WKScriptMessageHandler { switch message.name { - case MessageHandlerNames.cache: - handleCache(message: message) - case MessageHandlerNames.signpost: handleSignpost(message: message) @@ -627,14 +620,6 @@ extension TabViewController: WKScriptMessageHandler { private func handleLog(message: WKScriptMessage) { Logger.log(text: String(describing: message.body)) } - - private func handleCache(message: WKScriptMessage) { - Logger.log(text: "\(MessageHandlerNames.cache)") - guard let dict = message.body as? [String: Any] else { return } - guard let name = dict["name"] as? String else { return } - guard let data = dict["data"] as? String else { return } - ContentBlockerStringCache().put(name: name, value: data) - } private func handleSignpost(message: WKScriptMessage) { guard let dict = message.body as? [String: Any], @@ -645,10 +630,16 @@ extension TabViewController: WKScriptMessageHandler { let url = dict["url"] as? String { instrumentation.request(url: url, allowedIn: elapsedTimeInMs) } - } else if event == "Request Blocked" { + } else if event == "Tracker Allowed" { + if let elapsedTimeInMs = dict["time"] as? Double, + let url = dict["url"] as? String, + let reason = dict["reason"] as? String? { + instrumentation.tracker(url: url, allowedIn: elapsedTimeInMs, reason: reason) + } + } else if event == "Tracker Blocked" { if let elapsedTimeInMs = dict["time"] as? Double, let url = dict["url"] as? String { - instrumentation.request(url: url, blockedIn: elapsedTimeInMs) + instrumentation.tracker(url: url, blockedIn: elapsedTimeInMs) } } else if event == "Generic" { if let name = dict["name"] as? String, @@ -672,16 +663,8 @@ extension TabViewController: WKScriptMessageHandler { return } - let url = URL(string: urlString.trimWhitespace()) - var networkName: String? - var category: String? - if let domain = url?.host { - let networkNameAndCategory = storageCache.disconnectMeStore.networkNameAndCategory(forDomain: domain) - networkName = networkNameAndCategory.networkName - category = networkNameAndCategory.category - } - - let tracker = DetectedTracker(url: urlString, networkName: networkName, category: category, blocked: blocked) + let tracker = trackerFromUrl(urlString.trimWhitespace(), blocked) + siteRating.trackerDetected(tracker) onSiteRatingChanged() @@ -689,8 +672,8 @@ extension TabViewController: WKScriptMessageHandler { NetworkLeaderboard.shared.incrementPagesWithTrackers() pageHasTrackers = true } - - if let networkName = networkName { + + if let networkName = tracker.knownTracker?.owner?.name { if !trackerNetworksDetectedOnPage.contains(networkName) { trackerNetworksDetectedOnPage.insert(networkName) NetworkLeaderboard.shared.incrementDetectionCount(forNetworkNamed: networkName) @@ -699,6 +682,13 @@ extension TabViewController: WKScriptMessageHandler { } } + + private func trackerFromUrl(_ urlString: String, _ blocked: Bool) -> DetectedTracker { + let knownTracker = TrackerDataManager.shared.findTracker(forUrl: urlString) + let entity = TrackerDataManager.shared.findEntity(byName: knownTracker?.owner?.name ?? "") + return DetectedTracker(url: urlString, knownTracker: knownTracker, entity: entity, blocked: blocked) + } + } extension TabViewController: WKNavigationDelegate { diff --git a/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index 9d997749f5..06438d39bb 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -94,8 +94,6 @@ public struct UserText { public static let privacyProtectionTOSMixed = NSLocalizedString("privacy.protection.tos.mixed", comment: "Mixed Privacy Practices") public static let privacyProtectionTOSPoor = NSLocalizedString("privacy.protection.tos.poor", comment: "Poor Privacy Practices") - public static let privacyProtectionReloadBlockerLists = NSLocalizedString("privacy.protection.reload.blocker.lists", comment: "This can be caused by a loss of internet connection when loading the content blocking rules.") - public static let ppEncryptionCertError = NSLocalizedString("privacy.protection.encryption.cert.error", comment: "Error extracting certificate") public static let ppEncryptionSubjectName = NSLocalizedString("privacy.protection.encryption.subject.name", comment: "Subject Name") public static let ppEncryptionPublicKey = NSLocalizedString("privacy.protection.encryption.public.key", comment: "Public Key") diff --git a/DuckDuckGo/WhitelistManager.swift b/DuckDuckGo/WhitelistManager.swift index 797025f647..5b11105981 100644 --- a/DuckDuckGo/WhitelistManager.swift +++ b/DuckDuckGo/WhitelistManager.swift @@ -27,7 +27,9 @@ public class WhitelistManager { return contentBlockerConfigurationStore.domainWhitelist.count } - private var domains: [String]? + var domains: [String]? { + Array(contentBlockerConfigurationStore.domainWhitelist).sorted() + } public init(contentBlockerConfigurationStore: ContentBlockerConfigurationStore = ContentBlockerConfigurationUserDefaults()) { self.contentBlockerConfigurationStore = contentBlockerConfigurationStore @@ -35,12 +37,10 @@ public class WhitelistManager { public func add(domain: String) { contentBlockerConfigurationStore.addToWhitelist(domain: domain) - domains = nil } public func remove(domain: String) { contentBlockerConfigurationStore.removeFromWhitelist(domain: domain) - domains = nil } public func isWhitelisted(domain: String) -> Bool { @@ -48,10 +48,7 @@ public class WhitelistManager { } public func domain(at index: Int) -> String? { - if self.domains == nil { - self.domains = Array(contentBlockerConfigurationStore.domainWhitelist).sorted() - } - return self.domains?[index] + return domains?[index] } } diff --git a/DuckDuckGoTests/APIRequestTests.swift b/DuckDuckGoTests/APIRequestTests.swift index 0e3a260989..058f94f27f 100644 --- a/DuckDuckGoTests/APIRequestTests.swift +++ b/DuckDuckGoTests/APIRequestTests.swift @@ -23,8 +23,8 @@ import OHHTTPStubs class APIRequestTests: XCTestCase { - let host = AppUrls().disconnectMeBlockList.host! - let url = AppUrls().disconnectMeBlockList + let host = AppUrls().surrogates.host! + let url = AppUrls().surrogates override func tearDown() { OHHTTPStubs.removeAllStubs() diff --git a/DuckDuckGoTests/ContentBlockerLoaderTests.swift b/DuckDuckGoTests/ContentBlockerLoaderTests.swift index d5aad2ec88..51de9d80f9 100644 --- a/DuckDuckGoTests/ContentBlockerLoaderTests.swift +++ b/DuckDuckGoTests/ContentBlockerLoaderTests.swift @@ -31,7 +31,7 @@ class ContentBlockerLoaderTests: XCTestCase { mockRequest.mockResponse = .error let loader = ContentBlockerLoader(etagStorage: mockEtagStorage) - XCTAssertFalse(loader.checkForUpdates(with: mockStorageCache, dataSource: mockRequest)) + XCTAssertFalse(loader.checkForUpdates(dataSource: mockRequest)) } func testWhenNoEtagIsPresentThenResponseIsStored() { @@ -39,57 +39,59 @@ class ContentBlockerLoaderTests: XCTestCase { mockRequest.mockResponse = .success(etag: "test", data: Data()) let loader = ContentBlockerLoader(etagStorage: mockEtagStorage) - XCTAssert(loader.checkForUpdates(with: mockStorageCache, dataSource: mockRequest)) + XCTAssertTrue(loader.checkForUpdates(dataSource: mockRequest)) - XCTAssertEqual(mockEtagStorage.etags[.disconnectMe], nil) + XCTAssertEqual(mockEtagStorage.etags[.surrogates], nil) loader.applyUpdate(to: mockStorageCache) - XCTAssertEqual(mockEtagStorage.etags[.disconnectMe], "test") - XCTAssertNotNil(mockStorageCache.processedUpdates[.disconnectMe]) + XCTAssertEqual(mockEtagStorage.etags[.surrogates], "test") + XCTAssertNotNil(mockStorageCache.processedUpdates[.surrogates]) } - + func testWhenEtagIsPresentThenResponseIsStoredOnlyWhenNeeded() { mockRequest.mockResponse = .success(etag: "test", data: Data()) - mockEtagStorage.set(etag: "test", for: .disconnectMe) - mockEtagStorage.set(etag: "old", for: .surrogates) + // Incorect etag should be updated + mockEtagStorage.set(etag: "old", for: .surrogates) + + // Has data and the correct etag + XCTAssertTrue(FileStore().persist("{}".data(using: .utf8), forConfiguration: .trackerDataSet)) + mockEtagStorage.set(etag: "test", for: .trackerDataSet) + let loader = ContentBlockerLoader(etagStorage: mockEtagStorage) - XCTAssert(loader.checkForUpdates(with: mockStorageCache, dataSource: mockRequest)) + XCTAssertTrue(loader.checkForUpdates(dataSource: mockRequest)) - XCTAssertEqual(mockEtagStorage.etags[.disconnectMe], "test") + XCTAssertEqual(mockEtagStorage.etags[.trackerDataSet], "test") XCTAssertEqual(mockEtagStorage.etags[.surrogates], "old") - XCTAssertEqual(mockEtagStorage.etags[.trackersWhitelist], nil) + XCTAssertEqual(mockEtagStorage.etags[.temporaryWhitelist], nil) loader.applyUpdate(to: mockStorageCache) - XCTAssertEqual(mockEtagStorage.etags[.disconnectMe], "test") + XCTAssertEqual(mockEtagStorage.etags[.trackerDataSet], "test") XCTAssertEqual(mockEtagStorage.etags[.surrogates], "test") - XCTAssertEqual(mockEtagStorage.etags[.trackersWhitelist], "test") + XCTAssertEqual(mockEtagStorage.etags[.temporaryWhitelist], "test") - XCTAssertNil(mockStorageCache.processedUpdates[.disconnectMe]) + XCTAssertNil(mockStorageCache.processedUpdates[.trackerDataSet]) XCTAssertNotNil(mockStorageCache.processedUpdates[.surrogates]) - XCTAssertNotNil(mockStorageCache.processedUpdates[.trackersWhitelist]) + XCTAssertNotNil(mockStorageCache.processedUpdates[.temporaryWhitelist]) } - + func testWhenEtagIsMissingThenResponseIsStored() { mockRequest.mockResponse = .success(etag: nil, data: Data()) - mockEtagStorage.set(etag: "test", for: .disconnectMe) + mockEtagStorage.set(etag: "test", for: .surrogates) let loader = ContentBlockerLoader(etagStorage: mockEtagStorage) - XCTAssert(loader.checkForUpdates(with: mockStorageCache, dataSource: mockRequest)) + XCTAssertTrue(loader.checkForUpdates(dataSource: mockRequest)) - XCTAssertEqual(mockEtagStorage.etags[.disconnectMe], "test") - XCTAssertEqual(mockEtagStorage.etags[.surrogates], nil) + XCTAssertEqual(mockEtagStorage.etags[.surrogates], "test") loader.applyUpdate(to: mockStorageCache) - XCTAssertEqual(mockEtagStorage.etags[.disconnectMe], "test") - XCTAssertEqual(mockEtagStorage.etags[.surrogates], nil) + XCTAssertEqual(mockEtagStorage.etags[.surrogates], "test") - XCTAssertNotNil(mockStorageCache.processedUpdates[.disconnectMe]) XCTAssertNotNil(mockStorageCache.processedUpdates[.surrogates]) } @@ -98,15 +100,15 @@ class ContentBlockerLoaderTests: XCTestCase { mockRequest.mockResponse = .success(etag: "test", data: Data()) let loader = ContentBlockerLoader(etagStorage: mockEtagStorage) - XCTAssert(loader.checkForUpdates(with: mockStorageCache, dataSource: mockRequest)) + XCTAssertTrue(loader.checkForUpdates(dataSource: mockRequest)) - XCTAssertNil(mockEtagStorage.etags[.disconnectMe]) + XCTAssertNil(mockEtagStorage.etags[.surrogates]) mockStorageCache.shouldFail = true loader.applyUpdate(to: mockStorageCache) - XCTAssertNil(mockEtagStorage.etags[.disconnectMe]) - XCTAssertNotNil(mockStorageCache.processedUpdates[.disconnectMe]) + XCTAssertNil(mockEtagStorage.etags[.surrogates]) + XCTAssertNotNil(mockStorageCache.processedUpdates[.surrogates]) } // Etag OOS tests @@ -114,29 +116,30 @@ class ContentBlockerLoaderTests: XCTestCase { func testWhenEtagIsPresentButStoreHasNoDataThenResponseIsStored() { mockRequest.mockResponse = .success(etag: "test", data: Data()) - mockEtagStorage.set(etag: "test", for: .disconnectMe) - mockEtagStorage.set(etag: "test", for: .trackersWhitelist) - - let loader = ContentBlockerLoader(etagStorage: mockEtagStorage) + mockEtagStorage.set(etag: "test", for: .surrogates) - mockStorageCache.hasDisconnectMeData = false - mockStorageCache.hasEasylistData = false + let loader = ContentBlockerLoader(etagStorage: mockEtagStorage, fileStore: MockFileStore()) - XCTAssert(loader.checkForUpdates(with: mockStorageCache, dataSource: mockRequest)) + XCTAssertTrue(loader.checkForUpdates(dataSource: mockRequest)) - XCTAssertEqual(mockEtagStorage.etags[.disconnectMe], "test") - XCTAssertEqual(mockEtagStorage.etags[.trackersWhitelist], "test") + XCTAssertEqual(mockEtagStorage.etags[.surrogates], "test") loader.applyUpdate(to: mockStorageCache) - XCTAssertEqual(mockEtagStorage.etags[.disconnectMe], "test") - XCTAssertEqual(mockEtagStorage.etags[.trackersWhitelist], "test") + XCTAssertEqual(mockEtagStorage.etags[.surrogates], "test") - XCTAssertNotNil(mockStorageCache.processedUpdates[.disconnectMe]) - XCTAssertNotNil(mockStorageCache.processedUpdates[.trackersWhitelist]) + XCTAssertNotNil(mockStorageCache.processedUpdates[.surrogates]) } } +class MockFileStore: FileStore { + + override func loadAsData(forConfiguration config: ContentBlockerRequest.Configuration) -> Data? { + return nil + } + +} + class MockEtagStorage: BlockerListETagStorage { var etags = [ContentBlockerRequest.Configuration: String]() @@ -165,10 +168,7 @@ class MockContenBlockingRequest: ContentBlockerRemoteDataSource { } } -class MockStorageCache: StorageCacheUpdating, EtagOOSCheckStore { - - var hasDisconnectMeData: Bool = true - var hasEasylistData: Bool = true +class MockStorageCache: StorageCacheUpdating { var processedUpdates = [ContentBlockerRequest.Configuration: Any]() diff --git a/DuckDuckGoTests/ContentBlockerStringCacheTests.swift b/DuckDuckGoTests/ContentBlockerStringCacheTests.swift index e409db7d8e..bfa6b38b14 100644 --- a/DuckDuckGoTests/ContentBlockerStringCacheTests.swift +++ b/DuckDuckGoTests/ContentBlockerStringCacheTests.swift @@ -22,41 +22,24 @@ import XCTest class ContentBlockerStringCacheTests: XCTestCase { - private var testee: ContentBlockerStringCache! - private var userDefaults: UserDefaults! - - override func setUp() { - userDefaults = UserDefaults(suiteName: "test") - userDefaults.removePersistentDomain(forName: "test") - testee = ContentBlockerStringCache(userDefaults: userDefaults) - } - - func testWhenItemsAddedAndCacheClearedItemIsNotReturned() { - testee.put(name: "item", value: "value") - userDefaults.removePersistentDomain(forName: "test") - XCTAssertNil(ContentBlockerStringCache(userDefaults: userDefaults).get(named: "item")) - } - - func testWhenItemAddedDifferentInstanceReturnsIt() { - let expected = UUID.init().uuidString - testee.put(name: "uuid", value: expected) - XCTAssertEqual(expected, ContentBlockerStringCache(userDefaults: userDefaults).get(named: "uuid")) - } - - func testWhenItemRemovedGetReturnsNil() { - testee.put(name: "value", value: "some value") - testee.remove(named: "value") - XCTAssertNil(testee.get(named: "value")) - } - - func testWhenAddItemGetReturnsIt() { - let expected = UUID.init().uuidString - testee.put(name: "uuid", value: expected) - XCTAssertEqual(expected, testee.get(named: "uuid")) - } - - func testWhenGetUnknownItemReturnsNil() { - XCTAssertNil(testee.get(named: "nonesense")) + func testWhenRemovingLegacyDataThenStringCacheDirectoryIsRemoved() { + + let fileManager = FileManager.default + let groupName = ContentBlockerStoreConstants.groupName + let cacheDir = fileManager.containerURL(forSecurityApplicationGroupIdentifier: groupName)!.appendingPathComponent("string-cache") + try? fileManager.createDirectory(at: cacheDir, withIntermediateDirectories: true, attributes: nil) + let file = cacheDir.appendingPathComponent("test") + + do { + try "test".write(to: file, atomically: true, encoding: .utf8) + } catch { + XCTFail("Unable to write file \(error.localizedDescription)") + } + + ContentBlockerStringCache.removeLegacyData() + + XCTAssertFalse(fileManager.fileExists(atPath: cacheDir.absoluteString)) + } } diff --git a/DuckDuckGoTests/DetectedTrackerTests.swift b/DuckDuckGoTests/DetectedTrackerTests.swift index d585f9a094..8fc43e6503 100644 --- a/DuckDuckGoTests/DetectedTrackerTests.swift +++ b/DuckDuckGoTests/DetectedTrackerTests.swift @@ -27,52 +27,19 @@ class DetectedTrackerTests: XCTestCase { static let anotherUrl = "www.anotherurl.com" static let aParentDomain = "adomain.com" static let anotherParentDomain = "anotherdomain.com" - static let ipUrl = "http://192.168.0.1" } - func testWhenUrlWithIPThenIPTracker() { - let testee = DetectedTracker(url: Constants.ipUrl, networkName: Constants.aParentDomain, category: "Category", blocked: true) - XCTAssertTrue(testee.isIpTracker) - } - - func testWhenUrlWithDomainNotIPTracker() { - let testee = DetectedTracker(url: Constants.aUrl, networkName: Constants.aParentDomain, category: "Category", blocked: true) - XCTAssertFalse(testee.isIpTracker) - } + func testWhenTrackersHaveSameEntityThenHashMatchesAndIsEqualsIsTrue() { + + let entity1 = Entity(displayName: "Entity", domains: nil, prevalence: nil) + let entity2 = Entity(displayName: "Entity", domains: [ Constants.aParentDomain ], prevalence: 1) - func testThatHashMatchesAndEqualsIsTrueWhenAllPropertiesAreSame() { - let lhs = DetectedTracker(url: Constants.aUrl, networkName: Constants.aParentDomain, category: "Category", blocked: true) - let rhs = DetectedTracker(url: Constants.aUrl, networkName: Constants.aParentDomain, category: "Category", blocked: true) - XCTAssertEqual(lhs, rhs) - XCTAssertEqual(lhs.hashValue, rhs.hashValue) - } - - func testThatHashDoesntMatchAndEqualsFailsWhenBlockedDifferent() { - let lhs = DetectedTracker(url: Constants.aUrl, networkName: Constants.aParentDomain, category: "Category", blocked: true) - let rhs = DetectedTracker(url: Constants.aUrl, networkName: Constants.aParentDomain, category: "Category", blocked: false) - XCTAssertNotEqual(lhs, rhs) - XCTAssertNotEqual(lhs.hashValue, rhs.hashValue) - } + let tracker1 = DetectedTracker(url: Constants.aUrl, knownTracker: nil, entity: entity1, blocked: true) + let tracker2 = DetectedTracker(url: Constants.anotherUrl, knownTracker: nil, entity: entity2, blocked: false) - func testThatHashDoesntMatchAndEqualsFailsWhenUrlsDifferent() { - let lhs = DetectedTracker(url: Constants.aUrl, networkName: Constants.aParentDomain, category: "Category", blocked: true) - let rhs = DetectedTracker(url: Constants.anotherUrl, networkName: Constants.aParentDomain, category: "Category", blocked: true) - XCTAssertNotEqual(lhs, rhs) - XCTAssertNotEqual(lhs.hashValue, rhs.hashValue) + XCTAssertEqual(tracker1.hashValue, tracker2.hashValue) + XCTAssertEqual(tracker1, tracker2) + } - - func testThatHashDoesntMatchEqualsFailsWhenNetworkNamesAreDifferent() { - let lhs = DetectedTracker(url: Constants.aUrl, networkName: Constants.aParentDomain, category: "Category", blocked: true) - let rhs = DetectedTracker(url: Constants.aUrl, networkName: Constants.anotherParentDomain, category: "Category", blocked: true) - XCTAssertNotEqual(lhs, rhs) - XCTAssertNotEqual(lhs.hashValue, rhs.hashValue) - } - - func testThatHashDoesntMatchEqualsFailsWhenCategoriesAreDifferent() { - let lhs = DetectedTracker(url: Constants.aUrl, networkName: Constants.aParentDomain, category: "Category 1", blocked: true) - let rhs = DetectedTracker(url: Constants.aUrl, networkName: Constants.anotherParentDomain, category: "Category 2", blocked: true) - XCTAssertNotEqual(lhs, rhs) - XCTAssertNotEqual(lhs.hashValue, rhs.hashValue) - } - + } diff --git a/DuckDuckGoTests/DisconnectMeStoreTests.swift b/DuckDuckGoTests/DisconnectMeStoreTests.swift deleted file mode 100644 index 096f4568ea..0000000000 --- a/DuckDuckGoTests/DisconnectMeStoreTests.swift +++ /dev/null @@ -1,119 +0,0 @@ -// -// DisconnectMeStoreTests.swift -// DuckDuckGo -// -// Copyright © 2017 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import XCTest -@testable import Core - -class DisconnectMeStoreTests: XCTestCase { - - var trackerData: Data! - var cache: ContentBlockerStringCache! - var testee: DisconnectMeStore! - - let defaultJsValue = "{\n\n}" - - override func setUp() { - trackerData = JsonTestDataLoader().fromJsonFile("MockFiles/disconnect.json") - cache = ContentBlockerStringCache() - testee = DisconnectMeStore() - clearAll() - } - - override func tearDown() { - clearAll() - } - - func clearAll() { - try? testee.persist(data: "".data(using: .utf8)!) - try? FileManager.default.removeItem(at: DisconnectMeStore.persistenceLocation) - } - - func testWhenItemsAreInAllowedListTheyAppearInAllowedJson() { - try? testee.persist(data: trackerData) - XCTAssertFalse(testee.allowedTrackersJson.contains("99anothersocialurl.com")) - XCTAssertTrue(testee.allowedTrackersJson.contains("acontenturl.com")) - } - - func testWhenItemsAreInBannedListTheyAppearInBannedJson() { - try? testee.persist(data: trackerData) - XCTAssertTrue(testee.bannedTrackersJson.contains("99anothersocialurl.com")) - XCTAssertFalse(testee.bannedTrackersJson.contains("acontenturl.com")) - } - - func testWhenTrackersNotPersistedThenHasDataIsFalse() { - clearAll() - XCTAssertFalse(testee.hasData) - } - - func testWhenTrackersPersistedThenHasDataIsTrue() { - try? testee.persist(data: trackerData) - XCTAssertTrue(testee.hasData) - } - - func testWhenNewDisconnectDataIsPersistedJsBannedCacheIsInvalidated() { - cache.put(name: DisconnectMeStore.CacheKeys.disconnectJsonBanned, value: "someText") - try? testee.persist(data: trackerData) - XCTAssertNil(cache.get(named: DisconnectMeStore.CacheKeys.disconnectJsonBanned)) - } - - func testWhenNewDisconnectDataIsPersistedJsAllowedCacheIsInvalidated() { - cache.put(name: DisconnectMeStore.CacheKeys.disconnectJsonAllowed, value: "someText") - try? testee.persist(data: trackerData) - XCTAssertNil(cache.get(named: DisconnectMeStore.CacheKeys.disconnectJsonAllowed)) - } - - func testWhenBannedJsDoesNotHaveACachedValueThenComputedValueIsReturned() { - try? testee.persist(data: trackerData) - let result = testee.bannedTrackersJson - XCTAssertNotNil(result) - XCTAssertNotEqual(defaultJsValue, result) - } - - func testWhenAllowedJsDoesNotHaveACachedValueThenComputedValueIsReturned() { - try? testee.persist(data: trackerData) - let result = testee.allowedTrackersJson - XCTAssertNotNil(result) - XCTAssertNotEqual(defaultJsValue, result) - } - - func testWhenBannedJsDoesNotHaveACachedValueAndThereIsNoDataForComputationThenDefaultValueIsReturned() { - let result = testee.bannedTrackersJson - XCTAssertEqual(defaultJsValue, result) - } - - func testWhenAllowedJsDoesNotHaveACachedValueAndThereIsNoDataForComputationThenDefaultValueIsReturned() { - let result = testee.allowedTrackersJson - XCTAssertEqual( defaultJsValue, result) - } - - func testWhenNetworkNameAndCategoryExistsForUppercasedDomainTheyAreReturned() { - try? testee.persist(data: trackerData) - let nameAndCategory = testee.networkNameAndCategory(forDomain: "99asocialurl.com".uppercased()) - XCTAssertEqual("asocialurl.com", nameAndCategory.networkName) - XCTAssertEqual("Social", nameAndCategory.category) - } - - func testWhenNetworkNameAndCategoryExistsForDomainTheyAreReturned() { - try? testee.persist(data: trackerData) - let nameAndCategory = testee.networkNameAndCategory(forDomain: "99asocialurl.com") - XCTAssertEqual("asocialurl.com", nameAndCategory.networkName) - XCTAssertEqual("Social", nameAndCategory.category) - } - -} diff --git a/DuckDuckGoTests/DisconnectMeTrackerTests.swift b/DuckDuckGoTests/DisconnectMeTrackerTests.swift deleted file mode 100644 index e3d14b28d5..0000000000 --- a/DuckDuckGoTests/DisconnectMeTrackerTests.swift +++ /dev/null @@ -1,82 +0,0 @@ -// -// DisconnectMeTrackerTests.swift -// DuckDuckGo -// -// Copyright © 2017 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import XCTest -@testable import Core - -class DisconnectMeTrackerTests: XCTestCase { - - struct Constants { - static let gtmUrl = "http://www.googletagmanager.com" - static let yahooAnswersUrl = "http://www.yahooanswers.com" - static let googleNetwork = "Google" - static let yahooNetwork = "Yahoo!" - } - - func testWhenDictionaryHasTrackersCanFilterByCategory() { - let trackers = [ - "Tracker 1": DisconnectMeTracker(url: Constants.gtmUrl, networkName: Constants.googleNetwork, category: .analytics), - "Tracker 2": DisconnectMeTracker(url: Constants.gtmUrl, networkName: Constants.googleNetwork, category: .advertising), - "Tracker 3": DisconnectMeTracker(url: Constants.gtmUrl, networkName: Constants.googleNetwork, category: .analytics), - "Tracker 4": DisconnectMeTracker(url: Constants.gtmUrl, networkName: Constants.googleNetwork, category: .social) - ] - - let filtered = trackers.filter(byCategory: [ .social, .advertising ]) - - XCTAssertEqual(2, filtered.count) - XCTAssertTrue(filtered.contains(where: { $0.value.category == .social })) - XCTAssertTrue(filtered.contains(where: { $0.value.category == .advertising })) - } - - func testWhenTrackersWithSameUrlAndNetworkAndCategoryThenEqualAndHashSame() { - let lhs = DisconnectMeTracker(url: Constants.gtmUrl, networkName: Constants.googleNetwork, category: .social) - let rhs = DisconnectMeTracker(url: Constants.gtmUrl, networkName: Constants.googleNetwork, category: .social) - XCTAssertEqual(lhs, rhs) - XCTAssertEqual(lhs.hash, rhs.hash) - } - - func testWhenTrackersWithSameUrlAndNetworkAndDifferentCategoryThenNotEqualAndHashNotSame() { - let lhs = DisconnectMeTracker(url: Constants.gtmUrl, networkName: Constants.googleNetwork, category: .analytics) - let rhs = DisconnectMeTracker(url: Constants.gtmUrl, networkName: Constants.googleNetwork) - XCTAssertNotEqual(lhs, rhs) - XCTAssertNotEqual(lhs.hash, rhs.hash) - } - - func testWhenTrackersWithDifferentUrlAnSameNetworkThenNotEqualAndHashNotSame() { - let lhs = DisconnectMeTracker(url: Constants.yahooAnswersUrl, networkName: Constants.googleNetwork) - let rhs = DisconnectMeTracker(url: Constants.gtmUrl, networkName: Constants.googleNetwork) - XCTAssertNotEqual(lhs, rhs) - XCTAssertNotEqual(lhs.hash, rhs.hash) - } - - func testWhenTrackersWithSameUrlAndDifferentNetworkThenNotEqualAndHashNotSame() { - let lhs = DisconnectMeTracker(url: Constants.gtmUrl, networkName: Constants.googleNetwork) - let rhs = DisconnectMeTracker(url: Constants.gtmUrl, networkName: Constants.yahooNetwork) - XCTAssertNotEqual(lhs, rhs) - XCTAssertNotEqual(lhs.hash, rhs.hash) - } - - func testWhenTrackersWithSameUrlAndNetworkThenEqualAndHashSame() { - let lhs = DisconnectMeTracker(url: Constants.gtmUrl, networkName: Constants.googleNetwork) - let rhs = DisconnectMeTracker(url: Constants.gtmUrl, networkName: Constants.googleNetwork) - XCTAssertEqual(lhs, rhs) - XCTAssertEqual(lhs.hash, rhs.hash) - } - -} diff --git a/DuckDuckGoTests/DisconnectMeTrackersParserTests.swift b/DuckDuckGoTests/DisconnectMeTrackersParserTests.swift deleted file mode 100644 index 985a4a2848..0000000000 --- a/DuckDuckGoTests/DisconnectMeTrackersParserTests.swift +++ /dev/null @@ -1,84 +0,0 @@ -// -// DisconnectMeTrackersParserTests.swift -// DuckDuckGo -// -// Copyright © 2017 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import XCTest -@testable import Core - -class DisconnectMeTrackersParserTests: XCTestCase { - - private var data = JsonTestDataLoader() - private var testee = DisconnectMeTrackersParser() - - func testWhenDataEmptyThenInvalidJsonErrorThrown() { - XCTAssertThrowsError(try testee.convert(fromJsonData: data.empty()), "") { (error) in - XCTAssertEqual(error.localizedDescription, JsonError.invalidJson.localizedDescription) - } - } - - func testWhenJsonInvalidThenInvalidJsonErrorThrown() { - XCTAssertThrowsError(try testee.convert(fromJsonData: data.invalid()), "") { (error) in - XCTAssertEqual(error.localizedDescription, JsonError.invalidJson.localizedDescription) - } - } - - func testWhenJsonIncorrectForTypeThenTypeMismatchErrorThrown() { - let mismatchedJson = data.fromJsonFile("MockFiles/disconnect_mismatched.json") - XCTAssertThrowsError(try testee.convert(fromJsonData: mismatchedJson), "") { (error) in - XCTAssertEqual(error.localizedDescription, JsonError.typeMismatch.localizedDescription) - } - } - - func testWhenJsonValidThenNoErrorThrown() { - let validJson = data.fromJsonFile("MockFiles/disconnect.json") - XCTAssertNoThrow(try testee.convert(fromJsonData: validJson)) - } - - func testWhenJsonValidThenResultContainsAllTrackers() { - let validJson = data.fromJsonFile("MockFiles/disconnect.json") - guard let result = try? testee.convert(fromJsonData: validJson) else { - fatalError("Failed to convert from json data") - } - XCTAssertEqual(result.count, 12) - XCTAssertEqual(result["99anadurl.com"]?.networkName, "anadurl.com") - XCTAssertEqual(result["analyticsurl.com"]?.networkName, "analytics.com") - XCTAssertEqual(result["99asocialurl.com"]?.networkName, "asocialurl.com") - XCTAssertEqual(result["acontenturl.com"]?.networkName, "content.com") - XCTAssertEqual(result["adisconnecturl.com"]?.networkName, "disconnect.com") - XCTAssertEqual(result["anothersocialurl.com"]?.networkName, "anothersocialurl.com") - XCTAssertEqual(result["55anothersocialurl.com"]?.networkName, "anothersocialurl.com") - XCTAssertEqual(result["99anothersocialurl.com"]?.networkName, "anothersocialurl.com") - XCTAssertEqual(result["anunknowncategory.com"]?.networkName, "unknowncategory.com") - XCTAssertEqual(result["adisconnecturl.com"]?.networkName, "disconnect.com") - } - - func testWhenJsonValidButContainsDntEffElementInFirstPositionThenNoErrorThrown() { - let validJson = data.fromJsonFile("MockFiles/disconnect_dnt.json") - let result = try? testee.convert(fromJsonData: validJson) - XCTAssertNotNil(result) - XCTAssertEqual(result?.count, 1) - } - - func testWhenJsonValidButContainsDntEffElementInSecondPositionThenNoErrorThrown() { - let validJson = data.fromJsonFile("MockFiles/disconnect_dnt2.json") - let result = try? testee.convert(fromJsonData: validJson) - XCTAssertNotNil(result) - XCTAssertEqual(result?.count, 1) - } - -} diff --git a/DuckDuckGoTests/DownloadedEntityMappingStoreTests.swift b/DuckDuckGoTests/DownloadedEntityMappingStoreTests.swift deleted file mode 100644 index b28a83dc8f..0000000000 --- a/DuckDuckGoTests/DownloadedEntityMappingStoreTests.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// DownloadedEntityMappingStoreTests.swift -// DuckDuckGo -// -// Copyright © 2017 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import XCTest -@testable import Core - -class DownloadedEntityMappingStoreTests: XCTestCase { - - override func setUp() { - super.setUp() - - let path = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: ContentBlockerStoreConstants.groupName) - let url = path!.appendingPathComponent(DownloadedEntityMappingStore.filename) - try? FileManager.default.removeItem(at: url) - - } - - func testWhenDataSavedThenDataCanBeLoaded() { - let testee = DownloadedEntityMappingStore() - _ = testee.persist(data: "test".data(using: .utf8)!) - XCTAssertEqual("test", String(data: testee.load()!, encoding: .utf8)) - } - - func testWhenNoDataSavedThenLoadedDataIsNull() { - XCTAssertNil(DownloadedEntityMappingStore().load()) - } - -} diff --git a/DuckDuckGoTests/EasylistStoreTests.swift b/DuckDuckGoTests/EasylistStoreTests.swift deleted file mode 100644 index 0b240aa68d..0000000000 --- a/DuckDuckGoTests/EasylistStoreTests.swift +++ /dev/null @@ -1,110 +0,0 @@ -// -// EasylistStoreTests.swift -// DuckDuckGo -// -// Copyright © 2017 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import XCTest -@testable import Core - -class EasylistStoreTests: XCTestCase { - - override func tearDown() { - - EasylistStore().persistEasylist(data: "".data(using: .utf8)!) - EasylistStore().persistEasylistPrivacy(data: "".data(using: .utf8)!) - - } - - func testWhenEasylistPrivacyPersistedCacheIsInvalidated() { - - ContentBlockerStringCache().put(name: EasylistStore.CacheNames.easylistPrivacy, value: "hello") - - let value = UUID.init().uuidString - let store = EasylistStore() - store.persistEasylistPrivacy(data: value.data(using: .utf8)!) - - XCTAssertNil(ContentBlockerStringCache().get(named: EasylistStore.CacheNames.easylistPrivacy)) - - } - - func testWhenEasylistPersistedCacheIsInvalidated() { - - ContentBlockerStringCache().put(name: EasylistStore.CacheNames.easylist, value: "hello") - - let value = UUID.init().uuidString - let store = EasylistStore() - store.persistEasylist(data: value.data(using: .utf8)!) - - XCTAssertNil(ContentBlockerStringCache().get(named: EasylistStore.CacheNames.easylist)) - - } - - func testWhenEasylistPrivacyPersistedBackslashesAreEscaped() { - - let value = UUID.init().uuidString - let store = EasylistStore() - store.persistEasylistPrivacy(data: "\(value)\\".data(using: .utf8)!) - XCTAssertEqual("\(value)\\\\", store.easylistPrivacy) - - } - - func testWhenEasylistPersistedBackslashesAreEscaped() { - - let value = UUID.init().uuidString - let store = EasylistStore() - store.persistEasylist(data: "\(value)\\".data(using: .utf8)!) - XCTAssertEqual("\(value)\\\\", store.easylist) - - } - - func testWhenEasylistPrivacyPersistedBackticksAreEscaped() { - - let value = UUID.init().uuidString - let store = EasylistStore() - store.persistEasylistPrivacy(data: "\(value)`".data(using: .utf8)!) - XCTAssertEqual("\(value)\\`", store.easylistPrivacy) - - } - - func testWhenEasylistPersistedBackticksAreEscaped() { - - let value = UUID.init().uuidString - let store = EasylistStore() - store.persistEasylist(data: "\(value)`".data(using: .utf8)!) - XCTAssertEqual("\(value)\\`", store.easylist) - - } - - func testWhenEasylistPrivacyPersistedValueIsAvailable() { - - let value = UUID.init().uuidString - let store = EasylistStore() - store.persistEasylistPrivacy(data: value.data(using: .utf8)!) - XCTAssertEqual(value, store.easylistPrivacy) - - } - - func testWhenEasylistPersistedValueIsAvailable() { - - let value = UUID.init().uuidString - let store = EasylistStore() - store.persistEasylist(data: value.data(using: .utf8)!) - XCTAssertEqual(value, store.easylist) - - } - -} diff --git a/DuckDuckGoTests/EmbeddedPrevalenceStoreTests.swift b/DuckDuckGoTests/EmbeddedPrevalenceStoreTests.swift deleted file mode 100644 index cf7d1c9504..0000000000 --- a/DuckDuckGoTests/EmbeddedPrevalenceStoreTests.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// EmbeddedPrevalenceStoreTests.swift -// DuckDuckGo -// -// Copyright © 2017 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import XCTest -@testable import Core - -class EmbeddedPrevalenceStoreTests: XCTestCase { - - func testWhenEntityIsMajorNetworkThenMajorNetworkReturnsTrue() { - let store = EmbeddedPrevalenceStore() - XCTAssertTrue(store.isMajorNetwork(named: "Google")) - XCTAssertFalse(store.isMajorNetwork(named: "Fisku")) - } - - func testWhenStoreIsInstanciatedThenItLoadsPrevalenceJson() { - let store = EmbeddedPrevalenceStore() - XCTAssertEqual(83.513, store.prevalences["Google"]) - XCTAssertEqual(610, store.prevalences.count) - } - -} diff --git a/DuckDuckGoTests/EntityMappingTests.swift b/DuckDuckGoTests/EntityMappingTests.swift index 6de60311b3..0c9f8d91df 100644 --- a/DuckDuckGoTests/EntityMappingTests.swift +++ b/DuckDuckGoTests/EntityMappingTests.swift @@ -22,93 +22,12 @@ import XCTest class EntityMappingTests: XCTestCase { - struct SampleData { - - static let jsonWithUnexpectedItem = """ -{ - "365Media": { - "properties": [ - "aggregateintelligence.com" - ], - "resources": [ - "aggregateintelligence.com" - ], - "unexpected": 100 - } -} -""" - - static let validJson = """ -{ - "365Media": { - "properties": [ - "aggregateintelligence.com" - ], - "resources": [ - "365dm.com", - "365media.com", - "aggregateintelligence.com" - ] - }, - "4mads": { - "properties": [ - "4mads.com" - ], - "resources": [ - "4madsaye.com" - ] - } -} -""" - - static let invalidJson = "{" - - } - + /// This is now based on the embedded tracker data so if this test fails it might be because the embedded data was updated. func testWhenDomainHasSubdomainThenParentEntityIsFound() { - let testee = EntityMapping(store: MockEntityMappingStore(data: SampleData.validJson.data(using: .utf8))) - XCTAssertEqual("365Media", testee.findEntity(forHost: "sub.domain.365dm.com")) - XCTAssertEqual("4mads", testee.findEntity(forHost: "www.4mads.com")) - } - - func testWhenJsonContainsUnexpectedPropertiesThenCorrectEntitiesAreExtracted() { - let testee = EntityMapping(store: MockEntityMappingStore(data: SampleData.jsonWithUnexpectedItem.data(using: .utf8))) - XCTAssertEqual("365Media", testee.findEntity(forHost: "aggregateintelligence.com")) - } - - func testWhenJsonIsInvalidThenNoEntitiesFound() { - let testee = EntityMapping(store: MockEntityMappingStore(data: SampleData.invalidJson.data(using: .utf8))) - XCTAssertNil(testee.findEntity(forHost: "aggregateintelligence.com")) - } - - func testWhenJsonIsValidThenCorrectEntitiesAreExtracted() { - - let testee = EntityMapping(store: MockEntityMappingStore(data: SampleData.validJson.data(using: .utf8))) - XCTAssertEqual("365Media", testee.findEntity(forHost: "aggregateintelligence.com")) - XCTAssertEqual("365Media", testee.findEntity(forHost: "365media.com")) - XCTAssertEqual("365Media", testee.findEntity(forHost: "365dm.com")) - XCTAssertEqual("4mads", testee.findEntity(forHost: "4mads.com")) - XCTAssertEqual("4mads", testee.findEntity(forHost: "4madsaye.com")) - - } - -} - -private class MockEntityMappingStore: EntityMappingStore { - - var data: Data? - - init(data: Data?) { - self.data = data - } - - func load() -> Data? { - return data + let testee = EntityMapping() + XCTAssertEqual("comScore", testee.findEntity(forHost: "sub.domain.comscore.com")?.displayName) + XCTAssertEqual("comScore", testee.findEntity(forHost: "www.comscore.com")?.displayName) + XCTAssertEqual("comScore", testee.findEntity(forHost: "comscore.com")?.displayName) } - func persist(data: Data) -> Bool { - self.data = data - return true - } - } diff --git a/DuckDuckGoTests/FileStoreTests.swift b/DuckDuckGoTests/FileStoreTests.swift new file mode 100644 index 0000000000..97f50df753 --- /dev/null +++ b/DuckDuckGoTests/FileStoreTests.swift @@ -0,0 +1,87 @@ +// +// FileStoreTests.swift +// Core +// +// Copyright © 2019 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest +@testable import Core + +class FileStoreTests: XCTestCase { + + override func setUp() { + super.setUp() + try? FileManager.default.removeItem(at: FileStore().persistenceLocation(forConfiguration: .surrogates)) + try? FileManager.default.removeItem(at: FileStore().persistenceLocation(forConfiguration: .temporaryWhitelist)) + } + + func testWhenFileExistsThenHasDataReturnsTrue() { + let store = FileStore() + XCTAssertFalse(store.hasData(forConfiguration: .surrogates)) + + XCTAssertTrue(store.persist(Data(), forConfiguration: .surrogates)) + XCTAssertTrue(store.hasData(forConfiguration: .surrogates)) + } + + func testWhenNewThenStorageIsEmptyForConfiguration() { + let store = FileStore() + XCTAssertNil(store.loadAsString(forConfiguration: .surrogates)) + } + + func testWhenDataSavedForConfigurationItCanBeLoadedAsAString() { + let uuid = UUID().uuidString + let data = uuid.data(using: .utf8) + let store = FileStore() + XCTAssertTrue(store.persist(data, forConfiguration: .surrogates)) + XCTAssertEqual(uuid, store.loadAsString(forConfiguration: .surrogates)) + XCTAssertNil(store.loadAsString(forConfiguration: .temporaryWhitelist)) + } + + func testWhenRemovingLegacyDataThenItAllGetsDeleted() { + let store = FileStore() + let location = store.persistenceLocation(forConfiguration: .surrogates).deletingLastPathComponent() + + do { + try FileStore.Constants.legacyFiles.forEach { + try "test".write(to: location.appendingPathComponent($0), atomically: true, encoding: .utf8) + } + } catch { + XCTFail("Failed to write file \(error.localizedDescription)") + } + + FileStore().removeLegacyData() + + FileStore.Constants.legacyFiles.forEach { + assertDeleted(location.appendingPathComponent($0)) + } + } + + func testLegacyFiles() { + + XCTAssertEqual([ + "disconnectme.json", + "easylistWhitelist.txt", + "entitylist2.json", + "surrogate.js" + ], FileStore.Constants.legacyFiles) + + } + + private func assertDeleted(_ url: URL, file: StaticString = #file, line: UInt = #line) { + XCTAssertFalse(FileManager.default.fileExists(atPath: url.absoluteString), file: file, line: line) + } + +} diff --git a/DuckDuckGoTests/InitHelpers.swift b/DuckDuckGoTests/InitHelpers.swift index 040512ebd7..059d16ddcd 100644 --- a/DuckDuckGoTests/InitHelpers.swift +++ b/DuckDuckGoTests/InitHelpers.swift @@ -20,13 +20,6 @@ import Foundation @testable import Core -extension EntityMapping { - - public convenience init() { - self.init(store: DownloadedEntityMappingStore()) - } -} - extension PrivacyPractices { public convenience init(termsOfServiceStore: TermsOfServiceStore) { @@ -59,21 +52,18 @@ extension SiteRating { self.init(url: url, httpsForced: httpsForced, entityMapping: entityMapping, - privacyPractices: privacyPractices ?? PrivacyPractices(entityMapping: entityMapping), - prevalenceStore: EmbeddedPrevalenceStore()) + privacyPractices: privacyPractices ?? PrivacyPractices(entityMapping: entityMapping)) } public convenience init(url: URL, httpsForced: Bool = false, - entityMapping: EntityMapping = EntityMapping(), - prevalenceStore: PrevalenceStore) { + entityMapping: EntityMapping = EntityMapping()) { let privacyPractices = PrivacyPractices(entityMapping: entityMapping) self.init(url: url, httpsForced: httpsForced, entityMapping: entityMapping, - privacyPractices: privacyPractices, - prevalenceStore: prevalenceStore) + privacyPractices: privacyPractices) } } diff --git a/DuckDuckGoTests/KnownTrackerTests.swift b/DuckDuckGoTests/KnownTrackerTests.swift new file mode 100644 index 0000000000..5886a79ee8 --- /dev/null +++ b/DuckDuckGoTests/KnownTrackerTests.swift @@ -0,0 +1,49 @@ +// +// KnownTrackerTests.swift +// UnitTests +// +// Copyright © 2019 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest +@testable import Core + +class KnownTrackerTests: XCTestCase { + + func testWhenCategoriesIsPopulatedWithApprovedCategoryThenCategoryIsFirstApproved() { + let tracker = KnownTracker(domain: nil, defaultAction: nil, owner: nil, prevalence: nil, subdomains: nil, categories: [ + "one", "Advertising", "three" + ], rules: nil) + XCTAssertEqual("Advertising", tracker.category) + } + + func testWhenCategoriesIsPopulatedWithUnapprovedCategoriesThenCategoryIsNil() { + let tracker = KnownTracker(domain: nil, defaultAction: nil, owner: nil, prevalence: nil, subdomains: nil, categories: [ + "one", "two", "three" + ], rules: nil) + XCTAssertNil(tracker.category) + } + + func testWhenCategoriesIsEmptyThenCategoryIsNil() { + let tracker = KnownTracker(domain: nil, defaultAction: nil, owner: nil, prevalence: nil, subdomains: nil, categories: [], rules: nil) + XCTAssertNil(tracker.category) + } + + func testWhenCategoriesIsNilThenCategoryIsNil() { + let tracker = KnownTracker(domain: nil, defaultAction: nil, owner: nil, prevalence: nil, subdomains: nil, categories: nil, rules: nil) + XCTAssertNil(tracker.category) + } + +} diff --git a/Core/blockerdata.js b/DuckDuckGoTests/MockEntityMapping.swift similarity index 52% rename from Core/blockerdata.js rename to DuckDuckGoTests/MockEntityMapping.swift index 6b9769e8d5..368c0c8da8 100644 --- a/Core/blockerdata.js +++ b/DuckDuckGoTests/MockEntityMapping.swift @@ -1,8 +1,8 @@ // -// blockerdata.js -// DuckDuckGo +// MockEntityMapping.swift +// Core // -// Copyright © 2017 DuckDuckGo. All rights reserved. +// Copyright © 2019 DuckDuckGo. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -17,15 +17,20 @@ // limitations under the License. // -var duckduckgoBlockerData = { +import Foundation - blockingEnabled: ${blocking_enabled}, - disconnectmeBanned: ${disconnectmeBanned}, - disconnectmeAllowed: ${disconnectmeAllowed}, - whitelist: ${whitelist}, - easylist: {}, - easylistPrivacy: {}, - easylistWhitelist: {}, - surrogates: ${surrogates} +class MockEntityMapping: EntityMapping { + + private var entity: String? + private var prevalence: Double? + + init(entity: String?, prevalence: Double? = nil) { + self.entity = entity + self.prevalence = prevalence + } + override func findEntity(forHost host: String) -> Entity? { + return Entity(displayName: entity, domains: nil, prevalence: prevalence) + } + } diff --git a/DuckDuckGoTests/NetworkLeaderboardTests.swift b/DuckDuckGoTests/NetworkLeaderboardTests.swift index 388022832a..f1e12fd34a 100644 --- a/DuckDuckGoTests/NetworkLeaderboardTests.swift +++ b/DuckDuckGoTests/NetworkLeaderboardTests.swift @@ -25,8 +25,29 @@ class NetworkLeaderboardTests: XCTestCase { override func setUp() { NetworkLeaderboard().reset() + UserDefaults(suiteName: "test")?.removePersistentDomain(forName: "test") } + func testWhenNeedsDataResetThenDataIsRemoved() { + let userDefaults = UserDefaults(suiteName: "test")! + + let populatedLeaderboard = NetworkLeaderboard() + populatedLeaderboard.incrementDetectionCount(forNetworkNamed: "Test") + populatedLeaderboard.incrementPagesLoaded() + XCTAssertFalse(populatedLeaderboard.networksDetected().isEmpty) + + let updatedLeaderboard = NetworkLeaderboard(userDefaults: userDefaults) + XCTAssertTrue(updatedLeaderboard.networksDetected().isEmpty) + + updatedLeaderboard.incrementDetectionCount(forNetworkNamed: "Other") + updatedLeaderboard.incrementPagesLoaded() + XCTAssertFalse(populatedLeaderboard.networksDetected().isEmpty) + + let recreatedLeaderboard = NetworkLeaderboard(userDefaults: userDefaults) + XCTAssertFalse(recreatedLeaderboard.networksDetected().isEmpty) + + } + func testWhenNetworkOnLessThan1PercentOfSitesItIsExcludedFromTheView() { let leaderboard = NetworkLeaderboard() for _ in 0 ..< 200 { diff --git a/DuckDuckGoTests/PrivacyPracticesTests.swift b/DuckDuckGoTests/PrivacyPracticesTests.swift index 2f727d1cd3..9bdc9cecec 100644 --- a/DuckDuckGoTests/PrivacyPracticesTests.swift +++ b/DuckDuckGoTests/PrivacyPracticesTests.swift @@ -39,8 +39,7 @@ class PrivacyPracticesTests: XCTestCase { "sibling4.com": TermsOfService(classification: .d, score: 0, goodReasons: [], badReasons: []) ]) - let entityMappingStore = MockEntityMappingStore() - let entityMapping = EntityMapping(store: entityMappingStore) + let entityMapping = MockEntityMapping(entity: "Sibling", prevalence: 100) let testee = PrivacyPractices(termsOfServiceStore: tosdrStore, entityMapping: entityMapping) XCTAssertEqual(10, testee.findPractice(forHost: "sibling1.com").score) @@ -52,24 +51,21 @@ class PrivacyPracticesTests: XCTestCase { "orphan.com": TermsOfService(classification: .d, score: 0, goodReasons: [], badReasons: []) ]) - let entityMappingStore = MockEntityMappingStore() - let entityMapping = EntityMapping(store: entityMappingStore) + let entityMapping = MockEntityMapping(entity: "Orphan") let testee = PrivacyPractices(termsOfServiceStore: tosdrStore, entityMapping: entityMapping) XCTAssertEqual(10, testee.findPractice(forHost: "orphan.com").score) } func testWhenDomainUsedForLookupThenTermsAreReturned() { - let entityMappingStore = MockEntityMappingStore() - let entityMapping = EntityMapping(store: entityMappingStore) + let entityMapping = MockEntityMapping(entity: "Google") let testee = PrivacyPractices(entityMapping: entityMapping) let practice = testee.findPractice(forHost: "google.com") XCTAssertEqual(.poor, practice.summary) } func testWhenSubDomainUsedForLookupThenTermsAreReturned() { - let entityMappingStore = MockEntityMappingStore() - let entityMapping = EntityMapping(store: entityMappingStore) + let entityMapping = MockEntityMapping(entity: "Google") let testee = PrivacyPractices(entityMapping: entityMapping) let practice = testee.findPractice(forHost: "maps.google.com") XCTAssertEqual(.poor, practice.summary) @@ -82,31 +78,3 @@ private struct MockTermsOfServiceStore: TermsOfServiceStore { var terms: [String: TermsOfService] } - -private class MockEntityMappingStore: EntityMappingStore { - - func load() -> Data? { - return """ -{ - "Google": { - "properties": [ - "google.com" - ] - }, - "SharedParent": { - "properties": [ - "sibling1.com", - "sibling2.com", - "sibling3.com", - "sibling4.com" - ] - } -} -""".data(using: .utf8) - } - - func persist(data: Data) -> Bool { - return true - } - -} diff --git a/DuckDuckGoTests/PrivacyProtectionTrackerNetworksTests.swift b/DuckDuckGoTests/PrivacyProtectionTrackerNetworksTests.swift index ca24b208c3..48ff1eab2e 100644 --- a/DuckDuckGoTests/PrivacyProtectionTrackerNetworksTests.swift +++ b/DuckDuckGoTests/PrivacyProtectionTrackerNetworksTests.swift @@ -24,12 +24,10 @@ import XCTest class PrivacyProtectionTrackerNetworksTests: XCTestCase { - let prevalenceStore: PrevalenceStore = MockPrevalenceStore(prevalences: [ "Major 1": 25.0, "Major 2": 50.0 ]) - func testWhenNetworkNotKnownSectionHasNoRows() { - let trackers = [DetectedTracker(url: "http://tracker1.com", networkName: nil, category: nil, blocked: false): 1] + let trackers = [DetectedTracker(url: "http://tracker1.com", knownTracker: nil, entity: nil, blocked: false)] - let sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers, prevalenceStore: EmbeddedPrevalenceStore()).build() + let sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers).build() XCTAssertEqual(1, sections.count) XCTAssertEqual("tracker1.com", sections[0].name) @@ -37,14 +35,17 @@ class PrivacyProtectionTrackerNetworksTests: XCTestCase { } func testNetworkHasMultipleTrackersThenGroupedCorrectly() { + + let entity1 = Entity(displayName: "Entity 1", domains: nil, prevalence: 100) + let entity2 = Entity(displayName: "Entity 2", domains: nil, prevalence: 0.01) let trackers = [ - DetectedTracker(url: "http://tracker1.com", networkName: "Network 1", category: nil, blocked: false): 1, - DetectedTracker(url: "http://tracker2.com", networkName: "Network 1", category: nil, blocked: false): 1, - DetectedTracker(url: "http://tracker3.com", networkName: "Network 2", category: nil, blocked: false): 1 + DetectedTracker(url: "http://tracker1.com", knownTracker: nil, entity: entity1, blocked: false), + DetectedTracker(url: "http://tracker2.com", knownTracker: nil, entity: entity1, blocked: false), + DetectedTracker(url: "http://tracker3.com", knownTracker: nil, entity: entity2, blocked: false) ] - let sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers, prevalenceStore: EmbeddedPrevalenceStore()).build() + let sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers).build() XCTAssertEqual(2, sections.count) XCTAssertEqual(2, sections[0].rows.count) @@ -53,13 +54,16 @@ class PrivacyProtectionTrackerNetworksTests: XCTestCase { func testWhenMajorNetworkDetectedSectionBuiltWithRowPerUniqueMajorTracker() { + let major1 = Entity(displayName: "Major 1", domains: nil, prevalence: 100) + let major2 = Entity(displayName: "Major 2", domains: nil, prevalence: 99) + let trackers = [ - DetectedTracker(url: "http://tracker1.com", networkName: "Major 1", category: "Category 1", blocked: true): 1, - DetectedTracker(url: "http://tracker2.com", networkName: "Major 1", category: "Category 2", blocked: true): 1, - DetectedTracker(url: "http://tracker3.com", networkName: "Minor", category: "Category 3", blocked: true): 1 + DetectedTracker(url: "http://tracker1.com", knownTracker: nil, entity: major1, blocked: true), + DetectedTracker(url: "http://tracker2.com", knownTracker: nil, entity: major1, blocked: true), + DetectedTracker(url: "http://tracker3.com", knownTracker: nil, entity: major2, blocked: true) ] - let sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers, prevalenceStore: EmbeddedPrevalenceStore()).build() + let sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers).build() XCTAssertEqual(2, sections.count) XCTAssertEqual("Major 1", sections[0].name) @@ -70,13 +74,17 @@ class PrivacyProtectionTrackerNetworksTests: XCTestCase { func testWhenMajorNetworksInTrackersThenSortedToTopOrderedByPercentage() { + let major1 = Entity(displayName: "Major 1", domains: nil, prevalence: 99) + let major2 = Entity(displayName: "Major 2", domains: nil, prevalence: 100) + let minor = Entity(displayName: "Minor", domains: nil, prevalence: 0.01) + let trackers = [ - DetectedTracker(url: "http://tracker3.com", networkName: "Minor", category: "Category 1", blocked: true): 1, - DetectedTracker(url: "http://tracker1.com", networkName: "Major 1", category: "Category 2", blocked: true): 1, - DetectedTracker(url: "http://tracker2.com", networkName: "Major 2", category: "Category 3", blocked: true): 1 + DetectedTracker(url: "http://tracker3.com", knownTracker: nil, entity: minor, blocked: true), + DetectedTracker(url: "http://tracker1.com", knownTracker: nil, entity: major1, blocked: true), + DetectedTracker(url: "http://tracker2.com", knownTracker: nil, entity: major2, blocked: true) ] - let sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers, prevalenceStore: prevalenceStore).build() + let sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers).build() XCTAssertEqual(3, sections.count) XCTAssertEqual("Major 2", sections[0].name) @@ -87,13 +95,15 @@ class PrivacyProtectionTrackerNetworksTests: XCTestCase { func testWhenMajorTrackersThenDomainsAreSorted() { + let major = Entity(displayName: "Major 1", domains: nil, prevalence: 100) + let trackers = [ - DetectedTracker(url: "http://tracker3.com", networkName: "Major 1", category: "Category 1", blocked: true): 1, - DetectedTracker(url: "http://tracker1.com", networkName: "Major 1", category: "Category 2", blocked: true): 1, - DetectedTracker(url: "http://tracker2.com", networkName: "Major 1", category: "Category 3", blocked: true): 1 + DetectedTracker(url: "http://tracker3.com", knownTracker: nil, entity: major, blocked: true), + DetectedTracker(url: "http://tracker1.com", knownTracker: nil, entity: major, blocked: true), + DetectedTracker(url: "http://tracker2.com", knownTracker: nil, entity: major, blocked: true) ] - let sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers, prevalenceStore: prevalenceStore).build() + let sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers).build() XCTAssertEqual(1, sections.count) XCTAssertEqual("Major 1", sections[0].name) @@ -105,13 +115,15 @@ class PrivacyProtectionTrackerNetworksTests: XCTestCase { func testWhenMinorTrackersThenDomainsAreSorted() { + let minor = Entity(displayName: "Minor", domains: nil, prevalence: 0.01) + let trackers = [ - DetectedTracker(url: "http://tracker3.com", networkName: "Minor", category: "Category 1", blocked: true): 1, - DetectedTracker(url: "http://tracker1.com", networkName: "Minor", category: "Category 2", blocked: true): 1, - DetectedTracker(url: "http://tracker2.com", networkName: "Minor", category: "Category 3", blocked: true): 1 + DetectedTracker(url: "http://tracker3.com", knownTracker: nil, entity: minor, blocked: true), + DetectedTracker(url: "http://tracker1.com", knownTracker: nil, entity: minor, blocked: true), + DetectedTracker(url: "http://tracker2.com", knownTracker: nil, entity: minor, blocked: true) ] - let sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers, prevalenceStore: prevalenceStore).build() + let sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers).build() XCTAssertEqual(1, sections.count) XCTAssertEqual("Minor", sections[0].name) @@ -124,12 +136,12 @@ class PrivacyProtectionTrackerNetworksTests: XCTestCase { func testWhenUnknownTrackerNetworkThenDomainsAreSortedAndHaveOwnSection() { let trackers = [ - DetectedTracker(url: "http://tracker3.com", networkName: nil, category: "Category 1", blocked: true): 1, - DetectedTracker(url: "http://tracker1.com", networkName: nil, category: "Category 2", blocked: true): 1, - DetectedTracker(url: "http://tracker2.com", networkName: nil, category: "Category 3", blocked: true): 1 + DetectedTracker(url: "http://tracker3.com", knownTracker: nil, entity: nil, blocked: true), + DetectedTracker(url: "http://tracker1.com", knownTracker: nil, entity: nil, blocked: true), + DetectedTracker(url: "http://tracker2.com", knownTracker: nil, entity: nil, blocked: true) ] - let sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers, prevalenceStore: prevalenceStore).build() + let sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers).build() XCTAssertEqual(3, sections.count) XCTAssertEqual("tracker1.com", sections[0].name) @@ -141,10 +153,10 @@ class PrivacyProtectionTrackerNetworksTests: XCTestCase { func testWhenNoProtocolThenTrackerAddedByDomain() { let trackers = [ - DetectedTracker(url: "//tracker.com", networkName: nil, category: "Category 1", blocked: true): 1 + DetectedTracker(url: "//tracker.com", knownTracker: nil, entity: nil, blocked: true) ] - let sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers, prevalenceStore: prevalenceStore).build() + let sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers).build() XCTAssertEqual(1, sections.count) XCTAssertEqual("tracker.com", sections[0].name) @@ -153,21 +165,12 @@ class PrivacyProtectionTrackerNetworksTests: XCTestCase { func testWhenNoDomainThenTrackerIgnored() { let trackers = [ - DetectedTracker(url: "/tracker3.js", networkName: nil, category: "Category 1", blocked: true): 1 + DetectedTracker(url: "/tracker3.js", knownTracker: nil, entity: nil, blocked: true) ] - let sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers, prevalenceStore: prevalenceStore).build() + let sections = SiteRatingTrackerNetworkSectionBuilder(trackers: trackers).build() XCTAssertEqual(0, sections.count) } } - -private struct MockPrevalenceStore: PrevalenceStore { - - var prevalences: [String: Double] - - func isMajorNetwork(named: String?) -> Bool { - return true - } -} diff --git a/DuckDuckGoTests/SiteRatingPrivacyProtectionExtensionTests.swift b/DuckDuckGoTests/SiteRatingPrivacyProtectionExtensionTests.swift index df8ec5febf..08751689ae 100644 --- a/DuckDuckGoTests/SiteRatingPrivacyProtectionExtensionTests.swift +++ b/DuckDuckGoTests/SiteRatingPrivacyProtectionExtensionTests.swift @@ -28,89 +28,68 @@ class SiteRatingPrivacyProtectionExtensionTests: XCTestCase { struct Constants { static let pageURL = URL(string: "https://example.com")! - static let majorTracker = DisconnectMeTracker(url: Constants.pageURL.absoluteString, networkName: "major") + // static let majorTracker = DisconnectMeTracker(url: Constants.pageURL.absoluteString, networkName: "major") } func testMultipleMajorNetworksBlockedReturnsPluralText() { - let rating = SiteRating(url: Constants.pageURL, - entityMapping: EntityMapping(), - privacyPractices: PrivacyPractices(termsOfServiceStore: MockTermsOfServiceStore()), - prevalenceStore: MockPrevalenceStore(prevalences: [:], major: false)) - rating.trackerDetected(DetectedTracker(url: "someurl", networkName: "major1", category: nil, blocked: true)) - rating.trackerDetected(DetectedTracker(url: "otherurl", networkName: "major2", category: nil, blocked: true)) + let rating = SiteRating(url: Constants.pageURL) + rating.trackerDetected(DetectedTracker(url: "someurl", knownTracker: nil, entity: nil, blocked: true)) + rating.trackerDetected(DetectedTracker(url: "otherurl", knownTracker: nil, entity: nil, blocked: true)) XCTAssertTrue(rating.majorNetworksBlockedText().contains("Trackers Blocked")) } func testMultipleMajorNetworksDetectedReturnsPluralText() { - let rating = SiteRating(url: Constants.pageURL, - entityMapping: EntityMapping(), - privacyPractices: PrivacyPractices(termsOfServiceStore: MockTermsOfServiceStore()), - prevalenceStore: MockPrevalenceStore(prevalences: [:], major: false)) - rating.trackerDetected(DetectedTracker(url: "someurl", networkName: "major1", category: nil, blocked: false)) - rating.trackerDetected(DetectedTracker(url: "otherurl", networkName: "major2", category: nil, blocked: false)) + let rating = SiteRating(url: Constants.pageURL) + rating.trackerDetected(DetectedTracker(url: "someurl", knownTracker: nil, entity: nil, blocked: false)) + rating.trackerDetected(DetectedTracker(url: "otherurl", knownTracker: nil, entity: nil, blocked: false)) XCTAssertTrue(rating.majorNetworksDetectedText().contains("Trackers Found")) } func testMultipleNetworksBlockedReturnsPluralText() { let rating = SiteRating(url: Constants.pageURL) - rating.trackerDetected(DetectedTracker(url: "someurl", networkName: "minor1", category: nil, blocked: true)) - rating.trackerDetected(DetectedTracker(url: "otherurl", networkName: "minor2", category: nil, blocked: true)) + rating.trackerDetected(DetectedTracker(url: "someurl", knownTracker: nil, entity: nil, blocked: true)) + rating.trackerDetected(DetectedTracker(url: "otherurl", knownTracker: nil, entity: nil, blocked: true)) XCTAssertTrue(rating.networksBlockedText().contains("Trackers Blocked")) } func testMultipleNetworksDetectedReturnsPluralText() { let rating = SiteRating(url: Constants.pageURL) - rating.trackerDetected(DetectedTracker(url: "someurl", networkName: "minor1", category: nil, blocked: false)) - rating.trackerDetected(DetectedTracker(url: "otherurl", networkName: "minor2", category: nil, blocked: false)) + rating.trackerDetected(DetectedTracker(url: "someurl", knownTracker: nil, entity: nil, blocked: false)) + rating.trackerDetected(DetectedTracker(url: "otherurl", knownTracker: nil, entity: nil, blocked: false)) XCTAssertTrue(rating.networksDetectedText().contains("Trackers Found")) } func testSingleMajorNetworkBlockedReturnsSinglularText() { - let rating = SiteRating(url: Constants.pageURL, - entityMapping: EntityMapping(), - // disconnectMeTrackers: [ Constants.pageURL.host!: Constants.majorTracker], - privacyPractices: PrivacyPractices(termsOfServiceStore: MockTermsOfServiceStore()), - prevalenceStore: MockPrevalenceStore(prevalences: [ "major": 100.0 ], major: true)) - rating.trackerDetected(DetectedTracker(url: "someurl", networkName: "major", category: nil, blocked: true)) + let rating = SiteRating(url: Constants.pageURL) + let entity = Entity(displayName: "Entity", domains: nil, prevalence: 100) + rating.trackerDetected(DetectedTracker(url: "someurl", knownTracker: nil, entity: entity, blocked: true)) XCTAssertTrue(rating.majorNetworksBlockedText().contains("Tracker Blocked")) } func testSingleMajorNetworkDetectedReturnsSinglularText() { - let rating = SiteRating(url: Constants.pageURL, - entityMapping: EntityMapping(), - // disconnectMeTrackers: [ Constants.pageURL.host!: Constants.majorTracker], - privacyPractices: PrivacyPractices(termsOfServiceStore: MockTermsOfServiceStore()), - prevalenceStore: MockPrevalenceStore(prevalences: [ "major": 100.0 ], major: true)) - rating.trackerDetected(DetectedTracker(url: "someurl", networkName: "major", category: nil, blocked: false)) + let rating = SiteRating(url: Constants.pageURL) + let entity = Entity(displayName: "Entity", domains: nil, prevalence: 100) + rating.trackerDetected(DetectedTracker(url: "someurl", knownTracker: nil, entity: entity, blocked: false)) XCTAssertTrue(rating.majorNetworksDetectedText().contains("Tracker Found")) } func testSingleNetworkBlockedReturnsSinglularText() { let rating = SiteRating(url: Constants.pageURL) - rating.trackerDetected(DetectedTracker(url: "someurl", networkName: "minor", category: nil, blocked: true)) + let entity = Entity(displayName: "Entity", domains: nil, prevalence: nil) + rating.trackerDetected(DetectedTracker(url: "someurl", knownTracker: nil, entity: entity, blocked: true)) XCTAssertTrue(rating.networksBlockedText().contains("Tracker Blocked")) } func testSingleNetworkDetectedReturnsSinglularText() { let rating = SiteRating(url: Constants.pageURL) - rating.trackerDetected(DetectedTracker(url: "someurl", networkName: "minor", category: nil, blocked: false)) + let entity = Entity(displayName: "Entity", domains: nil, prevalence: nil) + rating.trackerDetected(DetectedTracker(url: "someurl", knownTracker: nil, entity: entity, blocked: false)) XCTAssertTrue(rating.networksDetectedText().contains("Tracker Found")) } } -private struct MockPrevalenceStore: PrevalenceStore { - - var prevalences: [String: Double] - var major: Bool - - func isMajorNetwork(named: String?) -> Bool { - return major - } - -} - private class MockTermsOfServiceStore: TermsOfServiceStore { var terms = [String: TermsOfService]() diff --git a/DuckDuckGoTests/SiteRatingTests.swift b/DuckDuckGoTests/SiteRatingTests.swift index c30d6040af..372018bcf9 100644 --- a/DuckDuckGoTests/SiteRatingTests.swift +++ b/DuckDuckGoTests/SiteRatingTests.swift @@ -37,12 +37,17 @@ class SiteRatingTests: XCTestCase { } struct TrackerMock { - static let blockedTracker = DetectedTracker(url: Url.tracker, networkName: Url.tracker, category: "tracker", blocked: true) - static let unblockedTracker = DetectedTracker(url: Url.tracker, networkName: Url.tracker, category: "tracker", blocked: false) - static let differentTracker = DetectedTracker(url: Url.differentTracker, - networkName: Url.differentTracker, - category: "tracker", - blocked: true) + + static let knownTracker1 = KnownTracker.build(domain: "tracker1.com", ownerName: "Owner 1", category: "tracker") + static let knownTracker2 = KnownTracker.build(domain: "tracker1.com", ownerName: "Owner 2", category: "tracker") + + static let entity1 = Entity(displayName: "Entity 1", domains: nil, prevalence: 1) + static let entity2 = Entity(displayName: "Entity 2", domains: nil, prevalence: 2) + + static let blockedTracker = DetectedTracker(url: Url.tracker, knownTracker: knownTracker1, entity: entity1, blocked: true) + static let unblockedTracker = DetectedTracker(url: Url.tracker, knownTracker: knownTracker1, entity: entity1, blocked: false) + static let differentTracker = DetectedTracker(url: Url.differentTracker, knownTracker: knownTracker2, entity: entity2, blocked: true) + } fileprivate let classATOS = MockTermsOfServiceStore().add(domain: "example.com", classification: .a, score: -100) @@ -66,18 +71,15 @@ class SiteRatingTests: XCTestCase { } func testWhenEntityHasHighPrevalenceThenScoreSetCorrectly() { - let entityMapping = MockEntityMapping(entity: "Google") - - let highPrevalenceStore = MockPrevalenceStore(prevalences: ["Google": 100.0], major: false) - let testeeHighPrevalence = SiteRating(url: Url.googlemail, entityMapping: entityMapping, prevalenceStore: highPrevalenceStore) + let entityMappingLow = MockEntityMapping(entity: "Google", prevalence: 100) + let testeeHighPrevalence = SiteRating(url: Url.googlemail, entityMapping: entityMappingLow) let highPrevalenceScore = testeeHighPrevalence.scores.site.score - let lowPrevalenceStore = MockPrevalenceStore(prevalences: ["Google": 0.1], major: false) - let testeeLowPrevalence = SiteRating(url: Url.googlemail, entityMapping: entityMapping, prevalenceStore: lowPrevalenceStore) + let entityMappingHigh = MockEntityMapping(entity: "Google", prevalence: 1) + let testeeLowPrevalence = SiteRating(url: Url.googlemail, entityMapping: entityMappingHigh) let lowPevalenceScore = testeeLowPrevalence.scores.site.score XCTAssertTrue(highPrevalenceScore > lowPevalenceScore) - } func testWhenUrlHasTosThenTosReturned() { @@ -107,60 +109,38 @@ class SiteRatingTests: XCTestCase { func testCountsAreInitiallyZero() { let testee = SiteRating(url: Url.https) XCTAssertEqual(testee.totalTrackersDetected, 0) - XCTAssertEqual(testee.uniqueTrackersDetected, 0) XCTAssertEqual(testee.totalTrackersBlocked, 0) - XCTAssertEqual(testee.uniqueTrackersBlocked, 0) } - func testWhenUniqueTrackersAreBlockedThenAllDetectionAndBlockCountsIncremenet() { + func testWhenUniqueTrackersAreBlockedThenBlockedCountsIncremented() { let testee = SiteRating(url: Url.https) testee.trackerDetected(TrackerMock.blockedTracker) testee.trackerDetected(TrackerMock.differentTracker) - XCTAssertEqual(testee.totalTrackersDetected, 2) - XCTAssertEqual(testee.uniqueTrackersDetected, 2) + XCTAssertEqual(testee.totalTrackersDetected, 0) XCTAssertEqual(testee.totalTrackersBlocked, 2) - XCTAssertEqual(testee.uniqueTrackersBlocked, 2) } func testWhenRepeatTrackersAreBlockedThenUniqueCountsOnlyIncrementOnce() { let testee = SiteRating(url: Url.https) testee.trackerDetected(TrackerMock.blockedTracker) testee.trackerDetected(TrackerMock.blockedTracker) - XCTAssertEqual(testee.totalTrackersDetected, 2) - XCTAssertEqual(testee.uniqueTrackersDetected, 1) - XCTAssertEqual(testee.totalTrackersBlocked, 2) - XCTAssertEqual(testee.uniqueTrackersBlocked, 1) + XCTAssertEqual(testee.totalTrackersDetected, 0) + XCTAssertEqual(testee.totalTrackersBlocked, 1) } - func testWhenNotBlockerThenDetectedCountsIncrementButBlockCountsDoNot() { + func testWhenRepeatTrackersAreDetectedThenUniqueCountsOnlyIncrementOnce() { let testee = SiteRating(url: Url.https) testee.trackerDetected(TrackerMock.unblockedTracker) + testee.trackerDetected(TrackerMock.unblockedTracker) XCTAssertEqual(testee.totalTrackersDetected, 1) - XCTAssertEqual(testee.uniqueTrackersDetected, 1) XCTAssertEqual(testee.totalTrackersBlocked, 0) - XCTAssertEqual(testee.uniqueTrackersBlocked, 0) } func testWhenUrlDoeNotHaveTosThenPrivacyPracticesSummaryIsUnknown() { let testee = SiteRating(url: Url.http) XCTAssertEqual(.unknown, testee.privacyPractice.summary) } - - func testUniqueMajorTrackersDetected() { - let tracker = DetectedTracker(url: "googlemail.com", networkName: "Google", category: nil, blocked: false) - let testee = SiteRating(url: Url.googlemail, entityMapping: MockEntityMapping(entity: "Google")) - testee.trackerDetected(tracker) - XCTAssertEqual(1, testee.uniqueMajorTrackerNetworksDetected) - XCTAssertEqual(0, testee.uniqueMajorTrackerNetworksBlocked) - } - - func testUniqueMajorTrackersBlocked() { - let tracker = DetectedTracker(url: "googlemail.com", networkName: "Google", category: nil, blocked: true) - let testee = SiteRating(url: Url.googlemail, entityMapping: MockEntityMapping(entity: "Google")) - testee.trackerDetected(tracker) - XCTAssertEqual(1, testee.uniqueMajorTrackerNetworksBlocked) - } - + func testWhenHttpsAndIsForcedThenEncryptionTypeIsForced() { let testee = SiteRating(url: Url.https, httpsForced: true) XCTAssertEqual(.forced, testee.encryptionType) @@ -189,11 +169,9 @@ class SiteRatingTests: XCTestCase { } func testWhenUrlBelongsToMajorNetworkThenIsMajorNetworkReturnsTrue() { - let mockPrevalenceStore = MockPrevalenceStore(prevalences: ["TrickyAds": 100.0], major: true) let testee = SiteRating(url: Url.http, - entityMapping: MockEntityMapping(entity: "TrickyAds"), - privacyPractices: PrivacyPractices(termsOfServiceStore: classATOS), - prevalenceStore: mockPrevalenceStore) + entityMapping: MockEntityMapping(entity: "TrickyAds", prevalence: 100), + privacyPractices: PrivacyPractices(termsOfServiceStore: classATOS)) XCTAssertTrue(testee.isMajorTrackerNetwork) } @@ -210,21 +188,6 @@ class SiteRatingTests: XCTestCase { } -private class MockEntityMapping: EntityMapping { - - private var entity: String? - - init(entity: String?) { - self.entity = entity - super.init(store: DownloadedEntityMappingStore()) - } - - override func findEntity(forHost host: String) -> String? { - return entity - } - -} - private class MockTermsOfServiceStore: TermsOfServiceStore { var terms = [String: TermsOfService]() @@ -248,13 +211,11 @@ private class MockTermsOfServiceStore: TermsOfServiceStore { } -private struct MockPrevalenceStore: PrevalenceStore { - - var prevalences: [String: Double] - var major: Bool +fileprivate extension KnownTracker { - func isMajorNetwork(named: String?) -> Bool { - return major + static func build(domain: String, ownerName: String, category: String) -> KnownTracker { + let owner = KnownTracker.Owner(name: ownerName, displayName: ownerName) + return KnownTracker(domain: domain, defaultAction: nil, owner: owner, prevalence: nil, subdomains: nil, categories: [category], rules: nil) } } diff --git a/DuckDuckGoTests/SurrogateTests.swift b/DuckDuckGoTests/SurrogateTests.swift deleted file mode 100644 index b824b63bd3..0000000000 --- a/DuckDuckGoTests/SurrogateTests.swift +++ /dev/null @@ -1,83 +0,0 @@ -// -// DisconnectMeStoreTests.swift -// DuckDuckGo -// -// Copyright © 2017 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import XCTest -@testable import Core - -class SurrogateTests: XCTestCase { - - private var bundle: Bundle { - return Bundle(for: type(of: self)) - } - - var data: Data! - - override func setUp() { - guard let data = try? FileLoader().load(fileName: "MockFiles/surrogates1.txt", fromBundle: bundle) else { - XCTFail("Failed to load MockFiles/surrogate1s.txt") - return - } - self.data = data - } - - func testWhenSurrogatesFileStoredThenCanBeLoadedLater() { - let persister = SurrogateStore() - persister.parseAndPersist(data: data) - - let loader = SurrogateStore() - XCTAssertNotNil(loader.jsFunctions) - XCTAssertEqual(2, loader.jsFunctions?.count) - } - - func testWhenSurrogatesFileProperlyFormattedThenParsedInToFunctionDictionary() { - - guard let surrogateFile = String(data: data, encoding: .utf8) else { - XCTFail("Failed to convert mock surrogate data in to a String") - return - } - - let dict = SurrogateParser.parse(lines: surrogateFile.components(separatedBy: .newlines)) - XCTAssertEqual(2, dict.count) - - XCTAssertTrue(dict["example.com/script1.js"]?.hasPrefix("(function() {") ?? false) - XCTAssertTrue(dict["example.com/script1.js"]?.contains("console.log(\"Sample function 1\")") ?? false) - XCTAssertTrue(dict["example.com/script1.js"]?.hasSuffix("})();") ?? false) - - XCTAssertTrue(dict["example.com/script2.js"]?.hasPrefix("(function() {") ?? false) - XCTAssertTrue(dict["example.com/script2.js"]?.contains("console.log(\"Sample function 2\")") ?? false) - XCTAssertTrue(dict["example.com/script2.js"]?.hasSuffix("}) ();") ?? false) - - } - - func testWhenFileHasTrailingWhitespaceThenParsingSucceeds() { - guard let data = try? FileLoader().load(fileName: "MockFiles/surrogates2.txt", fromBundle: bundle) else { - XCTFail("Failed to load MockFiles/surrogates2.txt") - return - } - - guard let surrogateFile = String(data: data, encoding: .utf8) else { - XCTFail("Failed to convert mock surrogate data in to a String") - return - } - - let dict = SurrogateParser.parse(lines: surrogateFile.components(separatedBy: .newlines)) - XCTAssertEqual(1, dict.count) - } - -} diff --git a/DuckDuckGoTests/TrackerDataManagerTests.swift b/DuckDuckGoTests/TrackerDataManagerTests.swift new file mode 100644 index 0000000000..2803427677 --- /dev/null +++ b/DuckDuckGoTests/TrackerDataManagerTests.swift @@ -0,0 +1,109 @@ +// +// TrackerDataManagerTests.swift +// Core +// +// Copyright © 2019 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest +@testable import Core + +class TrackerDataManagerTests: XCTestCase { + + override func setUp() { + super.setUp() + try? FileManager.default.removeItem(at: FileStore().persistenceLocation(forConfiguration: .trackerDataSet)) + } + + func testWhenReloadCalledInitiallyThenDataSetIsEmbedded() { + XCTAssertEqual(TrackerDataManager.shared.reload(), .embedded) + } + + func testFindTrackerByUrl() { + let tracker = TrackerDataManager.shared.findTracker(forUrl: "http://googletagmanager.com") + XCTAssertNotNil(tracker) + XCTAssertEqual("Google", tracker?.owner?.displayName) + } + + func testFindEntityByName() { + let entity = TrackerDataManager.shared.findEntity(byName: "Google LLC") + XCTAssertNotNil(entity) + XCTAssertEqual("Google", entity?.displayName) + } + + func testFindEntityForHost() { + let entity = TrackerDataManager.shared.findEntity(forHost: "www.google.com") + XCTAssertNotNil(entity) + XCTAssertEqual("Google", entity?.displayName) + } + + // swiftlint:disable function_body_length + func testWhenDownloadedDataAvailableThenReloadUsesIt() { + + let update = """ + { + "trackers": { + "notreal.io": { + "domain": "notreal.io", + "default": "block", + "owner": { + "name": "CleverDATA LLC", + "displayName": "CleverDATA", + "privacyPolicy": "https://hermann.ai/privacy-en", + "url": "http://hermann.ai" + }, + "source": [ + "DDG" + ], + "prevalence": 0.002, + "fingerprinting": 0, + "cookies": 0.002, + "performance": { + "time": 1, + "size": 1, + "cpu": 1, + "cache": 3 + }, + "categories": [ + "Ad Motivated Tracking", + "Advertising", + "Analytics", + "Third-Party Analytics Marketing" + ] + } + }, + "entities": { + "Not Real": { + "domains": [ + "notreal.io" + ], + "displayName": "Not Real", + "prevalence": 0.666 + } + }, + "domains": { + "notreal.io": "Not Real" + } + } + """ + + XCTAssertTrue(FileStore().persist(update.data(using: .utf8), forConfiguration: .trackerDataSet)) + XCTAssertEqual(TrackerDataManager.shared.reload(), .downloaded) + XCTAssertNil(TrackerDataManager.shared.findEntity(byName: "Google LLC")) + XCTAssertNotNil(TrackerDataManager.shared.findEntity(byName: "Not Real")) + + } + // swiftlint:enable function_body_length +} diff --git a/Instruments/Instruments.instrpkg b/Instruments/Instruments.instrpkg index c40fb04e6f..b496796fd9 100644 --- a/Instruments/Instruments.instrpkg +++ b/Instruments/Instruments.instrpkg @@ -120,7 +120,7 @@ "com.duckduckgo.instrumentation" - "[" ?website-url "] Request: " ?tracker-lookup-url " - " ?tracker-lookup-result " in " ?tracker-lookup-duration + "[" ?website-url "] Request: " ?tracker-lookup-url " - " ?tracker-lookup-type " - " ?tracker-lookup-result " (" ?tracker-lookup-reason ") in " ?tracker-lookup-duration @@ -137,6 +137,20 @@ ?tracker-lookup-url + + tracker-lookup-type + Type + raw-string + ?tracker-lookup-type + + + + tracker-lookup-reason + Reason + raw-string + ?tracker-lookup-reason + + tracker-lookup-duration Lookup duration @@ -192,6 +206,7 @@ tab-id Tab %s website-url + website-url @@ -254,6 +269,11 @@ Total time duration + + + Standard deviation + duration + @@ -261,7 +281,9 @@ tracker-events-table website-url tracker-lookup-url + tracker-lookup-type tracker-lookup-result + tracker-lookup-reason tracker-lookup-duration @@ -274,9 +296,17 @@ website-url + + tracker-lookup-type + + tracker-lookup-result + + + tracker-lookup-reason + @@ -366,6 +396,42 @@ website-url duration + + + Website Loading (aggregation) + website-events-table + + + Count + + + + + Min duration + duration + + + + Max duration + duration + + + + Average duration + duration + + + + Standard deviation + duration + + + + Total time + duration + + +