Skip to content

Commit

Permalink
Add feature flag to display blocked requests per-tab in shields panel…
Browse files Browse the repository at this point in the history
… on iOS.
  • Loading branch information
StephenHeaps committed Dec 11, 2024
1 parent 742d6f3 commit 16efa5a
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 6 deletions.
5 changes: 5 additions & 0 deletions components/brave_shields/core/common/features.cc
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ BASE_FEATURE(kBraveShredCacheData,
#else
base::FEATURE_DISABLED_BY_DEFAULT);
#endif
// When enabled, will display debug menu for adblock features in the Shields
// panel.
BASE_FEATURE(kBraveIOSDebugAdblock,
"BraveIOSDebugAdblock",
base::FEATURE_DISABLED_BY_DEFAULT);
// When enabled, show Strict (aggressive) fingerprinting mode in Brave Shields.
BASE_FEATURE(kBraveShowStrictFingerprintingMode,
"BraveShowStrictFingerprintingMode",
Expand Down
1 change: 1 addition & 0 deletions components/brave_shields/core/common/features.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ BASE_DECLARE_FEATURE(kBraveLocalhostAccessPermission);
BASE_DECLARE_FEATURE(kBraveReduceLanguage);
BASE_DECLARE_FEATURE(kBraveShredFeature);
BASE_DECLARE_FEATURE(kBraveShredCacheData);
BASE_DECLARE_FEATURE(kBraveIOSDebugAdblock);
BASE_DECLARE_FEATURE(kBraveShowStrictFingerprintingMode);
BASE_DECLARE_FEATURE(kCosmeticFilteringExtraPerfMetrics);
BASE_DECLARE_FEATURE(kCosmeticFilteringJsPerformance);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright 2024 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/.

import BraveShields
import Strings
import SwiftUI

struct AdblockBlockedRequestsView: View {

let url: String
@ObservedObject var contentBlockerHelper: ContentBlockerHelper

@State private var filterText: String = ""

private var blockedRequests: [BlockedRequestInfo] {
let blockedRequests = Array(contentBlockerHelper.blockedRequests)
guard !filterText.isEmpty else {
return blockedRequests
}
return blockedRequests.filter {
$0.requestURL.absoluteString.localizedCaseInsensitiveContains(filterText)
|| $0.sourceURL.absoluteString.localizedCaseInsensitiveContains(filterText)
|| $0.resourceType.rawValue.localizedCaseInsensitiveContains(filterText)
|| $0.location.display.localizedCaseInsensitiveContains(filterText)
}
}

public var body: some View {
List {
Section(header: Text(url)) {
ForEach(blockedRequests) { request in
VStack {
row(
title: String.localizedStringWithFormat("%@:", Strings.Shields.requestURLLabel),
detail: request.requestURL.absoluteString
)
row(
title: String.localizedStringWithFormat("%@:", Strings.Shields.sourceURLLabel),
detail: request.sourceURL.absoluteString
)
row(
title: String.localizedStringWithFormat("%@:", Strings.Shields.resourceTypeLabel),
detail: request.resourceType.rawValue
)
row(
title: String.localizedStringWithFormat("%@:", Strings.Shields.aggressiveLabel),
detail: "\(request.isAggressive)"
)
row(
title: String.localizedStringWithFormat("%@:", Strings.Shields.blockedByLabel),
detail: request.location.display
)
}
}
}
}
.navigationTitle(Strings.Shields.blockedRequestsTitle)
.toolbar(.visible)
.searchable(text: $filterText)
}

@ViewBuilder private func row(title: String, detail: String) -> some View {
Group {
Text(title)
Text(detail)
.font(.system(.caption, design: .monospaced))
.textSelection(.enabled)
}
.font(.body)
.frame(maxWidth: .infinity, alignment: .leading)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,15 @@ struct ShieldsPanelView: View {
}

private let url: URL
private weak var tab: Tab?
private let displayHost: String
@AppStorage("advancedShieldsExpanded") private var advancedShieldsExpanded = false
@ObservedObject private var viewModel: ShieldsSettingsViewModel
private var actionCallback: (Action) -> Void

@MainActor init(url: URL, tab: Tab, domain: Domain, callback: @escaping (Action) -> Void) {
self.url = url
self.tab = tab
self.viewModel = ShieldsSettingsViewModel(tab: tab, domain: domain)
self.actionCallback = callback
self.displayHost =
Expand Down Expand Up @@ -251,6 +253,25 @@ struct ShieldsPanelView: View {
.padding(.vertical, 4)
}
}
if FeatureList.kBraveIOSDebugAdblock.enabled, let contentBlocker = tab?.contentBlocker {
ShieldSettingRow {
NavigationLink {
AdblockBlockedRequestsView(
url: url.baseDomain ?? url.absoluteDisplayString,
contentBlockerHelper: contentBlocker
)
} label: {
ShieldSettingsNavigationWrapper {
Text(Strings.Shields.blockedRequestsTitle)
.frame(maxWidth: .infinity, alignment: .leading)
.multilineTextAlignment(.leading)
}
}
.foregroundStyle(Color(.bravePrimary))
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.vertical, 4)
}
}
}

@ViewBuilder private var globalSettingsSectionView: some View {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,18 @@ extension ContentBlockerHelper: TabContentScript {
}

// First check to make sure we're not counting the same repetitive requests multiple times
guard !self.blockedRequests.contains(requestURL) else { return }
self.blockedRequests.insert(requestURL)
guard !self.blockedRequests.contains(where: { $0.requestURL == requestURL }) else {
return
}
self.blockedRequests.append(
.init(
requestURL: requestURL,
sourceURL: sourceURL,
resourceType: dto.resourceType,
isAggressive: domain.globalBlockAdsAndTrackingLevel.isAggressive,
location: .contentBlocker
)
)

// Increase global stats (here due to BlocklistName being in Client and BraveGlobalShieldStats being
// in BraveShared)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,21 @@ class RequestBlockingContentScriptHandler: TabContentScript {
)
}

