Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Add domain blocking "Proceed" and "Go Back" actions #8628

Merged
merged 1 commit into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions Sources/Brave/Assets/Interstitial Pages/Pages/BlockedDomain.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,29 @@ <h1>%blocked_title%</h1>
<h2>%blocked_subtitle%</h2>
<pre class="domain">%blocked_domain%</pre>
<p class="description">%blocked_description%</p>
<div class="actions">
<button id="proceed-action" class="main-action">%proceed_action%</button>
<button id="go-back-action" class="secondary-action">%go_back_action%</button>
</div>
</div>

<script>
(() => {
const sendAction = (action) => {
webkit.messageHandlers["%message_handler%"].postMessage({
"securityToken": "%security_token%",
"action": action
})
}

document.getElementById('proceed-action').onclick = () => {
sendAction('didProceed')
}

document.getElementById('go-back-action').onclick = () => {
sendAction('didGoBack')
}
})()
</script>
</body>
</html>
62 changes: 54 additions & 8 deletions Sources/Brave/Assets/Interstitial Pages/Styles/BlockedDomain.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ html {
}

.post {
padding-top: max(25px, env(safe-area-inset-top));
padding-bottom: max(25px, env(safe-area-inset-bottom));
padding-left: max(25px, env(safe-area-inset-left));
padding-right: max(25px, env(safe-area-inset-right));
padding-top: max(25px, env(safe-area-inset-top));
padding-bottom: max(25px, env(safe-area-inset-bottom));
padding-left: max(25px, env(safe-area-inset-left));
padding-right: max(25px, env(safe-area-inset-right));
}

.background {
background-color: #FFFFFF;
background-color: #FFFFFF;
}

.icon {
width: 40px;
height: 40px;
margin-bottom: 1em;
width: 40px;
height: 40px;
margin-bottom: 1em;
}

