Skip to content
Open
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Support domain wildcards (`domain.*`) and domain regexes on Safari 26+ using `if-frame-url`/`unless-frame-url`: [#100]

[unreleased]: https://github.com/AdguardTeam/SafariConverterLib/compare/v4.1.0...HEAD
[#100]: https://github.com/AdguardTeam/SafariConverterLib/issues/100

## [v4.1.0]

Expand Down
25 changes: 14 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,13 @@ certainly supports the most important types of those rules.
`$domain=example.org|~sub.example.org`). Please upvote the
[feature request][webkitmixeddomainsissue] to WebKit to lift this
limitation.
- "Any TLD" (i.e. `domain.*`) is not fully supported. In the current
implementation the converter just replaces `.*` with top 100 popular TLDs.
This implementation will be improved [in the future][iftopurlissue].
- Using regular expressions in `$domain` is not supported, but it also will
be improved [in the future][iftopurlissue].
- "Any TLD" (i.e. `domain.*`):
- Safari 26+: supported via `if-frame-url`/`unless-frame-url`.
- Safari < 26: the converter replaces `.*` with top 100 popular TLDs.
- Using regular expressions in `$domain` (i.e. `$domain=/regexp/`):
- Safari 26+: supported via `if-frame-url`/`unless-frame-url` (limited to
regex that is [supported by Safari][safariregex]).
- Safari < 26: not supported.

- `$denyallow` - this modifier is supported via converting `$denyallow` rule to
a set of rules (one blocking rule + several unblocking rules).
Expand Down Expand Up @@ -239,7 +241,6 @@ certainly supports the most important types of those rules.
[safariregex]: https://developer.apple.com/documentation/safariservices/creating-a-content-blocker#Capture-URLs-by-pattern
[webkitmixeddomainsissue]: https://bugs.webkit.org/show_bug.cgi?id=226076
[domainmodifier]: https://adguard.com/kb/general/ad-filtering/create-own-filters/#domain-modifier
[iftopurlissue]: https://github.com/AdguardTeam/SafariConverterLib/issues/20#issuecomment-2532818732
[#69]: https://github.com/AdguardTeam/SafariConverterLib/issues/69
[#70]: https://github.com/AdguardTeam/SafariConverterLib/issues/70
[#71]: https://github.com/AdguardTeam/SafariConverterLib/issues/71
Expand Down Expand Up @@ -289,11 +290,13 @@ additional extension.
#### Limitations of cosmetic rules

- Specifying domains is subject to limitations:
- "Any TLD" (i.e. `domain.*`) is not fully supported. In the current
implementation the converter just replaces `.*` with top 100 popular TLDs.
This implementation will be improved [in the future][iftopurlissue].
- Using regular expressions in `$domain` is not supported, but it also will
be improved [in the future][iftopurlissue].
- "Any TLD" (i.e. `domain.*`):
- Safari 26+: supported via `if-frame-url`/`unless-frame-url`.
- Safari < 26: the converter replaces `.*` with top 100 popular TLDs.
- Using regular expressions in `$domain` (i.e. `$domain=/regexp/`):
- Safari 26+: supported via `if-frame-url`/`unless-frame-url` (limited to
regex that is [supported by Safari][safariregex]).
- Safari < 26: not supported.

- CSS exception rules (`#@#`) are supported with limitations:
- The same limitations for specifying domains as with other cosmetic rules.
Expand Down
19 changes: 16 additions & 3 deletions Sources/ContentBlockerConverter/Compiler/BlockerEntry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,21 @@ public struct BlockerEntry: Codable, Equatable, CustomStringConvertible {
public struct Trigger: Codable, Equatable {
public init(
ifDomain: [String]? = nil,
ifFrameUrl: [String]? = nil,
urlFilter: String? = nil,
unlessDomain: [String]? = nil,
unlessFrameUrl: [String]? = nil,
loadType: [String]? = nil,
resourceType: [String]? = nil,
requestMethod: String? = nil,
caseSensitive: Bool? = nil,
loadContext: [String]? = nil
) {
self.ifDomain = ifDomain
self.ifFrameUrl = ifFrameUrl
self.urlFilter = urlFilter
self.unlessDomain = unlessDomain
self.unlessFrameUrl = unlessFrameUrl
self.loadType = loadType
self.resourceType = resourceType
self.requestMethod = requestMethod
Expand All @@ -58,8 +62,10 @@ public struct BlockerEntry: Codable, Equatable, CustomStringConvertible {
}

public var ifDomain: [String]?
public var ifFrameUrl: [String]?
public var urlFilter: String?
public var unlessDomain: [String]?
public var unlessFrameUrl: [String]?
public var loadType: [String]?
public var resourceType: [String]?
public var requestMethod: String?
Expand All @@ -69,8 +75,10 @@ public struct BlockerEntry: Codable, Equatable, CustomStringConvertible {
// swiftlint:disable:next nesting
enum CodingKeys: String, CodingKey {
case ifDomain = "if-domain"
case ifFrameUrl = "if-frame-url"
case urlFilter = "url-filter"
case unlessDomain = "unless-domain"
case unlessFrameUrl = "unless-frame-url"
case loadType = "load-type"
case resourceType = "resource-type"
case requestMethod = "request-method"
Expand All @@ -81,9 +89,14 @@ public struct BlockerEntry: Codable, Equatable, CustomStringConvertible {
// Custom Equatable implementation
public static func == (lhs: Trigger, rhs: Trigger) -> Bool {
return lhs.ifDomain == rhs.ifDomain && lhs.urlFilter == rhs.urlFilter
&& lhs.unlessDomain == rhs.unlessDomain && lhs.loadType == rhs.loadType
&& lhs.resourceType == rhs.resourceType && lhs.requestMethod == rhs.requestMethod
&& lhs.caseSensitive == rhs.caseSensitive && lhs.loadContext == rhs.loadContext
&& lhs.ifFrameUrl == rhs.ifFrameUrl
&& lhs.unlessDomain == rhs.unlessDomain
&& lhs.unlessFrameUrl == rhs.unlessFrameUrl
&& lhs.loadType == rhs.loadType
&& lhs.resourceType == rhs.resourceType
&& lhs.requestMethod == rhs.requestMethod
&& lhs.caseSensitive == rhs.caseSensitive
&& lhs.loadContext == rhs.loadContext
}
}

Expand Down
10 changes: 10 additions & 0 deletions Sources/ContentBlockerConverter/Compiler/BlockerEntryEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,16 @@ class BlockerEntryEncoder {
result.append("\"")
}

if let ifFrameUrl = trigger.ifFrameUrl {
result.append(",\"if-frame-url\":")
result.append(ifFrameUrl.encodeToJSON(escape: true))
}

if let unlessFrameUrl = trigger.unlessFrameUrl {
result.append(",\"unless-frame-url\":")
result.append(unlessFrameUrl.encodeToJSON(escape: true))
}

if let ifDomain = trigger.ifDomain {
result.append(",\"if-domain\":")
result.append(ifDomain.encodeToJSON(escape: true))
Expand Down
Loading