Serenity Operating System
at master 186 lines 7.6 kB view raw
1/* 2 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org> 4 * Copyright (c) 2022, the SerenityOS developers. 5 * 6 * SPDX-License-Identifier: BSD-2-Clause 7 */ 8 9#include <Applications/Browser/Browser.h> 10#include <Applications/Browser/BrowserWindow.h> 11#include <Applications/Browser/CookieJar.h> 12#include <Applications/Browser/Database.h> 13#include <Applications/Browser/Tab.h> 14#include <Applications/Browser/WindowActions.h> 15#include <LibConfig/Client.h> 16#include <LibCore/ArgsParser.h> 17#include <LibCore/DeprecatedFile.h> 18#include <LibCore/FileWatcher.h> 19#include <LibCore/StandardPaths.h> 20#include <LibCore/System.h> 21#include <LibDesktop/Launcher.h> 22#include <LibGUI/Application.h> 23#include <LibGUI/BoxLayout.h> 24#include <LibGUI/Icon.h> 25#include <LibGUI/TabWidget.h> 26#include <LibMain/Main.h> 27#include <LibWeb/Loader/ResourceLoader.h> 28#include <LibWebView/OutOfProcessWebView.h> 29#include <LibWebView/RequestServerAdapter.h> 30#include <unistd.h> 31 32namespace Browser { 33 34DeprecatedString g_search_engine; 35DeprecatedString g_home_url; 36DeprecatedString g_new_tab_url; 37Vector<DeprecatedString> g_content_filters; 38bool g_content_filters_enabled { true }; 39Vector<DeprecatedString> g_proxies; 40HashMap<DeprecatedString, size_t> g_proxy_mappings; 41IconBag g_icon_bag; 42DeprecatedString g_webdriver_content_ipc_path; 43 44} 45 46static ErrorOr<void> load_content_filters() 47{ 48 auto file = TRY(Core::File::open(DeprecatedString::formatted("{}/BrowserContentFilters.txt", Core::StandardPaths::config_directory()), Core::File::OpenMode::Read)); 49 auto ad_filter_list = TRY(Core::BufferedFile::create(move(file))); 50 auto buffer = TRY(ByteBuffer::create_uninitialized(4096)); 51 while (TRY(ad_filter_list->can_read_line())) { 52 auto line = TRY(ad_filter_list->read_line(buffer)); 53 if (!line.is_empty()) 54 Browser::g_content_filters.append(line); 55 } 56 57 return {}; 58} 59 60ErrorOr<int> serenity_main(Main::Arguments arguments) 61{ 62 if (getuid() == 0) { 63 warnln("Refusing to run as root"); 64 return 1; 65 } 66 67 TRY(Core::System::pledge("stdio recvfd sendfd unix fattr cpath rpath wpath proc exec")); 68 69 Vector<DeprecatedString> specified_urls; 70 71 Core::ArgsParser args_parser; 72 args_parser.add_positional_argument(specified_urls, "URLs to open", "url", Core::ArgsParser::Required::No); 73 args_parser.add_option(Browser::g_webdriver_content_ipc_path, "Path to WebDriver IPC for WebContent", "webdriver-content-path", 0, "path"); 74 75 args_parser.parse(arguments); 76 77 auto app = TRY(GUI::Application::try_create(arguments)); 78 79 Config::pledge_domain("Browser"); 80 Config::monitor_domain("Browser"); 81 82 // Connect to LaunchServer immediately and let it know that we won't ask for anything other than opening 83 // the user's downloads directory. 84 // FIXME: This should go away with a standalone download manager at some point. 85 TRY(Desktop::Launcher::add_allowed_url(URL::create_with_file_scheme(Core::StandardPaths::downloads_directory()))); 86 TRY(Desktop::Launcher::seal_allowlist()); 87 88 if (!Browser::g_webdriver_content_ipc_path.is_empty()) 89 specified_urls.empend("about:blank"); 90 91 TRY(Core::System::unveil("/tmp/session/%sid/portal/filesystemaccess", "rw")); 92 TRY(Core::System::unveil("/tmp/session/%sid/portal/filesystemaccess", "rw")); 93 TRY(Core::System::unveil("/tmp/session/%sid/portal/image", "rw")); 94 TRY(Core::System::unveil("/tmp/session/%sid/portal/webcontent", "rw")); 95 TRY(Core::System::unveil("/tmp/session/%sid/portal/request", "rw")); 96 TRY(Core::System::unveil("/tmp/session/%sid/portal/sql", "rw")); 97 TRY(Core::System::unveil("/home", "rwc")); 98 TRY(Core::System::unveil("/res", "r")); 99 TRY(Core::System::unveil("/etc/passwd", "r")); 100 TRY(Core::System::unveil("/etc/timezone", "r")); 101 TRY(Core::System::unveil("/bin/BrowserSettings", "x")); 102 TRY(Core::System::unveil("/bin/Browser", "x")); 103 TRY(Core::System::unveil(nullptr, nullptr)); 104 105 Web::ResourceLoader::initialize(TRY(WebView::RequestServerAdapter::try_create())); 106 107 auto app_icon = GUI::Icon::default_icon("app-browser"sv); 108 109 Browser::g_home_url = Config::read_string("Browser"sv, "Preferences"sv, "Home"sv, "file:///res/html/misc/welcome.html"sv); 110 Browser::g_new_tab_url = Config::read_string("Browser"sv, "Preferences"sv, "NewTab"sv, "file:///res/html/misc/new-tab.html"sv); 111 Browser::g_search_engine = Config::read_string("Browser"sv, "Preferences"sv, "SearchEngine"sv, {}); 112 Browser::g_content_filters_enabled = Config::read_bool("Browser"sv, "Preferences"sv, "EnableContentFilters"sv, true); 113 114 Browser::g_icon_bag = TRY(Browser::IconBag::try_create()); 115 116 auto database = TRY(Browser::Database::create()); 117 TRY(load_content_filters()); 118 119 for (auto& group : Config::list_groups("Browser"sv)) { 120 if (!group.starts_with("Proxy:"sv)) 121 continue; 122 123 for (auto& key : Config::list_keys("Browser"sv, group)) { 124 auto proxy_spec = group.substring_view(6); 125 auto existing_proxy = Browser::g_proxies.find(proxy_spec); 126 if (existing_proxy.is_end()) 127 Browser::g_proxies.append(proxy_spec); 128 129 Browser::g_proxy_mappings.set(key, existing_proxy.index()); 130 } 131 } 132 133 auto url_from_argument_string = [](DeprecatedString const& string) -> URL { 134 if (Core::DeprecatedFile::exists(string)) { 135 return URL::create_with_file_scheme(Core::DeprecatedFile::real_path_for(string)); 136 } 137 return Browser::url_from_user_input(string); 138 }; 139 140 URL first_url = Browser::url_from_user_input(Browser::g_home_url); 141 if (!specified_urls.is_empty()) 142 first_url = url_from_argument_string(specified_urls.first()); 143 144 auto cookie_jar = TRY(Browser::CookieJar::create(*database)); 145 auto window = Browser::BrowserWindow::construct(cookie_jar, first_url); 146 147 auto content_filters_watcher = TRY(Core::FileWatcher::create()); 148 content_filters_watcher->on_change = [&](Core::FileWatcherEvent const&) { 149 dbgln("Reloading content filters because config file changed"); 150 auto error = load_content_filters(); 151 if (error.is_error()) { 152 dbgln("Reloading content filters failed: {}", error.release_error()); 153 return; 154 } 155 window->content_filters_changed(); 156 }; 157 TRY(content_filters_watcher->add_watch(DeprecatedString::formatted("{}/BrowserContentFilters.txt", Core::StandardPaths::config_directory()), Core::FileWatcherEvent::Type::ContentModified)); 158 159 app->on_action_enter = [&](GUI::Action& action) { 160 if (auto* browser_window = dynamic_cast<Browser::BrowserWindow*>(app->active_window())) { 161 auto* tab = static_cast<Browser::Tab*>(browser_window->tab_widget().active_widget()); 162 if (!tab) 163 return; 164 tab->action_entered(action); 165 } 166 }; 167 168 app->on_action_leave = [&](auto& action) { 169 if (auto* browser_window = dynamic_cast<Browser::BrowserWindow*>(app->active_window())) { 170 auto* tab = static_cast<Browser::Tab*>(browser_window->tab_widget().active_widget()); 171 if (!tab) 172 return; 173 tab->action_left(action); 174 } 175 }; 176 177 for (size_t i = 1; i < specified_urls.size(); ++i) 178 window->create_new_tab(url_from_argument_string(specified_urls[i]), false); 179 180 window->show(); 181 182 window->broadcast_window_position(window->position()); 183 window->broadcast_window_size(window->size()); 184 185 return app->exec(); 186}