diff --git a/App/iOS/Delegates/AppState.swift b/App/iOS/Delegates/AppState.swift
index eade1547a26..27b5acdbfb1 100644
--- a/App/iOS/Delegates/AppState.swift
+++ b/App/iOS/Delegates/AppState.swift
@@ -191,7 +191,8 @@ public class AppState {
(ErrorPageHandler.path, ErrorPageHandler()),
(ReaderModeHandler.path, ReaderModeHandler(profile: profile)),
(IPFSSchemeHandler.path, IPFSSchemeHandler()),
- (Web3DomainHandler.path, Web3DomainHandler())
+ (Web3DomainHandler.path, Web3DomainHandler()),
+ (InternalURL.Path.blocked.rawValue, BlockedDomainHandler())
]
responders.forEach { (path, responder) in
diff --git a/Package.swift b/Package.swift
index 201752d59c0..e264ffe2bb6 100644
--- a/Package.swift
+++ b/Package.swift
@@ -377,6 +377,7 @@ var braveTarget: PackageDescription.Target = .target(
.copy("Assets/Fonts/NewYorkMedium-BoldItalic.otf"),
.copy("Assets/Fonts/NewYorkMedium-Regular.otf"),
.copy("Assets/Fonts/NewYorkMedium-RegularItalic.otf"),
+ .copy("Assets/Interstitial Pages/Pages/BlockedDomain.html"),
.copy("Assets/Interstitial Pages/Pages/CertificateError.html"),
.copy("Assets/Interstitial Pages/Pages/GenericError.html"),
.copy("Assets/Interstitial Pages/Pages/NetworkError.html"),
@@ -392,6 +393,8 @@ var braveTarget: PackageDescription.Target = .target(
.copy("Assets/Interstitial Pages/Images/Warning.svg"),
.copy("Assets/Interstitial Pages/Images/BraveIPFS.svg"),
.copy("Assets/Interstitial Pages/Images/IPFSBackground.svg"),
+ .copy("Assets/Interstitial Pages/Images/warning-triangle-outline.svg"),
+ .copy("Assets/Interstitial Pages/Styles/BlockedDomain.css"),
.copy("Assets/Interstitial Pages/Styles/CertificateError.css"),
.copy("Assets/Interstitial Pages/Styles/InterstitialStyles.css"),
.copy("Assets/Interstitial Pages/Styles/NetworkError.css"),
diff --git a/Sources/Brave/Assets/Interstitial Pages/Images/warning-triangle-outline.svg b/Sources/Brave/Assets/Interstitial Pages/Images/warning-triangle-outline.svg
new file mode 100644
index 00000000000..8ef3c0d08de
--- /dev/null
+++ b/Sources/Brave/Assets/Interstitial Pages/Images/warning-triangle-outline.svg
@@ -0,0 +1,10 @@
+
diff --git a/Sources/Brave/Assets/Interstitial Pages/Pages/BlockedDomain.html b/Sources/Brave/Assets/Interstitial Pages/Pages/BlockedDomain.html
new file mode 100644
index 00000000000..b75fc84dc3e
--- /dev/null
+++ b/Sources/Brave/Assets/Interstitial Pages/Pages/BlockedDomain.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+ %page_title%
+
+
+
+
+
+
+
%blocked_title%
+
%blocked_subtitle%
+
%blocked_domain%
+
%blocked_description%
+
+
+
diff --git a/Sources/Brave/Assets/Interstitial Pages/Styles/BlockedDomain.css b/Sources/Brave/Assets/Interstitial Pages/Styles/BlockedDomain.css
new file mode 100644
index 00000000000..21fc3cdc934
--- /dev/null
+++ b/Sources/Brave/Assets/Interstitial Pages/Styles/BlockedDomain.css
@@ -0,0 +1,135 @@
+/*
+ Copyright (c) 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/.
+ */
+
+html {
+ overscroll-behavior: none;
+}
+
+.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));
+}
+
+.background {
+ background-color: #FFFFFF;
+}
+
+.icon {
+ width: 40px;
+ height: 40px;
+ margin-bottom: 1em;
+}
+
+h1 {
+ font-family: SFProDisplay-Medium, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ font-size: 22px;
+ font-weight: 500;
+ line-height: 28px;
+ letter-spacing: 0.35px;
+ text-align: left;
+ color: #0D0F14;
+ margin-bottom: 0px;
+}
+
+h2 {
+ font-family: SFProDisplay-Medium, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ font-size: 17px;
+ font-weight: 600;
+ line-height: 22px;
+ letter-spacing: -0.2px;
+ text-align: left;
+ color: #0D0F14;
+ margin-bottom: 0px;
+}
+
+.description {
+ font-family: SFProText-Regular, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ font-size: 15px;
+ font-weight: 400;
+ line-height: 20px;
+ letter-spacing: -0.2px;
+ text-align: left;
+ color: #3F4855;
+ margin-bottom: 0px;
+}
+
+.domain {
+ font-size: 15px;
+ font-weight: 400;
+ line-height: 20px;
+ letter-spacing: 0em;
+ text-align: left;
+ color: #3F4855;
+ margin-bottom: 0px;
+}
+
+.container {
+ display: flex;
+ flex-wrap: wrap;
+ flex-direction: column;
+ align-content: flex-start;
+}
+
+/** Center the content for iPads **/
+@media (min-width: 600px) and (min-height: 600px) {
+ .icon {
+ width: 64px;
+ height: 64px;
+ }
+
+ h1 {
+ font-size: 28px;
+ font-weight: 500;
+ line-height: 36px;
+ letter-spacing: 0em;
+ }
+
+ h2 {
+ font-size: 16px;
+ line-height: 26px;
+ }
+
+ .description {
+ font-size: 14px;
+ line-height: 22px;
+ letter-spacing: -0.1px;
+ }
+
+ .domain {
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 22px;
+ letter-spacing: 0em;
+ }
+
+ .content {
+ margin: 0;
+ position: absolute;
+ top: 40%;
+ left: 50%;
+ max-width: 650px;
+ -ms-transform: translate(-50%, -50%);
+ transform: translate(-50%, -50%);
+ }
+}
+
+
+@media (prefers-color-scheme: dark) {
+ .background {
+ background-color: #0D0F14;
+ }
+
+ h1, h2 {
+ color: #F6F7F8;
+ }
+
+ .description, .domain {
+ color: #DBDEE2;
+ }
+}
diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKNavigationDelegate.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKNavigationDelegate.swift
index 11968302b83..13f8cd00598 100644
--- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKNavigationDelegate.swift
+++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController+WKNavigationDelegate.swift
@@ -349,6 +349,25 @@ extension BrowserViewController: WKNavigationDelegate {
if ["http", "https", "data", "blob", "file"].contains(requestURL.scheme) {
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 {
+ var components = URLComponents(string: InternalURL.baseUrl)
+ components?.path = "/\(InternalURL.Path.blocked.rawValue)"
+ components?.queryItems = [URLQueryItem(name: "url", value: requestURL.absoluteString.escape()!)]
+
+ if let url = components?.url {
+ let request = PrivilegedRequest(url: url) as URLRequest
+ tab?.loadRequest(request)
+ return (.cancel, preferences)
+ }
+ }
}
pendingRequests[requestURL.absoluteString] = navigationAction.request
diff --git a/Sources/Brave/Frontend/Browser/Handlers/BlockedDomainHandler.swift b/Sources/Brave/Frontend/Browser/Handlers/BlockedDomainHandler.swift
new file mode 100644
index 00000000000..279318659cd
--- /dev/null
+++ b/Sources/Brave/Frontend/Browser/Handlers/BlockedDomainHandler.swift
@@ -0,0 +1,43 @@
+// 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 WebKit
+import Shared
+import BraveShared
+import BraveShields
+
+public class BlockedDomainHandler: InternalSchemeResponse {
+ public static let path = InternalURL.Path.blocked.rawValue
+
+ public init() {}
+
+ public func response(forRequest request: URLRequest) -> (URLResponse, Data)? {
+ guard let url = request.url, let internalURL = InternalURL(url), let originalURL = internalURL.extractedUrlParam else { return nil }
+ let response = InternalSchemeHandler.response(forUrl: internalURL.url)
+
+ guard let asset = Bundle.module.path(forResource: "BlockedDomain", ofType: "html") else {
+ assert(false)
+ return nil
+ }
+
+ var html = try? String(contentsOfFile: asset)
+ .replacingOccurrences(of: "%page_title%", with: Strings.Shields.domianBlockedTitle)
+ .replacingOccurrences(of: "%blocked_title%", with: Strings.Shields.domianBlockedPageTitle)
+ .replacingOccurrences(of: "%blocked_subtitle%", with: Strings.Shields.domianBlockedPageMessage)
+ .replacingOccurrences(of: "%blocked_domain%", with: originalURL.domainURL.absoluteDisplayString)
+ .replacingOccurrences(of: "%blocked_description%", with: Strings.Shields.domianBlockedPageDescription)
+
+ if #available(iOS 16.0, *) {
+ html = html?.replacingOccurrences(of: "", with: "")
+ }
+
+ guard let data = html?.data(using: .utf8) else {
+ return nil
+ }
+
+ return (response, data)
+ }
+}
diff --git a/Sources/Brave/Frontend/Browser/Interstitial Pages/InternalSchemeHandler.swift b/Sources/Brave/Frontend/Browser/Interstitial Pages/InternalSchemeHandler.swift
index 5db2e03bcf0..974c8828f1f 100644
--- a/Sources/Brave/Frontend/Browser/Interstitial Pages/InternalSchemeHandler.swift
+++ b/Sources/Brave/Frontend/Browser/Interstitial Pages/InternalSchemeHandler.swift
@@ -35,6 +35,7 @@ public class InternalSchemeHandler: NSObject, WKURLSchemeHandler {
let allowedInternalResources = [
// interstitial
"/interstitial-style/InterstitialStyles.css": "text/css",
+ "/interstitial-style/BlockedDomain.css": "text/css",
"/interstitial-style/NetworkError.css": "text/css",
"/interstitial-style/CertificateError.css": "text/css",
"/interstitial-style/Web3Domain.css": "text/css",
@@ -49,6 +50,7 @@ public class InternalSchemeHandler: NSObject, WKURLSchemeHandler {
"/interstitial-icon/Carret.png": "image/png",
"/interstitial-icon/BraveIPFS.svg": "image/svg+xml",
"/interstitial-icon/IPFSBackground.svg": "image/svg+xml",
+ "/interstitial-icon/warning-triangle-outline.svg": "image/svg+xml",
// readermode
"/\(InternalURL.Path.readermode.rawValue)/styles/Reader.css": "text/css",
diff --git a/Sources/Brave/WebFilters/AdblockRustEngine.swift b/Sources/Brave/WebFilters/AdblockRustEngine.swift
index 803aac3c67e..432d416f7f4 100644
--- a/Sources/Brave/WebFilters/AdblockRustEngine.swift
+++ b/Sources/Brave/WebFilters/AdblockRustEngine.swift
@@ -8,7 +8,7 @@ import BraveCore
extension AdblockEngine {
public enum ResourceType: String, Decodable {
- case xmlhttprequest, script, image
+ case xmlhttprequest, script, image, document
}
/// Check the rust engine if the request should be blocked given the `sourceURL` and `resourceType`.
diff --git a/Sources/BraveShared/Extensions/URLExtensions.swift b/Sources/BraveShared/Extensions/URLExtensions.swift
index a8d81717b07..79708464377 100644
--- a/Sources/BraveShared/Extensions/URLExtensions.swift
+++ b/Sources/BraveShared/Extensions/URLExtensions.swift
@@ -34,7 +34,7 @@ extension URL {
switch internalURL.urlType {
case .errorPage:
return internalURL.originalURLFromErrorPage
- case .web3Page, .sessionRestorePage, .readerModePage, .aboutHomePage:
+ case .web3Page, .sessionRestorePage, .readerModePage, .aboutHomePage, .blockedPage:
return internalURL.extractedUrlParam
default:
return nil
@@ -48,6 +48,7 @@ extension URL {
extension InternalURL {
enum URLType {
+ case blockedPage
case sessionRestorePage
case errorPage
case readerModePage
@@ -57,6 +58,10 @@ extension InternalURL {
}
var urlType: URLType {
+ if isBlockedPage {
+ return .blockedPage
+ }
+
if isErrorPage {
return .errorPage
}
diff --git a/Sources/BraveShields/ShieldStrings.swift b/Sources/BraveShields/ShieldStrings.swift
index 3cebb060082..a3cdb055619 100644
--- a/Sources/BraveShields/ShieldStrings.swift
+++ b/Sources/BraveShields/ShieldStrings.swift
@@ -146,3 +146,35 @@ public extension Strings.Shields {
comment: "A button that ignores the brave player"
)
}
+
+// MARK: - Blocked Page
+
+public extension Strings.Shields {
+ /// A tab title that appears when a page was blocked
+ static let domianBlockedTitle = NSLocalizedString(
+ "DomainBlockedTitle", tableName: "BraveShared", bundle: .module,
+ value: "Domain Blocked",
+ comment: "A tab title for the warning page that appears when a page was blocked"
+ )
+
+ /// A title in the warning page that appears when a page was blocked
+ static let domianBlockedPageTitle = NSLocalizedString(
+ "DomianBlockedPageTitle", tableName: "BraveShared", bundle: .module,
+ value: "This Site May Attempt to Track You Across Other Sites",
+ comment: "A title in the warning page that appears when a page was blocked"
+ )
+
+ /// A title in the warning page that appears when a page was blocked
+ static let domianBlockedPageMessage = NSLocalizedString(
+ "DomianBlockedPageMessage", tableName: "BraveShared", bundle: .module,
+ value: "Brave has prevented the following site from loading:",
+ comment: "A message in the warning page that appears when a page was blocked"
+ )
+
+ /// A description in the warning page that appears when a page was blocked
+ static let domianBlockedPageDescription = NSLocalizedString(
+ "DomianBlockedPageDescription", tableName: "BraveShared", bundle: .module,
+ 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"
+ )
+}
diff --git a/Sources/Shared/Extensions/URLExtensions.swift b/Sources/Shared/Extensions/URLExtensions.swift
index e06d3695dfa..b82ffc503b7 100644
--- a/Sources/Shared/Extensions/URLExtensions.swift
+++ b/Sources/Shared/Extensions/URLExtensions.swift
@@ -149,6 +149,10 @@ extension URL {
if let internalUrl = InternalURL(self), internalUrl.isSessionRestore || internalUrl.isWeb3URL {
return internalUrl.extractedUrlParam?.displayURL
}
+
+ if let internalUrl = InternalURL(self), internalUrl.isBlockedPage {
+ return internalUrl.extractedUrlParam?.displayURL
+ }
if !InternalURL.isValid(url: self) {
let url = self.havingRemovedAuthorisationComponents()
@@ -503,10 +507,13 @@ public struct InternalURL {
public static let scheme = "internal"
public static let host = "local"
public static let baseUrl = "\(scheme)://\(host)"
+
public enum Path: String {
- case errorpage = "errorpage"
- case sessionrestore = "sessionrestore"
+ case errorpage
+ case sessionrestore
case readermode = "reader-mode"
+ case blocked
+
func matches(_ string: String) -> Bool {
return string.range(of: "/?\(self.rawValue)", options: .regularExpression, range: nil, locale: nil) != nil
}
@@ -570,6 +577,10 @@ public struct InternalURL {
return InternalURL.Path.errorpage.matches(path ?? "")
}
+ public var isBlockedPage: Bool {
+ return InternalURL.Path.blocked.matches(url.path)
+ }
+
public var isReaderModePage: Bool {
return InternalURL.Path.readermode.matches(url.path)
}