This page lists the services and classes important to the eyeo Browser Ad-Filtering Solution, as well as the role each plays in the Solution's implementation.
The Solution consists of the following main KeyedService
services:
FilteringConfiguration
; allows the control of resource filtering settings
SubscriptionService
; maintains a state of available subscriptions and synchronizes it with persistent storage.
ResourceClassificationRunner
; decides whether to block or allow network requests.
ElementHider
; applies element hiding scripts and stylesheets on web pages.
AdblockTelemetryService
; reports anonymous usage statistics to eyeo.
SitekeyStorage
; extracts, validates, and stores SiteKeys from response headers.
The following Chromium classes play an important role in the Solution's implementation:
RenderFrameHost
; allows finding the frame hierarchy and executes element hiding CSS and JavaScript.
WebContentsObserver
; receives page load events and injects element hiding; extended by AdblockWebContentsObserver
.
ChromeContentBrowserClient
; extended by AdblockChromeContentBrowserClient
in order to set up an internal proxy for inspecting network requests.
The Solution's logic executes primarily in the Browser process. The only exception is code under third_party/blink/renderer/core/exported/web_document.cc
that injects element hiding stylesheets into the website - this runs in a Renderer process.
In the Browser process, code that might take a long time to execute runs asynchronously in the ThreadPool. Examples of such code:
Classification of network requests
Preparing element hiding selectors
Conversion/serialization of filter lists
Loading installed filter lists from disk
A sitekey is a special identifier that a server may attach to a resource. Sitekeys enable additional filters that determine whether to block or allow the resource.
Sitekeys are typically used to allowlist resources coming from a particular server that may serve many different domains.
Suppose a user visits two sites: https://catstoys.com and https://dogstoys.com. A single sitekey server handles both sites.
The network responses headers for both sites will contain x-adblock-key
. The sitekey is encoded within that header.
The sitekey server gets the following information:
The path with query of the URL requested by the client, for example, /page.html?param-value
The host that the client contacted, for example, catstoys.com
The client's user agent, like Mozilla/5.0 (X11; Linux x86_64)
, Chrome/92.0.4515.107 Safari/537.36
, and so on.
These three strings are connected into a single line of text, separated by null (\0
) symbols, as in the following example:
/page.html?param-value\0catstoys.com\0Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36
This becomes the input to the encryption algorithm. This input is different for every URL that the browser requests.
The sitekey server holds a secret private key that is only generated once and never changes. Using this private key, the sitekey server encrypts the input text and generates a unique signature, or a hash. The hash would look something like this:
nLH8Vbc1rzmy0Q+Xg+bvm43IEO42h8rq5D9C0WCn/Y3ykgAoV4npzm7eMlqBSwZBLA/0DuuVsfTJT9MOVaurcA==
The sitekey server then returns the following:
The signature, that is, is the encrypted output. This signature changes for every request.
A sitekey, which is the public key to the server's internal, secret private key. The public key never changes.
The sitekey and signature are glued together with the underscore (_
) as the separator, as in sitekey_signature
. In the example scenario, the string would look like this:
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANnylWw2vLY4hUn9w06zQKbhKBfvjFUCsdFlb6TdQhxb9RXWXuI4t31c+o8fYOv/s8q1LGPga3DE1L/tHU4LENMCAwEAAQ_nLH8Vbc1rzmy0Q+Xg+bvm43IEO42h8rq5D9C0WCn/Y3ykgAoV4npzm7eMlqBSwZBLA/0DuuVsfTJT9MOVaurcA=
The sitekey server then sends this string back to the browser in the x-adblock-key
header.
The following diagram illustrates the process in its entirety:
When the browser receives an HTTP response whose header contains x-adblock-key
, it splits the string back into a separate sitekey and signature.
Because the browser knows the signature's three parameters (host, URL, UserAgent
) and it has the public key, it can verify that the sender has the private key that corresponds to the sitekey that the browser received.
In other words, the browser now knows that the server is a legitimate provider of the MFwwDQYJKoZIhvcNAQ...
sitekey. This is because the server successfully encrypted inputs given by the browser in a way that's verifiable with that sitekey.
As a result, in the example, filter list allowing can now be applied on https://catstoys.com:
@@$document,sitekey=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANnylWw2vLY4hUn9w06zQKbhKBfvjFUCsdFlb6TdQhxb9RXWXuI4t31c+o8fYOv/s8q1LGPga3DE1L/tHU4LENMCAwEAAQ
The following diagram illustrates the browser-side sitekey process:
The filter used in the example on this page allows all resources to load and disables element hiding on any page with the specified sitekey. For more on filter language, see How to write filters.
Learn about the thinking behind the eyeo Browser Ad-Filtering Solution.
This page contains the eyeo Browser Ad-Filtering Solution architectural decision records (ADRs), which summarize the most important software design choices made during the development of the Solution.
These decisions address functional and non-functional requirements that eyeo finds architecturally significant.
Chromium contains some ad-filtering functionality in the form of the subresource filter. The Chromium subresource filter, however, doesn't support CSS element-hiding, JavaScript element hiding emulation, or snippets.
Patching these limitations requires significant tradeoffs, some of which include the following:
More conflicts to solve after Chromium updates
Less flexibility to introduce new features and optimizations
Irreconcilably different objectives between eyeo and Chrome's authors
Chromium authors control subresource filter development. As a result, they may have different (ad-related) business objectives that could take subresource filtering in a direction that doesn't fit with eyeo's vision.
For these reasons, the eyeo team chose not to build on top of the subresource filter, opting for an implementation with ad-filtering capabilities built from scratch.
In previous implementations of our ad-filtering core, eyeo used minified filter lists to reduce memory consumption for mobile users. More recent implementations achieved this reduction by adopting FlatButters as a filter list file format and by moving from V8 to a native implementation.
eyeo now uses full filter lists, which allow for more filter rules. This improves user experience and blocks a greater number of intrusive ads. More Acceptable Ads are also allowed, which increases for publishers who adhere to the Acceptable Ads standard.
Filter lists aren't consumed in plain text format right after download. Instead, they are converted from plain text into the FlatBuffers format, which offers the following advantages:
Reduced memory consumption, down to approximately 15 MB
Reduced startup time, from seconds down to milliseconds
No negative impact on page load time, except for sites with long URLs
Facilities for multi-threading and synchronous popup blocking
No required deserialization, because FlatBuffers is already in a binary format
FlatBuffers can be memory-mapped and accessed as memory directly from disk.
Accessing data in a flatbuffer is as fast as dereferencing pointers to memory.
A FlatBuffers file contains only one filter list, for the following reasons:
Filter lists updated at different times can be downloaded independently.
Less time consumed in conversion than if all selected filter lists are combined in one file
For long-term distribution of FlatBuffers files, having to provide a file containing all selected filter lists would cause excessive combinations.
Coupling the user counting service with filter lists downloads gives eyeo insight into Solution usage across Chromium, OS, and platform versions.
As a dedicated service, it also allows eyeo to monitor Acceptable Ads opt-out rate, while enabling partners to serve and monetize filter lists from their own servers.
For more information, view the user counting documentation.
Frame hierarchy is a term coined by eyeo. It represents a list of URLs where each element is embedded by the next one.
As an example, consider:
A website's main page is https://example.com
This website embeds an <iframe>
with src = https://iframe.com/content.html
This iframe contains an ad - an image with src = https://ads.com/ad.png
In this example, the image https://ads.com/ad.png
has a frame hierarchy of:
https://iframe.com/content.html
https://example.com
By convention, the first element on the frame hierarchy is the immediate parent frame of the resource.
The Browser Ad-Filtering Solution uses the frame hierarchy to establish whether allowing filters exist for parents of a resource.
Consider a blocking filter: ||ads.com/ad.png
This filter matches the URL of the ad image. When a website loads, a resource classifier will mark the image as an ad and stop the request from leaving the browser.
Now consider an allowing filter: @@||example.com^$document
This filter allows everything on the example.com
domain, it overrides any blocking filters.
If both filters are present, the resource classifier should allow the ad from this particular frame hierarchy to load. But the same ad loaded from a different frame hierarchy might still be blocked.
The resource classifier searches not only for blocking and allowing filters that match https://ads.com/ad.png
, but also for $document
-type allowing filters that match https://iframe.com/content.html
and https://example.com
.
This is critical for correctly implementing the Acceptable Ads allowlists.
The frame hierarchy can be assembled by recursively following content::RenderFrameHost::GetParent()
results.