Serenity Operating System
1/*
2 * Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include "BrowserWindow.h"
8#include "HelperProcess.h"
9#include "Settings.h"
10#include "Utilities.h"
11#include "WebContentView.h"
12#include <AK/OwnPtr.h>
13#include <Browser/CookieJar.h>
14#include <Browser/Database.h>
15#include <LibCore/ArgsParser.h>
16#include <LibCore/DeprecatedFile.h>
17#include <LibCore/EventLoop.h>
18#include <LibCore/System.h>
19#include <LibGfx/Font/FontDatabase.h>
20#include <LibMain/Main.h>
21#include <LibSQL/SQLClient.h>
22#include <QApplication>
23
24AK::OwnPtr<Browser::Settings> s_settings;
25
26static ErrorOr<void> handle_attached_debugger()
27{
28#ifdef AK_OS_LINUX
29 // Let's ignore SIGINT if we're being debugged because GDB
30 // incorrectly forwards the signal to us even when it's set to
31 // "nopass". See https://sourceware.org/bugzilla/show_bug.cgi?id=9425
32 // for details.
33 auto unbuffered_status_file = TRY(Core::File::open("/proc/self/status"sv, Core::File::OpenMode::Read));
34 auto status_file = TRY(Core::BufferedFile::create(move(unbuffered_status_file)));
35 auto buffer = TRY(ByteBuffer::create_uninitialized(4096));
36 while (TRY(status_file->can_read_line())) {
37 auto line = TRY(status_file->read_line(buffer));
38 auto const parts = line.split_view(':');
39 if (parts.size() < 2 || parts[0] != "TracerPid"sv)
40 continue;
41 auto tracer_pid = parts[1].to_uint<u32>();
42 if (tracer_pid != 0UL) {
43 dbgln("Debugger is attached, ignoring SIGINT");
44 TRY(Core::System::signal(SIGINT, SIG_IGN));
45 }
46 break;
47 }
48#endif
49 return {};
50}
51
52ErrorOr<int> serenity_main(Main::Arguments arguments)
53{
54 // NOTE: This is only used for the Core::Socket inside the IPC connections.
55 // FIXME: Refactor things so we can get rid of this somehow.
56 Core::EventLoop event_loop;
57
58 TRY(handle_attached_debugger());
59
60 QApplication app(arguments.argc, arguments.argv);
61
62 platform_init();
63
64 // NOTE: We only instantiate this to ensure that Gfx::FontDatabase has its default queries initialized.
65 Gfx::FontDatabase::set_default_font_query("Katica 10 400 0");
66 Gfx::FontDatabase::set_fixed_width_font_query("Csilla 10 400 0");
67
68 StringView raw_url;
69 StringView webdriver_content_ipc_path;
70
71 Core::ArgsParser args_parser;
72 args_parser.set_general_help("The Ladybird web browser :^)");
73 args_parser.add_positional_argument(raw_url, "URL to open", "url", Core::ArgsParser::Required::No);
74 args_parser.add_option(webdriver_content_ipc_path, "Path to WebDriver IPC for WebContent", "webdriver-content-path", 0, "path");
75 args_parser.parse(arguments);
76
77 auto get_formatted_url = [&](StringView const& raw_url) -> URL {
78 URL url = raw_url;
79 if (Core::DeprecatedFile::exists(raw_url))
80 url = URL::create_with_file_scheme(Core::DeprecatedFile::real_path_for(raw_url));
81 else if (!url.is_valid())
82 url = DeprecatedString::formatted("http://{}", raw_url);
83 return url;
84 };
85
86 auto sql_server_paths = TRY(get_paths_for_helper_process("SQLServer"sv));
87 auto sql_client = TRY(SQL::SQLClient::launch_server_and_create_client(move(sql_server_paths)));
88 auto database = TRY(Browser::Database::create(move(sql_client)));
89
90 auto cookie_jar = TRY(Browser::CookieJar::create(*database));
91
92 s_settings = adopt_own_if_nonnull(new Browser::Settings());
93 BrowserWindow window(cookie_jar, webdriver_content_ipc_path);
94 window.setWindowTitle("Ladybird");
95 window.resize(800, 600);
96 window.show();
97
98 if (auto url = get_formatted_url(raw_url); url.is_valid()) {
99 window.view().load(url);
100 } else if (!s_settings->homepage().isEmpty()) {
101 auto home_url = TRY(ak_string_from_qstring(s_settings->homepage()));
102 window.view().load(get_formatted_url(home_url.bytes_as_string_view()));
103 }
104
105 return app.exec();
106}