diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 13088b58c6..447fdaf4da 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -238,7 +238,6 @@ 3706FA80293F65D500E42796 /* TabLazyLoaderDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37534CA2281132CB002621E7 /* TabLazyLoaderDataSource.swift */; }; 3706FA81293F65D500E42796 /* LoginImport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B723DF426B0002B00E14D75 /* LoginImport.swift */; }; 3706FA83293F65D500E42796 /* LazyLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37534C9F28113101002621E7 /* LazyLoadable.swift */; }; - 3706FA84293F65D500E42796 /* ClickToLoadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAE427FF275D47FA00DAC26B /* ClickToLoadModel.swift */; }; 3706FA85293F65D500E42796 /* KeyedCodingExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0230C0A2272080090018F728 /* KeyedCodingExtension.swift */; }; 3706FA87293F65D500E42796 /* DownloadListStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6C0B22F26E61D630031CB7F /* DownloadListStore.swift */; }; 3706FA88293F65D500E42796 /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85799C1725DEBB3F0007EC87 /* Logging.swift */; }; @@ -685,14 +684,12 @@ 3706FCB7293F65D500E42796 /* 01_Fire_really_small.json in Resources */ = {isa = PBXBuildFile; fileRef = 8511E18325F82B34002F516B /* 01_Fire_really_small.json */; }; 3706FCB8293F65D500E42796 /* Onboarding.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85B7184927677C2D00B4277F /* Onboarding.storyboard */; }; 3706FCB9293F65D500E42796 /* FireproofDomains.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4B0511AD262CAA5A00F6079C /* FireproofDomains.storyboard */; }; - 3706FCBA293F65D500E42796 /* clickToLoadConfig.json in Resources */ = {isa = PBXBuildFile; fileRef = EA47767F272A21B700419EDA /* clickToLoadConfig.json */; }; 3706FCBC293F65D500E42796 /* dark-shield.json in Resources */ = {isa = PBXBuildFile; fileRef = AA34396F2754D4E900B241FA /* dark-shield.json */; }; 3706FCBD293F65D500E42796 /* dark-shield-mouse-over.json in Resources */ = {isa = PBXBuildFile; fileRef = AA7EB6EA27E880AE00036718 /* dark-shield-mouse-over.json */; }; 3706FCBE293F65D500E42796 /* autoconsent-bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = B31055C327A1BA1D001AC618 /* autoconsent-bundle.js */; }; 3706FCBF293F65D500E42796 /* ContentOverlay.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7B1E819C27C8874900FF0E60 /* ContentOverlay.storyboard */; }; 3706FCC0293F65D500E42796 /* FindInPage.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85A0117325AF2EDF00FA6A0C /* FindInPage.storyboard */; }; 3706FCC3293F65D500E42796 /* userscript.js in Resources */ = {isa = PBXBuildFile; fileRef = B31055BE27A1BA1D001AC618 /* userscript.js */; }; - 3706FCC4293F65D500E42796 /* fb-tds.json in Resources */ = {isa = PBXBuildFile; fileRef = EA4617EF273A28A700F110A2 /* fb-tds.json */; }; 3706FCC6293F65D500E42796 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = AA68C3D62490F821001B8783 /* README.md */; }; 3706FCC8293F65D500E42796 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AA585D85248FD31400E9A3E2 /* Assets.xcassets */; }; 3706FCC9293F65D500E42796 /* NavigationBar.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85589E8C27BBBB870038AD11 /* NavigationBar.storyboard */; }; @@ -714,7 +711,6 @@ 3706FCE4293F65D500E42796 /* Fire.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AAB7320626DD0C37002FACF9 /* Fire.storyboard */; }; 3706FCE6293F65D500E42796 /* social_images in Resources */ = {isa = PBXBuildFile; fileRef = EA18D1C9272F0DC8006DC101 /* social_images */; }; 3706FCE7293F65D500E42796 /* shield-dot-mouse-over.json in Resources */ = {isa = PBXBuildFile; fileRef = AA7EB6E827E880A600036718 /* shield-dot-mouse-over.json */; }; - 3706FCE9293F65D500E42796 /* fb-sdk.js in Resources */ = {isa = PBXBuildFile; fileRef = EAC80DDF271F6C0100BBF02D /* fb-sdk.js */; }; 3706FCEA293F65D500E42796 /* PasswordManager.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85625993269C8F9600EE44BC /* PasswordManager.storyboard */; }; 3706FCEB293F65D500E42796 /* dark-flame-mouse-over.json in Resources */ = {isa = PBXBuildFile; fileRef = AA7EB6E127E7D05500036718 /* dark-flame-mouse-over.json */; }; 3706FCEC293F65D500E42796 /* flame-mouse-over.json in Resources */ = {isa = PBXBuildFile; fileRef = AA7EB6E027E7D05500036718 /* flame-mouse-over.json */; }; @@ -724,11 +720,8 @@ 3706FCF0293F65D500E42796 /* httpsMobileV2BloomSpec.json in Resources */ = {isa = PBXBuildFile; fileRef = 4B677427255DBEB800025BD8 /* httpsMobileV2BloomSpec.json */; }; 3706FCF1293F65D500E42796 /* TabBarFooter.xib in Resources */ = {isa = PBXBuildFile; fileRef = AA2CB12C2587BB5600AA6FBE /* TabBarFooter.xib */; }; 3706FCF3293F65D500E42796 /* FirePopoverCollectionViewItem.xib in Resources */ = {isa = PBXBuildFile; fileRef = AAE246F22709EF3B00BEEAEE /* FirePopoverCollectionViewItem.xib */; }; - 3706FCF4293F65D500E42796 /* ProximaNova-Bold-webfont.woff2 in Resources */ = {isa = PBXBuildFile; fileRef = EAA29AE7278D2E43007070CF /* ProximaNova-Bold-webfont.woff2 */; }; 3706FCF5293F65D500E42796 /* dark-shield-dot.json in Resources */ = {isa = PBXBuildFile; fileRef = AA34396E2754D4E900B241FA /* dark-shield-dot.json */; }; 3706FCF6293F65D500E42796 /* trackers-2.json in Resources */ = {isa = PBXBuildFile; fileRef = AA3439742754D55100B241FA /* trackers-2.json */; }; - 3706FCF7293F65D500E42796 /* ProximaNova-Reg-webfont.woff2 in Resources */ = {isa = PBXBuildFile; fileRef = EAA29AE8278D2E43007070CF /* ProximaNova-Reg-webfont.woff2 */; }; - 3706FCF8293F65D500E42796 /* clickToLoad.js in Resources */ = {isa = PBXBuildFile; fileRef = EAFAD6C92728BD1200F9DF00 /* clickToLoad.js */; }; 3706FDDA293F661700E42796 /* EmbeddedTrackerDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9833913227AAAEEE00DAF119 /* EmbeddedTrackerDataTests.swift */; }; 3706FDDB293F661700E42796 /* AutofillPreferencesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3776583027F8325B009A6B35 /* AutofillPreferencesTests.swift */; }; 3706FDDC293F661700E42796 /* FileManagerExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B67C6C462654C643006C872E /* FileManagerExtensionTests.swift */; }; @@ -819,7 +812,6 @@ 3706FE3A293F661700E42796 /* MockVariantManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B69B504A2726CA2900758A2B /* MockVariantManager.swift */; }; 3706FE3C293F661700E42796 /* FireproofDomainsStoreMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6BBF1712744CE36004F850E /* FireproofDomainsStoreMock.swift */; }; 3706FE3D293F661700E42796 /* DataEncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BA1A6D8258C0CB300F6F690 /* DataEncryptionTests.swift */; }; - 3706FE3E293F661700E42796 /* ClickToLoadModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1E52B42798CF98002EC53C /* ClickToLoadModelTests.swift */; }; 3706FE3F293F661700E42796 /* FileStoreMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6A5A27D25B9403E00AA7ADA /* FileStoreMock.swift */; }; 3706FE40293F661700E42796 /* BWResponseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D3B1AC329378953006F4388 /* BWResponseTests.swift */; }; 3706FE41293F661700E42796 /* DownloadListCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B693955E26F1C17F0015B914 /* DownloadListCoordinatorTests.swift */; }; @@ -2524,15 +2516,7 @@ D64A5FF92AEA5C2B00B6D6E7 /* HomeButtonMenuFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64A5FF72AEA5C2B00B6D6E7 /* HomeButtonMenuFactory.swift */; }; EA0BA3A9272217E6002A0B6C /* ClickToLoadUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA0BA3A8272217E6002A0B6C /* ClickToLoadUserScript.swift */; }; EA18D1CA272F0DC8006DC101 /* social_images in Resources */ = {isa = PBXBuildFile; fileRef = EA18D1C9272F0DC8006DC101 /* social_images */; }; - EA1E52B52798CF98002EC53C /* ClickToLoadModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1E52B42798CF98002EC53C /* ClickToLoadModelTests.swift */; }; - EA4617F0273A28A700F110A2 /* fb-tds.json in Resources */ = {isa = PBXBuildFile; fileRef = EA4617EF273A28A700F110A2 /* fb-tds.json */; }; - EA477680272A21B700419EDA /* clickToLoadConfig.json in Resources */ = {isa = PBXBuildFile; fileRef = EA47767F272A21B700419EDA /* clickToLoadConfig.json */; }; EA8AE76A279FBDB20078943E /* ClickToLoadTDSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA8AE769279FBDB20078943E /* ClickToLoadTDSTests.swift */; }; - EAA29AE9278D2E43007070CF /* ProximaNova-Bold-webfont.woff2 in Resources */ = {isa = PBXBuildFile; fileRef = EAA29AE7278D2E43007070CF /* ProximaNova-Bold-webfont.woff2 */; }; - EAA29AEA278D2E43007070CF /* ProximaNova-Reg-webfont.woff2 in Resources */ = {isa = PBXBuildFile; fileRef = EAA29AE8278D2E43007070CF /* ProximaNova-Reg-webfont.woff2 */; }; - EAC80DE0271F6C0100BBF02D /* fb-sdk.js in Resources */ = {isa = PBXBuildFile; fileRef = EAC80DDF271F6C0100BBF02D /* fb-sdk.js */; }; - EAE42800275D47FA00DAC26B /* ClickToLoadModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAE427FF275D47FA00DAC26B /* ClickToLoadModel.swift */; }; - EAFAD6CA2728BD1200F9DF00 /* clickToLoad.js in Resources */ = {isa = PBXBuildFile; fileRef = EAFAD6C92728BD1200F9DF00 /* clickToLoad.js */; }; EE02D41A2BB4609900DBE6B3 /* UITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE02D4192BB4609900DBE6B3 /* UITests.swift */; }; EE02D41C2BB460A600DBE6B3 /* BrowsingHistoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE02D41B2BB460A600DBE6B3 /* BrowsingHistoryTests.swift */; }; EE02D4202BB460C000DBE6B3 /* BrowserServicesKit in Frameworks */ = {isa = PBXBuildFile; productRef = EE02D41F2BB460C000DBE6B3 /* BrowserServicesKit */; }; @@ -4110,15 +4094,7 @@ D64A5FF72AEA5C2B00B6D6E7 /* HomeButtonMenuFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeButtonMenuFactory.swift; sourceTree = ""; }; EA0BA3A8272217E6002A0B6C /* ClickToLoadUserScript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClickToLoadUserScript.swift; sourceTree = ""; }; EA18D1C9272F0DC8006DC101 /* social_images */ = {isa = PBXFileReference; lastKnownFileType = folder; path = social_images; sourceTree = ""; }; - EA1E52B42798CF98002EC53C /* ClickToLoadModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClickToLoadModelTests.swift; sourceTree = ""; }; - EA4617EF273A28A700F110A2 /* fb-tds.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "fb-tds.json"; sourceTree = ""; }; - EA47767F272A21B700419EDA /* clickToLoadConfig.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = clickToLoadConfig.json; sourceTree = ""; }; EA8AE769279FBDB20078943E /* ClickToLoadTDSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClickToLoadTDSTests.swift; sourceTree = ""; }; - EAA29AE7278D2E43007070CF /* ProximaNova-Bold-webfont.woff2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ProximaNova-Bold-webfont.woff2"; sourceTree = ""; }; - EAA29AE8278D2E43007070CF /* ProximaNova-Reg-webfont.woff2 */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ProximaNova-Reg-webfont.woff2"; sourceTree = ""; }; - EAC80DDF271F6C0100BBF02D /* fb-sdk.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "fb-sdk.js"; sourceTree = ""; }; - EAE427FF275D47FA00DAC26B /* ClickToLoadModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClickToLoadModel.swift; sourceTree = ""; }; - EAFAD6C92728BD1200F9DF00 /* clickToLoad.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = clickToLoad.js; sourceTree = ""; }; EE02D4192BB4609900DBE6B3 /* UITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITests.swift; sourceTree = ""; }; EE02D41B2BB460A600DBE6B3 /* BrowsingHistoryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BrowsingHistoryTests.swift; sourceTree = ""; }; EE0429DF2BA31D2F009EB20F /* FindInPageTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FindInPageTests.swift; sourceTree = ""; }; @@ -5304,21 +5280,15 @@ isa = PBXGroup; children = ( B68D21CE2ACBC9E7002DA3C2 /* Mocks */, - EAA29AEB278D2E51007070CF /* fonts */, 026ADE1326C3010C002518EE /* macos-config.json */, 9833913027AAA4B500DAF119 /* trackerData.json */, - EAE427FF275D47FA00DAC26B /* ClickToLoadModel.swift */, + EA0BA3A8272217E6002A0B6C /* ClickToLoadUserScript.swift */, 85AC3B0425D6B1D800C7D2AA /* ScriptSourceProviding.swift */, 9826B09F2747DF3D0092F683 /* ContentBlocking.swift */, 9812D894276CEDA5004B6181 /* ContentBlockerRulesLists.swift */, 9833912E27AAA3CE00DAF119 /* AppTrackerDataSetProvider.swift */, 9826B0A12747DFEB0092F683 /* AppPrivacyConfigurationDataProvider.swift */, EA18D1C9272F0DC8006DC101 /* social_images */, - EA0BA3A8272217E6002A0B6C /* ClickToLoadUserScript.swift */, - EAFAD6C92728BD1200F9DF00 /* clickToLoad.js */, - EA47767F272A21B700419EDA /* clickToLoadConfig.json */, - EA4617EF273A28A700F110A2 /* fb-tds.json */, - EAC80DDF271F6C0100BBF02D /* fb-sdk.js */, ); path = ContentBlocker; sourceTree = ""; @@ -5486,7 +5456,6 @@ children = ( 98EB5D0F27516A4800681FE6 /* AppPrivacyConfigurationTests.swift */, 9833913227AAAEEE00DAF119 /* EmbeddedTrackerDataTests.swift */, - EA1E52B42798CF98002EC53C /* ClickToLoadModelTests.swift */, EA8AE769279FBDB20078943E /* ClickToLoadTDSTests.swift */, B610F2E527AA388100FCEBE9 /* ContentBlockingUpdatingTests.swift */, B6AE39F029373AF200C37AA4 /* EmptyAttributionRulesProver.swift */, @@ -8247,15 +8216,6 @@ path = Resources; sourceTree = ""; }; - EAA29AEB278D2E51007070CF /* fonts */ = { - isa = PBXGroup; - children = ( - EAA29AE7278D2E43007070CF /* ProximaNova-Bold-webfont.woff2 */, - EAA29AE8278D2E43007070CF /* ProximaNova-Reg-webfont.woff2 */, - ); - path = fonts; - sourceTree = ""; - }; EEA3EEAF2B24EB5100E8333A /* VPNLocation */ = { isa = PBXGroup; children = ( @@ -9009,7 +8969,6 @@ 3706FCB8293F65D500E42796 /* Onboarding.storyboard in Resources */, 56CEE90F2B7A725C00CF10AA /* InfoPlist.xcstrings in Resources */, 3706FCB9293F65D500E42796 /* FireproofDomains.storyboard in Resources */, - 3706FCBA293F65D500E42796 /* clickToLoadConfig.json in Resources */, 3706FCBC293F65D500E42796 /* dark-shield.json in Resources */, 854DAAAE2A72B613001E2E24 /* BookmarksBarPromptAssets.xcassets in Resources */, 3706FCBD293F65D500E42796 /* dark-shield-mouse-over.json in Resources */, @@ -9018,7 +8977,6 @@ 3706FCC0293F65D500E42796 /* FindInPage.storyboard in Resources */, EEC8EB3F2982CA440065AA39 /* JSAlert.storyboard in Resources */, 3706FCC3293F65D500E42796 /* userscript.js in Resources */, - 3706FCC4293F65D500E42796 /* fb-tds.json in Resources */, 3706FCC6293F65D500E42796 /* README.md in Resources */, 3706FCC8293F65D500E42796 /* Assets.xcassets in Resources */, 3706FCC9293F65D500E42796 /* NavigationBar.storyboard in Resources */, @@ -9041,7 +8999,6 @@ 3706FCE4293F65D500E42796 /* Fire.storyboard in Resources */, 3706FCE6293F65D500E42796 /* social_images in Resources */, 3706FCE7293F65D500E42796 /* shield-dot-mouse-over.json in Resources */, - 3706FCE9293F65D500E42796 /* fb-sdk.js in Resources */, BD384ACB2BBC821B00EF3735 /* vpn-dark-mode.json in Resources */, 3706FCEA293F65D500E42796 /* PasswordManager.storyboard in Resources */, BD384ACC2BBC821B00EF3735 /* vpn-light-mode.json in Resources */, @@ -9053,11 +9010,8 @@ 3706FCF0293F65D500E42796 /* httpsMobileV2BloomSpec.json in Resources */, 3706FCF1293F65D500E42796 /* TabBarFooter.xib in Resources */, 3706FCF3293F65D500E42796 /* FirePopoverCollectionViewItem.xib in Resources */, - 3706FCF4293F65D500E42796 /* ProximaNova-Bold-webfont.woff2 in Resources */, 3706FCF5293F65D500E42796 /* dark-shield-dot.json in Resources */, 3706FCF6293F65D500E42796 /* trackers-2.json in Resources */, - 3706FCF7293F65D500E42796 /* ProximaNova-Reg-webfont.woff2 in Resources */, - 3706FCF8293F65D500E42796 /* clickToLoad.js in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -9203,7 +9157,6 @@ 85B7184A27677C2D00B4277F /* Onboarding.storyboard in Resources */, 56CEE90E2B7A725B00CF10AA /* InfoPlist.xcstrings in Resources */, 4B0511C3262CAA5A00F6079C /* FireproofDomains.storyboard in Resources */, - EA477680272A21B700419EDA /* clickToLoadConfig.json in Resources */, AA3439712754D4E900B241FA /* dark-shield.json in Resources */, 859F30672A72B38500C20372 /* BookmarksBarPromptAssets.xcassets in Resources */, AA7EB6EB27E880AE00036718 /* dark-shield-mouse-over.json in Resources */, @@ -9212,7 +9165,6 @@ 85A0117425AF2EDF00FA6A0C /* FindInPage.storyboard in Resources */, EEC111E4294D06020086524F /* JSAlert.storyboard in Resources */, B31055C627A1BA1D001AC618 /* userscript.js in Resources */, - EA4617F0273A28A700F110A2 /* fb-tds.json in Resources */, AA68C3D72490F821001B8783 /* README.md in Resources */, AA585D86248FD31400E9A3E2 /* Assets.xcassets in Resources */, 85589E8D27BBBB870038AD11 /* NavigationBar.storyboard in Resources */, @@ -9235,7 +9187,6 @@ AAB7320726DD0C37002FACF9 /* Fire.storyboard in Resources */, EA18D1CA272F0DC8006DC101 /* social_images in Resources */, AA7EB6E927E880A600036718 /* shield-dot-mouse-over.json in Resources */, - EAC80DE0271F6C0100BBF02D /* fb-sdk.js in Resources */, BD384AC92BBC821A00EF3735 /* vpn-dark-mode.json in Resources */, 85625994269C8F9600EE44BC /* PasswordManager.storyboard in Resources */, BD384ACA2BBC821A00EF3735 /* vpn-light-mode.json in Resources */, @@ -9247,11 +9198,8 @@ 4B677432255DBEB800025BD8 /* httpsMobileV2BloomSpec.json in Resources */, AA2CB12D2587BB5600AA6FBE /* TabBarFooter.xib in Resources */, AAE246F42709EF3B00BEEAEE /* FirePopoverCollectionViewItem.xib in Resources */, - EAA29AE9278D2E43007070CF /* ProximaNova-Bold-webfont.woff2 in Resources */, AA3439702754D4E900B241FA /* dark-shield-dot.json in Resources */, AA34397A2754D55100B241FA /* trackers-2.json in Resources */, - EAA29AEA278D2E43007070CF /* ProximaNova-Reg-webfont.woff2 in Resources */, - EAFAD6CA2728BD1200F9DF00 /* clickToLoad.js in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -9621,7 +9569,6 @@ 7BCB90C32C1863BA008E3543 /* VPNControllerXPCClient+ConvenienceInitializers.swift in Sources */, EEC4A66E2B2C894F00F7C0AA /* VPNLocationPreferenceItemModel.swift in Sources */, 3706FA83293F65D500E42796 /* LazyLoadable.swift in Sources */, - 3706FA84293F65D500E42796 /* ClickToLoadModel.swift in Sources */, 3706FA85293F65D500E42796 /* KeyedCodingExtension.swift in Sources */, 3706FA87293F65D500E42796 /* DownloadListStore.swift in Sources */, 37197EAB2942443D00394917 /* WebViewContainerView.swift in Sources */, @@ -10557,7 +10504,6 @@ 3706FE3A293F661700E42796 /* MockVariantManager.swift in Sources */, 3706FE3C293F661700E42796 /* FireproofDomainsStoreMock.swift in Sources */, 3706FE3D293F661700E42796 /* DataEncryptionTests.swift in Sources */, - 3706FE3E293F661700E42796 /* ClickToLoadModelTests.swift in Sources */, C17CA7B32B9B5317008EC3C1 /* MockAutofillPopoverPresenter.swift in Sources */, 3706FE3F293F661700E42796 /* FileStoreMock.swift in Sources */, B6619F042B17123200CD9186 /* DataImportViewModelTests.swift in Sources */, @@ -11014,7 +10960,6 @@ 4B9DB03E2A983B24000927DB /* JoinWaitlistView.swift in Sources */, 1ED910D52B63BFB300936947 /* IdentityTheftRestorationPagesUserScript.swift in Sources */, 37534CA028113101002621E7 /* LazyLoadable.swift in Sources */, - EAE42800275D47FA00DAC26B /* ClickToLoadModel.swift in Sources */, 0230C0A3272080090018F728 /* KeyedCodingExtension.swift in Sources */, 31AA6B972B960B870025014E /* DataBrokerProtectionLoginItemPixels.swift in Sources */, B6BF5D852946FFDA006742B1 /* PrivacyDashboardTabExtension.swift in Sources */, @@ -11969,7 +11914,6 @@ 4BA1A6D9258C0CB300F6F690 /* DataEncryptionTests.swift in Sources */, B6AA64732994B43300D99CD6 /* FutureExtensionTests.swift in Sources */, B603975329C1FFAE00902A34 /* ExpectedNavigationExtension.swift in Sources */, - EA1E52B52798CF98002EC53C /* ClickToLoadModelTests.swift in Sources */, 56BA1E802BAB2E43001CF69F /* ErrorPageTabExtensionTest.swift in Sources */, B603975029C1FF5F00902A34 /* TestsURLExtension.swift in Sources */, B6A5A27E25B9403E00AA7ADA /* FileStoreMock.swift in Sources */, @@ -13078,7 +13022,7 @@ repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 155.0.0; + version = 156.0.0; }; }; 9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index bbbe7f2f74..7f1e18672c 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/BrowserServicesKit", "state" : { - "revision" : "ddb0eb213c081a5d6d3f78f4df5dff11bc974218", - "version" : "155.0.0" + "revision" : "e9e239fe5dfeab87dcacbf5a31c8f555623a4ce2", + "version" : "156.0.0" } }, { @@ -176,8 +176,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/TrackerRadarKit", "state" : { - "revision" : "c01e6a59d000356b58ec77053e0a99d538be56a5", - "version" : "2.1.1" + "revision" : "1403e17eeeb8493b92fb9d11eb8c846bb9776581", + "version" : "2.1.2" } }, { diff --git a/DuckDuckGo/Application/AppConfigurationURLProvider.swift b/DuckDuckGo/Application/AppConfigurationURLProvider.swift index 8db6e037fe..686a623388 100644 --- a/DuckDuckGo/Application/AppConfigurationURLProvider.swift +++ b/DuckDuckGo/Application/AppConfigurationURLProvider.swift @@ -56,7 +56,7 @@ struct AppConfigurationURLProvider: ConfigurationURLProviding { case .bloomFilterExcludedDomains: return URL(string: "https://staticcdn.duckduckgo.com/https/https-mobile-v2-false-positives.json")! case .privacyConfiguration: return customPrivacyConfigurationUrl ?? URL(string: "https://staticcdn.duckduckgo.com/trackerblocking/config/v4/macos-config.json")! case .surrogates: return URL(string: "https://staticcdn.duckduckgo.com/surrogates.txt")! - case .trackerDataSet: return URL(string: "https://staticcdn.duckduckgo.com/trackerblocking/v5/current/macos-tds.json")! + case .trackerDataSet: return URL(string: "https://staticcdn.duckduckgo.com/trackerblocking/v6/current/macos-tds.json")! // In archived repo, to be refactored shortly (https://staticcdn.duckduckgo.com/useragents/social_ctp_configuration.json) case .FBConfig: return URL(string: "https://staticcdn.duckduckgo.com/useragents/")! } diff --git a/DuckDuckGo/ContentBlocker/AppPrivacyConfigurationDataProvider.swift b/DuckDuckGo/ContentBlocker/AppPrivacyConfigurationDataProvider.swift index 4c4e0cd185..71cc3faa7f 100644 --- a/DuckDuckGo/ContentBlocker/AppPrivacyConfigurationDataProvider.swift +++ b/DuckDuckGo/ContentBlocker/AppPrivacyConfigurationDataProvider.swift @@ -22,8 +22,8 @@ import BrowserServicesKit final class AppPrivacyConfigurationDataProvider: EmbeddedDataProvider { public struct Constants { - public static let embeddedDataETag = "\"c46f529760ac695b143fb664350ad760\"" - public static let embeddedDataSHA = "00383f1d5e5e8b5da067c1527ed88027093422c85f362fcfa95cda12c5870987" + public static let embeddedDataETag = "\"d05f1997ae293869ab8c448e4cbc3e39\"" + public static let embeddedDataSHA = "0ea4b7874c38d81d5e24505585d8b941069e7e7893a05a7cea9f1745adc4fa52" } var embeddedDataEtag: String { diff --git a/DuckDuckGo/ContentBlocker/AppTrackerDataSetProvider.swift b/DuckDuckGo/ContentBlocker/AppTrackerDataSetProvider.swift index 9783e9d521..e47ae7d39a 100644 --- a/DuckDuckGo/ContentBlocker/AppTrackerDataSetProvider.swift +++ b/DuckDuckGo/ContentBlocker/AppTrackerDataSetProvider.swift @@ -22,8 +22,8 @@ import BrowserServicesKit final class AppTrackerDataSetProvider: EmbeddedDataProvider { public struct Constants { - public static let embeddedDataETag = "\"ea184137cdaa19ca5de76352215a9e0e\"" - public static let embeddedDataSHA = "faa4dfbef4903710374b153c9a87e09b713fc19d64fa0bcfd1fd392fff93af21" + public static let embeddedDataETag = "\"6fa664462bd29b66c1b13eed51072afa\"" + public static let embeddedDataSHA = "52d29aa8942e518db24a5adbcca63148aac9c01a8a28c3f2c4bc517cb80b18ff" } var embeddedDataEtag: String { diff --git a/DuckDuckGo/ContentBlocker/ClickToLoadModel.swift b/DuckDuckGo/ContentBlocker/ClickToLoadModel.swift deleted file mode 100644 index 863b9d1018..0000000000 --- a/DuckDuckGo/ContentBlocker/ClickToLoadModel.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// ClickToLoadModel.swift -// -// Copyright © 2021 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 - -struct ClickToLoadModel { - - private static func loadFile(name: String) -> String? { - let pathPrefix = "social_images/" - let fileArgs = name.split(separator: ".") - let fileName = String(fileArgs[0]) - let fileExt = String(fileArgs[1]) - - let filePath = pathPrefix + fileName - - let imgURL = Bundle.main.url( - forResource: filePath, - withExtension: fileExt - ) - if imgURL == nil { - return nil - } - guard let base64String = try? Data(contentsOf: imgURL!).base64EncodedString() else { return nil } - let image = "data:image/" + (fileExt == "svg" ? "svg+xml" : fileExt) + ";base64," + base64String - return image - } - - static let getImage: [String: String] = { - return [ - "dax.png": Self.loadFile(name: "dax.png")!, - "loading_light.svg": Self.loadFile(name: "loading_light.svg")!, - "loading_dark.svg": Self.loadFile(name: "loading_dark.svg")!, - "blocked_facebook_logo.svg": Self.loadFile(name: "blocked_facebook_logo.svg")!, - "blocked_group.svg": Self.loadFile(name: "blocked_group.svg")!, - "blocked_page.svg": Self.loadFile(name: "blocked_page.svg")!, - "blocked_post.svg": Self.loadFile(name: "blocked_post.svg")!, - "blocked_video.svg": Self.loadFile(name: "blocked_video.svg")! - ] - }() -} diff --git a/DuckDuckGo/ContentBlocker/ClickToLoadUserScript.swift b/DuckDuckGo/ContentBlocker/ClickToLoadUserScript.swift index ef06cf4216..449a7fa4f0 100644 --- a/DuckDuckGo/ContentBlocker/ClickToLoadUserScript.swift +++ b/DuckDuckGo/ContentBlocker/ClickToLoadUserScript.swift @@ -17,77 +17,88 @@ // import WebKit +import Common import UserScript -import BrowserServicesKit protocol ClickToLoadUserScriptDelegate: AnyObject { - func clickToLoadUserScriptAllowFB(_ script: UserScript, replyHandler: @escaping (Bool) -> Void) + func clickToLoadUserScriptAllowFB() -> Bool } -final class ClickToLoadUserScript: NSObject, UserScript, WKScriptMessageHandlerWithReply { +final class ClickToLoadUserScript: NSObject, WKNavigationDelegate, Subfeature { + weak var broker: UserScriptMessageBroker? + weak var webView: WKWebView? - var injectionTime: WKUserScriptInjectionTime { .atDocumentEnd } - var forMainFrameOnly: Bool { false } - var messageNames: [String] { ["getImage", "enableFacebook", "initClickToLoad" ] } - let source: String + weak var delegate: ClickToLoadUserScriptDelegate? + +#if DEBUG + var devMode: Bool = true +#else + var devMode: Bool = false +#endif + + // this isn't an issue to be set to 'all' because the page + public let messageOriginPolicy: MessageOriginPolicy = .all + public let featureName: String = "clickToLoad" - init(scriptSourceProvider: ScriptSourceProviding) { - self.source = scriptSourceProvider.clickToLoadSource + // MARK: - Subfeature + + public func with(broker: UserScriptMessageBroker) { + self.broker = broker } - weak var delegate: ClickToLoadUserScriptDelegate? + // MARK: - MessageNames - @MainActor - func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage, - replyHandler: @escaping (Any?, String?) -> Void) { - if message.name == "initClickToLoad" { - let host = message.body as? String - let controller = userContentController as? UserContentController - let privacyConfigurationManager = controller!.privacyConfigurationManager - let privacyConfiguration = privacyConfigurationManager.privacyConfig - - let locallyProtected = privacyConfiguration.isProtected(domain: host) - let featureEnabled = privacyConfiguration.isFeature(.clickToPlay, enabledForDomain: host) - if locallyProtected && featureEnabled { - replyHandler(true, nil) - } else { - replyHandler(false, nil) - } - return - } - if message.name == "enableFacebook" { - guard let delegate = delegate else { return } - delegate.clickToLoadUserScriptAllowFB(self) { _ in - guard let isLogin = message.body as? Bool else { - replyHandler(nil, nil) - return - } - - replyHandler(isLogin, nil) - } - - return + enum MessageNames: String, CaseIterable { + case getClickToLoadState + case unblockClickToLoadContent + case updateFacebookCTLBreakageFlags + case addDebugFlag + } + + public func handler(forMethodNamed methodName: String) -> Handler? { + switch MessageNames(rawValue: methodName) { + case .getClickToLoadState: + return handleGetClickToLoadState + case .unblockClickToLoadContent: + return handleUnblockClickToLoadContent + case .updateFacebookCTLBreakageFlags: + return handleDebugFlagsMock + case .addDebugFlag: + return handleDebugFlagsMock + default: + assertionFailure("ClickToLoadUserScript: Failed to parse User Script message: \(methodName)") + return nil } + } - var image: String + @MainActor + private func handleGetClickToLoadState(params: Any, message: UserScriptMessage) -> Encodable? { + webView = message.messageWebView + return [ + "devMode": devMode, + "youtubePreviewsEnabled": false + ] + } - guard let arg = message.body as? String else { - replyHandler(nil, nil) - return - } - if message.name == "getImage" { - image = ClickToLoadModel.getImage[arg]! - } else { - assertionFailure("Uknown message type") - replyHandler(nil, nil) - return - } - replyHandler(image, nil) + @MainActor + private func handleUnblockClickToLoadContent(params: Any, message: UserScriptMessage) -> Encodable? { + guard let delegate = delegate else { return false } + + // only worry about CTL FB for now + return delegate.clickToLoadUserScriptAllowFB() } - func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { - assertionFailure("SHOULDN'T BE HERE!") + @MainActor + private func handleDebugFlagsMock(params: Any, message: UserScriptMessage) -> Encodable? { + // breakage flags not supported on Mac yet + return nil } + @MainActor + public func displayClickToLoadPlaceholders() { + guard let webView else { return } + + broker?.push(method: "displayClickToLoadPlaceholders", params: ["ruleAction": ["block"]], for: self, into: webView) + } } diff --git a/DuckDuckGo/ContentBlocker/ContentBlockerRulesLists.swift b/DuckDuckGo/ContentBlocker/ContentBlockerRulesLists.swift index 4c1f3f335d..144d5eb300 100644 --- a/DuckDuckGo/ContentBlocker/ContentBlockerRulesLists.swift +++ b/DuckDuckGo/ContentBlocker/ContentBlockerRulesLists.swift @@ -23,35 +23,6 @@ import CryptoKit final class ContentBlockerRulesLists: DefaultContentBlockerRulesListsSource { - enum Constants { - static let clickToLoadRulesListName = "ClickToLoad" - } - - static var fbTrackerDataFile: Data = { - do { - let url = Bundle.main.url(forResource: "fb-tds", withExtension: "json")! - return try Data(contentsOf: url) - } catch { - fatalError("Failed to load FB-TDS") - } - }() - - static var fbTrackerDataSet: TrackerRadarKit.TrackerData = { - do { - return try JSONDecoder().decode(TrackerData.self, from: fbTrackerDataFile) - } catch { - fatalError("Failed to JSON decode FB-TDS") - } - }() - - func MD5(data: Data) -> String { - let digest = Insecure.MD5.hash(data: data) - - return digest.map { - String(format: "%02hhx", $0) - }.joined() - } - private let adClickAttribution: AdClickAttributing init(trackerDataManager: TrackerDataManager, adClickAttribution: AdClickAttributing) { @@ -66,21 +37,24 @@ final class ContentBlockerRulesLists: DefaultContentBlockerRulesListsSource { let tdsRulesIndex = result.firstIndex(where: { $0.name == DefaultContentBlockerRulesListsSource.Constants.trackerDataSetRulesListName }) { let tdsRules = result[tdsRulesIndex] let allowlistedTrackerNames = adClickAttribution.allowlist.map { $0.entity } - let splitter = AdClickAttributionRulesSplitter(rulesList: tdsRules, allowlistedTrackerNames: allowlistedTrackerNames) - if let splitRules = splitter.split() { + let adSplitter = AdClickAttributionRulesSplitter(rulesList: tdsRules, allowlistedTrackerNames: allowlistedTrackerNames) + if let splitRules = adSplitter.split() { result.remove(at: tdsRulesIndex) result.append(splitRules.0) result.append(splitRules.1) } } - // Add new ones - let etag = MD5(data: Self.fbTrackerDataFile) - let dataSet: TrackerDataManager.DataSet = TrackerDataManager.DataSet(Self.fbTrackerDataSet, etag) - let CTLRulesList = ContentBlockerRulesList(name: Constants.clickToLoadRulesListName, - trackerData: nil, - fallbackTrackerData: dataSet) - result.append(CTLRulesList) + // split CTL rules so they can be managed separately from the main list when user clicks through a CTL dialog + if let tdsRulesIndex = result.firstIndex(where: { $0.name == DefaultContentBlockerRulesListsSource.Constants.trackerDataSetRulesListName }) { + let tdsRules = result[tdsRulesIndex] + let ctlSplitter = ClickToLoadRulesSplitter(rulesList: tdsRules) + if let splitRules = ctlSplitter.split() { + result.remove(at: tdsRulesIndex) + result.append(splitRules.withoutBlockCTL) + result.append(splitRules.withBlockCTL) + } + } return result } diff --git a/DuckDuckGo/ContentBlocker/ContentBlocking.swift b/DuckDuckGo/ContentBlocker/ContentBlocking.swift index d935081755..7ece42d1fa 100644 --- a/DuckDuckGo/ContentBlocker/ContentBlocking.swift +++ b/DuckDuckGo/ContentBlocker/ContentBlocking.swift @@ -137,7 +137,7 @@ final class AppContentBlocking { switch listName { case defaultTDSListName: listType = .tds - case ContentBlockerRulesLists.Constants.clickToLoadRulesListName: + case DefaultContentBlockerRulesListsSource.Constants.clickToLoadRulesListName: listType = .clickToLoad case AdClickAttributionRulesSplitter.blockingAttributionRuleListName(forListNamed: defaultTDSListName): listType = .blockingAttribution diff --git a/DuckDuckGo/ContentBlocker/ScriptSourceProviding.swift b/DuckDuckGo/ContentBlocker/ScriptSourceProviding.swift index 979ecaf469..c432f0fa26 100644 --- a/DuckDuckGo/ContentBlocker/ScriptSourceProviding.swift +++ b/DuckDuckGo/ContentBlocker/ScriptSourceProviding.swift @@ -21,6 +21,7 @@ import Combine import Common import BrowserServicesKit import Configuration +import TrackerRadarKit protocol ScriptSourceProviding { @@ -29,7 +30,6 @@ protocol ScriptSourceProviding { var privacyConfigurationManager: PrivacyConfigurationManaging { get } var autofillSourceProvider: AutofillUserScriptSourceProvider? { get } var sessionKey: String? { get } - var clickToLoadSource: String { get } func buildAutofillSource() -> AutofillUserScriptSourceProvider } @@ -46,7 +46,6 @@ struct ScriptSourceProvider: ScriptSourceProviding { private(set) var surrogatesConfig: SurrogatesUserScriptConfig? private(set) var autofillSourceProvider: AutofillUserScriptSourceProvider? private(set) var sessionKey: String? - private(set) var clickToLoadSource: String = "" let configStorage: ConfigurationStoring let privacyConfigurationManager: PrivacyConfigurationManaging @@ -72,7 +71,6 @@ struct ScriptSourceProvider: ScriptSourceProviding { self.contentBlockerRulesConfig = buildContentBlockerRulesConfig() self.surrogatesConfig = buildSurrogatesConfig() self.sessionKey = generateSessionKey() - self.clickToLoadSource = buildClickToLoadSource() self.autofillSourceProvider = buildAutofillSource() } @@ -97,8 +95,8 @@ struct ScriptSourceProvider: ScriptSourceProviding { let trackerData = contentBlockingManager.currentRules.first(where: { $0.name == tdsName})?.trackerData let ctlTrackerData = (contentBlockingManager.currentRules.first(where: { - $0.name == ContentBlockerRulesLists.Constants.clickToLoadRulesListName - })?.trackerData) ?? ContentBlockerRulesLists.fbTrackerDataSet + $0.name == DefaultContentBlockerRulesListsSource.Constants.clickToLoadRulesListName + })?.trackerData) return DefaultContentBlockerUserScriptConfig(privacyConfiguration: privacyConfigurationManager.privacyConfig, trackerData: trackerData, @@ -117,12 +115,11 @@ struct ScriptSourceProvider: ScriptSourceProviding { #endif let surrogates = configStorage.loadData(for: .surrogates)?.utf8String() ?? "" - let tdsName = DefaultContentBlockerRulesListsSource.Constants.trackerDataSetRulesListName - let rules = contentBlockingManager.currentRules.first(where: { $0.name == tdsName}) + let allTrackers = mergeTrackerDataSets(rules: contentBlockingManager.currentRules) return DefaultSurrogatesUserScriptConfig(privacyConfig: privacyConfigurationManager.privacyConfig, surrogates: surrogates, - trackerData: rules?.trackerData, - encodedSurrogateTrackerData: rules?.encodedTrackerData, + trackerData: allTrackers.trackerData, + encodedSurrogateTrackerData: allTrackers.encodedTrackerData, trackerDataManager: trackerDataManager, tld: tld, isDebugBuild: isDebugBuild) @@ -141,35 +138,40 @@ struct ScriptSourceProvider: ScriptSourceProviding { return data } - private func loadFont(_ fileName: String, _ fileExt: String) -> String? { - let url = Bundle.main.url( - forResource: fileName, - withExtension: fileExt - ) - guard let base64String = try? Data(contentsOf: url!).base64EncodedString() else { - assertionFailure("Failed to load font") - return nil + private func mergeTrackerDataSets(rules: [ContentBlockerRulesManager.Rules]) -> (trackerData: TrackerData, encodedTrackerData: String) { + var combinedTrackers: [String: KnownTracker] = [:] + var combinedEntities: [String: Entity] = [:] + var combinedDomains: [String: String] = [:] + var cnames: [TrackerData.CnameDomain: TrackerData.TrackerDomain]? = [:] + + let setsToCombine = [ DefaultContentBlockerRulesListsSource.Constants.trackerDataSetRulesListName, DefaultContentBlockerRulesListsSource.Constants.clickToLoadRulesListName ] + + for setName in setsToCombine { + if let ruleSetIndex = contentBlockingManager.currentRules.firstIndex(where: { $0.name == setName }) { + let ruleSet = rules[ruleSetIndex] + + combinedTrackers = combinedTrackers.merging(ruleSet.trackerData.trackers) { (_, new) in new } + combinedEntities = combinedEntities.merging(ruleSet.trackerData.entities) { (_, new) in new } + combinedDomains = combinedDomains.merging(ruleSet.trackerData.domains) { (_, new) in new } + if setName == DefaultContentBlockerRulesListsSource.Constants.trackerDataSetRulesListName { + cnames = ruleSet.trackerData.cnames + } + } } - let font = "data:application/octet-stream;base64," + base64String - return font - } + let combinedTrackerData = TrackerData(trackers: combinedTrackers, + entities: combinedEntities, + domains: combinedDomains, + cnames: cnames) - private func buildClickToLoadSource() -> String { - // For now bundle FB SDK and associated config, as they diverged from the extension - let fbSDK = loadTextFile("fb-sdk", "js") - var config = loadTextFile("clickToLoadConfig", "json")! - if #unavailable(OSX 11) { // disable CTL for Catalina and earlier - config = "{}" - } - let proximaRegFont = loadFont("ProximaNova-Reg-webfont", "woff2") - let proximaBoldFont = loadFont("ProximaNova-Bold-webfont", "woff2") - return ContentBlockerRulesUserScript.loadJS("clickToLoad", from: .main, withReplacements: [ - "${fb-sdk.js}": fbSDK!, - "${clickToLoadConfig.json}": config, - "${proximaRegFont}": proximaRegFont!, - "${proximaBoldFont}": proximaBoldFont! - ]) + let surrogateTDS = ContentBlockerRulesManager.extractSurrogates(from: combinedTrackerData) + let encodedTrackerData = encodeTrackerData(surrogateTDS) + + return (trackerData: combinedTrackerData, encodedTrackerData: encodedTrackerData) } + private func encodeTrackerData(_ trackerData: TrackerData) -> String { + let encodedData = try? JSONEncoder().encode(trackerData) + return String(data: encodedData!, encoding: .utf8)! + } } diff --git a/DuckDuckGo/ContentBlocker/clickToLoad.js b/DuckDuckGo/ContentBlocker/clickToLoad.js deleted file mode 100644 index 0cdcdc567f..0000000000 --- a/DuckDuckGo/ContentBlocker/clickToLoad.js +++ /dev/null @@ -1,1138 +0,0 @@ -(function clickToLoad () { -// function sendMessage (messageType, options, callback) { -// TODO chrome.runtime.sendMessage({ messageType, options }, callback) -// } - let appID - const loadingImages = { - darkMode: '', - lightMode: '' - } - let logoImg - const titleID = 'DuckDuckGoPrivacyEssentialsCTLElementTitle' - const entities = [] - const entityData = {} - const fbSurrogate = `${fb-sdk.js}` // eslint-disable-line - const fbConfig = JSON.parse(`${clickToLoadConfig.json}`.replace(/\\/g, '')) // eslint-disable-line - const proximaRegFontInline = `${proximaRegFont}` // eslint-disable-line - const proximaBoldFontInline = `${proximaBoldFont}` // eslint-disable-line - - /** - * - * Base64 encode / decode - * http://www.webtoolkit.info/ - * - **/ - const Base64 = { - - // private property - _keyStr: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', - - // public method for encoding - encode: function (input) { - let output = '' - let chr1, chr2, chr3, enc1, enc2, enc3, enc4 - let i = 0 - - input = Base64._utf8_encode(input) - - while (i < input.length) { - chr1 = input.charCodeAt(i++) - chr2 = input.charCodeAt(i++) - chr3 = input.charCodeAt(i++) - - enc1 = chr1 >> 2 - enc2 = ((chr1 & 3) << 4) | (chr2 >> 4) - enc3 = ((chr2 & 15) << 2) | (chr3 >> 6) - enc4 = chr3 & 63 - - if (isNaN(chr2)) { - enc3 = enc4 = 64 - } else if (isNaN(chr3)) { - enc4 = 64 - } - - output = output + - this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + - this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4) - } - - return output - }, - - // private method for UTF-8 encoding - _utf8_encode: function (string) { - string = string.replace(/\r\n/g, '\n') - let utftext = '' - - for (let n = 0; n < string.length; n++) { - const c = string.charCodeAt(n) - - if (c < 128) { - utftext += String.fromCharCode(c) - } else if ((c > 127) && (c < 2048)) { - utftext += String.fromCharCode((c >> 6) | 192) - utftext += String.fromCharCode((c & 63) | 128) - } else { - utftext += String.fromCharCode((c >> 12) | 224) - utftext += String.fromCharCode(((c >> 6) & 63) | 128) - utftext += String.fromCharCode((c & 63) | 128) - } - } - - return utftext - } - } - // Buffer - class Buffer { - static from (string, type) { - return new Buffer(string) - } - - constructor (string) { - this.string = string - } - - toString (type) { - return Base64.encode(this.string) - } - } - - function b64EncodeSurrogate (sur) { - const b64dataheader = 'data:application/javascript;base64,' - - // remove comment lines - const lines = sur.split('\n').filter((line) => { - return !(/^#.*/).test(line) - }) - - const b64surrogate = Buffer.from(lines.join('\n').toString(), 'binary').toString('base64') - return b64dataheader + b64surrogate - } - - function loadSurrogate (surrogate) { - const s = document.createElement('script') - s.type = 'application/javascript' - s.async = false - s.src = surrogate - const scripts = document.getElementsByTagName('script') - if (scripts && scripts.length > 0) { - scripts[0].parentNode.insertBefore(s, scripts[0]) - s.remove() - } - } - - 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) - } - } - - /********************************************************* - * Style Definitions - *********************************************************/ - const styles = { - fontStyle: ` - @font-face{ - font-family: DuckDuckGoPrivacyEssentials; - src: url(${proximaRegFontInline}); - } - @font-face{ - font-family: DuckDuckGoPrivacyEssentialsBold; - font-weight: bold; - src: url(${proximaBoldFontInline}); - } - `, - darkMode: { - background: ` - background: #111111; - `, - textFont: ` - color: rgba(255, 255, 255, 0.9); - `, - buttonFont: ` - color: #111111; - `, - linkFont: ` - color: #5784FF; - `, - buttonBackground: ` - background: #5784FF; - ` - }, - lightMode: { - background: ` - background: #FFFFFF; - `, - textFont: ` - color: #222222; - `, - buttonFont: ` - color: #FFFFFF; - `, - linkFont: ` - color: #3969EF; - `, - buttonBackground: ` - background: #3969EF; - ` - }, - loginMode: { - buttonBackground: ` - background: #666666; - `, - buttonFont: ` - color: #FFFFFF; - ` - }, - cancelMode: { - buttonBackground: ` - background: rgba(34, 34, 34, 0.1); - `, - buttonFont: ` - color: #222222; - ` - }, - button: ` - border-radius: 8px; - - padding: 11px 22px; - font-weight: bold; - margin: auto; - border-color: #3969EF; - border: none; - - font-family: DuckDuckGoPrivacyEssentialsBold; - font-size: 14px; - - position: relative; - cursor: pointer; - box-shadow: none; - z-index: 2147483646; - `, - circle: ` - border-radius: 50%; - width: 18px; - height: 18px; - background: #E0E0E0; - border: 1px solid #E0E0E0; - position: absolute; - top: -8px; - right: -8px; - `, - loginIcon: ` - position: absolute; - top: -13px; - right: -10px; - height: 28px; - width: 28px; - `, - rectangle: ` - width: 12px; - height: 3px; - background: #666666; - position: relative; - top: 42.5%; - margin: auto; - `, - textBubble: ` - background: #FFFFFF; - border: 1px solid rgba(0, 0, 0, 0.1); - border-radius: 16px; - box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.12), 0px 8px 16px rgba(0, 0, 0, 0.08); - width: 360px; - margin-top: 10px; - z-index: 2147483647; - position: absolute; - `, - textBubbleWidth: 360, // Should match the width rule in textBubble - textBubbleLeftShift: 100, // Should match the CSS left: rule in textBubble - textArrow: ` - display: inline-block; - background: #FFFFFF; - border: solid rgba(0, 0, 0, 0.1); - border-width: 0 1px 1px 0; - padding: 5px; - transform: rotate(-135deg); - -webkit-transform: rotate(-135deg); - position: relative; - top: -9px; - `, - arrowDefaultLocationPercent: 50, - hoverTextTitle: ` - padding: 0px 12px 12px; - margin-top: -5px; - `, - hoverTextBody: ` - font-family: DuckDuckGoPrivacyEssentials; - font-size: 14px; - line-height: 21px; - margin: auto; - padding: 17px; - text-align: left; - `, - hoverContainer: ` - padding-bottom: 10px; - `, - buttonTextContainer: ` - display: flex; - flex-direction: row; - align-items: center; - `, - headerRow: ` - - `, - block: ` - box-sizing: border-box; - border: 1px solid rgba(0,0,0,0.1); - border-radius: 12px; - max-width: 600px; - min-height: 300px; - margin: auto; - display: flex; - flex-direction: column; - - font-family: DuckDuckGoPrivacyEssentials; - line-height: 1; - `, - imgRow: ` - display: flex; - flex-direction: column; - margin: 20px 0px; - `, - content: ` - display: flex; - flex-direction: column; - margin: auto; - `, - titleBox: ` - display: flex; - padding: 12px; - max-height: 44px; - border-bottom: 1px solid; - border-color: rgba(196, 196, 196, 0.3); - `, - title: ` - font-family: DuckDuckGoPrivacyEssentials; - line-height: 1.4; - font-size: 14px; - margin: auto 10px; - flex-basis: 100%; - height: 1.4em; - flex-wrap: wrap; - overflow: hidden; - text-align: left; - `, - buttonRow: ` - display: flex; - height: 100% - flex-direction: row; - margin: 20px auto 0px; - padding-bottom: 36px; - `, - modalContentTitle: ` - font-family: DuckDuckGoPrivacyEssentialsBold; - font-size: 17px; - font-weight: bold; - line-height: 21px; - margin: 27px auto 10px; - text-align: center; - `, - modalContentText: ` - font-family: DuckDuckGoPrivacyEssentials; - font-size: 14px; - line-height: 21px; - margin: 0px auto 24px; - text-align: center; - `, - modalButton: ` - `, - modalIcon: ` - display: block; - `, - contentTitle: ` - font-family: DuckDuckGoPrivacyEssentialsBold; - font-size: 17px; - font-weight: bold; - margin: 20px auto 10px; - padding: 0px 30px; - text-align: center; - `, - contentText: ` - font-family: DuckDuckGoPrivacyEssentials; - font-size: 14px; - line-height: 21px; - margin: auto; - padding: 0px 40px; - text-align: center; - `, - icon: ` - height: 80px; - width: 80px; - margin: auto; - `, - logo: ` - flex-basis: 0%; - min-width: 20px; - height: 21px; - `, - logoImg: ` - height: 21px; - width: 21px; - `, - loadingImg: ` - display: block; - margin: 0px 8px 0px 0px; - height: 14px; - width: 14px; - `, - modal: ` - width: 312px; - margin: auto; - background-color: #FFFFFF; - position: absolute; - left: calc(50% - 312px/2); - top: calc(50% - 356px/2 + 0.5px); - display: block; - box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.08), 0px 2px 4px rgba(0, 0, 0, 0.1); - border-radius: 12px; - `, - modalContent: ` - padding: 24px; - display: flex; - flex-direction: column; - `, - overlay: ` - height: 100%; - width: 100%; - background-color: #666666; - opacity: .5; - display: block; - position: fixed; - top: 0; - right: 0; - `, - modalContainer: ` - height: 100%; - width: 100%; - z-index: 2147483647; - display: block; - position: fixed; - `, - headerLinkContainer: ` - flex-basis: 100%; - display: grid; - justify-content: flex-end; - `, - headerLink: ` - line-height: 1.4; - font-size: 14px; - font-weight: bold; - font-family: DuckDuckGoPrivacyEssentialsBold; - text-decoration: none; - cursor: pointer; - min-width: 100px; - text-align: end; - float: right; - display: none; - `, - generalLink: ` - line-height: 1.4; - font-size: 14px; - font-weight: bold; - font-family: DuckDuckGoPrivacyEssentialsBold; - cursor: pointer; - text-decoration: none; - `, - wrapperDiv: ` - display: inline-block; - border: 0; - padding: 0; - margin: 0; - ` - } - - /********************************************************* - * Widget Replacement logic - *********************************************************/ - class DuckWidget { - constructor (widgetData, originalElement, entity) { - this.clickAction = { ...widgetData.clickAction } // shallow copy - this.replaceSettings = widgetData.replaceSettings - this.originalElement = originalElement - this.dataElements = {} - this.gatherDataElements() - this.entity = entity - } - - // Collect and store data elements from original widget. Store default values - // from config if not present. - gatherDataElements () { - if (!this.clickAction.urlDataAttributesToPreserve) { - return - } - for (const [attrName, attrSettings] of Object.entries(this.clickAction.urlDataAttributesToPreserve)) { - let value = this.originalElement.getAttribute(attrName) - if (!value) { - if (attrSettings.required) { - // missing a required attribute means we won't be able to replace it - // with a light version, replace with full version. - this.clickAction.type = 'allowFull' - } - value = attrSettings.default - } - this.dataElements[attrName] = value - } - } - - // Return the facebook content URL to use when a user has clicked. - getTargetURL () { - // Copying over data fields should be done lazily, since some required data may not be - // captured until after page scripts run. - this.copySocialDataFields() - return this.clickAction.targetURL - } - - // Determine if element should render in dark mode - getMode () { - // Login buttons are always the login style types - if (this.replaceSettings.type === 'loginButton') { - return 'loginMode' - } - const mode = this.originalElement.getAttribute('data-colorscheme') - if (mode === 'dark') { - return 'darkMode' - } - return 'lightMode' - } - - // The config file offers the ability to style the replaced facebook widget. This - // collects the style from the original element & any specified in config for the element - // type and returns a CSS string. - getStyle () { - let styleString = 'border: none;' - - if (this.clickAction.styleDataAttributes) { - // Copy elements from the original div into style attributes as directed by config - for (const [attr, valAttr] of Object.entries(this.clickAction.styleDataAttributes)) { - let valueFound = this.dataElements[valAttr.name] - if (!valueFound) { - valueFound = this.dataElements[valAttr.fallbackAttribute] - } - let partialStyleString = '' - if (valueFound) { - partialStyleString += `${attr}: ${valueFound}` - } - if (!partialStyleString.includes(valAttr.unit)) { - partialStyleString += valAttr.unit - } - partialStyleString += ';' - styleString += partialStyleString - } - } - - return styleString - } - - // Some data fields are 'kept' from the original element. These are used both in - // replacement styling (darkmode, width, height), and when returning to a FB element. - copySocialDataFields () { - if (!this.clickAction.urlDataAttributesToPreserve) { - return - } - - // App ID may be set by client scripts, and is required for some elements. - if (this.dataElements.app_id_replace && appID != null) { - this.clickAction.targetURL = this.clickAction.targetURL.replace('app_id_replace', appID) - } - - for (const key of Object.keys(this.dataElements)) { - const attrValue = encodeURIComponent(this.dataElements[key]) - if (attrValue) { - this.clickAction.targetURL = this.clickAction.targetURL.replace(key, attrValue) - } - } - } - - /* - * Creates an iFrame for this facebook content. - * - * @returns {Element} - */ - createFBIFrame () { - const frame = document.createElement('iframe') - - frame.setAttribute('src', this.getTargetURL()) - frame.setAttribute('style', this.getStyle()) - - return frame - } - - /* - * Fades out the given element. Returns a promise that resolves when the fade is complete. - * @param {Element} element - the element to fade in or out - * @param {int} interval - frequency of opacity updates (ms) - * @param {bool} fadeIn - true if the element should fade in instead of out - */ - fadeElement (element, interval, fadeIn) { - return new Promise((resolve, reject) => { - let opacity = fadeIn ? 0 : 1 - const originStyle = element.style.cssText - const fadeOut = setInterval(function () { - opacity += fadeIn ? 0.03 : -0.03 - element.style.cssText = originStyle + `opacity: ${opacity};` - if (opacity <= 0 || opacity >= 1) { - clearInterval(fadeOut) - resolve() - } - }, interval) - }) - } - - fadeOutElement (element) { - return this.fadeElement(element, 10, false) - } - - fadeInElement (element) { - return this.fadeElement(element, 10, true) - } - - clickFunction (originalElement, replacementElement) { - let clicked = false - const handleClick = function handleClick (e) { - // Ensure that the click is created by a user event & prevent double clicks from adding more animations - if (e.isTrusted && !clicked) { - clicked = true - let isLogin = false - if (this.replaceSettings.type === 'loginButton') { - isLogin = true - } - enableSocialTracker(this.entity, isLogin) - const parent = replacementElement.parentNode - - // If we allow everything when this element is clicked, - // notify surrogate to enable SDK and replace original element. - if (this.clickAction.type === 'allowFull') { - parent.replaceChild(originalElement, replacementElement) - // window.dispatchEvent(new CustomEvent(`Load${this.entity}SDK`)) - return - } - // Create a container for the new FB element - const fbContainer = document.createElement('div') - fbContainer.style.cssText = styles.wrapperDiv - const fadeIn = document.createElement('div') - fadeIn.style.cssText = 'display: none; opacity: 0;' - - // Loading animation (FB can take some time to load) - const loadingImg = document.createElement('img') - loadingImg.setAttribute('src', loadingImages[this.getMode()]) - loadingImg.setAttribute('height', '14px') - loadingImg.style.cssText = styles.loadingImg - - // Always add the animation to the button, regardless of click source - if (e.srcElement.nodeName === 'BUTTON') { - e.srcElement.firstElementChild.insertBefore(loadingImg, e.srcElement.firstElementChild.firstChild) - } else { - // try to find the button - let el = e.srcElement - let button = null - while (button === null && el !== null) { - button = el.querySelector('button') - el = el.parentElement - } - if (button) { - button.firstElementChild.insertBefore(loadingImg, button.firstElementChild.firstChild) - } - } - - fbContainer.appendChild(fadeIn) - // default case is this.clickAction.type === 'originalElement' - let fbElement = originalElement - - if (this.clickAction.type === 'iFrame') { - fbElement = this.createFBIFrame() - } - - /* - * Modify the overlay to include a Facebook iFrame, which - * starts invisible. Once loaded, fade out and remove the overlay - * then fade in the Facebook content - */ - parent.replaceChild(fbContainer, replacementElement) - fbContainer.appendChild(replacementElement) - fadeIn.appendChild(fbElement) - fbElement.addEventListener('load', () => { - this.fadeOutElement(replacementElement) - .then(v => { - fbContainer.replaceWith(fbElement) - this.fadeInElement(fadeIn).then(v => { - fbElement.focus() // focus on new element for screen readers - }) - }) - }) - } - }.bind(this) - // If this is a login button, show modal if needed - if (this.replaceSettings.type === 'loginButton' && entityData[this.entity].shouldShowLoginModal) { - return function handleLoginClick (e) { - makeModal(this.entity, handleClick, e) - }.bind(this) - } - return handleClick - } - } - - function init () { - const fbSurrogateB64 = b64EncodeSurrogate(fbSurrogate) - loadSurrogate(fbSurrogateB64) - - for (const entity of Object.keys(fbConfig)) { - entities.push(entity) - entityData[entity] = { - shouldShowLoginModal: true, - modalIcon: fbConfig[entity].informationalModal.icon, - modalTitle: fbConfig[entity].informationalModal.messageTitle, - modalText: fbConfig[entity].informationalModal.messageBody, - modalAcceptText: fbConfig[entity].informationalModal.confirmButtonText, - modalRejectText: fbConfig[entity].informationalModal.rejectButtonText, - simpleVersion: fbConfig[entity].simpleVersion - } - } - replaceClickToLoadElements(fbConfig) - } - - /** - * Creates a safe element and replaces the original tracking element with it. - * @param {Object} widgetData - a single entry from elementData - * @param {Element} originalElement - the element on the page we are replacing - */ - function createReplacementWidget (entity, widgetData, originalElement) { - // Construct the widget based on data in the original element - const widget = new DuckWidget(widgetData, originalElement, entity) - const parent = originalElement.parentNode - - if (widgetData.replaceSettings.type === 'blank') { - const el = document.createElement('div') - parent.replaceChild(el, originalElement) - } - if (widgetData.replaceSettings.type === 'loginButton') { - window.webkit.messageHandlers.getImage.postMessage(widgetData.replaceSettings.icon).then((icon) => { - // TODO shortcircuit and below - // Create a button to replace old element - const { button, container } = makeLoginButton(widgetData.replaceSettings.buttonText, widget.getMode(), widgetData.replaceSettings.popupTitleText, widgetData.replaceSettings.popupBodyText, icon, originalElement) - button.addEventListener('click', widget.clickFunction(originalElement, container)) - parent.replaceChild(container, originalElement) - }) - .catch((e) => console.log('messageHandler exception', e)) - } - if (widgetData.replaceSettings.type === 'dialog') { - window.webkit.messageHandlers.getImage.postMessage(widgetData.replaceSettings.icon).then((icon) => { - const button = makeButton(widgetData.replaceSettings.buttonText, widget.getMode()) - const textButton = makeTextButton(widgetData.replaceSettings.buttonText, widget.getMode()) - const { contentBlock, shadowRoot } = createContentBlock( - widget, button, textButton, icon - ) - button.addEventListener('click', widget.clickFunction(originalElement, contentBlock)) - textButton.addEventListener('click', widget.clickFunction(originalElement, contentBlock)) - parent.replaceChild(contentBlock, originalElement) - - // Show an unblock link if parent element forces small height - // which may hide video. - if ((!!contentBlock.offsetHeight && contentBlock.offsetHeight <= 200) || - (!!contentBlock.parentNode && contentBlock.parentNode.offsetHeight <= 200)) { - const textButton = shadowRoot.querySelector(`#${titleID + 'TextButton'}`) - textButton.style.cssText += 'display: block' - } - }) - .catch((e) => console.log('messageHandler exception', e)) - } - } - - function replaceClickToLoadElements (config) { - for (const entity of Object.keys(config)) { - for (const widget of Object.values(config[entity].elementData)) { - const els = document.querySelectorAll(widget.selectors.join()) - for (const el of els) { - createReplacementWidget(entity, widget, el) - } - } - } - } - - /********************************************************* - * Messaging to surrogates & extension - *********************************************************/ - function enableSocialTracker (entity, isLogin) { - console.log('enableSocialTracker, isLogin=', isLogin) - window.webkit.messageHandlers.enableFacebook.postMessage(isLogin).then((enable) => { - console.log('enableSocialTracker PM callback', enable) - window.dispatchEvent(new CustomEvent('LoadFacebookSDK')) - }) - .catch((e) => console.log('messageHandler exception', e)) - } - - const hostname = getTopLevelURL().hostname - const fbExcludedDomains = fbConfig['Facebook'].excludedDomains.map(x => x.domain); - if (fbExcludedDomains.includes(hostname)) { - // console.warn('BAILING ON excludedDomains'); - return; - } - window.webkit.messageHandlers.initClickToLoad.postMessage(hostname).then((blocked) => { - if (!blocked || hostname === '') { - return - } - window.setTimeout(() => { - init() - }) - }) - - // Fetch reusable assets - window.webkit.messageHandlers.getImage.postMessage('loading_light.svg').then((response) => { - loadingImages.lightMode = response - }) - .catch((e) => console.log('messageHandler exception', e)) - - window.webkit.messageHandlers.getImage.postMessage('loading_dark.svg').then((response) => { - loadingImages.darkMode = response - }) - .catch((e) => console.log('messageHandler exception', e)) - - window.webkit.messageHandlers.getImage.postMessage('dax.png').then((response) => { - logoImg = response - }) - .catch((e) => console.log('messageHandler exception', e)) - - // Listen for events from surrogates - addEventListener('ddgClickToLoad', (event) => { - if (!event.detail) return - const entity = event.detail.entity - if (!entities.includes(entity)) { - // Unknown entity, reject - return - } - if (event.detail.appID) { - appID = JSON.stringify(event.detail.appID).replace(/"/g, '') - } - // Handle login call - if (event.detail.action === 'login') { - if (entityData[entity].shouldShowLoginModal) { - makeModal(entity, runLogin, entity) - } else { - runLogin(entity) - } - } - }) - - function runLogin (entity) { - enableSocialTracker(entity, true) - window.dispatchEvent(new CustomEvent(`Run${entity}Login`)) - } - - /********************************************************* - * Widget building blocks - *********************************************************/ - function getLearnMoreLink (mode) { - if (!mode) { - mode = 'lightMode' - } - const linkElement = document.createElement('a') - linkElement.style.cssText = styles.generalLink + styles[mode].linkFont - linkElement.ariaLabel = 'Read about this privacy protection' - linkElement.href = 'https://help.duckduckgo.com/duckduckgo-help-pages/privacy/embedded-content-protection/' - linkElement.target = '_blank' - linkElement.textContent = 'Learn More' - return linkElement - } - - function makeTextButton (linkText, mode) { - const linkElement = document.createElement('a') - linkElement.style.cssText = styles.headerLink + styles[mode].linkFont - linkElement.textContent = linkText - return linkElement - } - - function makeButton (buttonText, mode) { - const button = document.createElement('button') - button.style.cssText = styles.button + styles[mode].buttonBackground - const textContainer = document.createElement('div') - textContainer.style.cssText = styles.buttonTextContainer + styles[mode].buttonFont - textContainer.textContent = buttonText - button.appendChild(textContainer) - return button - } - - /* If there isn't an image available, just make a default block symbol */ - function makeDefaultBlockIcon () { - const blockedIcon = document.createElement('div') - const dash = document.createElement('div') - blockedIcon.appendChild(dash) - blockedIcon.style.cssText = styles.circle - dash.style.cssText = styles.rectangle - return blockedIcon - } - - /* FB login replacement button, with hover text */ - function makeLoginButton (buttonText, mode, hoverTextTitle, hoverTextBody, icon, originalElement) { - const container = document.createElement('div') - container.style.cssText = 'position: relative;' - // inherit any class styles on the button - container.className = 'fb-login-button FacebookLogin__button' - const styleElement = document.createElement('style') - styleElement.textContent = ` - #DuckDuckGoPrivacyEssentialsHoverableText { - display: none; - } - #DuckDuckGoPrivacyEssentialsHoverable:hover #DuckDuckGoPrivacyEssentialsHoverableText { - display: block; - } - ` - container.appendChild(styleElement) - - const hoverContainer = document.createElement('div') - hoverContainer.id = 'DuckDuckGoPrivacyEssentialsHoverable' - hoverContainer.style.cssText = styles.hoverContainer - container.appendChild(hoverContainer) - - // Make the button - const button = makeButton(buttonText, mode) - // Add blocked icon - if (!icon) { - button.appendChild(makeDefaultBlockIcon()) - } else { - const imgElement = document.createElement('img') - imgElement.style.cssText = styles.loginIcon - imgElement.setAttribute('src', icon) - imgElement.setAttribute('height', '28px') - button.appendChild(imgElement) - } - hoverContainer.appendChild(button) - - // hover action - const hoverBox = document.createElement('div') - hoverBox.id = 'DuckDuckGoPrivacyEssentialsHoverableText' - hoverBox.style.cssText = styles.textBubble - const arrow = document.createElement('div') - arrow.style.cssText = styles.textArrow - hoverBox.appendChild(arrow) - const branding = createTitleRow('DuckDuckGo') - branding.style.cssText += styles.hoverTextTitle - hoverBox.appendChild(branding) - const hoverText = document.createElement('div') - hoverText.style.cssText = styles.hoverTextBody - hoverText.textContent = hoverTextBody + ' ' - hoverText.appendChild(getLearnMoreLink()) - hoverBox.appendChild(hoverText) - - hoverContainer.appendChild(hoverBox) - const rect = originalElement.getBoundingClientRect() - /* - * The left side of the hover popup may go offscreen if the - * login button is all the way on the left side of the page. This - * If that is the case, dynamically shift the box right so it shows - * properly. - */ - if (rect.left < styles.textBubbleLeftShift) { - const leftShift = -rect.left + 10 // 10px away from edge of the screen - hoverBox.style.cssText += `left: ${leftShift}px;` - const change = (1 - (rect.left / styles.textBubbleLeftShift)) * (100 - styles.arrowDefaultLocationPercent) - arrow.style.cssText += `left: ${Math.max(10, styles.arrowDefaultLocationPercent - change)}%;` - } else if (rect.left + styles.textBubbleWidth - styles.textBubbleLeftShift > window.innerWidth) { - const rightShift = rect.left + styles.textBubbleWidth - styles.textBubbleLeftShift - const diff = Math.min(rightShift - window.innerWidth, styles.textBubbleLeftShift) - const rightMargin = 20 // Add some margin to the page, so scrollbar doesn't overlap. - hoverBox.style.cssText += `left: -${styles.textBubbleLeftShift + diff + rightMargin}px;` - const change = ((diff / styles.textBubbleLeftShift)) * (100 - styles.arrowDefaultLocationPercent) - arrow.style.cssText += `left: ${Math.max(10, styles.arrowDefaultLocationPercent + change)}%;` - } else { - hoverBox.style.cssText += `left: -${styles.textBubbleLeftShift}px;` - arrow.style.cssText += `left: ${styles.arrowDefaultLocationPercent}%;` - } - - return { - button: button, - container: container - } - } - - function makeModal (entity, acceptFunction, ...acceptFunctionParams) { - window.webkit.messageHandlers.getImage.postMessage(entityData[entity].modalIcon).then((icon) => { - // TODO short circuit this - const modalContainer = document.createElement('div') - modalContainer.style.cssText = styles.modalContainer - const pageOverlay = document.createElement('div') - pageOverlay.style.cssText = styles.overlay - const modal = document.createElement('div') - modal.style.cssText = styles.modal - - // Title - const modalTitle = createTitleRow('DuckDuckGo') - modal.appendChild(modalTitle) - - // Content - const modalContent = document.createElement('div') - modalContent.style.cssText = styles.modalContent - - const iconElement = document.createElement('img') - iconElement.style.cssText = styles.icon + styles.modalIcon - iconElement.setAttribute('src', icon) - iconElement.setAttribute('height', '70px') - - const title = document.createElement('div') - title.style.cssText = styles.modalContentTitle - title.textContent = entityData[entity].modalTitle - - const message = document.createElement('div') - message.style.cssText = styles.modalContentText - message.textContent = entityData[entity].modalText + ' ' - message.appendChild(getLearnMoreLink()) - - modalContent.appendChild(iconElement) - modalContent.appendChild(title) - modalContent.appendChild(message) - - // Buttons - const buttonRow = document.createElement('div') - buttonRow.style.cssText = 'margin:auto;' - const allowButton = makeButton(entityData[entity].modalAcceptText, 'lightMode') - allowButton.style.cssText += styles.modalButton + 'margin-right: 15px;' - allowButton.addEventListener('click', function doLogin () { - acceptFunction(...acceptFunctionParams) - document.body.removeChild(modalContainer) - }) - const rejectButton = makeButton(entityData[entity].modalRejectText, 'cancelMode') - rejectButton.style.cssText += styles.modalButton + 'float: right;' - rejectButton.addEventListener('click', function cancelLogin () { - document.body.removeChild(modalContainer) - }) - - buttonRow.appendChild(allowButton) - buttonRow.appendChild(rejectButton) - modalContent.appendChild(buttonRow) - - modal.appendChild(modalContent) - - modalContainer.appendChild(pageOverlay) - modalContainer.appendChild(modal) - document.body.insertBefore(modalContainer, document.body.childNodes[0]) - }) - } - - function createTitleRow (message, textButton) { - // Create row container - const row = document.createElement('div') - row.style.cssText = styles.titleBox - - // Logo - const logoContainer = document.createElement('div') - logoContainer.style.cssText = styles.logo - const logoElement = document.createElement('img') - logoElement.setAttribute('src', logoImg) - logoElement.setAttribute('height', '21px') - logoElement.style.cssText = styles.logoImg - logoContainer.appendChild(logoElement) - row.appendChild(logoContainer) - - // Content box title - const msgElement = document.createElement('div') - msgElement.id = titleID // Ensure we can find this to potentially hide it later. - msgElement.textContent = message - msgElement.style.cssText = styles.title - row.appendChild(msgElement) - - // Text button for very small boxes - if (textButton) { - textButton.id = titleID + 'TextButton' - row.appendChild(textButton) - } - - return row - } - - // Create the content block to replace other divs/iframes with - function createContentBlock (widget, button, textButton, img) { - const contentBlock = document.createElement('div') - contentBlock.style.cssText = styles.wrapperDiv - - // Put our custom font-faces inside the wrapper element, since - // @font-face does not work inside a shadowRoot. - // See https://github.com/mdn/interactive-examples/issues/887. - const fontFaceStyleElement = document.createElement('style') - fontFaceStyleElement.textContent = styles.fontStyle - contentBlock.appendChild(fontFaceStyleElement) - - // Put everyting else inside the shadowRoot of the wrapper element to - // reduce the chances of the website's stylesheets messing up the - // placeholder's appearance. - const shadowRoot = contentBlock.attachShadow({ mode: 'closed' }) - - // Style element includes our font & overwrites page styles - const styleElement = document.createElement('style') - const wrapperClass = 'DuckDuckGoSocialContainer' - styleElement.textContent = ` - .${wrapperClass} a { - ${styles[widget.getMode()].linkFont} - font-weight: bold; - } - .${wrapperClass} a:hover { - ${styles[widget.getMode()].linkFont} - font-weight: bold; - } - ` - shadowRoot.appendChild(styleElement) - - // Create overall grid structure - const element = document.createElement('div') - element.style.cssText = styles.block + styles[widget.getMode()].background + styles[widget.getMode()].textFont - element.className = wrapperClass - shadowRoot.appendChild(element) - - // grid of three rows - const titleRow = document.createElement('div') - titleRow.style.cssText = styles.headerRow - element.appendChild(titleRow) - titleRow.appendChild(createTitleRow('DuckDuckGo', textButton)) - - const contentRow = document.createElement('div') - contentRow.style.cssText = styles.content - - if (img) { - const imageRow = document.createElement('div') - imageRow.style.cssText = styles.imgRow - const imgElement = document.createElement('img') - imgElement.style.cssText = styles.icon - imgElement.setAttribute('src', img) - imgElement.setAttribute('height', '70px') - imageRow.appendChild(imgElement) - element.appendChild(imageRow) - } - - const contentTitle = document.createElement('div') - contentTitle.style.cssText = styles.contentTitle - if (entityData[widget.entity].simpleVersion && widget.replaceSettings.simpleInfoTitle) { - contentTitle.textContent = widget.replaceSettings.simpleInfoTitle - } else { - contentTitle.textContent = widget.replaceSettings.infoTitle - } - contentRow.appendChild(contentTitle) - const contentText = document.createElement('div') - contentText.style.cssText = styles.contentText - if (entityData[widget.entity].simpleVersion && widget.replaceSettings.simpleInfoText) { - contentText.textContent = widget.replaceSettings.simpleInfoText + ' ' - } else { - contentText.textContent = widget.replaceSettings.infoText + ' ' - } - contentText.appendChild(getLearnMoreLink()) - contentRow.appendChild(contentText) - element.appendChild(contentRow) - - const buttonRow = document.createElement('div') - buttonRow.style.cssText = styles.buttonRow - buttonRow.appendChild(button) - contentRow.appendChild(buttonRow) - - return { contentBlock, shadowRoot } - } -})() diff --git a/DuckDuckGo/ContentBlocker/clickToLoadConfig.json b/DuckDuckGo/ContentBlocker/clickToLoadConfig.json deleted file mode 100644 index 07dad4a49e..0000000000 --- a/DuckDuckGo/ContentBlocker/clickToLoadConfig.json +++ /dev/null @@ -1,414 +0,0 @@ -{ - "Facebook": { - "domains": [ - "facebook.com", - "facebook.net" - ], - "excludedSubdomains": [ - "graph.facebook.com" - ], - "excludedDomains": [ - { - "domain": "www.figma.com", - "reason": "breakage due to file browser fb-page element" - }, - { - "domain": "facebook.com", - "reason": "FB breakage" - }, - { - "domain": "apps.facebook.com", - "reason": "FB breakage" - }, - { - "domain": "smartypance.com", - "reason": "https://app.asana.com/0/1200277586140538/1205348923041541/f" - }, - { - "domain": "www.dreamsresorts.com", - "reason": "https://app.asana.com/0/1200277586140538/1205409536696340/f" - }, - { - "domain": "www.standard.co.uk", - "reason": "https://app.asana.com/0/1200277586140538/1205326380537958/f" - } - ], - "surrogates": [ - { - "rule": "facebook\\.net\\/[a-z_A-Z]+\\/sdk.js", - "surrogate": "fb-sdk.js", - "xray": "fb-surrogate-xray.js" - }, - { - "rule": "facebook\\.net\\/[a-z_A-Z]+\\/all.js", - "surrogate": "fb-sdk.js", - "xray": "fb-surrogate-xray.js" - }, - { - "rule": "facebook\\.com\\/[a-z_A-Z]+\\/sdk.js", - "surrogate": "fb-sdk.js", - "xray": "fb-surrogate-xray.js" - } - ], - "informationalModal": { - "icon": "blocked_facebook_logo.svg", - "messageTitle": "Logging in with Facebook lets them track you", - "messageBody": "Once you're logged in, DuckDuckGo can't block Facebook content from tracking you on this site.", - "confirmButtonText": "Log In", - "rejectButtonText": "Go back" - }, - "clicksBeforeSimpleVersion": 3, - "elementData": { - "FB Like Button": { - "selectors": [ - ".fb-like" - ], - "replaceSettings": { - "type": "blank" - } - }, - "FB Button iFrames": { - "selectors": [ - "iframe[src*='://www.facebook.com/plugins/like.php']", - "iframe[src*='://www.facebook.com/v2.0/plugins/like.php']", - "iframe[src*='://www.facebook.com/plugins/share_button.php']", - "iframe[src*='://www.facebook.com/v2.0/plugins/share_button.php']" - ], - "replaceSettings": { - "type": "blank" - } - }, - "FB Save Button": { - "selectors": [ - ".fb-save" - ], - "replaceSettings": { - "type": "blank" - } - }, - "FB Share Button": { - "selectors": [ - ".fb-share-button" - ], - "replaceSettings": { - "type": "blank" - } - }, - "FB Page iFrames": { - "selectors": [ - "iframe[src*='://www.facebook.com/plugins/page.php']", - "iframe[src*='://www.facebook.com/v2.0/plugins/page.php']" - ], - "replaceSettings": { - "type": "dialog", - "buttonText": "Unblock Content", - "infoTitle": "DuckDuckGo blocked this content to prevent Facebook from tracking you", - "infoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity.", - "simpleInfoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity." - }, - "clickAction": { - "type": "originalElement" - } - }, - "FB Page Div": { - "selectors": [ - ".fb-page" - ], - "replaceSettings": { - "type": "dialog", - "buttonText": "Unblock Content", - "infoTitle": "DuckDuckGo blocked this content to prevent Facebook from tracking you", - "infoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity.", - "simpleInfoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity." - }, - "clickAction": { - "type": "iFrame", - "targetURL": "https://www.facebook.com/plugins/page.php?href=data-href&tabs=data-tabs&width=data-width&height=data-height", - "urlDataAttributesToPreserve": { - "data-href": { - "default": "", - "required": true - }, - "data-tabs": { - "default": "timeline" - }, - "data-height": { - "default": "500" - }, - "data-width": { - "default": "500" - } - }, - "styleDataAttributes": { - "width": { - "name": "data-width", - "unit": "px" - }, - "height": { - "name": "data-height", - "unit": "px" - } - } - } - }, - "FB Comment iFrames": { - "selectors": [ - "iframe[src*='://www.facebook.com/plugins/comment_embed.php']", - "iframe[src*='://www.facebook.com/v2.0/plugins/comment_embed.php']" - ], - "replaceSettings": { - "type": "dialog", - "buttonText": "Unblock Comment", - "infoTitle": "DuckDuckGo blocked this comment to prevent Facebook from tracking you", - "infoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity.", - "simpleInfoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity." - }, - "clickAction": { - "type": "originalElement" - } - }, - "FB Comments": { - "selectors": [ - ".fb-comments", - "fb\\3Acomments" - ], - "replaceSettings": { - "type": "dialog", - "buttonText": "Unblock Comments", - "infoTitle": "DuckDuckGo blocked these comments to prevent Facebook from tracking you", - "infoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity.", - "simpleInfoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity." - }, - "clickAction": { - "type": "allowFull", - "targetURL": "https://www.facebook.com/v9.0/plugins/comments.php?href=data-href&numposts=data-numposts&sdk=joey&version=v9.0&width=data-width", - "urlDataAttributesToPreserve": { - "data-href": { - "default": "", - "required": true - }, - "data-numposts": { - "default": 10 - }, - "data-width": { - "default": "500" - } - } - } - }, - "FB Embedded Comment Div": { - "selectors": [ - ".fb-comment-embed" - ], - "replaceSettings": { - "type": "dialog", - "buttonText": "Unblock Comment", - "infoTitle": "DuckDuckGo blocked this comment to prevent Facebook from tracking you", - "infoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity.", - "simpleInfoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity." - }, - "clickAction": { - "type": "iFrame", - "targetURL": "https://www.facebook.com/v9.0/plugins/comment_embed.php?href=data-href&sdk=joey&width=data-width&include_parent=data-include-parent", - "urlDataAttributesToPreserve": { - "data-href": { - "default": "", - "required": true - }, - "data-width": { - "default": "500" - }, - "data-include-parent": { - "default": "false" - } - }, - "styleDataAttributes": { - "width": { - "name": "data-width", - "unit": "px" - } - } - } - }, - "FB Post iFrames": { - "selectors": [ - "iframe[src*='://www.facebook.com/plugins/post.php']", - "iframe[src*='://www.facebook.com/v2.0/plugins/post.php']" - ], - "replaceSettings": { - "type": "dialog", - "buttonText": "Unblock Post", - "infoTitle": "DuckDuckGo blocked this post to prevent Facebook from tracking you", - "infoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity.", - "simpleInfoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity." - }, - "clickAction": { - "type": "originalElement" - } - }, - "FB Posts Div": { - "selectors": [ - ".fb-post" - ], - "replaceSettings": { - "type": "dialog", - "buttonText": "Unblock Post", - "infoTitle": "DuckDuckGo blocked this post to prevent Facebook from tracking you", - "infoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity.", - "simpleInfoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity." - }, - "clickAction": { - "type": "allowFull", - "targetURL": "https://www.facebook.com/v9.0/plugins/post.php?href=data-href&sdk=joey&show_text=true&width=data-width", - "urlDataAttributesToPreserve": { - "data-href": { - "default": "", - "required": true - }, - "data-width": { - "default": "500" - } - }, - "styleDataAttributes": { - "width": { - "name": "data-width", - "unit": "px" - }, - "height": { - "name": "data-height", - "unit": "px", - "fallbackAttribute": "data-width" - } - } - } - }, - "FB Video iFrames": { - "selectors": [ - "iframe[src*='://www.facebook.com/plugins/video.php']", - "iframe[src*='://www.facebook.com/v2.0/plugins/video.php']" - ], - "replaceSettings": { - "type": "dialog", - "buttonText": "Unblock Video", - "infoTitle": "DuckDuckGo blocked this video to prevent Facebook from tracking you", - "infoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity.", - "simpleInfoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity." - }, - "clickAction": { - "type": "originalElement" - } - }, - "FB Video": { - "selectors": [ - ".fb-video" - ], - "replaceSettings": { - "type": "dialog", - "buttonText": "Unblock Video", - "infoTitle": "DuckDuckGo blocked this video to prevent Facebook from tracking you", - "infoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity.", - "simpleInfoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity." - }, - "clickAction": { - "type": "iFrame", - "targetURL": "https://www.facebook.com/plugins/video.php?href=data-href&show_text=true&width=data-width", - "urlDataAttributesToPreserve": { - "data-href": { - "default": "", - "required": true - }, - "data-width": { - "default": "500" - } - }, - "styleDataAttributes": { - "width": { - "name": "data-width", - "unit": "px" - }, - "height": { - "name": "data-height", - "unit": "px", - "fallbackAttribute": "data-width" - } - } - } - }, - "FB Group iFrames": { - "selectors": [ - "iframe[src*='://www.facebook.com/plugins/group.php']", - "iframe[src*='://www.facebook.com/v2.0/plugins/group.php']" - ], - "replaceSettings": { - "type": "dialog", - "buttonText": "Unblock Content", - "infoTitle": "DuckDuckGo blocked this content to prevent Facebook from tracking you", - "infoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity.", - "simpleInfoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity." - }, - "clickAction": { - "type": "originalElement" - } - }, - "FB Group": { - "selectors": [ - ".fb-group" - ], - "replaceSettings": { - "type": "dialog", - "buttonText": "Unblock Content", - "infoTitle": "DuckDuckGo blocked this content to prevent Facebook from tracking you", - "infoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity.", - "simpleInfoText": "We blocked Facebook from tracking you when the page loaded. If you unblock this content, Facebook will know your activity." - }, - "clickAction": { - "type": "iFrame", - "targetURL": "https://www.facebook.com/plugins/group.php?href=data-href&width=data-width", - "urlDataAttributesToPreserve": { - "data-href": { - "default": "", - "required": true - }, - "data-width": { - "default": "500" - } - }, - "styleDataAttributes": { - "width": { - "name": "data-width", - "unit": "px" - } - } - } - }, - "FB Login Button": { - "selectors": [ - ".fb-login-button" - ], - "replaceSettings": { - "type": "loginButton", - "icon": "blocked_facebook_logo.svg", - "buttonText": "Log in with Facebook", - "popupTitleText": "DuckDuckGo blocked this Facebook login", - "popupBodyText": "Facebook tracks your activity on a site when you use them to login." - }, - "clickAction": { - "type": "allowFull", - "targetURL": "https://www.facebook.com/v9.0/plugins/login_button.php?app_id=app_id_replace&auto_logout_link=false&button_type=continue_with&sdk=joey&size=large&use_continue_as=false&width=", - "urlDataAttributesToPreserve": { - "data-href": { - "default": "", - "required": true - }, - "data-width": { - "default": "500" - }, - "app_id_replace": { - "default": "null" - } - } - } - } - } - } -} \ No newline at end of file diff --git a/DuckDuckGo/ContentBlocker/fb-sdk.js b/DuckDuckGo/ContentBlocker/fb-sdk.js deleted file mode 100644 index 481326dead..0000000000 --- a/DuckDuckGo/ContentBlocker/fb-sdk.js +++ /dev/null @@ -1,188 +0,0 @@ -(() => { -// facebook.net/sdk.js application/javascript - 'use strict' - // const originalFBURL = document.currentScript.src - let siteInit = function () {} - let fbIsEnabled = false - let initData = {} - let runInit = false - const parseCalls = [] - const popupName = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 12) - - const fbLogin = { - callback: function () {}, - params: undefined, - shouldRun: false - } - - function messageAddon (detailObject) { - detailObject.entity = 'Facebook' - const event = new CustomEvent('ddgClickToLoad', { - detail: detailObject, - bubbles: false, - cancelable: false, - composed: false - }) - dispatchEvent(event) - } - - /** - * When setting up the Facebook SDK, the site may define a function called window.fbAsyncInit. - * Once the SDK loads, it searches for and calls window.fbAsyncInit. However, some sites may - * not use this, and just call FB.init directly at some point (after ensuring that the script has loaded). - * - * Our surrogate (defined below in window.FB) captures calls made to init by page scripts. If at a - * later point we load the real sdk here, we then re-call init with whatever arguments the page passed in - * originally. The runInit param should be true when a page has called init directly. - * Because we put it in asyncInit, the flow will be something like: - * - * FB SDK loads -> SDK calls window.fbAsyncInit -> Our function calls window.FB.init (maybe) -> - * our function calls original fbAsyncInit (if it existed) - */ - function enableFacebookSDK () { - if (!fbIsEnabled) { - window.FB = undefined - window.fbAsyncInit = function () { - if (runInit && initData) { - window.FB.init(initData) - } - siteInit() - if (fbLogin.shouldRun) { - window.FB.login(fbLogin.callback, fbLogin.params) - } - } - const fbScript = document.createElement('script') - fbScript.setAttribute('crossorigin', 'anonymous') - fbScript.setAttribute('async', '') - fbScript.setAttribute('defer', '') - fbScript.src = 'https://connect.facebook.net/en_US/sdk.js?XFBML=false' // originalFBURL - fbScript.onload = function () { - for (const node of parseCalls) { - window.FB.XFBML.parse.apply(window.FB.XFBML, node) - } - } - document.head.appendChild(fbScript) - fbIsEnabled = true - } else { - if (initData) { - window.FB.init(initData) - } - } - } - - function removeFacebookSurrogate () { - window.fbAsyncInit = function () { - if (runInit && initData) { - window.FB.init(initData) - } - siteInit() - if (fbLogin.shouldRun) { - window.FB.login(fbLogin.callback, fbLogin.params) - } - } - if (window.FB && window.FB.isSurrogate) { - window.FB = undefined - } - } - - function runFacebookLogin () { - fbLogin.shouldRun = true - replaceWindowOpen() - loginPopup() - // enableFacebookSDK() - } - - function replaceWindowOpen () { - const oldOpen = window.open - window.open = function (url, name, windowParams) { - const u = new URL(url) - if (u.origin === 'https://www.facebook.com') { - name = popupName - } - return oldOpen.call(window, url, name, windowParams) - } - } - - function loginPopup () { - const width = Math.min(window.screen.width, 450) - const height = Math.min(window.screen.height, 450) - const popupParams = 'width=' + width + ',height=' + height + '},scrollbars=1,location=1' - window.open('about:blank', popupName, popupParams) - } - - window.addEventListener('LoadFacebookSDK', enableFacebookSDK) - window.addEventListener('RunFacebookLogin', runFacebookLogin) - window.addEventListener('RemoveFacebookSurrogate', removeFacebookSurrogate) - - function init () { - if (window.fbAsyncInit) { - siteInit = window.fbAsyncInit - window.fbAsyncInit() - } - } - - if (!window.FB) { - window.FB = { - api: function (url, cb) { cb() }, - init: function (obj) { - if (obj) { - initData = obj - runInit = true - messageAddon({ - appID: obj.appId - }) - } - }, - ui: function (obj, cb) { - if (obj.method && obj.method === 'share') { - const shareLink = 'https://www.facebook.com/sharer/sharer.php?u=' + obj.href - window.open(shareLink, 'share-facebook', 'width=550,height=235') - } - // eslint-disable-next-line node/no-callback-literal - cb({}) - }, - getAccessToken: function () {}, - getAuthResponse: function () { - return { status: '' } - }, - // eslint-disable-next-line node/no-callback-literal - getLoginStatus: function (callback) { callback({ status: 'unknown' }) }, - getUserID: function () {}, - login: function (cb, params) { - fbLogin.callback = cb - fbLogin.params = params - messageAddon({ - action: 'login' - }) - }, - logout: function () {}, - AppEvents: { - EventNames: {}, - logEvent: function (a, b, c) {}, - logPageView: function () {} - }, - Event: { - subscribe: function (event, callback) { - if (event === 'xfbml.render') { - callback() - } - }, - unsubscribe: function () {} - }, - XFBML: { - parse: function (n) { - parseCalls.push(n) - } - }, - isSurrogate: true - } - if (document.readyState === 'complete') { - init() - } else { - // sdk script loaded before page content, so wait for load. - window.addEventListener('load', (event) => { - init() - }) - } - } -})() diff --git a/DuckDuckGo/ContentBlocker/fb-tds.json b/DuckDuckGo/ContentBlocker/fb-tds.json deleted file mode 100644 index 698e3e7e3d..0000000000 --- a/DuckDuckGo/ContentBlocker/fb-tds.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "trackers": { - "facebook.com": { - "domain": "facebook.com", - "owner": { - "name": "Facebook, Inc.", - "displayName": "Facebook" - }, - "default": "ignore", - "rules": [ - { - "rule": "facebook\\.com\\/[a-z_A-Z]+\\/sdk\\.js", - "exceptions": { - "domains": [ - "graph.facebook.com" - ] - }, - "action": "block" - } - ] - }, - "facebook.net": { - "domain": "facebook.net", - "owner": { - "name": "Facebook, Inc.", - "displayName": "Facebook" - }, - "default": "ignore", - "rules": [ - { - "rule": "facebook\\.net\\/[a-z_A-Z]+\\/sdk\\.js", - "exceptions": { - "domains": [ - "graph.facebook.com" - ] - }, - "action": "block" - }, - { - "rule": "facebook\\.net\\/[a-z_A-Z]+\\/all\\.js", - "exceptions": { - "domains": [ - "graph.facebook.com" - ] - }, - "action": "block" } - ] - } - }, - "entities": { - "Facebook, Inc.": { - "domains": [ - "facebook.com", - "facebook.net", - "graph.facebook.com" - ], - "displayName": "Facebook" - } - }, - "domains": { - "facebook.com": "Facebook, Inc.", - "facebook.net": "Facebook, Inc.", - "graph.facebook.com": "Facebook, Inc." - } -} diff --git a/DuckDuckGo/ContentBlocker/fonts/ProximaNova-Bold-webfont.woff2 b/DuckDuckGo/ContentBlocker/fonts/ProximaNova-Bold-webfont.woff2 deleted file mode 100644 index d88ee9b097..0000000000 Binary files a/DuckDuckGo/ContentBlocker/fonts/ProximaNova-Bold-webfont.woff2 and /dev/null differ diff --git a/DuckDuckGo/ContentBlocker/fonts/ProximaNova-Reg-webfont.woff2 b/DuckDuckGo/ContentBlocker/fonts/ProximaNova-Reg-webfont.woff2 deleted file mode 100644 index 7c75b9edc2..0000000000 Binary files a/DuckDuckGo/ContentBlocker/fonts/ProximaNova-Reg-webfont.woff2 and /dev/null differ diff --git a/DuckDuckGo/ContentBlocker/macos-config.json b/DuckDuckGo/ContentBlocker/macos-config.json index 5dbf1f1058..cceed355c5 100644 --- a/DuckDuckGo/ContentBlocker/macos-config.json +++ b/DuckDuckGo/ContentBlocker/macos-config.json @@ -1,6 +1,6 @@ { "readme": "https://github.com/duckduckgo/privacy-configuration", - "version": 1717679238533, + "version": 1718303485463, "features": { "adClickAttribution": { "readme": "https://help.duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/#3rd-party-tracker-loading-protection", @@ -306,6 +306,9 @@ { "domain": "sports.tipico.de" }, + { + "domain": "lotusbakeries.com" + }, { "domain": "marvel.com" }, @@ -321,7 +324,7 @@ ] }, "state": "enabled", - "hash": "0e21f0096d2a135a3c36332ae79b656c" + "hash": "168b747ef8adb3bd717684f1e1724c05" }, "autofill": { "exceptions": [ @@ -946,7 +949,7 @@ "ruleActions": [ "block-ctl-fb" ], - "state": "disabled" + "state": "enabled" }, "Youtube": { "ruleActions": [ @@ -955,8 +958,9 @@ "state": "disabled" } }, - "state": "disabled", - "hash": "770f7ae0f752e976764771bccec352b2" + "state": "enabled", + "minSupportedVersion": "1.93.0", + "hash": "3b8fd857f944f1997ab9593de582491b" }, "clickToPlay": { "exceptions": [ @@ -4625,6 +4629,9 @@ { "domain": "proton.me" }, + { + "domain": "nordstrom.com" + }, { "domain": "marvel.com" }, @@ -4666,7 +4673,7 @@ } ], "state": "enabled", - "hash": "aefca8e7a9a3d9b65370608dd639cd3f" + "hash": "eb816f6eaec8c0c712d583fc490ffe59" }, "fingerprintingScreenSize": { "settings": { @@ -5084,6 +5091,26 @@ ], "hash": "936f00970c108fd646f73d00b3f3f5b5" }, + "pluginPointFocusedViewPlugin": { + "state": "disabled", + "exceptions": [], + "hash": "c292bb627849854515cebbded288ef5a" + }, + "pluginPointNewTabPagePlugin": { + "state": "disabled", + "exceptions": [], + "hash": "c292bb627849854515cebbded288ef5a" + }, + "pluginPointNewTabPageSectionPlugin": { + "state": "disabled", + "exceptions": [], + "hash": "c292bb627849854515cebbded288ef5a" + }, + "pluginPointNewTabPageShortcutPlugin": { + "state": "disabled", + "exceptions": [], + "hash": "c292bb627849854515cebbded288ef5a" + }, "privacyDashboard": { "exceptions": [], "features": { @@ -6644,6 +6671,12 @@ "" ] }, + { + "rule": "google.com/adsense/search/", + "domains": [ + "ihowlist.com" + ] + }, { "rule": "marketingplatform.google.com/about/enterprise", "domains": [ @@ -8565,7 +8598,7 @@ "domain": "sundancecatalog.com" } ], - "hash": "af06003f902448cd26fd4e538e64e70f" + "hash": "97d6c51d24af39c870faf3f05c18746b" }, "trackingCookies1p": { "settings": { @@ -8942,17 +8975,15 @@ "state": "disabled", "hash": "728493ef7a1488e4781656d3f9db84aa" }, + "windowsSpellChecker": { + "exceptions": [], + "state": "disabled", + "hash": "728493ef7a1488e4781656d3f9db84aa" + }, "windowsStartupBoost": { - "exceptions": [ - { - "domain": "marvel.com" - }, - { - "domain": "sundancecatalog.com" - } - ], + "exceptions": [], "state": "disabled", - "hash": "dc1b4fa301193a03ddcd4bdf7ee3e610" + "hash": "728493ef7a1488e4781656d3f9db84aa" }, "windowsWaitlist": { "exceptions": [], diff --git a/DuckDuckGo/ContentBlocker/trackerData.json b/DuckDuckGo/ContentBlocker/trackerData.json index ef11c78f3c..7a12422f7a 100644 --- a/DuckDuckGo/ContentBlocker/trackerData.json +++ b/DuckDuckGo/ContentBlocker/trackerData.json @@ -47,36 +47,6 @@ ], "default": "block" }, - "1gr.cz": { - "domain": "1gr.cz", - "owner": { - "name": "MAFRA, a.s.", - "displayName": "MAFRA" - }, - "prevalence": 0.0000885, - "fingerprinting": 3, - "cookies": 0.0000545, - "categories": [], - "default": "ignore", - "rules": [ - { - "rule": "1gr\\.cz\\/js\\/uni\\/uni\\.js", - "fingerprinting": 2, - "cookies": 0.000034 - }, - { - "rule": "1gr\\.cz\\/js\\/uni\\/paticka\\.js", - "fingerprinting": 2, - "cookies": 0 - }, - { - "rule": "1gr\\.cz\\/mafra\\/SETSV\\/TTL=300\\/LASTPBFCID=160547\\/append", - "fingerprinting": 0, - "cookies": 0, - "comment": "pixel" - } - ] - }, "1rx.io": { "domain": "1rx.io", "owner": { @@ -7247,6 +7217,34 @@ } ] }, + "d1y068gyog18cq.cloudfront.net": { + "domain": "d1y068gyog18cq.cloudfront.net", + "owner": { + "name": "Amazon Technologies, Inc.", + "displayName": "Amazon.com", + "privacyPolicy": "https://www.amazon.com/gp/help/customer/display.html?nodeId=468496", + "url": "http://www.amazon.com/" + }, + "prevalence": 0.0000272, + "fingerprinting": 2, + "cookies": 0.0000272, + "categories": [ + "Content Delivery" + ], + "default": "ignore", + "rules": [ + { + "rule": "d1y068gyog18cq\\.cloudfront\\.net\\/psjhjhsb\\.js", + "fingerprinting": 2, + "cookies": 0.0000136 + }, + { + "rule": "d1y068gyog18cq\\.cloudfront\\.net\\/sp\\.js", + "fingerprinting": 2, + "cookies": 0.0000136 + } + ] + }, "d214hhm15p4t1d.cloudfront.net": { "domain": "d214hhm15p4t1d.cloudfront.net", "owner": { @@ -9712,28 +9710,15 @@ "cookies": 0.000034 }, { - "rule": "facebook\\.com\\/tr\\/", - "fingerprinting": 0, - "cookies": 0.000538, - "comment": "pixel" - }, - { - "rule": "facebook\\.com\\/tr", - "fingerprinting": 0, - "cookies": 0.0000272, - "comment": "pixel" - }, - { - "rule": "facebook\\.com\\/ajax\\/bz", - "fingerprinting": 0, - "cookies": 0, - "comment": "pixel" + "rule": "facebook\\.com/[a-z_A-Z]+/sdk\\.js", + "surrogate": "fb-sdk.js", + "action": "block-ctl-fb", + "fingerprinting": 1, + "cookies": 0 }, { - "rule": "facebook\\.com\\/123450\\/picture", - "fingerprinting": 0, - "cookies": 0, - "comment": "pixel" + "rule": "facebook\\.com/", + "action": "block-ctl-fb" } ] }, @@ -9764,11 +9749,25 @@ ], "default": "ignore", "rules": [ + { + "rule": "facebook\\.net/.*/all\\.js", + "surrogate": "fb-sdk.js", + "action": "block-ctl-fb", + "fingerprinting": 1, + "cookies": 0.0000408 + }, { "rule": "facebook\\.net/.*/fbevents\\.js", "fingerprinting": 1, "cookies": 0.108 }, + { + "rule": "facebook\\.net/[a-z_A-Z]+/sdk\\.js", + "surrogate": "fb-sdk.js", + "action": "block-ctl-fb", + "fingerprinting": 1, + "cookies": 0.000334 + }, { "rule": "facebook\\.net/signals/config/", "fingerprinting": 1, @@ -9795,16 +9794,8 @@ "cookies": 0 }, { - "rule": "facebook\\.net\\/\\/log\\/error", - "fingerprinting": 0, - "cookies": 0, - "comment": "pixel" - }, - { - "rule": "facebook\\.net\\/en_US\\/fp\\.js", - "fingerprinting": 0, - "cookies": 0, - "comment": "pixel" + "rule": "facebook\\.net/", + "action": "block-ctl-fb" } ] }, @@ -11974,31 +11965,6 @@ } ] }, - "gsitrix.com": { - "domain": "gsitrix.com", - "owner": { - "name": "GP One GmbH", - "displayName": "GP One" - }, - "prevalence": 0.0000953, - "fingerprinting": 3, - "cookies": 0.0000953, - "categories": [], - "default": "ignore", - "rules": [ - { - "rule": "gsitrix\\.com\\/page\\/", - "fingerprinting": 3, - "cookies": 0.0000545 - }, - { - "rule": "gsitrix\\.com\\/js\\/ax\\.php", - "fingerprinting": 0, - "cookies": 0, - "comment": "pixel" - } - ] - }, "gsspat.jp": { "domain": "gsspat.jp", "owner": { @@ -28146,6 +28112,25 @@ "categories": [], "default": "block" }, + "uio.no": { + "domain": "uio.no", + "owner": { + "name": "Universitetet i Oslo", + "displayName": "Universitetet i Oslo" + }, + "prevalence": 0.00000681, + "fingerprinting": 2, + "cookies": 0.00000681, + "categories": [], + "default": "ignore", + "rules": [ + { + "rule": "uio\\.no\\/js\\/analytics\\/v3\\/analytics\\.js", + "fingerprinting": 2, + "cookies": 0.0000136 + } + ] + }, "uk-v1.edgekey.net": { "domain": "uk-v1.edgekey.net", "owner": { @@ -28946,6 +28931,25 @@ } ] }, + "visilabs.net": { + "domain": "visilabs.net", + "owner": { + "name": "Portakal Yazilim Danismanlik Reklamcilik ve Yayincilik San. Tic. A.S.", + "displayName": "Portakal Yazilim Danismanlik Reklamcilik ve Yayincilik San. Tic" + }, + "prevalence": 0.0000545, + "fingerprinting": 2, + "cookies": 0.0000545, + "categories": [], + "default": "ignore", + "rules": [ + { + "rule": "visilabs\\.net/.*/Visilabs\\.min\\.js", + "fingerprinting": 2, + "cookies": 0.0000136 + } + ] + }, "visualwebsiteoptimizer.com": { "domain": "visualwebsiteoptimizer.com", "owner": { @@ -30901,7 +30905,23 @@ "Content Delivery", "Embedded Content" ], - "default": "ignore" + "default": "ignore", + "rules": [ + { + "rule": "youtube-nocookie\\.com/iframe_api", + "surrogate": "youtube-iframe-api.js", + "action": "block-ctl-yt" + }, + { + "rule": "youtube-nocookie\\.com/player_api", + "surrogate": "youtube-iframe-api.js", + "action": "block-ctl-yt" + }, + { + "rule": "youtube-nocookie\\.com/", + "action": "block-ctl-yt" + } + ] }, "youtube.com": { "domain": "youtube.com", @@ -30919,7 +30939,23 @@ "Content Delivery", "Embedded Content" ], - "default": "ignore" + "default": "ignore", + "rules": [ + { + "rule": "youtube\\.com/iframe_api", + "surrogate": "youtube-iframe-api.js", + "action": "block-ctl-yt" + }, + { + "rule": "youtube\\.com/player_api", + "surrogate": "youtube-iframe-api.js", + "action": "block-ctl-yt" + }, + { + "rule": "youtube\\.com/", + "action": "block-ctl-yt" + } + ] }, "zemanta.com": { "domain": "zemanta.com", @@ -31236,6 +31272,25 @@ } ] }, + "gov.bc.ca": { + "domain": "gov.bc.ca", + "owner": { + "name": "Government of the Province of British Columbia", + "displayName": "Government of the Province of British Columbia" + }, + "prevalence": 0.000703, + "fingerprinting": 2, + "cookies": 0.000502, + "categories": [], + "default": "ignore", + "rules": [ + { + "rule": "gov\\.bc\\.ca\\/StaticWebResources\\/static\\/sp\\/sp-2-14-0\\.js", + "fingerprinting": 2, + "cookies": 0.000502 + } + ] + }, "google.ch": { "domain": "google.ch", "owner": { @@ -31299,6 +31354,25 @@ } ] }, + "framasoft.org": { + "domain": "framasoft.org", + "owner": { + "name": "Alexis Kauffmann", + "displayName": "Alexis Kauffmann" + }, + "prevalence": 0.000402, + "fingerprinting": 2, + "cookies": 0, + "categories": [], + "default": "ignore", + "rules": [ + { + "rule": "framasoft\\.org\\/nav\\/nav\\.js", + "fingerprinting": 2, + "cookies": 0 + } + ] + }, "veoxa.com": { "domain": "veoxa.com", "owner": { @@ -39866,16 +39940,6 @@ "prevalence": 0.0007, "displayName": "CleverDATA" }, - "MAFRA, a.s.": { - "domains": [ - "1gr.cz", - "emimino.cz", - "idnes.cz", - "lidovky.cz" - ], - "prevalence": 0.0054, - "displayName": "MAFRA" - }, "RhythmOne": { "domains": [ "1rx.io", @@ -44580,13 +44644,6 @@ "prevalence": 0.0143, "displayName": "GrowSumo" }, - "GP One GmbH": { - "domains": [ - "gsitrix.com" - ], - "prevalence": 0.0095, - "displayName": "GP One" - }, "buySAFE Inc": { "domains": [ "buysafe.com", @@ -48938,6 +48995,13 @@ "prevalence": 0, "displayName": "uidapi.com" }, + "Universitetet i Oslo": { + "domains": [ + "uio.no" + ], + "prevalence": 0.0007, + "displayName": "Universitetet i Oslo" + }, "SEOPULT LTD": { "domains": [ "getsale.io", @@ -49090,6 +49154,13 @@ "prevalence": 0.282, "displayName": "Soluciones Corporativas IP" }, + "Portakal Yazilim Danismanlik Reklamcilik ve Yayincilik San. Tic. A.S.": { + "domains": [ + "visilabs.net" + ], + "prevalence": 0.0054, + "displayName": "Portakal Yazilim Danismanlik Reklamcilik ve Yayincilik San. Tic" + }, "Wingify": { "domains": [ "pushcrew.com", @@ -49601,6 +49672,14 @@ "prevalence": 0.0041, "displayName": "Telstra" }, + "Government of the Province of British Columbia": { + "domains": [ + "gov.bc.ca", + "healthlinkbc.ca" + ], + "prevalence": 0.0007, + "displayName": "Government of the Province of British Columbia" + }, "opencmp.net": { "domains": [ "opencmp.net" @@ -49615,6 +49694,13 @@ "prevalence": 0, "displayName": "axept.io" }, + "Alexis Kauffmann": { + "domains": [ + "framasoft.org" + ], + "prevalence": 0.0402, + "displayName": "Alexis Kauffmann" + }, "J S WEB PRODUCTION": { "domains": [ "ad2perf.com", @@ -50741,10 +50827,6 @@ "datacamp.com": "DataCamp Limited", "rdocumentation.org": "DataCamp Limited", "1dmp.io": "CleverDATA LLC", - "1gr.cz": "MAFRA, a.s.", - "emimino.cz": "MAFRA, a.s.", - "idnes.cz": "MAFRA, a.s.", - "lidovky.cz": "MAFRA, a.s.", "1rx.io": "RhythmOne", "allmovie.com": "RhythmOne", "allmusic.com": "RhythmOne", @@ -54402,7 +54484,6 @@ "tvfanatic.com": "Mediavine, Inc.", "growsumo.com": "GrowSumo Inc.", "grsm.io": "GrowSumo Inc.", - "gsitrix.com": "GP One GmbH", "buysafe.com": "buySAFE Inc", "guarantee-cdn.com": "buySAFE Inc", "gumgum.com": "GumGum", @@ -56354,6 +56435,7 @@ "ubembed.com": "Unbounce", "unbounce.com": "Unbounce", "uidapi.com": "uidapi.com", + "uio.no": "Universitetet i Oslo", "getsale.io": "SEOPULT LTD", "ulogin.ru": "SEOPULT LTD", "unbxdapi.com": "Unbxd Software Private Limited", @@ -56392,6 +56474,7 @@ "videohub.tv": "Tremor Video DSP", "eleconomista.es": "Soluciones Corporativas IP, SL", "vidoomy.com": "Soluciones Corporativas IP, SL", + "visilabs.net": "Portakal Yazilim Danismanlik Reklamcilik ve Yayincilik San. Tic. A.S.", "pushcrew.com": "Wingify", "visualwebsiteoptimizer.com": "Wingify", "vwo.com": "Wingify", @@ -56597,8 +56680,11 @@ "shop.pe": "Add Shoppers", "telstra.com": "Telstra Corporation Limited", "telstra.com.au": "Telstra Corporation Limited", + "gov.bc.ca": "Government of the Province of British Columbia", + "healthlinkbc.ca": "Government of the Province of British Columbia", "opencmp.net": "opencmp.net", "axept.io": "axept.io", + "framasoft.org": "Alexis Kauffmann", "ad2perf.com": "J S WEB PRODUCTION", "moxielinks.com": "J S WEB PRODUCTION", "veoxa.com": "J S WEB PRODUCTION", diff --git a/DuckDuckGo/Tab/TabExtensions/ContentBlockingTabExtension.swift b/DuckDuckGo/Tab/TabExtensions/ContentBlockingTabExtension.swift index ad5e60576e..7ab4f5ddc6 100644 --- a/DuckDuckGo/Tab/TabExtensions/ContentBlockingTabExtension.swift +++ b/DuckDuckGo/Tab/TabExtensions/ContentBlockingTabExtension.swift @@ -139,6 +139,9 @@ extension ContentBlockingTabExtension: ContentBlockerRulesUserScriptDelegate { func contentBlockerRulesUserScript(_ script: ContentBlockerRulesUserScript, detectedTracker tracker: DetectedRequest) { trackersSubject.send(DetectedTracker(request: tracker, type: .tracker)) + if tracker.state == BlockingState.blocked && tracker.ownerName == fbBlockingEnabledProvider.fbEntity { + fbBlockingEnabledProvider.trackerDetected() + } } func contentBlockerRulesUserScript(_ script: ContentBlockerRulesUserScript, detectedThirdPartyRequest request: DetectedRequest) { @@ -153,6 +156,10 @@ extension ContentBlockingTabExtension: SurrogatesUserScriptDelegate { return true } + func surrogatesUserScriptShouldProcessCTLTrackers(_ script: SurrogatesUserScript) -> Bool { + fbBlockingEnabledProvider.fbBlockingEnabled + } + func surrogatesUserScript(_ script: SurrogatesUserScript, detectedTracker tracker: DetectedRequest, withSurrogate host: String) { trackersSubject.send(DetectedTracker(request: tracker, type: .trackerWithSurrogate(host: host))) } diff --git a/DuckDuckGo/Tab/TabExtensions/FBProtectionTabExtension.swift b/DuckDuckGo/Tab/TabExtensions/FBProtectionTabExtension.swift index fdc188e6e0..313ada0701 100644 --- a/DuckDuckGo/Tab/TabExtensions/FBProtectionTabExtension.swift +++ b/DuckDuckGo/Tab/TabExtensions/FBProtectionTabExtension.swift @@ -26,9 +26,12 @@ final class FBProtectionTabExtension { private let privacyConfigurationManager: PrivacyConfigurationManaging private weak var userContentController: UserContentControllerProtocol? + private weak var clickToLoadUserScript: ClickToLoadUserScript? private var cancellables = Set() + let fbEntity = "Facebook, Inc." + var fbBlockingEnabled = true init(privacyConfigurationManager: PrivacyConfigurationManaging, @@ -41,47 +44,50 @@ final class FBProtectionTabExtension { }.store(in: &cancellables) clickToLoadUserScriptPublisher.sink { [weak self] clickToLoadUserScript in clickToLoadUserScript?.delegate = self + self?.clickToLoadUserScript = clickToLoadUserScript }.store(in: &cancellables) } + @MainActor + public func trackerDetected() { + clickToLoadUserScript?.displayClickToLoadPlaceholders() + } } extension FBProtectionTabExtension { - private func toggleFBProtection(for url: URL) { + private func setFBProtection(for url: URL) { // Enable/disable FBProtection only after UserScripts are installed (awaitContentBlockingAssetsInstalled) let privacyConfiguration = privacyConfigurationManager.privacyConfig - let featureEnabled = privacyConfiguration.isFeature(.clickToPlay, enabledForDomain: url.host) - setFBProtection(enabled: featureEnabled) + let featureEnabled = privacyConfiguration.isFeature(.clickToLoad, enabledForDomain: url.host) + setFBProtection(enable: featureEnabled) } @discardableResult - private func setFBProtection(enabled: Bool) -> Bool { + private func setFBProtection(enable: Bool) -> Bool { if #unavailable(OSX 11) { // disable CTL for Catalina and earlier return false } - guard self.fbBlockingEnabled != enabled else { return false } + guard self.fbBlockingEnabled != enable else { return false } guard let userContentController else { assertionFailure("Missing UserContentController") return false } - if enabled { + if enable { do { - try userContentController.enableGlobalContentRuleList(withIdentifier: ContentBlockerRulesLists.Constants.clickToLoadRulesListName) + try userContentController.enableGlobalContentRuleList(withIdentifier: DefaultContentBlockerRulesListsSource.Constants.clickToLoadRulesListName) } catch { - assertionFailure("Missing FB List") return false } } else { do { - try userContentController.disableGlobalContentRuleList(withIdentifier: ContentBlockerRulesLists.Constants.clickToLoadRulesListName) + try userContentController.disableGlobalContentRuleList(withIdentifier: DefaultContentBlockerRulesListsSource.Constants.clickToLoadRulesListName) } catch { - assertionFailure("FB List was not enabled") return false } } - self.fbBlockingEnabled = enabled + self.fbBlockingEnabled = enable return true } @@ -90,24 +96,27 @@ extension FBProtectionTabExtension { extension FBProtectionTabExtension: ClickToLoadUserScriptDelegate { - func clickToLoadUserScriptAllowFB(_ script: UserScript, replyHandler: @escaping (Bool) -> Void) { + func clickToLoadUserScriptAllowFB() -> Bool { guard self.fbBlockingEnabled else { - replyHandler(true) - return + return true } - if setFBProtection(enabled: false) { - replyHandler(true) + if setFBProtection(enable: false) { + return true } else { - replyHandler(false) + return false } } + } extension FBProtectionTabExtension: NavigationResponder { func decidePolicy(for navigationAction: NavigationAction, preferences: inout NavigationPreferences) async -> NavigationActionPolicy? { - toggleFBProtection(for: navigationAction.url) + if navigationAction.navigationType == NavigationType.other && navigationAction.isUserInitiated == false { + return .next + } + setFBProtection(for: navigationAction.url) return .next } @@ -115,6 +124,8 @@ extension FBProtectionTabExtension: NavigationResponder { protocol FbBlockingEnabledProvider { var fbBlockingEnabled: Bool { get } + var fbEntity: String { get } + func trackerDetected() } protocol FBProtectionExtensionProtocol: AnyObject, FbBlockingEnabledProvider, NavigationResponder { diff --git a/DuckDuckGo/Tab/UserScripts/UserScripts.swift b/DuckDuckGo/Tab/UserScripts/UserScripts.swift index 4820025ba0..75dd486ab0 100644 --- a/DuckDuckGo/Tab/UserScripts/UserScripts.swift +++ b/DuckDuckGo/Tab/UserScripts/UserScripts.swift @@ -47,7 +47,7 @@ final class UserScripts: UserScriptsProvider { let sslErrorPageUserScript: SSLErrorPageUserScript? init(with sourceProvider: ScriptSourceProviding) { - clickToLoadScript = ClickToLoadUserScript(scriptSourceProvider: sourceProvider) + clickToLoadScript = ClickToLoadUserScript() contentBlockerRulesScript = ContentBlockerRulesUserScript(configuration: sourceProvider.contentBlockerRulesConfig!) surrogatesScript = SurrogatesUserScript(configuration: sourceProvider.surrogatesConfig!) @@ -78,6 +78,8 @@ final class UserScripts: UserScriptsProvider { userScripts.append(autoconsentUserScript) + contentScopeUserScriptIsolated.registerSubfeature(delegate: clickToLoadScript) + if let youtubeOverlayScript { contentScopeUserScriptIsolated.registerSubfeature(delegate: youtubeOverlayScript) } @@ -112,7 +114,6 @@ final class UserScripts: UserScriptsProvider { pageObserverScript, printingUserScript, hoverUserScript, - clickToLoadScript, contentScopeUserScript, contentScopeUserScriptIsolated, autofillScript diff --git a/LocalPackages/DataBrokerProtection/Package.swift b/LocalPackages/DataBrokerProtection/Package.swift index bba94fab9c..66ee3ef5c0 100644 --- a/LocalPackages/DataBrokerProtection/Package.swift +++ b/LocalPackages/DataBrokerProtection/Package.swift @@ -29,7 +29,7 @@ let package = Package( targets: ["DataBrokerProtection"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "155.0.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "156.0.0"), .package(path: "../SwiftUIExtensions"), .package(path: "../XPCHelper"), ], diff --git a/LocalPackages/NetworkProtectionMac/Package.swift b/LocalPackages/NetworkProtectionMac/Package.swift index f5b3780070..be86553cca 100644 --- a/LocalPackages/NetworkProtectionMac/Package.swift +++ b/LocalPackages/NetworkProtectionMac/Package.swift @@ -32,7 +32,7 @@ let package = Package( .library(name: "VPNAppLauncher", targets: ["VPNAppLauncher"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "155.0.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "156.0.0"), .package(url: "https://github.com/airbnb/lottie-spm", exact: "4.4.1"), .package(path: "../AppLauncher"), .package(path: "../UDSHelper"), diff --git a/LocalPackages/SubscriptionUI/Package.swift b/LocalPackages/SubscriptionUI/Package.swift index b0dd9bb4cd..96175ad8fd 100644 --- a/LocalPackages/SubscriptionUI/Package.swift +++ b/LocalPackages/SubscriptionUI/Package.swift @@ -12,7 +12,7 @@ let package = Package( targets: ["SubscriptionUI"]), ], dependencies: [ - .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "155.0.0"), + .package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "156.0.0"), .package(path: "../SwiftUIExtensions") ], targets: [ diff --git a/Submodules/privacy-reference-tests b/Submodules/privacy-reference-tests index a603ff9af2..6b7ad1e7f1 160000 --- a/Submodules/privacy-reference-tests +++ b/Submodules/privacy-reference-tests @@ -1 +1 @@ -Subproject commit a603ff9af22ca3ff7ce2e7ffbfe18c447d9f23e8 +Subproject commit 6b7ad1e7f15270f9dfeb58a272199f4d57c3eb22 diff --git a/UnitTests/AppDelegate/AppConfigurationURLProviderTests.swift b/UnitTests/AppDelegate/AppConfigurationURLProviderTests.swift index 4b300a8347..b23486adf8 100644 --- a/UnitTests/AppDelegate/AppConfigurationURLProviderTests.swift +++ b/UnitTests/AppDelegate/AppConfigurationURLProviderTests.swift @@ -27,7 +27,7 @@ final class AppConfigurationURLProviderTests: XCTestCase { XCTAssertEqual(AppConfigurationURLProvider().url(for: .bloomFilterExcludedDomains).absoluteString, "https://staticcdn.duckduckgo.com/https/https-mobile-v2-false-positives.json") XCTAssertEqual(AppConfigurationURLProvider().url(for: .privacyConfiguration).absoluteString, "https://staticcdn.duckduckgo.com/trackerblocking/config/v4/macos-config.json") XCTAssertEqual(AppConfigurationURLProvider().url(for: .surrogates).absoluteString, "https://staticcdn.duckduckgo.com/surrogates.txt") - XCTAssertEqual(AppConfigurationURLProvider().url(for: .trackerDataSet).absoluteString, "https://staticcdn.duckduckgo.com/trackerblocking/v5/current/macos-tds.json") + XCTAssertEqual(AppConfigurationURLProvider().url(for: .trackerDataSet).absoluteString, "https://staticcdn.duckduckgo.com/trackerblocking/v6/current/macos-tds.json") XCTAssertEqual(AppConfigurationURLProvider().url(for: .FBConfig).absoluteString, "https://staticcdn.duckduckgo.com/useragents/") } diff --git a/UnitTests/ContentBlocker/ClickToLoadModelTests.swift b/UnitTests/ContentBlocker/ClickToLoadModelTests.swift deleted file mode 100644 index 4e938e8800..0000000000 --- a/UnitTests/ContentBlocker/ClickToLoadModelTests.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// ClickToLoadModelTests.swift -// -// Copyright © 2021 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 -import TrackerRadarKit -import BrowserServicesKit -@testable import DuckDuckGo_Privacy_Browser - -class ClickToLoadModelTests: XCTestCase { - - func testClickToLoadModelInagesArePresent() { - - let nmodelImages = ClickToLoadModel.getImage - let expectedImages = [ - "dax.png", - "loading_light.svg", - "loading_dark.svg", - "blocked_facebook_logo.svg", - "blocked_group.svg", - "blocked_page.svg", - "blocked_post.svg", - "blocked_video.svg" - ] - - for (image) in expectedImages { - XCTAssertNotNil(nmodelImages[image], "Error: missing ClickToLoadModel image: " + image) - } - } - -} diff --git a/UnitTests/ContentBlocker/ClickToLoadTDSTests.swift b/UnitTests/ContentBlocker/ClickToLoadTDSTests.swift index 77f6d6f2f9..19dcd4344a 100644 --- a/UnitTests/ContentBlocker/ClickToLoadTDSTests.swift +++ b/UnitTests/ContentBlocker/ClickToLoadTDSTests.swift @@ -21,12 +21,32 @@ import TrackerRadarKit import BrowserServicesKit @testable import DuckDuckGo_Privacy_Browser +private extension KnownTracker { + + var countCTLActions: Int { rules?.filter { $0.action == .blockCTLFB }.count ?? 0 } + +} + class ClickToLoadTDSTests: XCTestCase { func testEnsureClickToLoadTDSCompiles() throws { - let tds = ContentBlockerRulesLists.fbTrackerDataSet - let builder = ContentBlockerRulesBuilder(trackerData: tds) + let provider = AppTrackerDataSetProvider() + let trackerData = provider.embeddedData + let etag = provider.embeddedDataEtag + let trackerManager = TrackerDataManager(etag: etag, + data: trackerData, + embeddedDataProvider: provider) + + let cbrLists = ContentBlockerRulesLists(trackerDataManager: trackerManager, adClickAttribution: MockAttributing()) + let ruleSets = cbrLists.contentBlockerRulesLists + let tdsName = DefaultContentBlockerRulesListsSource.Constants.clickToLoadRulesListName + + let ctlRules = ruleSets.first(where: { $0.name == tdsName}) + let ctlTrackerData = ctlRules?.trackerData + let ctlTds = ctlTrackerData?.tds + + let builder = ContentBlockerRulesBuilder(trackerData: ctlTds!) let rules = builder.buildRules(withExceptions: [], andTemporaryUnprotectedDomains: [], @@ -44,7 +64,7 @@ class ClickToLoadTDSTests: XCTestCase { XCTAssertNotNil(result) XCTAssertNil(error) compiled.fulfill() - } + } wait(for: [compiled], timeout: 30.0) @@ -56,4 +76,44 @@ class ClickToLoadTDSTests: XCTestCase { wait(for: [removed], timeout: 5.0) } + + func testClickToLoadTDSSplit() throws { + + let provider = AppTrackerDataSetProvider() + + let trackerManager = TrackerDataManager( + etag: provider.embeddedDataEtag, + data: provider.embeddedData, + embeddedDataProvider: provider + ) + + let cbrLists = ContentBlockerRulesLists(trackerDataManager: trackerManager, adClickAttribution: MockAttributing()) + let ruleSets = cbrLists.contentBlockerRulesLists + + let mainTdsName = DefaultContentBlockerRulesListsSource.Constants.trackerDataSetRulesListName + let ctlTdsName = DefaultContentBlockerRulesListsSource.Constants.clickToLoadRulesListName + + let (fbMainRules, mainCTLRuleCount) = getFBTrackerRules(for: mainTdsName, ruleSets: ruleSets) + let (fbCTLRules, ctlCTLRuleCount) = getFBTrackerRules(for: ctlTdsName, ruleSets: ruleSets) + + let fbMainRuleCount = fbMainRules!.count + let fbCTLRuleCount = fbCTLRules!.count + + // ensure both rulesets contains facebook.net rules + XCTAssert(fbMainRuleCount > 0) + XCTAssert(fbCTLRuleCount > 0) + + // ensure FB CTL rules include CTL custom actions, and main rules FB do not + XCTAssert(mainCTLRuleCount == 0) + XCTAssert(ctlCTLRuleCount > 0) + + // ensure FB CTL rules are the sum of the main rules + CTL custom action rules + XCTAssert(fbMainRuleCount + ctlCTLRuleCount == fbCTLRuleCount) + + } +} + +func getFBTrackerRules(for name: String, ruleSets: [ContentBlockerRulesList]) -> (rules: [KnownTracker.Rule]?, countCTLActions: Int) { + let tracker = ruleSets.first { $0.name == name }?.trackerData?.tds.trackers["facebook.net"] + return (tracker?.rules, tracker?.countCTLActions ?? 0) } diff --git a/scripts/update_embedded.sh b/scripts/update_embedded.sh index 3083a0e983..d084df555f 100755 --- a/scripts/update_embedded.sh +++ b/scripts/update_embedded.sh @@ -2,9 +2,9 @@ set -eo pipefail -# The following URLs shall match the ones in AppConfigurationURLprovider.swift. +# The following URLs shall match the ones in AppConfigurationURLprovider.swift. # Danger checks that the URLs match on every PR. If the code changes, the regex that Danger uses may need an update. -TDS_URL="https://staticcdn.duckduckgo.com/trackerblocking/v5/current/macos-tds.json" +TDS_URL="https://staticcdn.duckduckgo.com/trackerblocking/v6/current/macos-tds.json" CONFIG_URL="https://staticcdn.duckduckgo.com/trackerblocking/config/v4/macos-config.json" # If -c is passed, then check the URLs in the Configuration files are correct.