Skip to content

Commit a6d52e0

Browse files
committed
LibWeb: Add a basic content filter (ad blocking!) :^)
This patch adds a global (per-process) filter list to LibWeb that is used to filter all outgoing resource load requests. Basically we check the URL against a list of filter patterns and if it's a match for any one of them, we immediately fail the load. The filter list is a simple text file: ~/.config/BrowserContentFilters.txt It's one filter per line and they are simple glob filters for now, with implicit asterisks (*) at the start and end of the line.
1 parent 1c8eaf2 commit a6d52e0

File tree

5 files changed

+141
-1
lines changed

5 files changed

+141
-1
lines changed

Applications/Browser/main.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include <LibGUI/TabWidget.h>
4444
#include <LibGUI/Window.h>
4545
#include <LibGfx/Bitmap.h>
46+
#include <LibWeb/Loader/ContentFilter.h>
4647
#include <LibWeb/Loader/ResourceLoader.h>
4748
#include <stdio.h>
4849
#include <stdlib.h>
@@ -132,6 +133,17 @@ int main(int argc, char** argv)
132133
auto m_config = Core::ConfigFile::get_for_app("Browser");
133134
Browser::g_home_url = m_config->read_entry("Preferences", "Home", "about:blank");
134135

136+
auto ad_filter_list_or_error = Core::File::open(String::formatted("{}/BrowserContentFilters.txt", Core::StandardPaths::config_directory()), Core::IODevice::ReadOnly);
137+
if (!ad_filter_list_or_error.is_error()) {
138+
auto& ad_filter_list = *ad_filter_list_or_error.value();
139+
while (!ad_filter_list.eof()) {
140+
auto line = ad_filter_list.read_line();
141+
if (line.is_empty())
142+
continue;
143+
Web::ContentFilter::the().add_pattern(line);
144+
}
145+
}
146+
135147
bool bookmarksbar_enabled = true;
136148
auto bookmarks_bar = Browser::BookmarksBarWidget::construct(Browser::bookmarks_file_path(), bookmarksbar_enabled);
137149

Libraries/LibWeb/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ set(SOURCES
177177
Layout/TreeBuilder.cpp
178178
Layout/WidgetBox.cpp
179179
LayoutTreeModel.cpp
180+
Loader/ContentFilter.cpp
180181
Loader/FrameLoader.cpp
181182
Loader/ImageLoader.cpp
182183
Loader/ImageResource.cpp
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
*
8+
* 1. Redistributions of source code must retain the above copyright notice, this
9+
* list of conditions and the following disclaimer.
10+
*
11+
* 2. Redistributions in binary form must reproduce the above copyright notice,
12+
* this list of conditions and the following disclaimer in the documentation
13+
* and/or other materials provided with the distribution.
14+
*
15+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25+
*/
26+
27+
#include <AK/StringBuilder.h>
28+
#include <LibWeb/Loader/ContentFilter.h>
29+
30+
namespace Web {
31+
32+
ContentFilter& ContentFilter::the()
33+
{
34+
static ContentFilter* filter = new ContentFilter;
35+
return *filter;
36+
}
37+
38+
ContentFilter::ContentFilter()
39+
{
40+
}
41+
42+
ContentFilter::~ContentFilter()
43+
{
44+
}
45+
46+
bool ContentFilter::is_filtered(const URL& url) const
47+
{
48+
auto url_string = url.to_string();
49+
50+
for (auto& pattern : m_patterns) {
51+
if (url_string.matches(pattern.text, CaseSensitivity::CaseSensitive))
52+
return true;
53+
}
54+
return false;
55+
}
56+
57+
void ContentFilter::add_pattern(const String& pattern)
58+
{
59+
StringBuilder builder;
60+
if (!pattern.starts_with('*'))
61+
builder.append('*');
62+
builder.append(pattern);
63+
if (!pattern.ends_with('*'))
64+
builder.append('*');
65+
m_patterns.empend(builder.to_string());
66+
}
67+
68+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
*
8+
* 1. Redistributions of source code must retain the above copyright notice, this
9+
* list of conditions and the following disclaimer.
10+
*
11+
* 2. Redistributions in binary form must reproduce the above copyright notice,
12+
* this list of conditions and the following disclaimer in the documentation
13+
* and/or other materials provided with the distribution.
14+
*
15+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19+
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20+
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21+
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22+
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23+
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25+
*/
26+
27+
#pragma once
28+
29+
#include <AK/URL.h>
30+
#include <AK/Vector.h>
31+
32+
namespace Web {
33+
34+
class ContentFilter {
35+
public:
36+
static ContentFilter& the();
37+
38+
bool is_filtered(const URL&) const;
39+
void add_pattern(const String&);
40+
41+
private:
42+
ContentFilter();
43+
~ContentFilter();
44+
45+
struct Pattern {
46+
String text;
47+
};
48+
Vector<Pattern> m_patterns;
49+
};
50+
51+
}

Libraries/LibWeb/Loader/ResourceLoader.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
2+
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -31,6 +31,7 @@
3131
#include <LibCore/File.h>
3232
#include <LibProtocol/Client.h>
3333
#include <LibProtocol/Download.h>
34+
#include <LibWeb/Loader/ContentFilter.h>
3435
#include <LibWeb/Loader/LoadRequest.h>
3536
#include <LibWeb/Loader/Resource.h>
3637
#include <LibWeb/Loader/ResourceLoader.h>
@@ -110,11 +111,18 @@ RefPtr<Resource> ResourceLoader::load_resource(Resource::Type type, const LoadRe
110111
void ResourceLoader::load(const LoadRequest& request, Function<void(ReadonlyBytes, const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)> success_callback, Function<void(const String&)> error_callback)
111112
{
112113
auto& url = request.url();
114+
113115
if (is_port_blocked(url.port())) {
114116
dbg() << "ResourceLoader::load: Error: blocked port " << url.port() << " for URL: " << url;
115117
return;
116118
}
117119

120+
if (ContentFilter::the().is_filtered(url)) {
121+
dbgln("\033[32;1mResourceLoader::load: URL was filtered! {}\033[0m", url);
122+
error_callback("URL was filtered");
123+
return;
124+
}
125+
118126
if (url.protocol() == "about") {
119127
dbg() << "Loading about: URL " << url;
120128
deferred_invoke([success_callback = move(success_callback)](auto&) {

0 commit comments

Comments
 (0)