Serenity Operating System
1/*
2 * Copyright (c) 2023, Linus Groh <linusg@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/Error.h>
8#include <AK/String.h>
9#include <LibWebView/ViewImplementation.h>
10
11namespace WebView {
12
13WebContentClient& ViewImplementation::client()
14{
15 VERIFY(m_client_state.client);
16 return *m_client_state.client;
17}
18
19WebContentClient const& ViewImplementation::client() const
20{
21 VERIFY(m_client_state.client);
22 return *m_client_state.client;
23}
24
25void ViewImplementation::load(AK::URL const& url)
26{
27 m_url = url;
28 client().async_load_url(url);
29}
30
31void ViewImplementation::load_html(StringView html, AK::URL const& url)
32{
33 m_url = url;
34 client().async_load_html(html, url);
35}
36
37void ViewImplementation::load_empty_document()
38{
39 load_html(""sv, {});
40}
41
42void ViewImplementation::zoom_in()
43{
44 if (m_zoom_level >= ZOOM_MAX_LEVEL)
45 return;
46 m_zoom_level += ZOOM_STEP;
47 update_zoom();
48}
49
50void ViewImplementation::zoom_out()
51{
52 if (m_zoom_level <= ZOOM_MIN_LEVEL)
53 return;
54 m_zoom_level -= ZOOM_STEP;
55 update_zoom();
56}
57
58void ViewImplementation::reset_zoom()
59{
60 m_zoom_level = 1.0f;
61 update_zoom();
62}
63
64void ViewImplementation::set_preferred_color_scheme(Web::CSS::PreferredColorScheme color_scheme)
65{
66 client().async_set_preferred_color_scheme(color_scheme);
67}
68
69DeprecatedString ViewImplementation::selected_text()
70{
71 return client().get_selected_text();
72}
73
74void ViewImplementation::select_all()
75{
76 client().async_select_all();
77}
78
79void ViewImplementation::get_source()
80{
81 client().async_get_source();
82}
83
84void ViewImplementation::inspect_dom_tree()
85{
86 client().async_inspect_dom_tree();
87}
88
89void ViewImplementation::inspect_accessibility_tree()
90{
91 client().async_inspect_accessibility_tree();
92}
93
94ErrorOr<ViewImplementation::DOMNodeProperties> ViewImplementation::inspect_dom_node(i32 node_id, Optional<Web::CSS::Selector::PseudoElement> pseudo_element)
95{
96 auto response = client().inspect_dom_node(node_id, pseudo_element);
97 if (!response.has_style())
98 return Error::from_string_view("Inspected node returned no style"sv);
99 return DOMNodeProperties {
100 .computed_style_json = TRY(String::from_deprecated_string(response.take_computed_style())),
101 .resolved_style_json = TRY(String::from_deprecated_string(response.take_resolved_style())),
102 .custom_properties_json = TRY(String::from_deprecated_string(response.take_custom_properties())),
103 .node_box_sizing_json = TRY(String::from_deprecated_string(response.take_node_box_sizing())),
104 };
105}
106
107void ViewImplementation::clear_inspected_dom_node()
108{
109 client().inspect_dom_node(0, {});
110}
111
112i32 ViewImplementation::get_hovered_node_id()
113{
114 return client().get_hovered_node_id();
115}
116
117void ViewImplementation::debug_request(DeprecatedString const& request, DeprecatedString const& argument)
118{
119 client().async_debug_request(request, argument);
120}
121
122void ViewImplementation::run_javascript(StringView js_source)
123{
124 client().async_run_javascript(js_source);
125}
126
127#if !defined(AK_OS_SERENITY)
128
129ErrorOr<NonnullRefPtr<WebView::WebContentClient>> ViewImplementation::launch_web_content_process(ReadonlySpan<String> candidate_web_content_paths, StringView webdriver_content_ipc_path)
130{
131 int socket_fds[2] {};
132 TRY(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fds));
133
134 int ui_fd = socket_fds[0];
135 int wc_fd = socket_fds[1];
136
137 int fd_passing_socket_fds[2] {};
138 TRY(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, fd_passing_socket_fds));
139
140 int ui_fd_passing_fd = fd_passing_socket_fds[0];
141 int wc_fd_passing_fd = fd_passing_socket_fds[1];
142
143 if (auto child_pid = TRY(Core::System::fork()); child_pid == 0) {
144 TRY(Core::System::close(ui_fd_passing_fd));
145 TRY(Core::System::close(ui_fd));
146
147 auto takeover_string = TRY(String::formatted("WebContent:{}", wc_fd));
148 TRY(Core::System::setenv("SOCKET_TAKEOVER"sv, takeover_string, true));
149
150 auto webcontent_fd_passing_socket_string = TRY(String::number(wc_fd_passing_fd));
151
152 Vector<StringView> arguments {
153 "WebContent"sv,
154 "--webcontent-fd-passing-socket"sv,
155 webcontent_fd_passing_socket_string
156 };
157
158 if (!webdriver_content_ipc_path.is_empty()) {
159 TRY(arguments.try_append("--webdriver-content-path"sv));
160 TRY(arguments.try_append(webdriver_content_ipc_path));
161 }
162
163 ErrorOr<void> result;
164 for (auto const& path : candidate_web_content_paths) {
165 result = Core::System::exec(path, arguments, Core::System::SearchInPath::Yes);
166 if (!result.is_error())
167 break;
168 }
169
170 if (result.is_error())
171 warnln("Could not launch any of {}: {}", candidate_web_content_paths, result.error());
172 VERIFY_NOT_REACHED();
173 }
174
175 TRY(Core::System::close(wc_fd_passing_fd));
176 TRY(Core::System::close(wc_fd));
177
178 auto socket = TRY(Core::LocalSocket::adopt_fd(ui_fd));
179 TRY(socket->set_blocking(true));
180
181 auto new_client = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) WebView::WebContentClient(move(socket), *this)));
182 new_client->set_fd_passing_socket(TRY(Core::LocalSocket::adopt_fd(ui_fd_passing_fd)));
183
184 return new_client;
185}
186
187#endif
188
189}