WorkerAgent.cpp 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /*
  2. * Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/LexicalPath.h>
  7. #include <LibCore/Socket.h>
  8. #include <LibCore/System.h>
  9. #include <LibWeb/HTML/WorkerAgent.h>
  10. #include <LibWeb/Worker/WebWorkerClient.h>
  11. // FIXME: Deduplicate this code with ladybird!!
  12. #ifndef AK_OS_SERENITY
  13. namespace {
  14. ErrorOr<String> application_directory()
  15. {
  16. auto current_executable_path = TRY(Core::System::current_executable_path());
  17. auto dirname = LexicalPath::dirname(current_executable_path);
  18. return String::from_byte_string(dirname);
  19. }
  20. ErrorOr<Vector<String>> get_paths_for_helper_process(StringView process_name)
  21. {
  22. auto application_path = TRY(application_directory());
  23. Vector<String> paths;
  24. TRY(paths.try_append(TRY(String::formatted("{}/{}", application_path, process_name))));
  25. TRY(paths.try_append(TRY(String::formatted("./{}", process_name))));
  26. // NOTE: Add platform-specific paths here
  27. return paths;
  28. }
  29. ErrorOr<NonnullRefPtr<Web::HTML::WebWorkerClient>> launch_web_worker_process(ReadonlySpan<String> candidate_web_content_paths)
  30. {
  31. int socket_fds[2] {};
  32. TRY(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, socket_fds));
  33. int ui_fd = socket_fds[0];
  34. int wc_fd = socket_fds[1];
  35. int fd_passing_socket_fds[2] {};
  36. TRY(Core::System::socketpair(AF_LOCAL, SOCK_STREAM, 0, fd_passing_socket_fds));
  37. int ui_fd_passing_fd = fd_passing_socket_fds[0];
  38. int wc_fd_passing_fd = fd_passing_socket_fds[1];
  39. if (auto child_pid = TRY(Core::System::fork()); child_pid == 0) {
  40. TRY(Core::System::close(ui_fd_passing_fd));
  41. TRY(Core::System::close(ui_fd));
  42. auto takeover_string = TRY(String::formatted("WebWorker:{}", wc_fd));
  43. TRY(Core::System::setenv("SOCKET_TAKEOVER"sv, takeover_string, true));
  44. auto webcontent_fd_passing_socket_string = TRY(String::number(wc_fd_passing_fd));
  45. ErrorOr<void> result;
  46. for (auto const& path : candidate_web_content_paths) {
  47. if (Core::System::access(path, X_OK).is_error())
  48. continue;
  49. auto arguments = Vector {
  50. path.bytes_as_string_view(),
  51. "--fd-passing-socket"sv,
  52. webcontent_fd_passing_socket_string
  53. };
  54. result = Core::System::exec(arguments[0], arguments.span(), Core::System::SearchInPath::Yes);
  55. if (!result.is_error())
  56. break;
  57. }
  58. if (result.is_error())
  59. warnln("Could not launch any of {}: {}", candidate_web_content_paths, result.error());
  60. VERIFY_NOT_REACHED();
  61. }
  62. TRY(Core::System::close(wc_fd_passing_fd));
  63. TRY(Core::System::close(wc_fd));
  64. auto socket = TRY(Core::LocalSocket::adopt_fd(ui_fd));
  65. TRY(socket->set_blocking(true));
  66. auto new_client = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) Web::HTML::WebWorkerClient(move(socket))));
  67. new_client->set_fd_passing_socket(TRY(Core::LocalSocket::adopt_fd(ui_fd_passing_fd)));
  68. return new_client;
  69. }
  70. }
  71. #endif
  72. namespace Web::HTML {
  73. JS_DEFINE_ALLOCATOR(WorkerAgent);
  74. WorkerAgent::WorkerAgent(AK::URL url, WorkerOptions const& options, JS::GCPtr<MessagePort> outside_port)
  75. : m_worker_options(options)
  76. , m_url(move(url))
  77. , m_outside_port(outside_port)
  78. {
  79. }
  80. void WorkerAgent::initialize(JS::Realm& realm)
  81. {
  82. Base::initialize(realm);
  83. m_message_port = MessagePort::create(realm);
  84. m_message_port->entangle_with(*m_outside_port);
  85. #ifndef AK_OS_SERENITY
  86. auto paths = MUST(get_paths_for_helper_process("WebWorker"sv));
  87. m_worker_ipc = MUST(launch_web_worker_process(paths));
  88. #else
  89. m_worker_ipc = MUST(Web::HTML::WebWorkerClient::try_create());
  90. #endif
  91. TransferDataHolder data_holder;
  92. MUST(m_message_port->transfer_steps(data_holder));
  93. m_worker_ipc->async_start_dedicated_worker(m_url, m_worker_options.type, m_worker_options.credentials, m_worker_options.name, move(data_holder));
  94. }
  95. void WorkerAgent::visit_edges(Cell::Visitor& visitor)
  96. {
  97. Base::visit_edges(visitor);
  98. visitor.visit(m_message_port);
  99. visitor.visit(m_outside_port);
  100. }
  101. }