From 874467a268d0390a698f0c3f6fded5afe1c6f041 Mon Sep 17 00:00:00 2001 From: Jacob Sikorski Date: Mon, 8 Jan 2024 13:54:49 -0700 Subject: [PATCH] Fix #8556: Add GPC toggle (#8593) --- Package.swift | 1 + Sources/Brave/Frontend/Browser/PageData.swift | 5 ++++- .../Browser/User Scripts/ScriptFactory.swift | 5 +++++ .../Browser/User Scripts/ScriptSourceType.swift | 3 +++ .../Browser/User Scripts/UserScriptType.swift | 7 ++++++- .../DefaultShieldsSectionView.swift | 6 ++++++ .../AllFrames/AtDocumentStart/gpc.js | 13 ------------- .../Scripts_Dynamic/Scripts/Paged/gpc.js | 13 +++++++++++++ Sources/BraveShields/ShieldPreferences.swift | 6 ++++++ Sources/BraveShields/ShieldStrings.swift | 17 +++++++++++++++++ Tests/ClientTests/PageDataTests.swift | 6 +++--- 11 files changed, 64 insertions(+), 18 deletions(-) delete mode 100644 Sources/Brave/Frontend/UserContent/UserScripts/AllFrames/AtDocumentStart/gpc.js create mode 100644 Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/Scripts/Paged/gpc.js diff --git a/Package.swift b/Package.swift index 201752d59c0..4915f74ff86 100644 --- a/Package.swift +++ b/Package.swift @@ -409,6 +409,7 @@ var braveTarget: PackageDescription.Target = .target( .copy("Frontend/UserContent/UserScripts/Scripts_Dynamic/Scripts/DomainSpecific/Paged/FrameCheckWrapper.js"), .copy("Frontend/UserContent/UserScripts/Scripts_Dynamic/Scripts/Paged/CookieControlScript.js"), .copy("Frontend/UserContent/UserScripts/Scripts_Dynamic/Scripts/Paged/FarblingProtectionScript.js"), + .copy("Frontend/UserContent/UserScripts/Scripts_Dynamic/Scripts/Paged/gpc.js"), .copy("Frontend/UserContent/UserScripts/Scripts_Dynamic/Scripts/Paged/MediaBackgroundingScript.js"), .copy("Frontend/UserContent/UserScripts/Scripts_Dynamic/Scripts/Paged/PlaylistScript.js"), .copy("Frontend/UserContent/UserScripts/Scripts_Dynamic/Scripts/Paged/PlaylistSwizzlerScript.js"), diff --git a/Sources/Brave/Frontend/Browser/PageData.swift b/Sources/Brave/Frontend/Browser/PageData.swift index 7a2aeefc3ca..56ce7ae6e14 100644 --- a/Sources/Brave/Frontend/Browser/PageData.swift +++ b/Sources/Brave/Frontend/Browser/PageData.swift @@ -9,6 +9,7 @@ import Data import BraveShared import BraveCore import Shared +import BraveShields /// The data for the current web-page which is needed for loading and executing privacy scripts /// @@ -71,7 +72,9 @@ struct PageData { /// Return all the user script types for this page. The number of script types grows as more frames are loaded. @MainActor func makeUserScriptTypes(domain: Domain) async -> Set { - var userScriptTypes: Set = [.siteStateListener] + var userScriptTypes: Set = [ + .siteStateListener, .gpc(ShieldPreferences.enableGPC.value) + ] // Handle dynamic domain level scripts on the main document. // These are scripts that change depending on the domain and the main document diff --git a/Sources/Brave/Frontend/Browser/User Scripts/ScriptFactory.swift b/Sources/Brave/Frontend/Browser/User Scripts/ScriptFactory.swift index 4a0858314ef..933f8da5852 100644 --- a/Sources/Brave/Frontend/Browser/User Scripts/ScriptFactory.swift +++ b/Sources/Brave/Frontend/Browser/User Scripts/ScriptFactory.swift @@ -124,6 +124,11 @@ class ScriptFactory { let source = try makeScriptSource(of: .nacl) resultingScript = WKUserScript(source: source, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .page) + case .gpc(let isEnabled): + let source = try makeScriptSource(of: .gpc) + .replacingOccurrences(of: "$", with: isEnabled ? "true" : "false") + resultingScript = WKUserScript(source: source, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: .page) + case .domainUserScript(let domainUserScript): resultingScript = try self.makeScript(for: domainUserScript) diff --git a/Sources/Brave/Frontend/Browser/User Scripts/ScriptSourceType.swift b/Sources/Brave/Frontend/Browser/User Scripts/ScriptSourceType.swift index 23b45e4c693..86778a81c7f 100644 --- a/Sources/Brave/Frontend/Browser/User Scripts/ScriptSourceType.swift +++ b/Sources/Brave/Frontend/Browser/User Scripts/ScriptSourceType.swift @@ -22,6 +22,8 @@ enum ScriptSourceType { /// This script is a modification of the android and desktop script found here: /// https://github.com/brave/brave-core/blob/master/components/cosmetic_filters/resources/data/content_cosmetic.ts case selectorsPoller + /// Global Privacy Control script + case gpc var fileName: String { switch self { @@ -29,6 +31,7 @@ enum ScriptSourceType { case .farblingProtection: return "FarblingProtectionScript" case .frameCheckWrapper: return "FrameCheckWrapper" case .selectorsPoller: return "SelectorsPollerScript" + case .gpc: return "gpc" } } diff --git a/Sources/Brave/Frontend/Browser/User Scripts/UserScriptType.swift b/Sources/Brave/Frontend/Browser/User Scripts/UserScriptType.swift index 70dfdfccebe..a6f997083ed 100644 --- a/Sources/Brave/Frontend/Browser/User Scripts/UserScriptType.swift +++ b/Sources/Brave/Frontend/Browser/User Scripts/UserScriptType.swift @@ -63,6 +63,8 @@ enum UserScriptType: Hashable { /// Selectors poller script (aka cosmetic filtering script) is responsible for hiding and unhiding css elements as dictated by the ad-block engines. /// This script is actually executed rather than injected and this type is solely used for the creation rather than the injection of the script. case selectorsPoller(SelectorsPollerSetup) + /// Global Privacy Control (GPC) script + case gpc(Bool) /// The order in which we want to inject the scripts var order: Int { @@ -72,7 +74,8 @@ enum UserScriptType: Hashable { case .domainUserScript: return 2 case .siteStateListener: return 3 case .selectorsPoller: return 4 - case .engineScript(let configuration): return 5 + configuration.order + case .gpc: return 5 + case .engineScript(let configuration): return 6 + configuration.order } } } @@ -88,6 +91,8 @@ extension UserScriptType: CustomDebugStringConvertible { return "farblingProtection(\(etld))" case .nacl: return "nacl" + case .gpc(let isEnabled): + return "gpc(\(isEnabled)" case .siteStateListener: return "siteStateListener" case .selectorsPoller: diff --git a/Sources/Brave/Frontend/Settings/Features/ShieldsPrivacy/DefaultShieldsSectionView.swift b/Sources/Brave/Frontend/Settings/Features/ShieldsPrivacy/DefaultShieldsSectionView.swift index ddf250a2475..9db3ff5bccd 100644 --- a/Sources/Brave/Frontend/Settings/Features/ShieldsPrivacy/DefaultShieldsSectionView.swift +++ b/Sources/Brave/Frontend/Settings/Features/ShieldsPrivacy/DefaultShieldsSectionView.swift @@ -97,6 +97,12 @@ struct DefaultShieldsViewView: View { option: Preferences.Shields.fingerprintingProtection ) + OptionToggleView( + title: Strings.Shields.enableGPCLabel, + subtitle: Strings.Shields.enableGPCDescription, + option: ShieldPreferences.enableGPC + ) + ShieldToggleView( title: Strings.blockCookieConsentNotices, subtitle: nil, diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/AllFrames/AtDocumentStart/gpc.js b/Sources/Brave/Frontend/UserContent/UserScripts/AllFrames/AtDocumentStart/gpc.js deleted file mode 100644 index 043f1cea1dd..00000000000 --- a/Sources/Brave/Frontend/UserContent/UserScripts/AllFrames/AtDocumentStart/gpc.js +++ /dev/null @@ -1,13 +0,0 @@ -/* vim: set ts=2 sts=2 sw=2 et tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -'use strict' - -Object.defineProperty(navigator, 'globalPrivacyControl', { - enumerable: false, - configurable: false, - writable: false, - value: true -}) diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/Scripts/Paged/gpc.js b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/Scripts/Paged/gpc.js new file mode 100644 index 00000000000..4c6e8475ad5 --- /dev/null +++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/Scripts/Paged/gpc.js @@ -0,0 +1,13 @@ +// Copyright 2023 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +'use strict' + +Object.defineProperty(navigator, 'globalPrivacyControl', { + enumerable: false, + configurable: false, + writable: false, + value: $ +}) diff --git a/Sources/BraveShields/ShieldPreferences.swift b/Sources/BraveShields/ShieldPreferences.swift index 0ad897aa6f1..112ffc1405f 100644 --- a/Sources/BraveShields/ShieldPreferences.swift +++ b/Sources/BraveShields/ShieldPreferences.swift @@ -21,4 +21,10 @@ public class ShieldPreferences { get { ShieldLevel(rawValue: blockAdsAndTrackingLevelRaw.value) ?? defaultBlockAdsAndTrackingLevel } set { blockAdsAndTrackingLevelRaw.value = newValue.rawValue } } + + /// A boolean value inidicating if GPC is enabled + public static var enableGPC = Preferences.Option( + key: "shields.enable-gpc", + default: true + ) } diff --git a/Sources/BraveShields/ShieldStrings.swift b/Sources/BraveShields/ShieldStrings.swift index 3cebb060082..20e6bd2b2a7 100644 --- a/Sources/BraveShields/ShieldStrings.swift +++ b/Sources/BraveShields/ShieldStrings.swift @@ -146,3 +146,20 @@ public extension Strings.Shields { comment: "A button that ignores the brave player" ) } + +// MARK: - Shields +public extension Strings.Shields { + /// A label of the GPC toggle + static let enableGPCLabel = NSLocalizedString( + "EnableGPCLabel", tableName: "BraveShared", bundle: .module, + value: "Enable Global Privacy Control", + comment: "A label of the GPC toggle" + ) + + /// A description of what the Enable GPC toggle does + static let enableGPCDescription = NSLocalizedString( + "EnableGPCDescription", tableName: "BraveShared", bundle: .module, + value: "Enable the Global Privacy Control JS API", + comment: "A description of what the Enable GPC toggle does" + ) +} diff --git a/Tests/ClientTests/PageDataTests.swift b/Tests/ClientTests/PageDataTests.swift index cb14943cb7d..7603e168770 100644 --- a/Tests/ClientTests/PageDataTests.swift +++ b/Tests/ClientTests/PageDataTests.swift @@ -5,6 +5,7 @@ import XCTest import WebKit +import BraveShields @testable import Brave final class PageDataTests: XCTestCase { @@ -14,7 +15,6 @@ final class PageDataTests: XCTestCase { let mainFrameURL = URL(string: "http://example.com")! let subFrameURL = URL(string: "http://example.com/1p/subframe")! let upgradedMainFrameURL = URL(string: "https://example.com")! - let upgradedSubFrameURL = URL(string: "https://example.com/1p/subframe")! var pageData = PageData(mainFrameURL: mainFrameURL, adBlockStats: AdBlockStats()) let expectation = expectation(description: "") @@ -28,7 +28,7 @@ final class PageDataTests: XCTestCase { // We get only entries of the main frame // NOTE: If we were to add some engines we might see additional types let expectedMainFrameTypes: Set = [ - .siteStateListener, .nacl, .farblingProtection(etld: "example.com") + .siteStateListener, .nacl, .farblingProtection(etld: "example.com"), .gpc(ShieldPreferences.enableGPC.value) ] XCTAssertEqual(mainFrameRequestTypes, expectedMainFrameTypes) @@ -50,7 +50,7 @@ final class PageDataTests: XCTestCase { // If we were to add some engines we might see additional types let addedSubFrameFrameRequestTypes = await pageData.makeUserScriptTypes(domain: domain) let expectedMainAndSubFrameTypes: Set = [ - .siteStateListener, .nacl, .farblingProtection(etld: "example.com") + .siteStateListener, .nacl, .farblingProtection(etld: "example.com"), .gpc(ShieldPreferences.enableGPC.value) ] XCTAssertEqual(expectedMainAndSubFrameTypes, addedSubFrameFrameRequestTypes)