diff --git a/App/iOS/Delegates/AppState.swift b/App/iOS/Delegates/AppState.swift
index c2094e98b8d..813d98705f1 100644
--- a/App/iOS/Delegates/AppState.swift
+++ b/App/iOS/Delegates/AppState.swift
@@ -192,7 +192,8 @@ public class AppState {
(ErrorPageHandler.path, ErrorPageHandler()),
(ReaderModeHandler.path, ReaderModeHandler(profile: profile)),
(IPFSSchemeHandler.path, IPFSSchemeHandler()),
- (Web3DomainHandler.path, Web3DomainHandler())
+ (Web3DomainHandler.path, Web3DomainHandler()),
+ (BlockedDomainHandler.path, BlockedDomainHandler())
]
responders.forEach { (path, responder) in
diff --git a/Package.swift b/Package.swift
index b47c23d2330..b5de278e499 100644
--- a/Package.swift
+++ b/Package.swift
@@ -376,6 +376,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"),
@@ -391,6 +392,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..b79155175a4
--- /dev/null
+++ b/Sources/Brave/Assets/Interstitial Pages/Pages/BlockedDomain.html
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+ %page_title%
+
+
+
+
+
+
+
%blocked_title%
+
%blocked_subtitle%
+
%blocked_domain%
+
%blocked_description%
+
+
+
+
+
+
+
+
+
diff --git a/Sources/Brave/Assets/Interstitial Pages/Scripts/BlockedDomain.js b/Sources/Brave/Assets/Interstitial Pages/Scripts/BlockedDomain.js
new file mode 100644
index 00000000000..e69de29bb2d
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..b00c2c7f892
--- /dev/null
+++ b/Sources/Brave/Assets/Interstitial Pages/Styles/BlockedDomain.css
@@ -0,0 +1,181 @@
+/*
+ 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;
+}
+
+.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 {
+ 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%);
+ }
+
+ .actions {
+ flex-direction: row-reverse;
+ }
+
+ button {
+ width: auto;
+ }
+}
+
+
+@media (prefers-color-scheme: dark) {
+ .background {
+ background-color: #0D0F14;
+ }
+
+ h1, h2 {
+ color: #F6F7F8;
+ }
+
+ .description, .domain {
+ color: #DBDEE2;
+ }
+
+ .secondary-action {
+ color: #7C91FF;
+ }
+}
diff --git a/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+WKNavigationDelegate.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+WKNavigationDelegate.swift
index 48b717f99ef..0a58c67f48e 100644
--- a/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+WKNavigationDelegate.swift
+++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BVC+WKNavigationDelegate.swift
@@ -356,6 +356,27 @@ extension BrowserViewController: WKNavigationDelegate {
if ["http", "https", "data", "blob", "file"].contains(requestURL.scheme) {
if navigationAction.targetFrame?.isMainFrame == true {
tab?.updateUserAgent(webView, newURL: requestURL)
+
+ if let etldP1 = requestURL.baseDomain, tab?.proceedAnywaysDomainList.contains(etldP1) == false {
+ 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 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/BrowserViewController/BrowserViewController.swift b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController.swift
index 593b29935fa..4c9f461078b 100644
--- a/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController.swift
+++ b/Sources/Brave/Frontend/Browser/BrowserViewController/BrowserViewController.swift
@@ -2507,6 +2507,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),
diff --git a/Sources/Brave/Frontend/Browser/Handlers/BlockedDomainHandler.swift b/Sources/Brave/Frontend/Browser/Handlers/BlockedDomainHandler.swift
new file mode 100644
index 00000000000..bb048f24607
--- /dev/null
+++ b/Sources/Brave/Frontend/Browser/Handlers/BlockedDomainHandler.swift
@@ -0,0 +1,47 @@
+// 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.domainBlockedTitle)
+ .replacingOccurrences(of: "%blocked_title%", with: Strings.Shields.domainBlockedPageTitle)
+ .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: "", 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/Frontend/Browser/Tab.swift b/Sources/Brave/Frontend/Browser/Tab.swift
index 8e1a621fd04..23e0e385035 100644
--- a/Sources/Brave/Frontend/Browser/Tab.swift
+++ b/Sources/Brave/Frontend/Browser/Tab.swift
@@ -594,6 +594,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 = []
var canGoBack: Bool {
return webView?.canGoBack ?? false
diff --git a/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/BlockedDomainScriptHandler.swift b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/BlockedDomainScriptHandler.swift
new file mode 100644
index 00000000000..95dcce2d86c
--- /dev/null
+++ b/Sources/Brave/Frontend/UserContent/UserScripts/Scripts_Dynamic/ScriptHandlers/Internal/BlockedDomainScriptHandler.swift
@@ -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)
+ }
+}
diff --git a/Sources/Brave/WebFilters/AdblockRustEngine.swift b/Sources/Brave/WebFilters/AdblockRustEngine.swift
index 24ab0ec9602..c0bd3daf7ae 100644
--- a/Sources/Brave/WebFilters/AdblockRustEngine.swift
+++ b/Sources/Brave/WebFilters/AdblockRustEngine.swift
@@ -11,6 +11,7 @@ extension AdblockEngine {
case xmlhttprequest
case script
case image
+ case document
case subdocument
}
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 20e6bd2b2a7..e54fef2b7ee 100644
--- a/Sources/BraveShields/ShieldStrings.swift
+++ b/Sources/BraveShields/ShieldStrings.swift
@@ -147,7 +147,8 @@ public extension Strings.Shields {
)
}
-// MARK: - Shields
+// MARK: - GPC
+
public extension Strings.Shields {
/// A label of the GPC toggle
static let enableGPCLabel = NSLocalizedString(
@@ -163,3 +164,49 @@ public extension Strings.Shields {
comment: "A description of what the Enable GPC toggle does"
)
}
+
+// MARK: - Blocked Page
+
+public extension Strings.Shields {
+ /// A tab title that appears when a page was blocked
+ static let domainBlockedTitle = 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 domainBlockedPageTitle = NSLocalizedString(
+ "DomainBlockedPageTitle", 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 domainBlockedPageMessage = NSLocalizedString(
+ "DomainBlockedPageMessage", 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 domainBlockedPageDescription = NSLocalizedString(
+ "DomainBlockedPageDescription", 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"
+ )
+
+ /// 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"
+ )
+}
diff --git a/Sources/Shared/Extensions/URLExtensions.swift b/Sources/Shared/Extensions/URLExtensions.swift
index e06d3695dfa..08f99e5ff96 100644
--- a/Sources/Shared/Extensions/URLExtensions.swift
+++ b/Sources/Shared/Extensions/URLExtensions.swift
@@ -146,7 +146,7 @@ extension URL {
return internalUrl.originalURLFromErrorPage?.displayURL
}
- if let internalUrl = InternalURL(self), internalUrl.isSessionRestore || internalUrl.isWeb3URL {
+ if let internalUrl = InternalURL(self), internalUrl.isSessionRestore || internalUrl.isWeb3URL || internalUrl.isBlockedPage {
return internalUrl.extractedUrlParam?.displayURL
}
@@ -503,10 +503,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 +573,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)
}