h1 {
Expand Down Expand Up @@ -76,6 +76,40 @@ h2 {
align-content: flex-start;
}

.actions {
margin-top: 48px;
display: flex;
flex-wrap: wrap;
flex-direction: column;
align-content: flex-start;
justify-content: space-between;
}

button {
font-family: SFProDisplay-Medium, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
font-size: 15px;
font-weight: 600;
line-height: 20px;
letter-spacing: -0.2px;
text-align: center;
padding: 12px 16px 12px 16px;
border-radius: 12px;
margin: 4px 0;
width: 100%;
}

.main-action {
color: white;
background: #3F39E8;
border: 1px solid #3F39E8;
}

.secondary-action {
color: #3F39E8;
background: #545FF800;
border: 1px solid #545FF866;
}

/** Center the content for iPads **/
@media (min-width: 600px) and (min-height: 600px) {
.icon {
Expand Down Expand Up @@ -117,6 +151,14 @@ h2 {
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}

.actions {
flex-direction: row-reverse;
}

button {
width: auto;
}
}


Expand All @@ -132,4 +174,8 @@ h2 {
.description, .domain {
color: #DBDEE2;
}

.secondary-action {
color: #7C91FF;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -355,22 +355,24 @@ extension BrowserViewController: WKNavigationDelegate {
if navigationAction.targetFrame?.isMainFrame == true {
tab?.updateUserAgent(webView, newURL: requestURL)

let domain = Domain.getOrCreate(forUrl: requestURL, persistent: !isPrivateBrowsing)

let shouldBlock = await AdBlockStats.shared.shouldBlock(
requestURL: requestURL, sourceURL: requestURL, resourceType: .document,
isAggressiveMode: domain.blockAdsAndTrackingLevel.isAggressive
)

if shouldBlock, let escapingURL = requestURL.absoluteString.escape() {
var components = URLComponents(string: InternalURL.baseUrl)
components?.path = "/\(InternalURL.Path.blocked.rawValue)"
components?.queryItems = [URLQueryItem(name: "url", value: escapingURL)]
if let etldP1 = requestURL.baseDomain, tab?.proceedAnywaysDomainList.contains(etldP1) == false {
let domain = Domain.getOrCreate(forUrl: requestURL, persistent: !isPrivateBrowsing)

if let url = components?.url {
let request = PrivilegedRequest(url: url) as URLRequest
tab?.loadRequest(request)
return (.cancel, preferences)
let shouldBlock = await AdBlockStats.shared.shouldBlock(
requestURL: requestURL, sourceURL: requestURL, resourceType: .document,
isAggressiveMode: domain.blockAdsAndTrackingLevel.isAggressive
)

if shouldBlock, let escapingURL = requestURL.absoluteString.escape() {
var components = URLComponents(string: InternalURL.baseUrl)
components?.path = "/\(InternalURL.Path.blocked.rawValue)"
components?.queryItems = [URLQueryItem(name: "url", value: escapingURL)]

if let url = components?.url {
let request = PrivilegedRequest(url: url) as URLRequest
tab?.loadRequest(request)
return (.cancel, preferences)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2503,6 +2503,7 @@ extension BrowserViewController: TabDelegate {
ReaderModeScriptHandler(tab: tab),
ErrorPageHelper(certStore: profile.certStore),
SessionRestoreScriptHandler(tab: tab),
BlockedDomainScriptHandler(tab: tab),
PrintScriptHandler(browserController: self, tab: tab),
CustomSearchScriptHandler(tab: tab),
NightModeScriptHandler(tab: tab),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ public class BlockedDomainHandler: InternalSchemeResponse {
.replacingOccurrences(of: "%blocked_subtitle%", with: Strings.Shields.domainBlockedPageMessage)
.replacingOccurrences(of: "%blocked_domain%", with: originalURL.domainURL.absoluteDisplayString)
.replacingOccurrences(of: "%blocked_description%", with: Strings.Shields.domainBlockedPageDescription)
.replacingOccurrences(of: "%proceed_action%", with: Strings.Shields.domainBlockedProceedAction)
.replacingOccurrences(of: "%go_back_action%", with: Strings.Shields.domainBlockedGoBackAction)
.replacingOccurrences(of: "%message_handler%", with: BlockedDomainScriptHandler.messageHandlerName)
.replacingOccurrences(of: "%security_token%", with: UserScriptManager.securityToken)

if #available(iOS 16.0, *) {
html = html?.replacingOccurrences(of: "<html lang=\"en\">", with: "<html lang=\"\(Locale.current.language.minimalIdentifier)\">")
Expand Down
3 changes: 3 additions & 0 deletions Sources/Brave/Frontend/Browser/Tab.swift
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,9 @@ class Tab: NSObject {
}
return favicon
}

/// A list of domains that we want to proceed to anyways regardless of any ad-blocking
var proceedAnywaysDomainList: Set<String> = []

var canGoBack: Bool {
return webView?.canGoBack ?? false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// 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/.

import Foundation
import Shared
import WebKit

class BlockedDomainScriptHandler: TabContentScript {
private weak var tab: Tab?

required init(tab: Tab) {
self.tab = tab
}

static let scriptName = "BlockedDomainScript"
static let scriptId = UUID().uuidString
static let messageHandlerName = "\(scriptName)_\(messageUUID)"
static let scriptSandbox: WKContentWorld = .page
static let userScript: WKUserScript? = nil

func userContentController(_ userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage, replyHandler: (Any?, String?) -> Void) {
defer { replyHandler(nil, nil) }

if !verifyMessage(message: message, securityToken: UserScriptManager.securityToken) {
assertionFailure("Missing required security token.")
return
}

guard let params = message.body as? [String: AnyObject], let action = params["action"] as? String else {
assertionFailure("Missing required params.")
return
}

switch action {
case "didProceed":
blockedDomainDidProceed()
case "didGoBack":
blockedDomainDidGoBack()
default:
assertionFailure("Unhandled action `\(action)`")
}
}

private func blockedDomainDidProceed() {
guard let url = tab?.url?.stippedInternalURL, let etldP1 = url.baseDomain else {
assertionFailure("There should be no way this method can be triggered if the tab is not on an internal url")
return
}

let request = URLRequest(url: url)
tab?.proceedAnywaysDomainList.insert(etldP1)
tab?.loadRequest(request)
}

private func blockedDomainDidGoBack() {
guard let url = tab?.url?.stippedInternalURL else {
assertionFailure("There should be no way this method can be triggered if the tab is not on an internal url")
return
}

guard let listItem = tab?.backList?.reversed().first(where: { $0.url != url }) else {
// How is this even possible?
// All testing indicates no, so we will not handle.
// If we find it is, then we need to disable or hide the "Go Back" button in these cases.
// But this would require heavy changes or ugly mechanisms to InternalSchemeHandler.
return
}

tab?.goToBackForwardListItem(listItem)
}
}
14 changes: 14 additions & 0 deletions Sources/BraveShields/ShieldStrings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,18 @@ public extension Strings.Shields {
value: "Because you requested to aggressively block trackers and ads, Brave is blocking this site before the first network connection.",
comment: "A description in the warning page that appears when a page was blocked"
)

/// Text for a button in a blocked page info screen that allows you to proceed regardless of the privacy warning
static let domainBlockedProceedAction = NSLocalizedString(
"DomainBlockedProceedAction", tableName: "BraveShared", bundle: .module,
value: "Proceed",
comment: "Text for a button in a blocked page info screen that allows you to proceed regardless of the privacy warning"
)

/// A description in the warning page that appears when a page was blocked
static let domainBlockedGoBackAction = NSLocalizedString(
"DomainBlockedGoBackAction", tableName: "BraveShared", bundle: .module,
value: "Go Back",
comment: "Text for a button in a blocked page info screen that takes you back where you came from"
)
}