HeadlessWebView.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. /*
  2. * Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibGfx/Bitmap.h>
  7. #include <LibGfx/ShareableBitmap.h>
  8. #include <LibWeb/Crypto/Crypto.h>
  9. #include <LibWebView/HelperProcess.h>
  10. #include <LibWebView/Utilities.h>
  11. #include <UI/Headless/Application.h>
  12. #include <UI/Headless/HeadlessWebView.h>
  13. namespace Ladybird {
  14. static Web::DevicePixelRect const screen_rect { 0, 0, 1920, 1080 };
  15. HeadlessWebView::HeadlessWebView(Core::AnonymousBuffer theme, Web::DevicePixelSize viewport_size)
  16. : m_theme(move(theme))
  17. , m_viewport_size(viewport_size)
  18. , m_test_promise(TestPromise::construct())
  19. {
  20. on_new_web_view = [this](auto, auto, Optional<u64> page_index) {
  21. if (page_index.has_value()) {
  22. auto& web_view = Application::the().create_child_web_view(*this, *page_index);
  23. return web_view.handle();
  24. }
  25. auto& web_view = Application::the().create_web_view(m_theme, m_viewport_size);
  26. return web_view.handle();
  27. };
  28. on_request_worker_agent = []() {
  29. auto web_worker_paths = MUST(WebView::get_paths_for_helper_process("WebWorker"sv));
  30. auto worker_client = MUST(WebView::launch_web_worker_process(web_worker_paths));
  31. return worker_client->clone_transport();
  32. };
  33. on_reposition_window = [this](auto position) {
  34. client().async_set_window_position(m_client_state.page_index, position.template to_type<Web::DevicePixels>());
  35. client().async_did_update_window_rect(m_client_state.page_index);
  36. };
  37. on_resize_window = [this](auto size) {
  38. m_viewport_size = size.template to_type<Web::DevicePixels>();
  39. client().async_set_window_size(m_client_state.page_index, m_viewport_size);
  40. client().async_set_viewport_size(m_client_state.page_index, m_viewport_size);
  41. client().async_did_update_window_rect(m_client_state.page_index);
  42. };
  43. on_restore_window = [this]() {
  44. set_system_visibility_state(Web::HTML::VisibilityState::Visible);
  45. };
  46. on_minimize_window = [this]() {
  47. set_system_visibility_state(Web::HTML::VisibilityState::Hidden);
  48. };
  49. on_maximize_window = [this]() {
  50. m_viewport_size = screen_rect.size();
  51. client().async_set_window_position(m_client_state.page_index, screen_rect.location());
  52. client().async_set_window_size(m_client_state.page_index, screen_rect.size());
  53. client().async_set_viewport_size(m_client_state.page_index, screen_rect.size());
  54. client().async_did_update_window_rect(m_client_state.page_index);
  55. };
  56. on_fullscreen_window = [this]() {
  57. m_viewport_size = screen_rect.size();
  58. client().async_set_window_position(m_client_state.page_index, screen_rect.location());
  59. client().async_set_window_size(m_client_state.page_index, screen_rect.size());
  60. client().async_set_viewport_size(m_client_state.page_index, screen_rect.size());
  61. client().async_did_update_window_rect(m_client_state.page_index);
  62. };
  63. on_request_alert = [this](auto const&) {
  64. m_pending_dialog = Web::Page::PendingDialog::Alert;
  65. };
  66. on_request_confirm = [this](auto const&) {
  67. m_pending_dialog = Web::Page::PendingDialog::Confirm;
  68. };
  69. on_request_prompt = [this](auto const&, auto const& prompt_text) {
  70. m_pending_dialog = Web::Page::PendingDialog::Prompt;
  71. m_pending_prompt_text = prompt_text;
  72. };
  73. on_request_set_prompt_text = [this](auto const& prompt_text) {
  74. m_pending_prompt_text = prompt_text;
  75. };
  76. on_request_accept_dialog = [this]() {
  77. switch (m_pending_dialog) {
  78. case Web::Page::PendingDialog::None:
  79. VERIFY_NOT_REACHED();
  80. break;
  81. case Web::Page::PendingDialog::Alert:
  82. alert_closed();
  83. break;
  84. case Web::Page::PendingDialog::Confirm:
  85. confirm_closed(true);
  86. break;
  87. case Web::Page::PendingDialog::Prompt:
  88. prompt_closed(move(m_pending_prompt_text));
  89. break;
  90. }
  91. m_pending_dialog = Web::Page::PendingDialog::None;
  92. };
  93. on_request_dismiss_dialog = [this]() {
  94. switch (m_pending_dialog) {
  95. case Web::Page::PendingDialog::None:
  96. VERIFY_NOT_REACHED();
  97. break;
  98. case Web::Page::PendingDialog::Alert:
  99. alert_closed();
  100. break;
  101. case Web::Page::PendingDialog::Confirm:
  102. confirm_closed(false);
  103. break;
  104. case Web::Page::PendingDialog::Prompt:
  105. prompt_closed({});
  106. break;
  107. }
  108. m_pending_dialog = Web::Page::PendingDialog::None;
  109. m_pending_prompt_text.clear();
  110. };
  111. m_system_visibility_state = Web::HTML::VisibilityState::Visible;
  112. }
  113. NonnullOwnPtr<HeadlessWebView> HeadlessWebView::create(Core::AnonymousBuffer theme, Web::DevicePixelSize window_size)
  114. {
  115. auto view = adopt_own(*new HeadlessWebView(move(theme), window_size));
  116. view->initialize_client(CreateNewClient::Yes);
  117. return view;
  118. }
  119. NonnullOwnPtr<HeadlessWebView> HeadlessWebView::create_child(HeadlessWebView const& parent, u64 page_index)
  120. {
  121. auto view = adopt_own(*new HeadlessWebView(parent.m_theme, parent.m_viewport_size));
  122. view->m_client_state.client = parent.client();
  123. view->m_client_state.page_index = page_index;
  124. view->initialize_client(CreateNewClient::No);
  125. return view;
  126. }
  127. void HeadlessWebView::initialize_client(CreateNewClient create_new_client)
  128. {
  129. if (create_new_client == CreateNewClient::Yes) {
  130. auto request_server_socket = WebView::connect_new_request_server_client().release_value_but_fixme_should_propagate_errors();
  131. auto image_decoder_socket = WebView::connect_new_image_decoder_client().release_value_but_fixme_should_propagate_errors();
  132. auto web_content_paths = WebView::get_paths_for_helper_process("WebContent"sv).release_value_but_fixme_should_propagate_errors();
  133. m_client_state.client = WebView::launch_web_content_process(*this, web_content_paths, move(image_decoder_socket), move(request_server_socket)).release_value_but_fixme_should_propagate_errors();
  134. } else {
  135. m_client_state.client->register_view(m_client_state.page_index, *this);
  136. }
  137. m_client_state.client_handle = MUST(Web::Crypto::generate_random_uuid());
  138. client().async_set_window_handle(m_client_state.page_index, m_client_state.client_handle);
  139. client().async_update_system_theme(m_client_state.page_index, m_theme);
  140. client().async_set_viewport_size(m_client_state.page_index, viewport_size());
  141. client().async_set_window_size(m_client_state.page_index, viewport_size());
  142. client().async_update_screen_rects(m_client_state.page_index, { screen_rect }, 0);
  143. set_system_visibility_state(m_system_visibility_state);
  144. if (Application::chrome_options().allow_popups == WebView::AllowPopups::Yes)
  145. client().async_debug_request(m_client_state.page_index, "block-pop-ups"sv, "off"sv);
  146. if (auto const& web_driver_ipc_path = Application::chrome_options().webdriver_content_ipc_path; web_driver_ipc_path.has_value())
  147. client().async_connect_to_webdriver(m_client_state.page_index, *web_driver_ipc_path);
  148. m_client_state.client->on_web_content_process_crash = [this] {
  149. Core::deferred_invoke([this] {
  150. handle_web_content_process_crash(LoadErrorPage::No);
  151. if (on_web_content_crashed)
  152. on_web_content_crashed();
  153. });
  154. };
  155. }
  156. void HeadlessWebView::clear_content_filters()
  157. {
  158. client().async_set_content_filters(m_client_state.page_index, {});
  159. }
  160. NonnullRefPtr<Core::Promise<RefPtr<Gfx::Bitmap>>> HeadlessWebView::take_screenshot()
  161. {
  162. VERIFY(!m_pending_screenshot);
  163. m_pending_screenshot = Core::Promise<RefPtr<Gfx::Bitmap>>::construct();
  164. client().async_take_document_screenshot(0);
  165. return *m_pending_screenshot;
  166. }
  167. void HeadlessWebView::did_receive_screenshot(Badge<WebView::WebContentClient>, Gfx::ShareableBitmap const& screenshot)
  168. {
  169. VERIFY(m_pending_screenshot);
  170. auto pending_screenshot = move(m_pending_screenshot);
  171. pending_screenshot->resolve(screenshot.bitmap());
  172. }
  173. void HeadlessWebView::on_test_complete(TestCompletion completion)
  174. {
  175. m_test_promise->resolve(move(completion));
  176. }
  177. }