if shouldBlock && !tab.contentBlocker.blockedRequests.contains(requestURL) {
if shouldBlock
&& !tab.contentBlocker.blockedRequests.contains(where: { $0.requestURL == requestURL })
{
BraveGlobalShieldStats.shared.adblock += 1
let stats = tab.contentBlocker.stats
tab.contentBlocker.stats = stats.adding(adCount: 1)
tab.contentBlocker.blockedRequests.insert(requestURL)
tab.contentBlocker.blockedRequests.append(
.init(
requestURL: requestURL,
sourceURL: windowOriginURL,
resourceType: dto.data.resourceType,
isAggressive: domain.globalBlockAdsAndTrackingLevel.isAggressive,
location: .requestBlocking
)
)
}

replyHandler(shouldBlock, nil)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// 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/.

import BraveCore
import BraveShared
import Collections
import Combine
import Data
import Shared
Expand Down Expand Up @@ -37,7 +39,32 @@ enum BlockingStrength: String {
static let allOptions: [BlockingStrength] = [.basic, .strict]
}

class ContentBlockerHelper {
struct BlockedRequestInfo: Hashable, Identifiable {
enum Location: String {
case contentBlocker
case requestBlocking

var display: String {
switch self {
case .contentBlocker:
return Strings.Shields.contentBlocker
case .requestBlocking:
return Strings.Shields.requestBlocking
}
}
}
let requestURL: URL
let sourceURL: URL
let resourceType: AdblockEngine.ResourceType
let isAggressive: Bool
let location: Location

var id: String {
"\(requestURL)\(sourceURL)\(resourceType.rawValue)\(isAggressive)\(location.rawValue)"
}
}

class ContentBlockerHelper: ObservableObject {
private(set) weak var tab: Tab?

/// The rule lists that are loaded into the current tab
Expand All @@ -54,7 +81,7 @@ class ContentBlockerHelper {
}

var statsDidChange: ((TPPageStats) -> Void)?
var blockedRequests: Set<URL> = []
@Published var blockedRequests: OrderedSet<BlockedRequestInfo> = []

init(tab: Tab) {
self.tab = tab
Expand Down
69 changes: 69 additions & 0 deletions ios/brave-ios/Sources/BraveShields/ShieldStrings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,75 @@ extension Strings.Shields {
)
}

// MARK: - Adblock Debugging

extension Strings.Shields {
public static let blockedRequestsTitle = NSLocalizedString(
"blockedRequestsTitle",
tableName: "BraveShared",
bundle: .module,
value: "Blocked Requests",
comment:
"The title displayed in the navigation bar of Blocked Requests view."
)
public static let requestURLLabel = NSLocalizedString(
"requestURLLabel",
tableName: "BraveShared",
bundle: .module,
value: "Request URL",
comment:
"A label displayed above the request url that was blocked in Blocked Requests view."
)
public static let sourceURLLabel = NSLocalizedString(
"sourceURLLabel",
tableName: "BraveShared",
bundle: .module,
value: "Source URL",
comment:
"A label displayed above the source url of a request that was blocked in Blocked Requests view."
)
public static let resourceTypeLabel = NSLocalizedString(
"resourceTypeLabel",
tableName: "BraveShared",
bundle: .module,
value: "Resource Type",
comment:
"A label displayed above the resource type of a request that was blocked in Blocked Requests view."
)
public static let aggressiveLabel = NSLocalizedString(
"aggressiveLabel",
tableName: "BraveShared",
bundle: .module,
value: "Aggressive",
comment:
"A label displayed above a value indicating if the site is in aggressive mode in Blocked Requests view."
)
public static let blockedByLabel = NSLocalizedString(
"blockedByLabel",
tableName: "BraveShared",
bundle: .module,
value: "Blocked By",
comment:
"A label displayed above the location a request was blocked in Blocked Requests view."
)
public static let contentBlocker = NSLocalizedString(
"contentBlocker",
tableName: "BraveShared",
bundle: .module,
value: "Content Blocker",
comment:
"Used to describe when a request was blocked by the Content Blocker in Blocked Requests view."
)
public static let requestBlocking = NSLocalizedString(
"requestBlocking",
tableName: "BraveShared",
bundle: .module,
value: "Request Blocking",
comment:
"Used to describe when a request was blocked by our request blocking scripts in Blocked Requests view."
)
}

// MARK: - Anti Ad-Block Warning

extension Strings.Shields {
Expand Down
1 change: 1 addition & 0 deletions ios/browser/api/features/features.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ OBJC_EXPORT
@property(class, nonatomic, readonly) Feature* kBraveSearchDefaultAPIFeature;
@property(class, nonatomic, readonly) Feature* kBraveShredFeature;
@property(class, nonatomic, readonly) Feature* kBraveShredCacheData;
@property(class, nonatomic, readonly) Feature* kBraveIOSDebugAdblock;
@property(class, nonatomic, readonly)
Feature* kBraveShowStrictFingerprintingMode;
@property(class, nonatomic, readonly) Feature* kBraveSync;
Expand Down
5 changes: 5 additions & 0 deletions ios/browser/api/features/features.mm
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ + (Feature*)kBraveShredCacheData {
initWithFeature:&brave_shields::features::kBraveShredCacheData];
}

+ (Feature*)kBraveIOSDebugAdblock {
return [[Feature alloc]
initWithFeature:&brave_shields::features::kBraveIOSDebugAdblock];
}

+ (Feature*)kBraveShowStrictFingerprintingMode {
return
[[Feature alloc] initWithFeature:&brave_shields::features::
Expand Down
7 changes: 7 additions & 0 deletions ios/browser/flags/about_flags.mm
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@
flags_ui::kOsIos, \
FEATURE_VALUE_TYPE( \
security_interstitials::features::kHttpsOnlyMode), \
}, \
{ \
"ios-debug-adblock", \
"Enable Debug Adblock views", \
"Enable debug view for adblock features in Shields panel", \
flags_ui::kOsIos, \
FEATURE_VALUE_TYPE(brave_shields::features::kBraveIOSDebugAdblock), \
})

#define BRAVE_AI_CHAT_FEATURE_ENTRIES \
Expand Down

0 comments on commit 16efa5a

Please sign in to comment.