| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2022, Dex♪ <dexes.ttp@gmail.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <AK/Assertions.h>
 | 
					
						
							|  |  |  | #include <AK/ByteBuffer.h>
 | 
					
						
							|  |  |  | #include <AK/Format.h>
 | 
					
						
							|  |  |  | #include <AK/HashTable.h>
 | 
					
						
							| 
									
										
										
										
											2022-07-18 20:23:19 +02:00
										 |  |  | #include <AK/LexicalPath.h>
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | #include <AK/NonnullOwnPtr.h>
 | 
					
						
							|  |  |  | #include <AK/StringBuilder.h>
 | 
					
						
							|  |  |  | #include <AK/Types.h>
 | 
					
						
							|  |  |  | #include <LibCore/ArgsParser.h>
 | 
					
						
							| 
									
										
										
										
											2022-09-30 14:59:37 +08:00
										 |  |  | #include <LibCore/ConfigFile.h>
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | #include <LibCore/EventLoop.h>
 | 
					
						
							|  |  |  | #include <LibCore/File.h>
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  | #include <LibCore/MemoryStream.h>
 | 
					
						
							|  |  |  | #include <LibCore/Stream.h>
 | 
					
						
							| 
									
										
										
										
											2022-02-26 17:50:31 +01:00
										 |  |  | #include <LibCore/System.h>
 | 
					
						
							| 
									
										
										
										
											2022-11-21 20:03:45 -05:00
										 |  |  | #include <LibCore/SystemServerTakeover.h>
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | #include <LibCore/Timer.h>
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  | #include <LibGemini/GeminiRequest.h>
 | 
					
						
							|  |  |  | #include <LibGemini/GeminiResponse.h>
 | 
					
						
							|  |  |  | #include <LibGemini/Job.h>
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | #include <LibGfx/Bitmap.h>
 | 
					
						
							|  |  |  | #include <LibGfx/Font/FontDatabase.h>
 | 
					
						
							|  |  |  | #include <LibGfx/ImageDecoder.h>
 | 
					
						
							|  |  |  | #include <LibGfx/PNGWriter.h>
 | 
					
						
							|  |  |  | #include <LibGfx/Rect.h>
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  | #include <LibHTTP/HttpRequest.h>
 | 
					
						
							|  |  |  | #include <LibHTTP/HttpResponse.h>
 | 
					
						
							|  |  |  | #include <LibHTTP/HttpsJob.h>
 | 
					
						
							|  |  |  | #include <LibHTTP/Job.h>
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | #include <LibMain/Main.h>
 | 
					
						
							|  |  |  | #include <LibWeb/Cookie/ParsedCookie.h>
 | 
					
						
							|  |  |  | #include <LibWeb/DOM/Document.h>
 | 
					
						
							|  |  |  | #include <LibWeb/HTML/BrowsingContext.h>
 | 
					
						
							|  |  |  | #include <LibWeb/Layout/InitialContainingBlock.h>
 | 
					
						
							|  |  |  | #include <LibWeb/Loader/ResourceLoader.h>
 | 
					
						
							|  |  |  | #include <LibWeb/Page/Page.h>
 | 
					
						
							|  |  |  | #include <LibWeb/Painting/PaintableBox.h>
 | 
					
						
							| 
									
										
										
										
											2022-09-21 17:22:53 +01:00
										 |  |  | #include <LibWeb/Platform/EventLoopPluginSerenity.h>
 | 
					
						
							|  |  |  | #include <LibWeb/Platform/FontPluginSerenity.h>
 | 
					
						
							| 
									
										
										
										
											2022-09-16 15:01:47 +02:00
										 |  |  | #include <LibWeb/Platform/ImageCodecPlugin.h>
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | #include <LibWeb/WebSockets/WebSocket.h>
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  | #include <LibWebSocket/ConnectionInfo.h>
 | 
					
						
							|  |  |  | #include <LibWebSocket/Message.h>
 | 
					
						
							|  |  |  | #include <LibWebSocket/WebSocket.h>
 | 
					
						
							| 
									
										
										
										
											2022-11-21 20:03:45 -05:00
										 |  |  | #include <WebContent/WebDriverConnection.h>
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | class HeadlessBrowserPageClient final : public Web::PageClient { | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |     static NonnullOwnPtr<HeadlessBrowserPageClient> create() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return adopt_own(*new HeadlessBrowserPageClient()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-21 15:22:26 -05:00
										 |  |  |     virtual Web::Page& page() override { return *m_page; } | 
					
						
							|  |  |  |     virtual Web::Page const& page() const override { return *m_page; } | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Web::Layout::InitialContainingBlock* layout_root() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         auto* document = page().top_level_browsing_context().active_document(); | 
					
						
							|  |  |  |         if (!document) | 
					
						
							|  |  |  |             return nullptr; | 
					
						
							|  |  |  |         return document->layout_node(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void load(AK::URL const& url) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         page().load(url); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-25 17:07:19 +00:00
										 |  |  |     virtual void paint(Web::DevicePixelRect const& content_rect, Gfx::Bitmap& target) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							|  |  |  |         Gfx::Painter painter(target); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (auto* document = page().top_level_browsing_context().active_document()) | 
					
						
							|  |  |  |             document->update_layout(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-02 17:35:53 +00:00
										 |  |  |         painter.fill_rect({ {}, content_rect.size().to_type<int>() }, palette().base()); | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         auto* layout_root = this->layout_root(); | 
					
						
							|  |  |  |         if (!layout_root) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-26 20:53:41 +01:00
										 |  |  |         Web::PaintContext context(painter, palette(), device_pixels_per_css_pixel()); | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |         context.set_should_show_line_box_borders(false); | 
					
						
							| 
									
										
										
										
											2022-10-27 14:37:05 +01:00
										 |  |  |         context.set_device_viewport_rect(content_rect); | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |         context.set_has_focus(true); | 
					
						
							|  |  |  |         layout_root->paint_all_phases(context); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void setup_palette(Core::AnonymousBuffer theme_buffer) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         m_palette_impl = Gfx::PaletteImpl::create_with_anonymous_buffer(theme_buffer); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-18 18:44:22 +02:00
										 |  |  |     void set_viewport_rect(Gfx::IntRect viewport_rect) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-11-03 12:49:54 +00:00
										 |  |  |         page().top_level_browsing_context().set_viewport_rect(page().device_to_css_rect(viewport_rect.to_type<Web::DevicePixels>())); | 
					
						
							| 
									
										
										
										
											2022-07-18 18:44:22 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-25 17:07:19 +00:00
										 |  |  |     void set_screen_rect(Web::DevicePixelRect screen_rect) | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							|  |  |  |         m_screen_rect = screen_rect; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-21 20:03:45 -05:00
										 |  |  |     ErrorOr<void> connect_to_webdriver(StringView webdriver_ipc_path) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         VERIFY(!m_webdriver); | 
					
						
							|  |  |  |         m_webdriver = TRY(WebContent::WebDriverConnection::connect(*this, webdriver_ipc_path)); | 
					
						
							|  |  |  |         return {}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     // ^Web::PageClient
 | 
					
						
							| 
									
										
										
										
											2022-11-21 15:18:42 -05:00
										 |  |  |     virtual bool is_connection_open() const override | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-11-21 20:03:45 -05:00
										 |  |  |         if (m_webdriver) | 
					
						
							|  |  |  |             return m_webdriver->is_open(); | 
					
						
							| 
									
										
										
										
											2022-11-21 15:18:42 -05:00
										 |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     virtual Gfx::Palette palette() const override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return Gfx::Palette(*m_palette_impl); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-25 17:07:19 +00:00
										 |  |  |     virtual Web::DevicePixelRect screen_rect() const override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							|  |  |  |         return m_screen_rect; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-25 17:07:19 +00:00
										 |  |  |     virtual float device_pixels_per_css_pixel() const override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return 1.0f; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     virtual Web::CSS::PreferredColorScheme preferred_color_scheme() const override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return m_preferred_color_scheme; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     virtual void page_did_change_title(DeprecatedString const&) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-10 00:29:35 +01:00
										 |  |  |     virtual void page_did_start_loading(AK::URL const&, bool) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     virtual void page_did_finish_loading(AK::URL const&) override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     virtual void page_did_change_selection() override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     virtual void page_did_request_cursor_change(Gfx::StandardCursor) override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-02 17:35:53 +00:00
										 |  |  |     virtual void page_did_request_context_menu(Web::CSSPixelPoint) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-02 17:35:53 +00:00
										 |  |  |     virtual void page_did_request_link_context_menu(Web::CSSPixelPoint, AK::URL const&, DeprecatedString const&, unsigned) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-02 17:35:53 +00:00
										 |  |  |     virtual void page_did_request_image_context_menu(Web::CSSPixelPoint, AK::URL const&, DeprecatedString const&, unsigned, Gfx::Bitmap const*) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     virtual void page_did_click_link(AK::URL const&, DeprecatedString const&, unsigned) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     virtual void page_did_middle_click_link(AK::URL const&, DeprecatedString const&, unsigned) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-02 17:35:53 +00:00
										 |  |  |     virtual void page_did_enter_tooltip_area(Web::CSSPixelPoint, DeprecatedString const&) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     virtual void page_did_leave_tooltip_area() override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     virtual void page_did_hover_link(AK::URL const&) override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     virtual void page_did_unhover_link() override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-02 17:35:53 +00:00
										 |  |  |     virtual void page_did_invalidate(Web::CSSPixelRect const&) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     virtual void page_did_change_favicon(Gfx::Bitmap const&) override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     virtual void page_did_layout() override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-02 17:35:53 +00:00
										 |  |  |     virtual void page_did_request_scroll_into_view(Web::CSSPixelRect const&) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     virtual void page_did_request_alert(DeprecatedString const&) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     virtual void page_did_request_confirm(DeprecatedString const&) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     virtual void page_did_request_prompt(DeprecatedString const&, DeprecatedString const&) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     virtual DeprecatedString page_did_request_cookie(AK::URL const&, Web::Cookie::Source) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         return DeprecatedString::empty(); | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     virtual void page_did_set_cookie(AK::URL const&, Web::Cookie::ParsedCookie const&, Web::Cookie::Source) override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-26 17:50:31 +01:00
										 |  |  |     void request_file(NonnullRefPtr<Web::FileRequest>& request) override | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         auto const file = Core::System::open(request->path(), O_RDONLY); | 
					
						
							|  |  |  |         request->on_file_request_finish(file); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | private: | 
					
						
							|  |  |  |     HeadlessBrowserPageClient() | 
					
						
							|  |  |  |         : m_page(make<Web::Page>(*this)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NonnullOwnPtr<Web::Page> m_page; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     RefPtr<Gfx::PaletteImpl> m_palette_impl; | 
					
						
							| 
									
										
										
										
											2022-11-25 17:07:19 +00:00
										 |  |  |     Web::DevicePixelRect m_screen_rect { 0, 0, 800, 600 }; | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     Web::CSS::PreferredColorScheme m_preferred_color_scheme { Web::CSS::PreferredColorScheme::Auto }; | 
					
						
							| 
									
										
										
										
											2022-11-21 20:03:45 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     RefPtr<WebContent::WebDriverConnection> m_webdriver; | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 15:01:47 +02:00
										 |  |  | class ImageCodecPluginHeadless : public Web::Platform::ImageCodecPlugin { | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | public: | 
					
						
							| 
									
										
										
										
											2022-09-16 15:01:47 +02:00
										 |  |  |     ImageCodecPluginHeadless() = default; | 
					
						
							|  |  |  |     virtual ~ImageCodecPluginHeadless() override = default; | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 15:01:47 +02:00
										 |  |  |     virtual Optional<Web::Platform::DecodedImage> decode_image(ReadonlyBytes data) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2023-01-15 21:50:32 +02:00
										 |  |  |         auto decoder = Gfx::ImageDecoder::try_create_for_raw_bytes(data); | 
					
						
							| 
									
										
										
										
											2022-04-03 18:17:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (!decoder) | 
					
						
							| 
									
										
										
										
											2022-09-16 15:01:47 +02:00
										 |  |  |             return Web::Platform::DecodedImage { false, 0, Vector<Web::Platform::Frame> {} }; | 
					
						
							| 
									
										
										
										
											2022-04-03 18:17:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (!decoder->frame_count()) | 
					
						
							| 
									
										
										
										
											2022-09-16 15:01:47 +02:00
										 |  |  |             return Web::Platform::DecodedImage { false, 0, Vector<Web::Platform::Frame> {} }; | 
					
						
							| 
									
										
										
										
											2022-04-03 18:17:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 15:01:47 +02:00
										 |  |  |         Vector<Web::Platform::Frame> frames; | 
					
						
							| 
									
										
										
										
											2022-04-03 18:17:42 +02:00
										 |  |  |         for (size_t i = 0; i < decoder->frame_count(); ++i) { | 
					
						
							|  |  |  |             auto frame_or_error = decoder->frame(i); | 
					
						
							|  |  |  |             if (frame_or_error.is_error()) { | 
					
						
							|  |  |  |                 frames.append({ {}, 0 }); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 auto frame = frame_or_error.release_value(); | 
					
						
							|  |  |  |                 frames.append({ move(frame.image), static_cast<size_t>(frame.duration) }); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 15:01:47 +02:00
										 |  |  |         return Web::Platform::DecodedImage { | 
					
						
							| 
									
										
										
										
											2022-04-03 18:17:42 +02:00
										 |  |  |             decoder->is_animated(), | 
					
						
							|  |  |  |             static_cast<u32>(decoder->loop_count()), | 
					
						
							|  |  |  |             frames, | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  | static HashTable<RefPtr<Web::ResourceLoaderConnectorRequest>> s_all_requests; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | class HeadlessRequestServer : public Web::ResourceLoaderConnector { | 
					
						
							|  |  |  | public: | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |     class HTTPHeadlessRequest | 
					
						
							|  |  |  |         : public Web::ResourceLoaderConnectorRequest | 
					
						
							|  |  |  |         , public Weakable<HTTPHeadlessRequest> { | 
					
						
							|  |  |  |     public: | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         static ErrorOr<NonnullRefPtr<HTTPHeadlessRequest>> create(DeprecatedString const& method, AK::URL const& url, HashMap<DeprecatedString, DeprecatedString> const& request_headers, ReadonlyBytes request_body, Core::ProxyData const&) | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |         { | 
					
						
							|  |  |  |             auto stream_backing_buffer = TRY(ByteBuffer::create_uninitialized(1 * MiB)); | 
					
						
							|  |  |  |             auto underlying_socket = TRY(Core::Stream::TCPSocket::connect(url.host(), url.port().value_or(80))); | 
					
						
							|  |  |  |             TRY(underlying_socket->set_blocking(false)); | 
					
						
							|  |  |  |             auto socket = TRY(Core::Stream::BufferedSocket<Core::Stream::TCPSocket>::create(move(underlying_socket))); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             HTTP::HttpRequest request; | 
					
						
							|  |  |  |             if (method.equals_ignoring_case("head"sv)) | 
					
						
							|  |  |  |                 request.set_method(HTTP::HttpRequest::HEAD); | 
					
						
							|  |  |  |             else if (method.equals_ignoring_case("get"sv)) | 
					
						
							|  |  |  |                 request.set_method(HTTP::HttpRequest::GET); | 
					
						
							|  |  |  |             else if (method.equals_ignoring_case("post"sv)) | 
					
						
							|  |  |  |                 request.set_method(HTTP::HttpRequest::POST); | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |                 request.set_method(HTTP::HttpRequest::Invalid); | 
					
						
							|  |  |  |             request.set_url(move(url)); | 
					
						
							|  |  |  |             request.set_headers(request_headers); | 
					
						
							|  |  |  |             request.set_body(TRY(ByteBuffer::copy(request_body))); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return adopt_ref(*new HTTPHeadlessRequest(move(request), move(socket), move(stream_backing_buffer))); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         virtual ~HTTPHeadlessRequest() override | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         virtual void set_should_buffer_all_input(bool) override | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         virtual bool stop() override | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-22 05:09:11 +01:00
										 |  |  |         virtual void stream_into(AK::Stream&) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |         { | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private: | 
					
						
							|  |  |  |         HTTPHeadlessRequest(HTTP::HttpRequest&& request, NonnullOwnPtr<Core::Stream::BufferedSocketBase> socket, ByteBuffer&& stream_backing_buffer) | 
					
						
							|  |  |  |             : m_stream_backing_buffer(move(stream_backing_buffer)) | 
					
						
							| 
									
										
										
										
											2022-12-07 15:47:44 +01:00
										 |  |  |             , m_output_stream(Core::Stream::FixedMemoryStream::construct(m_stream_backing_buffer.bytes()).release_value_but_fixme_should_propagate_errors()) | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |             , m_socket(move(socket)) | 
					
						
							|  |  |  |             , m_job(HTTP::Job::construct(move(request), *m_output_stream)) | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2022-11-19 01:09:53 +00:00
										 |  |  |             m_job->on_headers_received = [weak_this = make_weak_ptr()](auto& response_headers, auto response_code) { | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |                 if (auto strong_this = weak_this.strong_ref()) { | 
					
						
							|  |  |  |                     strong_this->m_response_code = response_code; | 
					
						
							|  |  |  |                     for (auto& header : response_headers) { | 
					
						
							|  |  |  |                         strong_this->m_response_headers.set(header.key, header.value); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }; | 
					
						
							| 
									
										
										
										
											2022-11-19 01:09:53 +00:00
										 |  |  |             m_job->on_finish = [weak_this = make_weak_ptr()](bool success) { | 
					
						
							|  |  |  |                 Core::deferred_invoke([weak_this, success] { | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |                     if (auto strong_this = weak_this.strong_ref()) { | 
					
						
							|  |  |  |                         ReadonlyBytes response_bytes { strong_this->m_output_stream->bytes().data(), strong_this->m_output_stream->offset() }; | 
					
						
							|  |  |  |                         auto response_buffer = ByteBuffer::copy(response_bytes).release_value_but_fixme_should_propagate_errors(); | 
					
						
							|  |  |  |                         strong_this->on_buffered_request_finish(success, strong_this->m_output_stream->offset(), strong_this->m_response_headers, strong_this->m_response_code, response_buffer); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 }); | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |             m_job->start(*m_socket); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Optional<u32> m_response_code; | 
					
						
							|  |  |  |         ByteBuffer m_stream_backing_buffer; | 
					
						
							| 
									
										
										
										
											2022-12-07 15:47:44 +01:00
										 |  |  |         NonnullOwnPtr<Core::Stream::FixedMemoryStream> m_output_stream; | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |         NonnullOwnPtr<Core::Stream::BufferedSocketBase> m_socket; | 
					
						
							|  |  |  |         NonnullRefPtr<HTTP::Job> m_job; | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         HashMap<DeprecatedString, DeprecatedString, CaseInsensitiveStringTraits> m_response_headers; | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     class HTTPSHeadlessRequest | 
					
						
							|  |  |  |         : public Web::ResourceLoaderConnectorRequest | 
					
						
							|  |  |  |         , public Weakable<HTTPSHeadlessRequest> { | 
					
						
							|  |  |  |     public: | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         static ErrorOr<NonnullRefPtr<HTTPSHeadlessRequest>> create(DeprecatedString const& method, AK::URL const& url, HashMap<DeprecatedString, DeprecatedString> const& request_headers, ReadonlyBytes request_body, Core::ProxyData const&) | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |         { | 
					
						
							|  |  |  |             auto stream_backing_buffer = TRY(ByteBuffer::create_uninitialized(1 * MiB)); | 
					
						
							| 
									
										
										
										
											2022-07-18 18:39:09 +02:00
										 |  |  |             auto underlying_socket = TRY(TLS::TLSv12::connect(url.host(), url.port().value_or(443))); | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |             TRY(underlying_socket->set_blocking(false)); | 
					
						
							|  |  |  |             auto socket = TRY(Core::Stream::BufferedSocket<TLS::TLSv12>::create(move(underlying_socket))); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             HTTP::HttpRequest request; | 
					
						
							|  |  |  |             if (method.equals_ignoring_case("head"sv)) | 
					
						
							|  |  |  |                 request.set_method(HTTP::HttpRequest::HEAD); | 
					
						
							|  |  |  |             else if (method.equals_ignoring_case("get"sv)) | 
					
						
							|  |  |  |                 request.set_method(HTTP::HttpRequest::GET); | 
					
						
							|  |  |  |             else if (method.equals_ignoring_case("post"sv)) | 
					
						
							|  |  |  |                 request.set_method(HTTP::HttpRequest::POST); | 
					
						
							|  |  |  |             else | 
					
						
							|  |  |  |                 request.set_method(HTTP::HttpRequest::Invalid); | 
					
						
							|  |  |  |             request.set_url(move(url)); | 
					
						
							|  |  |  |             request.set_headers(request_headers); | 
					
						
							|  |  |  |             request.set_body(TRY(ByteBuffer::copy(request_body))); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return adopt_ref(*new HTTPSHeadlessRequest(move(request), move(socket), move(stream_backing_buffer))); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         virtual ~HTTPSHeadlessRequest() override | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         virtual void set_should_buffer_all_input(bool) override | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         virtual bool stop() override | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-22 05:09:11 +01:00
										 |  |  |         virtual void stream_into(AK::Stream&) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |         { | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private: | 
					
						
							|  |  |  |         HTTPSHeadlessRequest(HTTP::HttpRequest&& request, NonnullOwnPtr<Core::Stream::BufferedSocketBase> socket, ByteBuffer&& stream_backing_buffer) | 
					
						
							|  |  |  |             : m_stream_backing_buffer(move(stream_backing_buffer)) | 
					
						
							| 
									
										
										
										
											2022-12-07 15:47:44 +01:00
										 |  |  |             , m_output_stream(Core::Stream::FixedMemoryStream::construct(m_stream_backing_buffer.bytes()).release_value_but_fixme_should_propagate_errors()) | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |             , m_socket(move(socket)) | 
					
						
							|  |  |  |             , m_job(HTTP::HttpsJob::construct(move(request), *m_output_stream)) | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2022-11-19 01:09:53 +00:00
										 |  |  |             m_job->on_headers_received = [weak_this = make_weak_ptr()](auto& response_headers, auto response_code) { | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |                 if (auto strong_this = weak_this.strong_ref()) { | 
					
						
							|  |  |  |                     strong_this->m_response_code = response_code; | 
					
						
							|  |  |  |                     for (auto& header : response_headers) { | 
					
						
							|  |  |  |                         strong_this->m_response_headers.set(header.key, header.value); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }; | 
					
						
							| 
									
										
										
										
											2022-11-19 01:09:53 +00:00
										 |  |  |             m_job->on_finish = [weak_this = make_weak_ptr()](bool success) { | 
					
						
							|  |  |  |                 Core::deferred_invoke([weak_this, success] { | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |                     if (auto strong_this = weak_this.strong_ref()) { | 
					
						
							|  |  |  |                         ReadonlyBytes response_bytes { strong_this->m_output_stream->bytes().data(), strong_this->m_output_stream->offset() }; | 
					
						
							|  |  |  |                         auto response_buffer = ByteBuffer::copy(response_bytes).release_value_but_fixme_should_propagate_errors(); | 
					
						
							|  |  |  |                         strong_this->on_buffered_request_finish(success, strong_this->m_output_stream->offset(), strong_this->m_response_headers, strong_this->m_response_code, response_buffer); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 }); | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |             m_job->start(*m_socket); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Optional<u32> m_response_code; | 
					
						
							|  |  |  |         ByteBuffer m_stream_backing_buffer; | 
					
						
							| 
									
										
										
										
											2022-12-07 15:47:44 +01:00
										 |  |  |         NonnullOwnPtr<Core::Stream::FixedMemoryStream> m_output_stream; | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |         NonnullOwnPtr<Core::Stream::BufferedSocketBase> m_socket; | 
					
						
							|  |  |  |         NonnullRefPtr<HTTP::HttpsJob> m_job; | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         HashMap<DeprecatedString, DeprecatedString, CaseInsensitiveStringTraits> m_response_headers; | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     class GeminiHeadlessRequest | 
					
						
							|  |  |  |         : public Web::ResourceLoaderConnectorRequest | 
					
						
							|  |  |  |         , public Weakable<GeminiHeadlessRequest> { | 
					
						
							|  |  |  |     public: | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         static ErrorOr<NonnullRefPtr<GeminiHeadlessRequest>> create(DeprecatedString const&, AK::URL const& url, HashMap<DeprecatedString, DeprecatedString> const&, ReadonlyBytes, Core::ProxyData const&) | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |         { | 
					
						
							|  |  |  |             auto stream_backing_buffer = TRY(ByteBuffer::create_uninitialized(1 * MiB)); | 
					
						
							|  |  |  |             auto underlying_socket = TRY(Core::Stream::TCPSocket::connect(url.host(), url.port().value_or(80))); | 
					
						
							|  |  |  |             TRY(underlying_socket->set_blocking(false)); | 
					
						
							|  |  |  |             auto socket = TRY(Core::Stream::BufferedSocket<Core::Stream::TCPSocket>::create(move(underlying_socket))); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             Gemini::GeminiRequest request; | 
					
						
							|  |  |  |             request.set_url(url); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             return adopt_ref(*new GeminiHeadlessRequest(move(request), move(socket), move(stream_backing_buffer))); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         virtual ~GeminiHeadlessRequest() override | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         virtual void set_should_buffer_all_input(bool) override | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         virtual bool stop() override | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-22 05:09:11 +01:00
										 |  |  |         virtual void stream_into(AK::Stream&) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |         { | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private: | 
					
						
							|  |  |  |         GeminiHeadlessRequest(Gemini::GeminiRequest&& request, NonnullOwnPtr<Core::Stream::BufferedSocketBase> socket, ByteBuffer&& stream_backing_buffer) | 
					
						
							|  |  |  |             : m_stream_backing_buffer(move(stream_backing_buffer)) | 
					
						
							| 
									
										
										
										
											2022-12-07 15:47:44 +01:00
										 |  |  |             , m_output_stream(Core::Stream::FixedMemoryStream::construct(m_stream_backing_buffer.bytes()).release_value_but_fixme_should_propagate_errors()) | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |             , m_socket(move(socket)) | 
					
						
							|  |  |  |             , m_job(Gemini::Job::construct(move(request), *m_output_stream)) | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2022-11-19 01:09:53 +00:00
										 |  |  |             m_job->on_headers_received = [weak_this = make_weak_ptr()](auto& response_headers, auto response_code) { | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |                 if (auto strong_this = weak_this.strong_ref()) { | 
					
						
							|  |  |  |                     strong_this->m_response_code = response_code; | 
					
						
							|  |  |  |                     for (auto& header : response_headers) { | 
					
						
							|  |  |  |                         strong_this->m_response_headers.set(header.key, header.value); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }; | 
					
						
							| 
									
										
										
										
											2022-11-19 01:09:53 +00:00
										 |  |  |             m_job->on_finish = [weak_this = make_weak_ptr()](bool success) { | 
					
						
							|  |  |  |                 Core::deferred_invoke([weak_this, success] { | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |                     if (auto strong_this = weak_this.strong_ref()) { | 
					
						
							|  |  |  |                         ReadonlyBytes response_bytes { strong_this->m_output_stream->bytes().data(), strong_this->m_output_stream->offset() }; | 
					
						
							|  |  |  |                         auto response_buffer = ByteBuffer::copy(response_bytes).release_value_but_fixme_should_propagate_errors(); | 
					
						
							|  |  |  |                         strong_this->on_buffered_request_finish(success, strong_this->m_output_stream->offset(), strong_this->m_response_headers, strong_this->m_response_code, response_buffer); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 }); | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |             m_job->start(*m_socket); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Optional<u32> m_response_code; | 
					
						
							|  |  |  |         ByteBuffer m_stream_backing_buffer; | 
					
						
							| 
									
										
										
										
											2022-12-07 15:47:44 +01:00
										 |  |  |         NonnullOwnPtr<Core::Stream::FixedMemoryStream> m_output_stream; | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |         NonnullOwnPtr<Core::Stream::BufferedSocketBase> m_socket; | 
					
						
							|  |  |  |         NonnullRefPtr<Gemini::Job> m_job; | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         HashMap<DeprecatedString, DeprecatedString, CaseInsensitiveStringTraits> m_response_headers; | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     static NonnullRefPtr<HeadlessRequestServer> create() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return adopt_ref(*new HeadlessRequestServer()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     virtual ~HeadlessRequestServer() override { } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     virtual void prefetch_dns(AK::URL const&) override { } | 
					
						
							|  |  |  |     virtual void preconnect(AK::URL const&) override { } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     virtual RefPtr<Web::ResourceLoaderConnectorRequest> start_request(DeprecatedString const& method, AK::URL const& url, HashMap<DeprecatedString, DeprecatedString> const& request_headers, ReadonlyBytes request_body, Core::ProxyData const& proxy) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |         RefPtr<Web::ResourceLoaderConnectorRequest> request; | 
					
						
							| 
									
										
										
										
											2022-09-29 01:30:58 +02:00
										 |  |  |         if (url.scheme().equals_ignoring_case("http"sv)) { | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |             auto request_or_error = HTTPHeadlessRequest::create(method, url, request_headers, request_body, proxy); | 
					
						
							|  |  |  |             if (request_or_error.is_error()) | 
					
						
							|  |  |  |                 return {}; | 
					
						
							|  |  |  |             request = request_or_error.release_value(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-09-29 01:30:58 +02:00
										 |  |  |         if (url.scheme().equals_ignoring_case("https"sv)) { | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |             auto request_or_error = HTTPSHeadlessRequest::create(method, url, request_headers, request_body, proxy); | 
					
						
							|  |  |  |             if (request_or_error.is_error()) | 
					
						
							|  |  |  |                 return {}; | 
					
						
							|  |  |  |             request = request_or_error.release_value(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-09-29 01:30:58 +02:00
										 |  |  |         if (url.scheme().equals_ignoring_case("gemini"sv)) { | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |             auto request_or_error = GeminiHeadlessRequest::create(method, url, request_headers, request_body, proxy); | 
					
						
							|  |  |  |             if (request_or_error.is_error()) | 
					
						
							|  |  |  |                 return {}; | 
					
						
							|  |  |  |             request = request_or_error.release_value(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (request) | 
					
						
							|  |  |  |             s_all_requests.set(request); | 
					
						
							|  |  |  |         return request; | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							|  |  |  |     HeadlessRequestServer() { } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class HeadlessWebSocketClientManager : public Web::WebSockets::WebSocketClientManager { | 
					
						
							|  |  |  | public: | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |     class HeadlessWebSocket | 
					
						
							|  |  |  |         : public Web::WebSockets::WebSocketClientSocket | 
					
						
							|  |  |  |         , public Weakable<HeadlessWebSocket> { | 
					
						
							|  |  |  |     public: | 
					
						
							|  |  |  |         static NonnullRefPtr<HeadlessWebSocket> create(NonnullRefPtr<WebSocket::WebSocket> underlying_socket) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             return adopt_ref(*new HeadlessWebSocket(move(underlying_socket))); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         virtual ~HeadlessWebSocket() override | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         virtual Web::WebSockets::WebSocket::ReadyState ready_state() override | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             switch (m_websocket->ready_state()) { | 
					
						
							|  |  |  |             case WebSocket::ReadyState::Connecting: | 
					
						
							|  |  |  |                 return Web::WebSockets::WebSocket::ReadyState::Connecting; | 
					
						
							|  |  |  |             case WebSocket::ReadyState::Open: | 
					
						
							|  |  |  |                 return Web::WebSockets::WebSocket::ReadyState::Open; | 
					
						
							|  |  |  |             case WebSocket::ReadyState::Closing: | 
					
						
							|  |  |  |                 return Web::WebSockets::WebSocket::ReadyState::Closing; | 
					
						
							|  |  |  |             case WebSocket::ReadyState::Closed: | 
					
						
							|  |  |  |                 return Web::WebSockets::WebSocket::ReadyState::Closed; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             VERIFY_NOT_REACHED(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         virtual void send(ByteBuffer binary_or_text_message, bool is_text) override | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             m_websocket->send(WebSocket::Message(binary_or_text_message, is_text)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         virtual void send(StringView message) override | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             m_websocket->send(WebSocket::Message(message)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |         virtual void close(u16 code, DeprecatedString reason) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |         { | 
					
						
							|  |  |  |             m_websocket->close(code, reason); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private: | 
					
						
							|  |  |  |         HeadlessWebSocket(NonnullRefPtr<WebSocket::WebSocket> underlying_socket) | 
					
						
							|  |  |  |             : m_websocket(move(underlying_socket)) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             m_websocket->on_open = [weak_this = make_weak_ptr()] { | 
					
						
							|  |  |  |                 if (auto strong_this = weak_this.strong_ref()) | 
					
						
							|  |  |  |                     if (strong_this->on_open) | 
					
						
							|  |  |  |                         strong_this->on_open(); | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |             m_websocket->on_message = [weak_this = make_weak_ptr()](auto message) { | 
					
						
							|  |  |  |                 if (auto strong_this = weak_this.strong_ref()) { | 
					
						
							|  |  |  |                     if (strong_this->on_message) { | 
					
						
							|  |  |  |                         strong_this->on_message(Web::WebSockets::WebSocketClientSocket::Message { | 
					
						
							|  |  |  |                             .data = move(message.data()), | 
					
						
							|  |  |  |                             .is_text = message.is_text(), | 
					
						
							|  |  |  |                         }); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |             m_websocket->on_error = [weak_this = make_weak_ptr()](auto error) { | 
					
						
							|  |  |  |                 if (auto strong_this = weak_this.strong_ref()) { | 
					
						
							|  |  |  |                     if (strong_this->on_error) { | 
					
						
							|  |  |  |                         switch (error) { | 
					
						
							|  |  |  |                         case WebSocket::WebSocket::Error::CouldNotEstablishConnection: | 
					
						
							|  |  |  |                             strong_this->on_error(Web::WebSockets::WebSocketClientSocket::Error::CouldNotEstablishConnection); | 
					
						
							|  |  |  |                             return; | 
					
						
							|  |  |  |                         case WebSocket::WebSocket::Error::ConnectionUpgradeFailed: | 
					
						
							|  |  |  |                             strong_this->on_error(Web::WebSockets::WebSocketClientSocket::Error::ConnectionUpgradeFailed); | 
					
						
							|  |  |  |                             return; | 
					
						
							|  |  |  |                         case WebSocket::WebSocket::Error::ServerClosedSocket: | 
					
						
							|  |  |  |                             strong_this->on_error(Web::WebSockets::WebSocketClientSocket::Error::ServerClosedSocket); | 
					
						
							|  |  |  |                             return; | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                         VERIFY_NOT_REACHED(); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             }; | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |             m_websocket->on_close = [weak_this = make_weak_ptr()](u16 code, DeprecatedString reason, bool was_clean) { | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |                 if (auto strong_this = weak_this.strong_ref()) | 
					
						
							|  |  |  |                     if (strong_this->on_close) | 
					
						
							|  |  |  |                         strong_this->on_close(code, move(reason), was_clean); | 
					
						
							|  |  |  |             }; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         NonnullRefPtr<WebSocket::WebSocket> m_websocket; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     static NonnullRefPtr<HeadlessWebSocketClientManager> create() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return adopt_ref(*new HeadlessWebSocketClientManager()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     virtual ~HeadlessWebSocketClientManager() override { } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |     virtual RefPtr<Web::WebSockets::WebSocketClientSocket> connect(AK::URL const& url, DeprecatedString const& origin) override | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-04-03 18:20:05 +02:00
										 |  |  |         WebSocket::ConnectionInfo connection_info(url); | 
					
						
							|  |  |  |         connection_info.set_origin(origin); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         auto connection = HeadlessWebSocket::create(WebSocket::WebSocket::create(move(connection_info))); | 
					
						
							|  |  |  |         return connection; | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							|  |  |  |     HeadlessWebSocketClientManager() { } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-21 20:03:45 -05:00
										 |  |  | static void load_page_for_screenshot_and_exit(HeadlessBrowserPageClient& page_client, int take_screenshot_after) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     dbgln("Taking screenshot after {} seconds", take_screenshot_after); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto timer = Core::Timer::create_single_shot( | 
					
						
							|  |  |  |         take_screenshot_after * 1000, | 
					
						
							|  |  |  |         [&]() { | 
					
						
							|  |  |  |             // FIXME: Allow passing the output path as argument
 | 
					
						
							| 
									
										
										
										
											2022-12-04 18:02:33 +00:00
										 |  |  |             DeprecatedString output_file_path = "output.png"; | 
					
						
							| 
									
										
										
										
											2022-11-21 20:03:45 -05:00
										 |  |  |             dbgln("Saving to {}", output_file_path); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (Core::File::exists(output_file_path)) | 
					
						
							| 
									
										
										
										
											2022-12-23 14:19:42 +01:00
										 |  |  |                 MUST(Core::File::remove(output_file_path, Core::File::RecursionMode::Disallowed)); | 
					
						
							| 
									
										
										
										
											2022-11-21 20:03:45 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |             auto output_file = MUST(Core::Stream::File::open(output_file_path, Core::Stream::OpenMode::Write)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             auto output_rect = page_client.screen_rect(); | 
					
						
							| 
									
										
										
										
											2023-01-20 20:06:05 +01:00
										 |  |  |             auto output_bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, output_rect.size().to_type<int>())); | 
					
						
							| 
									
										
										
										
											2022-11-21 20:03:45 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |             page_client.paint(output_rect, output_bitmap); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-05 20:34:27 +01:00
										 |  |  |             auto image_buffer = MUST(Gfx::PNGWriter::encode(output_bitmap)); | 
					
						
							| 
									
										
										
										
											2022-11-21 20:03:45 -05:00
										 |  |  |             MUST(output_file->write(image_buffer.bytes())); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             exit(0); | 
					
						
							| 
									
										
										
										
											2023-01-11 16:31:10 +00:00
										 |  |  |         }).release_value_but_fixme_should_propagate_errors(); | 
					
						
							| 
									
										
										
										
											2022-11-21 20:03:45 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     timer->start(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | ErrorOr<int> serenity_main(Main::Arguments arguments) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int take_screenshot_after = 1; | 
					
						
							|  |  |  |     StringView url; | 
					
						
							| 
									
										
										
										
											2022-07-18 20:23:19 +02:00
										 |  |  |     StringView resources_folder; | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     StringView error_page_url; | 
					
						
							| 
									
										
										
										
											2022-09-30 14:59:37 +08:00
										 |  |  |     StringView ca_certs_path; | 
					
						
							| 
									
										
										
										
											2022-11-21 20:03:45 -05:00
										 |  |  |     StringView webdriver_ipc_path; | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Core::EventLoop event_loop; | 
					
						
							|  |  |  |     Core::ArgsParser args_parser; | 
					
						
							|  |  |  |     args_parser.set_general_help("This utility runs the Browser in headless mode."); | 
					
						
							|  |  |  |     args_parser.add_option(take_screenshot_after, "Take a screenshot after [n] seconds (default: 1)", "screenshot", 's', "n"); | 
					
						
							| 
									
										
										
										
											2022-07-18 20:23:19 +02:00
										 |  |  |     args_parser.add_option(resources_folder, "Path of the base resources folder (defaults to /res)", "resources", 'r', "resources-root-path"); | 
					
						
							|  |  |  |     args_parser.add_option(error_page_url, "URL for the error page (defaults to file:///res/html/error.html)", "error-page", 'e', "error-page-url"); | 
					
						
							| 
									
										
										
										
											2022-09-30 14:59:37 +08:00
										 |  |  |     args_parser.add_option(ca_certs_path, "The bundled ca certificates file", "certs", 'c', "ca-certs-path"); | 
					
						
							| 
									
										
										
										
											2022-11-21 20:03:45 -05:00
										 |  |  |     args_parser.add_option(webdriver_ipc_path, "Path to the WebDriver IPC socket", "webdriver-ipc-path", 0, "path"); | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     args_parser.add_positional_argument(url, "URL to open", "url", Core::ArgsParser::Required::Yes); | 
					
						
							|  |  |  |     args_parser.parse(arguments); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-21 17:22:53 +01:00
										 |  |  |     Web::Platform::EventLoopPlugin::install(*new Web::Platform::EventLoopPluginSerenity); | 
					
						
							|  |  |  |     Web::Platform::FontPlugin::install(*new Web::Platform::FontPluginSerenity); | 
					
						
							| 
									
										
										
										
											2022-09-16 15:01:47 +02:00
										 |  |  |     Web::Platform::ImageCodecPlugin::install(*new ImageCodecPluginHeadless); | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     Web::ResourceLoader::initialize(HeadlessRequestServer::create()); | 
					
						
							|  |  |  |     Web::WebSockets::WebSocketClientManager::initialize(HeadlessWebSocketClientManager::create()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-18 20:23:19 +02:00
										 |  |  |     if (!resources_folder.is_empty()) { | 
					
						
							|  |  |  |         Web::FrameLoader::set_default_favicon_path(LexicalPath::join(resources_folder, "icons/16x16/app-browser.png"sv).string()); | 
					
						
							|  |  |  |         Gfx::FontDatabase::set_default_fonts_lookup_path(LexicalPath::join(resources_folder, "fonts"sv).string()); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-30 14:59:37 +08:00
										 |  |  |     if (!ca_certs_path.is_empty()) { | 
					
						
							|  |  |  |         auto config_result = Core::ConfigFile::open(ca_certs_path); | 
					
						
							|  |  |  |         if (config_result.is_error()) { | 
					
						
							|  |  |  |             dbgln("Failed to load CA Certificates: {}", config_result.error()); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             auto config = config_result.release_value(); | 
					
						
							|  |  |  |             DefaultRootCACertificates::the().reload_certificates(config); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Gfx::FontDatabase::set_default_font_query("Katica 10 400 0"); | 
					
						
							| 
									
										
										
										
											2022-07-31 18:41:07 +02:00
										 |  |  |     Gfx::FontDatabase::set_window_title_font_query("Katica 10 700 0"); | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     Gfx::FontDatabase::set_fixed_width_font_query("Csilla 10 400 0"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!error_page_url.is_empty()) | 
					
						
							|  |  |  |         Web::FrameLoader::set_error_page_url(error_page_url); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto page_client = HeadlessBrowserPageClient::create(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-06 16:26:13 +00:00
										 |  |  |     if (!resources_folder.is_empty()) { | 
					
						
							|  |  |  |         auto system_theme = TRY(Gfx::load_system_theme(LexicalPath::join(resources_folder, "themes/Default.ini"sv).string())); | 
					
						
							|  |  |  |         page_client->setup_palette(system_theme); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         auto system_theme = TRY(Gfx::load_system_theme("/res/themes/Default.ini")); | 
					
						
							|  |  |  |         page_client->setup_palette(system_theme); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     dbgln("Loading {}", url); | 
					
						
							|  |  |  |     page_client->load(AK::URL(url)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // FIXME: Allow passing these values as arguments
 | 
					
						
							| 
									
										
										
										
											2022-07-18 18:44:22 +02:00
										 |  |  |     page_client->set_viewport_rect({ 0, 0, 800, 600 }); | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  |     page_client->set_screen_rect({ 0, 0, 800, 600 }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-21 20:03:45 -05:00
										 |  |  |     if (!webdriver_ipc_path.is_empty()) | 
					
						
							|  |  |  |         TRY(page_client->connect_to_webdriver(webdriver_ipc_path)); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |         load_page_for_screenshot_and_exit(*page_client, take_screenshot_after); | 
					
						
							| 
									
										
										
										
											2022-04-03 18:15:36 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return event_loop.exec(); | 
					
						
							|  |  |  | } |