All about User Experience. Users want privacy and a better mobile web experience. Current 'Extension' solution used on OS X is resource intensive, relies on Javascript's onBeforeLoad
event, and can do all sorts of tracking and other potentially nefarious work.
How Safari Content Blocking addresses those potential issues:
- 'Precompiles' rules into Safari via app extensions, avoiding large processing and memory requirements of giant rules and stylesheets used by common extensions, improving battery life and resource allocation
- Rules exist in a simple JSON file that can either block or hide elements
- No executable or scripts run, eliminating tracking or other actions
In Xcode, create a new Application Extension target using the Content Blocker Extension option. Open the blockerList.json file, let's examine the included sample:
[
{
"action": {
"type": "block"
},
"trigger": {
"url-filter": "webkit.org/images/icon-gold.png"
}
}
]
Each dictionary requires an action and a trigger. In this example, we use a url-filter trigger with a block action. This would block Safari from requesting the resource at the given URL. Each rule we set up must have an action and at least one trigger. Here are the available options:
block
(blocks the request to the asset)block-cookies
(blocks cookies at the asset)css-display-none
(requires a CSS selector, loads the asset, but hides it using CSS)ignore-previous-rules
(overrides rules preceding the current)
url-filter
(required, regular expression against full url)load-type
(first-party or third-party)resource-type
(filters specific types of media, scripts, images, stylesheets, etc)url-filter-is-case-sensitive
(true or false)
Dean Murphy wrote a content blocker and tested it against iMore.com. His before and after:
Read more about Dean's experiment or find his JSON rules on Github.
There's controversy around ad or tracker blocking. Whatever side of the fence you stand on, the implementation for blocking assets is super simple, and the details apply to other content filtering as well.
Let's clean up The Verge. (No joke, this post on The Verge crashed Safari while I was preparing this README.) We could spend a lot of time refining this, but I bet we get a faster loading experience by simply blocking all third-party scripts.
[
{
"action":{
"type":"block"
},
"trigger":{
"url-filter":".*",
"resource-type":[
"script"
],
"load-type":[
"third-party"
]
}
}
]
I've been craving the Dell U3415W 34-inch monitor for a few months now. Maybe if I could hide the price so I can give my full pitch when asking my boss (or wife) permission to buy it.
[
{
"action": {
"type": "css-display-none",
"selector": "div[id*='price']"
},
"trigger": {
"url-filter": ".*"
}
}
]
Primitive, sure. But it can help block anything particularly heinous. Like anything related to the Los Angeles Lakers.
[
{
"action": {
"type": "block"
},
"trigger": {
"url-filter": ".*lakers.*"
}
}
]
- Your rules are compiled into Safari's master rule set when you activate the extension. If you are working in the code, you either need to deactivate and reactivate the extension, or use
SFContentBlockerManager.reloadContentBlockerWithIdentifier
to reload it programmatically. - As discussed during the presentation, you can use an array of attachments to send multiple JSON files, or build your own JSON file dynamically based on settings, simply serve it up in the ActionRequestHandler class.
- The documentation says that this works with the new CSS Level 4 selectors. Just be aware of which of those new selectors Safari supports. I shot 3.5 hours tinkering with the .has() selector before realizing it wasn't actually supported in any browser yet.
- Be aware that this only works in Safari and the new
SFSafariViewController
. - And, because this is Apple, be aware that if the system deems your rules as potentially negatively impacting user experience, they won't load.