38 次代码提交 94601e1ffd ... 14c3bff5da

作者 SHA1 备注 提交日期
  Timothy Flynn 14c3bff5da Meta: Add a flag to WPT.sh to run Ladybird headlessly 18 小时之前
  Timothy Flynn b24a7079f1 LibWeb+WebDriver: Add a flag to default WebDriver to headless mode 18 小时之前
  Timothy Flynn a67018b2fc WebDriver: Use the same command line arguments for all browser chromes 19 小时之前
  Timothy Flynn b73f9fef5a headless-browser: Support creating child web views 19 小时之前
  Timothy Flynn 34b8784dd9 headless-browser: Do not immediately exit when running under WebDriver 19 小时之前
  samu698 d08d305399 LibWeb: Fixed IDL for HTMLInputElement 2 天之前
  samu698 ab04f6d422 LibWeb: Fixed IDL for HTMLButtonElement 2 天之前
  samu698 6892482755 LibWeb: Use correct IDL for HTTPFormElement's method attribute 2 天之前
  Timothy Flynn ebe89a3207 WebContent: Parse the type hint in WebDriver's New Window endpoint 1 天之前
  Tim Ledbetter 74983e6966 WebDriver: Don't return from `new_window()` until WebDriver is connected 1 天之前
  Andreas Kling 72f4253911 LibWeb: Scale up "inspected node" hint text to match screen DPI 1 天之前
  Aliaksandr Kalenik 629a3ac61e LibWeb: Add missing check if scrollable overflow defined for paintable 1 天之前
  Jelle Raaijmakers 27928cd28c LibWeb: Add PointerEvent.getCoalescedEvents() and .getPredictedEvents() 4 天之前
  Jelle Raaijmakers effa21a69f LibWeb: Add PointerEvent.persistentDeviceId 4 天之前
  Andreas Kling 325ff4ac27 Revert "LibGfx: Use actual vector size as indicated by HarfBuzz" 1 天之前
  Andreas Kling fdfbfcab37 Revert "LibWeb: Unbreak harfbuzz text layout" 1 天之前
  Vincent Sgherzi 9ce139a3f0 LibWeb: Fix boolean logic mistake in XMLSerializer for empty public ID 2 周之前
  stelar7 d81f31c699 LibWeb/Meta: Adjust how missing/invalid default values are generated 1 天之前
  Andreas Kling a8d0712c28 LibWeb: Unbreak harfbuzz text layout 1 天之前
  Ben Wiederhake 14f5f51147 LibGfx: Use actual vector size as indicated by HarfBuzz 2 天之前
  Gabriel Tassinari 07400b515c Qt: Fix -Werror=deprecated-declarations when using Qt > 6.7 1 周之前
  Jim Broadbent 97ca6036fa LibWeb/XHR: Progess event handle empty length 2 天之前
  samu698 50f642613d LibWeb/HTML: Implement inner text set according to spec 2 天之前
  Andreas Kling 58c523ae46 LibWeb: Honor `appearance: none` when creating input element layout node 2 天之前
  stasoid 6b73a2d8a4 LibCore: Port `File` to Windows 2 天之前
  stasoid f6430035e7 LibCore/System: Add initial Windows support 2 天之前
  Timothy Flynn 6cba55893e WebContent: Return the handle of the newly opened window from New Window 2 天之前
  Timothy Flynn 981aaba96c LibWeb: Break the Window open steps into two methods for internal use 2 天之前
  Bastian Müller 748e3c2e6c LibWeb/XHR: Pass API URL character encoding 2 天之前
  Bastian Müller 3be93ac49f LibWeb/XHR: Parse URL to resolve blob 2 天之前
  Andreas Kling 4fdb266077 LibWeb: Make DOM Node unique IDs strongly typed (and 64 bit) 2 天之前
  Tim Ledbetter eca2318390 WebContent: Use window open steps to create a new window with WebDriver 2 天之前
  Tim Ledbetter f3c6326f27 LibWeb: Rename `Window::open_impl()` to `Window::window_open_steps()` 2 天之前
  Shannon Booth b999f925dc LibWeb: Allow splitting surrogate pairs in CharacterData.substringData() 2 天之前
  thislooksfun 0b775da7c7 LibWeb/CSS: Evaluate media queries in shadow roots 2 天之前
  Timothy Flynn 1aab7b51ea LibWebView: Generate hyperlinks for attributes that represent links 3 天之前
  Timothy Flynn 0703ba118b AK: Add a comparison operator for Utf32View 3 天之前
  Timothy Flynn cf9693169c AK: Allow constructing Utf32 from a span of u32 3 天之前
共有 91 个文件被更改,包括 1381 次插入384 次删除
  1. 8 0
      AK/Utf32View.cpp
  2. 7 0
      AK/Utf32View.h
  3. 2 2
      Ladybird/AppKit/UI/LadybirdWebView.mm
  4. 11 3
      Ladybird/Headless/Application.cpp
  5. 2 1
      Ladybird/Headless/Application.h
  6. 54 20
      Ladybird/Headless/HeadlessWebView.cpp
  7. 6 3
      Ladybird/Headless/HeadlessWebView.h
  8. 1 1
      Ladybird/Headless/Test.cpp
  9. 5 6
      Ladybird/Headless/main.cpp
  10. 4 0
      Ladybird/Qt/FindInPageWidget.cpp
  11. 16 0
      Ladybird/Qt/SettingsDialog.cpp
  12. 2 2
      Ladybird/Qt/Tab.cpp
  13. 28 21
      Ladybird/WebDriver/main.cpp
  14. 7 4
      Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp
  15. 26 11
      Meta/WPT.sh
  16. 77 0
      Tests/LibWeb/Layout/expected/css-appearance-none.txt
  17. 7 0
      Tests/LibWeb/Layout/input/css-appearance-none.html
  18. 5 0
      Tests/LibWeb/Text/expected/DOM/CharacterData-substringData-break-surrogate-pair.txt
  19. 3 0
      Tests/LibWeb/Text/expected/HTML/set-innerText.txt
  20. 1 0
      Tests/LibWeb/Text/expected/ShadowDOM/css-media-queries.txt
  21. 1 0
      Tests/LibWeb/Text/expected/XHR/XMLHttpRequest-request-is-blob.txt
  22. 103 0
      Tests/LibWeb/Text/expected/form-formEnctype-attribute.txt
  23. 103 0
      Tests/LibWeb/Text/expected/form-formMethod-attribute.txt
  24. 51 0
      Tests/LibWeb/Text/expected/form-method-attribute.txt
  25. 1 0
      Tests/LibWeb/Text/expected/query-scroll-size-of-inline-node.txt
  26. 18 0
      Tests/LibWeb/Text/input/DOM/CharacterData-substringData-break-surrogate-pair.html
  27. 12 0
      Tests/LibWeb/Text/input/HTML/set-innerText.html
  28. 21 0
      Tests/LibWeb/Text/input/ShadowDOM/css-media-queries.html
  29. 14 0
      Tests/LibWeb/Text/input/XHR/XMLHttpRequest-request-is-blob.html
  30. 40 0
      Tests/LibWeb/Text/input/form-formEnctype-attribute.html
  31. 36 0
      Tests/LibWeb/Text/input/form-formMethod-attribute.html
  32. 23 0
      Tests/LibWeb/Text/input/form-method-attribute.html
  33. 11 0
      Tests/LibWeb/Text/input/query-scroll-size-of-inline-node.html
  34. 2 1
      Userland/Libraries/LibCore/CMakeLists.txt
  35. 16 2
      Userland/Libraries/LibCore/File.cpp
  36. 37 25
      Userland/Libraries/LibCore/System.h
  37. 91 0
      Userland/Libraries/LibCore/SystemWindows.cpp
  38. 4 4
      Userland/Libraries/LibWeb/CSS/CountersSet.cpp
  39. 6 5
      Userland/Libraries/LibWeb/CSS/CountersSet.h
  40. 2 8
      Userland/Libraries/LibWeb/CSS/StyleComputer.cpp
  41. 4 3
      Userland/Libraries/LibWeb/CSS/StyleSheetIdentifier.cpp
  42. 2 1
      Userland/Libraries/LibWeb/CSS/StyleSheetIdentifier.h
  43. 1 1
      Userland/Libraries/LibWeb/DOM/AccessibilityTreeNode.cpp
  44. 2 2
      Userland/Libraries/LibWeb/DOM/CharacterData.cpp
  45. 18 5
      Userland/Libraries/LibWeb/DOM/Document.cpp
  46. 2 1
      Userland/Libraries/LibWeb/DOM/Document.h
  47. 9 3
      Userland/Libraries/LibWeb/DOM/Element.cpp
  48. 29 13
      Userland/Libraries/LibWeb/DOM/Node.cpp
  49. 6 5
      Userland/Libraries/LibWeb/DOM/Node.h
  50. 1 1
      Userland/Libraries/LibWeb/DOMParsing/XMLSerializer.cpp
  51. 15 1
      Userland/Libraries/LibWeb/Forward.h
  52. 2 2
      Userland/Libraries/LibWeb/HTML/HTMLButtonElement.idl
  53. 45 1
      Userland/Libraries/LibWeb/HTML/HTMLElement.cpp
  54. 1 0
      Userland/Libraries/LibWeb/HTML/HTMLElement.h
  55. 0 17
      Userland/Libraries/LibWeb/HTML/HTMLFormElement.cpp
  56. 0 1
      Userland/Libraries/LibWeb/HTML/HTMLFormElement.h
  57. 25 1
      Userland/Libraries/LibWeb/HTML/HTMLFormElement.idl
  58. 14 3
      Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp
  59. 2 2
      Userland/Libraries/LibWeb/HTML/HTMLInputElement.idl
  60. 20 10
      Userland/Libraries/LibWeb/HTML/Window.cpp
  61. 9 1
      Userland/Libraries/LibWeb/HTML/Window.h
  62. 12 8
      Userland/Libraries/LibWeb/Internals/Inspector.cpp
  63. 7 7
      Userland/Libraries/LibWeb/Internals/Inspector.h
  64. 7 7
      Userland/Libraries/LibWeb/Internals/Inspector.idl
  65. 3 3
      Userland/Libraries/LibWeb/Page/Page.cpp
  66. 11 11
      Userland/Libraries/LibWeb/Page/Page.h
  67. 1 1
      Userland/Libraries/LibWeb/Painting/PaintableBox.cpp
  68. 15 0
      Userland/Libraries/LibWeb/UIEvents/PointerEvent.cpp
  69. 17 0
      Userland/Libraries/LibWeb/UIEvents/PointerEvent.h
  70. 7 4
      Userland/Libraries/LibWeb/UIEvents/PointerEvent.idl
  71. 8 1
      Userland/Libraries/LibWeb/WebDriver/Capabilities.cpp
  72. 6 0
      Userland/Libraries/LibWeb/WebDriver/Capabilities.h
  73. 6 6
      Userland/Libraries/LibWeb/WebDriver/ElementReference.cpp
  74. 6 3
      Userland/Libraries/LibWeb/XHR/XMLHttpRequest.cpp
  75. 5 5
      Userland/Libraries/LibWebView/InspectorClient.cpp
  76. 5 5
      Userland/Libraries/LibWebView/InspectorClient.h
  77. 37 4
      Userland/Libraries/LibWebView/SourceHighlighter.cpp
  78. 4 2
      Userland/Libraries/LibWebView/SourceHighlighter.h
  79. 11 11
      Userland/Libraries/LibWebView/ViewImplementation.cpp
  80. 21 21
      Userland/Libraries/LibWebView/ViewImplementation.h
  81. 12 12
      Userland/Libraries/LibWebView/WebContentClient.cpp
  82. 10 10
      Userland/Libraries/LibWebView/WebContentClient.h
  83. 16 17
      Userland/Services/WebContent/ConnectionFromClient.cpp
  84. 11 11
      Userland/Services/WebContent/ConnectionFromClient.h
  85. 7 7
      Userland/Services/WebContent/PageClient.cpp
  86. 8 8
      Userland/Services/WebContent/PageClient.h
  87. 10 10
      Userland/Services/WebContent/WebContentClient.ipc
  88. 11 11
      Userland/Services/WebContent/WebContentServer.ipc
  89. 17 5
      Userland/Services/WebContent/WebDriverConnection.cpp
  90. 17 1
      Userland/Services/WebDriver/Client.cpp
  91. 2 0
      Userland/Services/WebDriver/Session.h

+ 8 - 0
AK/Utf32View.cpp

@@ -27,6 +27,14 @@ Optional<u32> Utf32CodePointIterator::peek(size_t offset) const
     return *new_iterator;
     return *new_iterator;
 }
 }
 
 
+bool Utf32View::operator==(Utf32View const& other) const
+{
+    ReadonlySpan<u32> code_points { m_code_points, m_length };
+    ReadonlySpan<u32> other_code_points { other.m_code_points, other.m_length };
+
+    return code_points == other_code_points;
+}
+
 ErrorOr<void> Formatter<Utf32View>::format(FormatBuilder& builder, Utf32View const& string)
 ErrorOr<void> Formatter<Utf32View>::format(FormatBuilder& builder, Utf32View const& string)
 {
 {
     return builder.builder().try_append(string);
     return builder.builder().try_append(string);

+ 7 - 0
AK/Utf32View.h

@@ -71,6 +71,11 @@ public:
         VERIFY(code_points || length == 0);
         VERIFY(code_points || length == 0);
     }
     }
 
 
+    Utf32View(ReadonlySpan<u32> code_points)
+        : Utf32View(code_points.data(), code_points.size())
+    {
+    }
+
     Utf32CodePointIterator begin() const
     Utf32CodePointIterator begin() const
     {
     {
         return { begin_ptr(), m_length };
         return { begin_ptr(), m_length };
@@ -114,6 +119,8 @@ public:
         return substring_view(offset, length() - offset);
         return substring_view(offset, length() - offset);
     }
     }
 
 
+    bool operator==(Utf32View const& other) const;
+
 private:
 private:
     u32 const* begin_ptr() const
     u32 const* begin_ptr() const
     {
     {

+ 2 - 2
Ladybird/AppKit/UI/LadybirdWebView.mm

@@ -1051,12 +1051,12 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_
         return Ladybird::ns_rect_to_gfx_rect([[self window] frame]);
         return Ladybird::ns_rect_to_gfx_rect([[self window] frame]);
     };
     };
 
 
-    m_web_view_bridge->on_received_source = [weak_self](auto const& url, auto const& source) {
+    m_web_view_bridge->on_received_source = [weak_self](auto const& url, auto const& base_url, auto const& source) {
         LadybirdWebView* self = weak_self;
         LadybirdWebView* self = weak_self;
         if (self == nil) {
         if (self == nil) {
             return;
             return;
         }
         }
-        auto html = WebView::highlight_source(MUST(url.to_string()), source, Syntax::Language::HTML, WebView::HighlightOutputMode::FullDocument);
+        auto html = WebView::highlight_source(url, base_url, source, Syntax::Language::HTML, WebView::HighlightOutputMode::FullDocument);
 
 
         [self.observer onCreateNewTab:html
         [self.observer onCreateNewTab:html
                                   url:url
                                   url:url

+ 11 - 3
Ladybird/Headless/Application.cpp

@@ -68,12 +68,20 @@ ErrorOr<void> Application::launch_services()
     return {};
     return {};
 }
 }
 
 
-ErrorOr<HeadlessWebView*> Application::create_web_view(Core::AnonymousBuffer theme, Gfx::IntSize window_size)
+HeadlessWebView& Application::create_web_view(Core::AnonymousBuffer theme, Gfx::IntSize window_size)
 {
 {
-    auto web_view = TRY(HeadlessWebView::create(move(theme), window_size));
+    auto web_view = HeadlessWebView::create(move(theme), window_size);
     m_web_views.append(move(web_view));
     m_web_views.append(move(web_view));
 
 
-    return m_web_views.last().ptr();
+    return *m_web_views.last();
+}
+
+HeadlessWebView& Application::create_child_web_view(HeadlessWebView const& parent, u64 page_index)
+{
+    auto web_view = HeadlessWebView::create_child(parent, page_index);
+    m_web_views.append(move(web_view));
+
+    return *m_web_views.last();
 }
 }
 
 
 void Application::destroy_web_views()
 void Application::destroy_web_views()

+ 2 - 1
Ladybird/Headless/Application.h

@@ -37,7 +37,8 @@ public:
     static Requests::RequestClient& request_client() { return *the().m_request_client; }
     static Requests::RequestClient& request_client() { return *the().m_request_client; }
     static ImageDecoderClient::Client& image_decoder_client() { return *the().m_image_decoder_client; }
     static ImageDecoderClient::Client& image_decoder_client() { return *the().m_image_decoder_client; }
 
 
-    ErrorOr<HeadlessWebView*> create_web_view(Core::AnonymousBuffer theme, Gfx::IntSize window_size);
+    HeadlessWebView& create_web_view(Core::AnonymousBuffer theme, Gfx::IntSize window_size);
+    HeadlessWebView& create_child_web_view(HeadlessWebView const&, u64 page_index);
     void destroy_web_views();
     void destroy_web_views();
 
 
     template<typename Callback>
     template<typename Callback>

+ 54 - 20
Ladybird/Headless/HeadlessWebView.cpp

@@ -10,13 +10,25 @@
 #include <Ladybird/Utilities.h>
 #include <Ladybird/Utilities.h>
 #include <LibGfx/Bitmap.h>
 #include <LibGfx/Bitmap.h>
 #include <LibGfx/ShareableBitmap.h>
 #include <LibGfx/ShareableBitmap.h>
+#include <LibWeb/Crypto/Crypto.h>
 
 
 namespace Ladybird {
 namespace Ladybird {
 
 
-HeadlessWebView::HeadlessWebView(Gfx::IntSize viewport_size)
-    : m_viewport_size(viewport_size)
+HeadlessWebView::HeadlessWebView(Core::AnonymousBuffer theme, Gfx::IntSize viewport_size)
+    : m_theme(move(theme))
+    , m_viewport_size(viewport_size)
     , m_test_promise(TestPromise::construct())
     , m_test_promise(TestPromise::construct())
 {
 {
+    on_new_web_view = [this](auto, auto, Optional<u64> page_index) {
+        if (page_index.has_value()) {
+            auto& web_view = Application::the().create_child_web_view(*this, *page_index);
+            return web_view.handle();
+        }
+
+        auto& web_view = Application::the().create_web_view(m_theme, m_viewport_size);
+        return web_view.handle();
+    };
+
     on_request_worker_agent = []() {
     on_request_worker_agent = []() {
         auto web_worker_paths = MUST(get_paths_for_helper_process("WebWorker"sv));
         auto web_worker_paths = MUST(get_paths_for_helper_process("WebWorker"sv));
         auto worker_client = MUST(launch_web_worker_process(web_worker_paths, Application::request_client()));
         auto worker_client = MUST(launch_web_worker_process(web_worker_paths, Application::request_client()));
@@ -25,38 +37,60 @@ HeadlessWebView::HeadlessWebView(Gfx::IntSize viewport_size)
     };
     };
 }
 }
 
 
-ErrorOr<NonnullOwnPtr<HeadlessWebView>> HeadlessWebView::create(Core::AnonymousBuffer theme, Gfx::IntSize window_size)
+NonnullOwnPtr<HeadlessWebView> HeadlessWebView::create(Core::AnonymousBuffer theme, Gfx::IntSize window_size)
 {
 {
-    auto view = TRY(adopt_nonnull_own_or_enomem(new (nothrow) HeadlessWebView(window_size)));
+    auto view = adopt_own(*new HeadlessWebView(move(theme), window_size));
+    view->initialize_client(CreateNewClient::Yes);
 
 
-    auto request_server_socket = TRY(connect_new_request_server_client(Application::request_client()));
-    auto image_decoder_socket = TRY(connect_new_image_decoder_client(Application::image_decoder_client()));
+    return view;
+}
 
 
-    auto candidate_web_content_paths = TRY(get_paths_for_helper_process("WebContent"sv));
-    view->m_client_state.client = TRY(launch_web_content_process(*view, candidate_web_content_paths, move(image_decoder_socket), move(request_server_socket)));
+NonnullOwnPtr<HeadlessWebView> HeadlessWebView::create_child(HeadlessWebView const& parent, u64 page_index)
+{
+    auto view = adopt_own(*new HeadlessWebView(parent.m_theme, parent.m_viewport_size));
 
 
-    view->client().async_update_system_theme(0, move(theme));
-    view->client().async_set_viewport_size(0, view->viewport_size());
-    view->client().async_set_window_size(0, view->viewport_size());
+    view->m_client_state.client = parent.client();
+    view->m_client_state.page_index = page_index;
+    view->initialize_client(CreateNewClient::No);
 
 
-    if (WebView::Application::chrome_options().allow_popups == WebView::AllowPopups::Yes)
-        view->client().async_debug_request(0, "block-pop-ups"sv, "off"sv);
+    return view;
+}
 
 
-    if (auto web_driver_ipc_path = WebView::Application::chrome_options().webdriver_content_ipc_path; web_driver_ipc_path.has_value())
-        view->client().async_connect_to_webdriver(0, *web_driver_ipc_path);
+void HeadlessWebView::initialize_client(CreateNewClient create_new_client)
+{
+    if (create_new_client == CreateNewClient::Yes) {
+        auto request_server_socket = connect_new_request_server_client(Application::request_client()).release_value_but_fixme_should_propagate_errors();
+        auto image_decoder_socket = connect_new_image_decoder_client(Application::image_decoder_client()).release_value_but_fixme_should_propagate_errors();
 
 
-    view->m_client_state.client->on_web_content_process_crash = [&view = *view] {
+        auto web_content_paths = get_paths_for_helper_process("WebContent"sv).release_value_but_fixme_should_propagate_errors();
+        m_client_state.client = launch_web_content_process(*this, web_content_paths, move(image_decoder_socket), move(request_server_socket)).release_value_but_fixme_should_propagate_errors();
+    } else {
+        m_client_state.client->register_view(m_client_state.page_index, *this);
+    }
+
+    m_client_state.client_handle = MUST(Web::Crypto::generate_random_uuid());
+    client().async_set_window_handle(m_client_state.page_index, m_client_state.client_handle);
+
+    client().async_update_system_theme(m_client_state.page_index, m_theme);
+    client().async_set_viewport_size(m_client_state.page_index, viewport_size());
+    client().async_set_window_size(m_client_state.page_index, viewport_size());
+
+    if (Application::chrome_options().allow_popups == WebView::AllowPopups::Yes)
+        client().async_debug_request(m_client_state.page_index, "block-pop-ups"sv, "off"sv);
+
+    if (auto const& web_driver_ipc_path = Application::chrome_options().webdriver_content_ipc_path; web_driver_ipc_path.has_value())
+        client().async_connect_to_webdriver(m_client_state.page_index, *web_driver_ipc_path);
+
+    m_client_state.client->on_web_content_process_crash = [this] {
         warnln("\033[31;1mWebContent Crashed!!\033[0m");
         warnln("\033[31;1mWebContent Crashed!!\033[0m");
-        warnln("    Last page loaded: {}", view.url());
+        warnln("    Last page loaded: {}", url());
         VERIFY_NOT_REACHED();
         VERIFY_NOT_REACHED();
     };
     };
-
-    return view;
 }
 }
 
 
 void HeadlessWebView::clear_content_filters()
 void HeadlessWebView::clear_content_filters()
 {
 {
-    client().async_set_content_filters(0, {});
+    client().async_set_content_filters(m_client_state.page_index, {});
 }
 }
 
 
 NonnullRefPtr<Core::Promise<RefPtr<Gfx::Bitmap>>> HeadlessWebView::take_screenshot()
 NonnullRefPtr<Core::Promise<RefPtr<Gfx::Bitmap>>> HeadlessWebView::take_screenshot()

+ 6 - 3
Ladybird/Headless/HeadlessWebView.h

@@ -20,7 +20,8 @@ namespace Ladybird {
 
 
 class HeadlessWebView final : public WebView::ViewImplementation {
 class HeadlessWebView final : public WebView::ViewImplementation {
 public:
 public:
-    static ErrorOr<NonnullOwnPtr<HeadlessWebView>> create(Core::AnonymousBuffer theme, Gfx::IntSize window_size);
+    static NonnullOwnPtr<HeadlessWebView> create(Core::AnonymousBuffer theme, Gfx::IntSize window_size);
+    static NonnullOwnPtr<HeadlessWebView> create_child(HeadlessWebView const&, u64 page_index);
 
 
     void clear_content_filters();
     void clear_content_filters();
 
 
@@ -30,10 +31,10 @@ public:
     void on_test_complete(TestCompletion);
     void on_test_complete(TestCompletion);
 
 
 private:
 private:
-    explicit HeadlessWebView(Gfx::IntSize viewport_size);
+    HeadlessWebView(Core::AnonymousBuffer theme, Gfx::IntSize viewport_size);
 
 
     void update_zoom() override { }
     void update_zoom() override { }
-    void initialize_client(CreateNewClient) override { }
+    void initialize_client(CreateNewClient) override;
 
 
     virtual Web::DevicePixelSize viewport_size() const override { return m_viewport_size.to_type<Web::DevicePixels>(); }
     virtual Web::DevicePixelSize viewport_size() const override { return m_viewport_size.to_type<Web::DevicePixels>(); }
     virtual Gfx::IntPoint to_content_position(Gfx::IntPoint widget_position) const override { return widget_position; }
     virtual Gfx::IntPoint to_content_position(Gfx::IntPoint widget_position) const override { return widget_position; }
@@ -41,7 +42,9 @@ private:
 
 
     virtual void did_receive_screenshot(Badge<WebView::WebContentClient>, Gfx::ShareableBitmap const& screenshot) override;
     virtual void did_receive_screenshot(Badge<WebView::WebContentClient>, Gfx::ShareableBitmap const& screenshot) override;
 
 
+    Core::AnonymousBuffer m_theme;
     Gfx::IntSize m_viewport_size;
     Gfx::IntSize m_viewport_size;
+
     RefPtr<Core::Promise<RefPtr<Gfx::Bitmap>>> m_pending_screenshot;
     RefPtr<Core::Promise<RefPtr<Gfx::Bitmap>>> m_pending_screenshot;
 
 
     NonnullRefPtr<TestPromise> m_test_promise;
     NonnullRefPtr<TestPromise> m_test_promise;

+ 1 - 1
Ladybird/Headless/Test.cpp

@@ -402,7 +402,7 @@ ErrorOr<void> run_tests(Core::AnonymousBuffer const& theme, Gfx::IntSize window_
     size_t loaded_web_views = 0;
     size_t loaded_web_views = 0;
 
 
     for (size_t i = 0; i < concurrency; ++i) {
     for (size_t i = 0; i < concurrency; ++i) {
-        auto& view = *TRY(app.create_web_view(theme, window_size));
+        auto& view = app.create_web_view(theme, window_size);
         view.on_load_finish = [&](auto const&) { ++loaded_web_views; };
         view.on_load_finish = [&](auto const&) { ++loaded_web_views; };
     }
     }
 
 

+ 5 - 6
Ladybird/Headless/main.cpp

@@ -81,7 +81,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
         return 0;
         return 0;
     }
     }
 
 
-    auto& view = *TRY(app->create_web_view(move(theme), window_size));
+    auto& view = app->create_web_view(move(theme), window_size);
 
 
     VERIFY(!WebView::Application::chrome_options().urls.is_empty());
     VERIFY(!WebView::Application::chrome_options().urls.is_empty());
     auto const& url = WebView::Application::chrome_options().urls.first();
     auto const& url = WebView::Application::chrome_options().urls.first();
@@ -98,10 +98,9 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
         return completion.result == Ladybird::TestResult::Pass ? 0 : 1;
         return completion.result == Ladybird::TestResult::Pass ? 0 : 1;
     }
     }
 
 
-    if (!WebView::Application::chrome_options().webdriver_content_ipc_path.has_value()) {
-        auto timer = TRY(load_page_for_screenshot_and_exit(Core::EventLoop::current(), view, url, app->screenshot_timeout));
-        return app->execute();
-    }
+    RefPtr<Core::Timer> timer;
+    if (!WebView::Application::chrome_options().webdriver_content_ipc_path.has_value())
+        timer = TRY(load_page_for_screenshot_and_exit(Core::EventLoop::current(), view, url, app->screenshot_timeout));
 
 
-    return 0;
+    return app->execute();
 }
 }

+ 4 - 0
Ladybird/Qt/FindInPageWidget.cpp

@@ -62,7 +62,11 @@ FindInPageWidget::FindInPageWidget(Tab* tab, WebContentView* content_view)
     m_match_case = new QCheckBox(this);
     m_match_case = new QCheckBox(this);
     m_match_case->setText("Match &Case");
     m_match_case->setText("Match &Case");
     m_match_case->setChecked(false);
     m_match_case->setChecked(false);
+#if (QT_VERSION > QT_VERSION_CHECK(6, 7, 0))
+    connect(m_match_case, &QCheckBox::checkStateChanged, this, [this] {
+#else
     connect(m_match_case, &QCheckBox::stateChanged, this, [this] {
     connect(m_match_case, &QCheckBox::stateChanged, this, [this] {
+#endif
         find_text_changed();
         find_text_changed();
     });
     });
 
 

+ 16 - 0
Ladybird/Qt/SettingsDialog.cpp

@@ -62,7 +62,11 @@ SettingsDialog::SettingsDialog(QMainWindow* window)
 
 
     m_enable_do_not_track = new QCheckBox(this);
     m_enable_do_not_track = new QCheckBox(this);
     m_enable_do_not_track->setChecked(Settings::the()->enable_do_not_track());
     m_enable_do_not_track->setChecked(Settings::the()->enable_do_not_track());
+#if (QT_VERSION > QT_VERSION_CHECK(6, 7, 0))
+    QObject::connect(m_enable_do_not_track, &QCheckBox::checkStateChanged, this, [&](int state) {
+#else
     QObject::connect(m_enable_do_not_track, &QCheckBox::stateChanged, this, [&](int state) {
     QObject::connect(m_enable_do_not_track, &QCheckBox::stateChanged, this, [&](int state) {
+#endif
         Settings::the()->set_enable_do_not_track(state == Qt::Checked);
         Settings::the()->set_enable_do_not_track(state == Qt::Checked);
     });
     });
 
 
@@ -73,7 +77,11 @@ SettingsDialog::SettingsDialog(QMainWindow* window)
         m_enable_autoplay->setChecked(Settings::the()->enable_autoplay());
         m_enable_autoplay->setChecked(Settings::the()->enable_autoplay());
     }
     }
 
 
+#if (QT_VERSION > QT_VERSION_CHECK(6, 7, 0))
+    QObject::connect(m_enable_autoplay, &QCheckBox::checkStateChanged, this, [&](int state) {
+#else
     QObject::connect(m_enable_autoplay, &QCheckBox::stateChanged, this, [&](int state) {
     QObject::connect(m_enable_autoplay, &QCheckBox::stateChanged, this, [&](int state) {
+#endif
         Settings::the()->set_enable_autoplay(state == Qt::Checked);
         Settings::the()->set_enable_autoplay(state == Qt::Checked);
     });
     });
 
 
@@ -129,12 +137,20 @@ void SettingsDialog::setup_search_engines()
     m_autocomplete_engine_dropdown->setMenu(autocomplete_engine_menu);
     m_autocomplete_engine_dropdown->setMenu(autocomplete_engine_menu);
     m_autocomplete_engine_dropdown->setEnabled(Settings::the()->enable_autocomplete());
     m_autocomplete_engine_dropdown->setEnabled(Settings::the()->enable_autocomplete());
 
 
+#if (QT_VERSION > QT_VERSION_CHECK(6, 7, 0))
+    connect(m_enable_search, &QCheckBox::checkStateChanged, this, [&](int state) {
+#else
     connect(m_enable_search, &QCheckBox::stateChanged, this, [&](int state) {
     connect(m_enable_search, &QCheckBox::stateChanged, this, [&](int state) {
+#endif
         Settings::the()->set_enable_search(state == Qt::Checked);
         Settings::the()->set_enable_search(state == Qt::Checked);
         m_search_engine_dropdown->setEnabled(state == Qt::Checked);
         m_search_engine_dropdown->setEnabled(state == Qt::Checked);
     });
     });
 
 
+#if (QT_VERSION > QT_VERSION_CHECK(6, 7, 0))
+    connect(m_enable_autocomplete, &QCheckBox::checkStateChanged, this, [&](int state) {
+#else
     connect(m_enable_autocomplete, &QCheckBox::stateChanged, this, [&](int state) {
     connect(m_enable_autocomplete, &QCheckBox::stateChanged, this, [&](int state) {
+#endif
         Settings::the()->set_enable_autocomplete(state == Qt::Checked);
         Settings::the()->set_enable_autocomplete(state == Qt::Checked);
         m_autocomplete_engine_dropdown->setEnabled(state == Qt::Checked);
         m_autocomplete_engine_dropdown->setEnabled(state == Qt::Checked);
     });
     });

+ 2 - 2
Ladybird/Qt/Tab.cpp

@@ -321,8 +321,8 @@ Tab::Tab(BrowserWindow* window, RefPtr<WebView::WebContentClient> parent_client,
 
 
     QObject::connect(focus_location_editor_action, &QAction::triggered, this, &Tab::focus_location_editor);
     QObject::connect(focus_location_editor_action, &QAction::triggered, this, &Tab::focus_location_editor);
 
 
-    view().on_received_source = [this](auto const& url, auto const& source) {
-        auto html = WebView::highlight_source(MUST(url.to_string()), source, Syntax::Language::HTML, WebView::HighlightOutputMode::FullDocument);
+    view().on_received_source = [this](auto const& url, auto const& base_url, auto const& source) {
+        auto html = WebView::highlight_source(url, base_url, source, Syntax::Language::HTML, WebView::HighlightOutputMode::FullDocument);
         m_window->new_tab_from_content(html, Web::HTML::ActivateTab::Yes);
         m_window->new_tab_from_content(html, Web::HTML::ActivateTab::Yes);
     };
     };
 
 

+ 28 - 21
Ladybird/WebDriver/main.cpp

@@ -14,11 +14,12 @@
 #include <LibCore/System.h>
 #include <LibCore/System.h>
 #include <LibCore/TCPServer.h>
 #include <LibCore/TCPServer.h>
 #include <LibMain/Main.h>
 #include <LibMain/Main.h>
+#include <LibWeb/WebDriver/Capabilities.h>
 #include <WebDriver/Client.h>
 #include <WebDriver/Client.h>
 
 
 static Vector<ByteString> certificates;
 static Vector<ByteString> certificates;
 
 
-static ErrorOr<pid_t> launch_process(StringView application, ReadonlySpan<char const*> arguments)
+static ErrorOr<pid_t> launch_process(StringView application, ReadonlySpan<ByteString> arguments)
 {
 {
     auto paths = TRY(get_paths_for_helper_process(application));
     auto paths = TRY(get_paths_for_helper_process(application));
 
 
@@ -32,11 +33,11 @@ static ErrorOr<pid_t> launch_process(StringView application, ReadonlySpan<char c
     return result;
     return result;
 }
 }
 
 
-static ErrorOr<pid_t> launch_browser(ByteString const& socket_path, bool force_cpu_painting)
+static Vector<ByteString> create_arguments(ByteString const& socket_path, bool force_cpu_painting)
 {
 {
-    auto arguments = Vector {
-        "--webdriver-content-path",
-        socket_path.characters(),
+    Vector<ByteString> arguments {
+        "--webdriver-content-path"sv,
+        socket_path,
     };
     };
 
 
     Vector<ByteString> certificate_args;
     Vector<ByteString> certificate_args;
@@ -45,28 +46,26 @@ static ErrorOr<pid_t> launch_browser(ByteString const& socket_path, bool force_c
         arguments.append(certificate_args.last().view().characters_without_null_termination());
         arguments.append(certificate_args.last().view().characters_without_null_termination());
     }
     }
 
 
-    arguments.append("--allow-popups");
-    arguments.append("--force-new-process");
-    arguments.append("--enable-autoplay");
+    arguments.append("--allow-popups"sv);
+    arguments.append("--force-new-process"sv);
+    arguments.append("--enable-autoplay"sv);
     if (force_cpu_painting)
     if (force_cpu_painting)
-        arguments.append("--force-cpu-painting");
+        arguments.append("--force-cpu-painting"sv);
 
 
-    arguments.append("about:blank");
+    arguments.append("about:blank"sv);
+    return arguments;
+}
 
 
+static ErrorOr<pid_t> launch_browser(ByteString const& socket_path, bool force_cpu_painting)
+{
+    auto arguments = create_arguments(socket_path, force_cpu_painting);
     return launch_process("Ladybird"sv, arguments.span());
     return launch_process("Ladybird"sv, arguments.span());
 }
 }
 
 
-static ErrorOr<pid_t> launch_headless_browser(ByteString const& socket_path)
+static ErrorOr<pid_t> launch_headless_browser(ByteString const& socket_path, bool force_cpu_painting)
 {
 {
-    auto resources = ByteString::formatted("{}/res", s_ladybird_resource_root);
-    return launch_process("headless-browser"sv,
-        Array {
-            "--resources",
-            resources.characters(),
-            "--webdriver-content-path",
-            socket_path.characters(),
-            "about:blank",
-        });
+    auto arguments = create_arguments(socket_path, force_cpu_painting);
+    return launch_process("headless-browser"sv, arguments.span());
 }
 }
 
 
 ErrorOr<int> serenity_main(Main::Arguments arguments)
 ErrorOr<int> serenity_main(Main::Arguments arguments)
@@ -76,12 +75,14 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
     auto listen_address = "0.0.0.0"sv;
     auto listen_address = "0.0.0.0"sv;
     int port = 8000;
     int port = 8000;
     bool force_cpu_painting = false;
     bool force_cpu_painting = false;
+    bool headless = false;
 
 
     Core::ArgsParser args_parser;
     Core::ArgsParser args_parser;
     args_parser.add_option(listen_address, "IP address to listen on", "listen-address", 'l', "listen_address");
     args_parser.add_option(listen_address, "IP address to listen on", "listen-address", 'l', "listen_address");
     args_parser.add_option(port, "Port to listen on", "port", 'p', "port");
     args_parser.add_option(port, "Port to listen on", "port", 'p', "port");
     args_parser.add_option(certificates, "Path to a certificate file", "certificate", 'C', "certificate");
     args_parser.add_option(certificates, "Path to a certificate file", "certificate", 'C', "certificate");
     args_parser.add_option(force_cpu_painting, "Launch browser with GPU painting disabled", "force-cpu-painting");
     args_parser.add_option(force_cpu_painting, "Launch browser with GPU painting disabled", "force-cpu-painting");
+    args_parser.add_option(headless, "Launch browser without a graphical interface", "headless");
     args_parser.parse(arguments);
     args_parser.parse(arguments);
 
 
     auto ipv4_address = IPv4Address::from_string(listen_address);
     auto ipv4_address = IPv4Address::from_string(listen_address);
@@ -97,6 +98,8 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
 
 
     platform_init();
     platform_init();
 
 
+    Web::WebDriver::set_default_interface_mode(headless ? Web::WebDriver::InterfaceMode::Headless : Web::WebDriver::InterfaceMode::Graphical);
+
     auto webdriver_socket_path = ByteString::formatted("{}/webdriver", TRY(Core::StandardPaths::runtime_directory()));
     auto webdriver_socket_path = ByteString::formatted("{}/webdriver", TRY(Core::StandardPaths::runtime_directory()));
     TRY(Core::Directory::create(webdriver_socket_path, Core::Directory::CreateDirectories::Yes));
     TRY(Core::Directory::create(webdriver_socket_path, Core::Directory::CreateDirectories::Yes));
 
 
@@ -121,7 +124,11 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
             return launch_browser(socket_path, force_cpu_painting);
             return launch_browser(socket_path, force_cpu_painting);
         };
         };
 
 
-        auto maybe_client = WebDriver::Client::try_create(maybe_buffered_socket.release_value(), { move(launch_browser_callback), launch_headless_browser }, server);
+        auto launch_headless_browser_callback = [&](ByteString const& socket_path) {
+            return launch_headless_browser(socket_path, force_cpu_painting);
+        };
+
+        auto maybe_client = WebDriver::Client::try_create(maybe_buffered_socket.release_value(), { move(launch_browser_callback), move(launch_headless_browser_callback) }, server);
         if (maybe_client.is_error()) {
         if (maybe_client.is_error()) {
             warnln("Could not create a WebDriver client: {}", maybe_client.error());
             warnln("Could not create a WebDriver client: {}", maybe_client.error());
             return;
             return;

+ 7 - 4
Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp

@@ -3499,21 +3499,24 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.getter_callback@)
                         //    or that it is in a state of attributeDefinition with no associated keyword value, then return the empty string.
                         //    or that it is in a state of attributeDefinition with no associated keyword value, then return the empty string.
                         //    NOTE: @invalid_enum_default_value@ is set to the empty string if it isn't present.
                         //    NOTE: @invalid_enum_default_value@ is set to the empty string if it isn't present.
                         attribute_generator.append(R"~~~(
                         attribute_generator.append(R"~~~(
-    if (!contentAttributeValue.has_value())
+    auto did_set_to_missing_value = false;
+    if (!contentAttributeValue.has_value()) {
         retval = "@missing_enum_default_value@"_string;
         retval = "@missing_enum_default_value@"_string;
+        did_set_to_missing_value = true;
+    }
 
 
     Array valid_values { @valid_enum_values@ };
     Array valid_values { @valid_enum_values@ };
 
 
-    auto found = false;
+    auto has_keyword = false;
     for (auto const& value : valid_values) {
     for (auto const& value : valid_values) {
         if (value.equals_ignoring_ascii_case(retval)) {
         if (value.equals_ignoring_ascii_case(retval)) {
-            found = true;
+            has_keyword = true;
             retval = value;
             retval = value;
             break;
             break;
         }
         }
     }
     }
 
 
-    if (!found)
+    if (!has_keyword && !did_set_to_missing_value) 
         retval = "@invalid_enum_default_value@"_string;
         retval = "@invalid_enum_default_value@"_string;
     )~~~");
     )~~~");
 
 

+ 26 - 11
Meta/WPT.sh

@@ -18,9 +18,9 @@ BUILD_DIR=$(get_build_dir "$BUILD_PRESET")
 
 
 default_binary_path() {
 default_binary_path() {
     if [ "$(uname -s)" = "Darwin" ]; then
     if [ "$(uname -s)" = "Darwin" ]; then
-        echo "${BUILD_DIR}/bin/Ladybird.app/Contents/MacOS/"
+        echo "${BUILD_DIR}/bin/Ladybird.app/Contents/MacOS"
     else
     else
-        echo "${BUILD_DIR}/bin/"
+        echo "${BUILD_DIR}/bin"
     fi
     fi
 }
 }
 
 
@@ -31,8 +31,7 @@ WPT_CERTIFICATES=(
   "tools/certs/cacert.pem"
   "tools/certs/cacert.pem"
   "${LADYBIRD_SOURCE_DIR}/Build/ladybird/Lagom/cacert.pem"
   "${LADYBIRD_SOURCE_DIR}/Build/ladybird/Lagom/cacert.pem"
 )
 )
-WPT_ARGS=( "--binary=${LADYBIRD_BINARY}"
-           "--webdriver-binary=${WEBDRIVER_BINARY}"
+WPT_ARGS=( "--webdriver-binary=${WEBDRIVER_BINARY}"
            "--install-webdriver"
            "--install-webdriver"
             "--processes=${WPT_PROCESSES}"
             "--processes=${WPT_PROCESSES}"
            "--webdriver-arg=--force-cpu-painting"
            "--webdriver-arg=--force-cpu-painting"
@@ -82,23 +81,39 @@ if [ "$CMD" = "--help" ] || [ "$CMD" = "help" ]; then
     exit 0
     exit 0
 fi
 fi
 
 
+set_logging_flags()
+{
+    [ -n "${1}" ] || usage;
+    [ -n "${2}" ] || usage;
+
+    log_type="${1}"
+    log_name="$(pwd -P)/${2}"
+
+    WPT_ARGS+=( "${log_type}=${log_name}" )
+}
+
 ARG=$1
 ARG=$1
-while [[ "$ARG" =~ ^--log(-(raw|unittest|xunit|html|mach|tbpl|grouped|chromium|wptreport|wptscreenshot))?$ ]]; do
+while [[ "$ARG" =~ ^(--headless|(--log(-(raw|unittest|xunit|html|mach|tbpl|grouped|chromium|wptreport|wptscreenshot))?))$ ]]; do
     case "$ARG" in
     case "$ARG" in
+        --headless)
+            LADYBIRD_BINARY="$(default_binary_path)/headless-browser"
+            WPT_ARGS+=( "--webdriver-arg=--headless" )
+            ;;
         --log)
         --log)
-            LOG_TYPE="--log-raw"
+            set_logging_flags "--log-raw" "${2}"
+            shift
             ;;
             ;;
         *)
         *)
-            LOG_TYPE="$ARG"
+            set_logging_flags "${ARG}" "${2}"
+            shift
             ;;
             ;;
     esac
     esac
-    shift
-    LOG_NAME="$(pwd -P)/$1"
-    [ -n "$LOG_NAME" ] || usage;
-    WPT_ARGS+=( "${LOG_TYPE}=${LOG_NAME}" )
+
     shift
     shift
     ARG=$1
     ARG=$1
 done
 done
+
+WPT_ARGS+=( "--binary=${LADYBIRD_BINARY}" )
 TEST_LIST=( "$@" )
 TEST_LIST=( "$@" )
 
 
 exit_if_running_as_root "Do not run WPT.sh as root"
 exit_if_running_as_root "Do not run WPT.sh as root"

+ 77 - 0
Tests/LibWeb/Layout/expected/css-appearance-none.txt

@@ -0,0 +1,77 @@
+Viewport <#document> at (0,0) content-size 800x600 children: not-inline
+  BlockContainer <html> at (0,0) content-size 800x126 [BFC] children: not-inline
+    BlockContainer <body> at (8,8) content-size 784x110 children: inline
+      frag 0 from BlockContainer start: 0, length: 0, rect: [13,10 0x17] baseline: 15.296875
+      frag 1 from TextNode start: 0, length: 13, rect: [18,10 116.171875x17] baseline: 13.296875
+          "Normal button"
+      frag 2 from BlockContainer start: 0, length: 0, rect: [13,31 0x17] baseline: 15.296875
+      frag 3 from TextNode start: 0, length: 20, rect: [18,31 181.203125x17] baseline: 13.296875
+          "No appearance button"
+      frag 4 from CheckBox start: 0, length: 0, rect: [8,50 13x13] baseline: 13
+      frag 5 from TextNode start: 0, length: 15, rect: [21,50 136.296875x17] baseline: 13.296875
+          "Normal checkbox"
+      frag 6 from BlockContainer start: 0, length: 0, rect: [8,80 0x0] baseline: 0
+      frag 7 from TextNode start: 0, length: 22, rect: [8,67 201.328125x17] baseline: 13.296875
+          "No appearance checkbox"
+      frag 8 from RadioButton start: 0, length: 0, rect: [8,85 12x12] baseline: 12
+      frag 9 from TextNode start: 0, length: 12, rect: [20,84 104.203125x17] baseline: 13.296875
+          "Normal radio"
+      frag 10 from BlockContainer start: 0, length: 0, rect: [8,114 0x0] baseline: 0
+      frag 11 from TextNode start: 0, length: 19, rect: [8,101 169.234375x17] baseline: 13.296875
+          "No appearance radio"
+      BlockContainer <input> at (13,10) content-size 0x17 inline-block [BFC] children: not-inline
+        BlockContainer <(anonymous)> at (13,10) content-size 0x17 flex-container(column) [FFC] children: not-inline
+          BlockContainer <(anonymous)> at (13,10) content-size 0x17 flex-item [BFC] children: inline
+            frag 0 from BlockContainer start: 0, length: 0, rect: [13,23 0x0] baseline: 0
+            BlockContainer <span> at (13,23) content-size 0x0 inline-block [BFC] children: inline
+              TextNode <#text>
+      TextNode <#text>
+      BreakNode <br>
+      TextNode <#text>
+      BlockContainer <input> at (13,31) content-size 0x17 inline-block [BFC] children: not-inline
+        BlockContainer <(anonymous)> at (13,31) content-size 0x17 flex-container(column) [FFC] children: not-inline
+          BlockContainer <(anonymous)> at (13,31) content-size 0x17 flex-item [BFC] children: inline
+            frag 0 from BlockContainer start: 0, length: 0, rect: [13,44 0x0] baseline: 0
+            BlockContainer <span> at (13,44) content-size 0x0 inline-block [BFC] children: inline
+              TextNode <#text>
+      TextNode <#text>
+      BreakNode <br>
+      TextNode <#text>
+      CheckBox <input> at (8,50) content-size 13x13 inline-block children: not-inline
+      TextNode <#text>
+      BreakNode <br>
+      TextNode <#text>
+      BlockContainer <input> at (8,80) content-size 0x0 inline-block [BFC] children: not-inline
+      TextNode <#text>
+      BreakNode <br>
+      TextNode <#text>
+      RadioButton <input> at (8,85) content-size 12x12 inline-block children: not-inline
+      TextNode <#text>
+      BreakNode <br>
+      TextNode <#text>
+      BlockContainer <input> at (8,114) content-size 0x0 inline-block [BFC] children: not-inline
+      TextNode <#text>
+      BreakNode <br>
+      TextNode <#text>
+
+ViewportPaintable (Viewport<#document>) [0,0 800x600]
+  PaintableWithLines (BlockContainer<HTML>) [0,0 800x126]
+    PaintableWithLines (BlockContainer<BODY>) [8,8 784x110]
+      PaintableWithLines (BlockContainer<INPUT>) [8,8 10x21]
+        PaintableWithLines (BlockContainer(anonymous)) [13,10 0x17]
+          PaintableWithLines (BlockContainer(anonymous)) [13,10 0x17]
+            PaintableWithLines (BlockContainer<SPAN>) [13,23 0x0]
+      TextPaintable (TextNode<#text>)
+      PaintableWithLines (BlockContainer<INPUT>) [8,29 10x21]
+        PaintableWithLines (BlockContainer(anonymous)) [13,31 0x17]
+          PaintableWithLines (BlockContainer(anonymous)) [13,31 0x17]
+            PaintableWithLines (BlockContainer<SPAN>) [13,44 0x0]
+      TextPaintable (TextNode<#text>)
+      CheckBoxPaintable (CheckBox<INPUT>) [8,50 13x13]
+      TextPaintable (TextNode<#text>)
+      PaintableWithLines (BlockContainer<INPUT>) [8,80 0x0]
+      TextPaintable (TextNode<#text>)
+      RadioButtonPaintable (RadioButton<INPUT>) [8,85 12x12]
+      TextPaintable (TextNode<#text>)
+      PaintableWithLines (BlockContainer<INPUT>) [8,114 0x0]
+      TextPaintable (TextNode<#text>)

+ 7 - 0
Tests/LibWeb/Layout/input/css-appearance-none.html

@@ -0,0 +1,7 @@
+<!doctype html>
+<input type="button">Normal button<br>
+<input type="button" style="appearance: none">No appearance button<br>
+<input type="checkbox">Normal checkbox<br>
+<input type="checkbox" style="appearance: none">No appearance checkbox<br>
+<input type="radio">Normal radio<br>
+<input type="radio" style="appearance: none">No appearance radio<br>

+ 5 - 0
Tests/LibWeb/Text/expected/DOM/CharacterData-substringData-break-surrogate-pair.txt

@@ -0,0 +1,5 @@
+Before substringData:
+[0]: 55357
+[1]: 56374
+After substringData(0, 1):
+[0]: 55357

+ 3 - 0
Tests/LibWeb/Text/expected/HTML/set-innerText.txt

@@ -0,0 +1,3 @@
+Empty string: ""
+Simple text: "foo"
+Text with newlines: "<br>foo<br>bar<br>baz<br><br>foobar<br>"

+ 1 - 0
Tests/LibWeb/Text/expected/ShadowDOM/css-media-queries.txt

@@ -0,0 +1 @@
+rgb(0, 255, 0)

+ 1 - 0
Tests/LibWeb/Text/expected/XHR/XMLHttpRequest-request-is-blob.txt

@@ -0,0 +1 @@
+PASS

+ 103 - 0
Tests/LibWeb/Text/expected/form-formEnctype-attribute.txt

@@ -0,0 +1,103 @@
+button: unset
+button.getAttribute('formEnctype') == 'null'
+button.formEnctype == ''
+
+button.setAttribute('formEnctype', '')
+button.getAttribute('formEnctype') == ''
+button.formEnctype == 'application/x-www-form-urlencoded'
+
+button.setAttribute('formEnctype', 'undefined')
+button.getAttribute('formEnctype') == 'undefined'
+button.formEnctype == 'application/x-www-form-urlencoded'
+
+button.setAttribute('formEnctype', 'null')
+button.getAttribute('formEnctype') == 'null'
+button.formEnctype == 'application/x-www-form-urlencoded'
+
+button.setAttribute('formEnctype', 'application/x-www-form-urlencoded')
+button.getAttribute('formEnctype') == 'application/x-www-form-urlencoded'
+button.formEnctype == 'application/x-www-form-urlencoded'
+
+button.setAttribute('formEnctype', 'multipart/form-data')
+button.getAttribute('formEnctype') == 'multipart/form-data'
+button.formEnctype == 'multipart/form-data'
+
+button.setAttribute('formEnctype', 'text/plain')
+button.getAttribute('formEnctype') == 'text/plain'
+button.formEnctype == 'text/plain'
+
+button.setAttribute('formEnctype', 'APPLICATION/X-WWW-FORM-URLENCODED')
+button.getAttribute('formEnctype') == 'APPLICATION/X-WWW-FORM-URLENCODED'
+button.formEnctype == 'application/x-www-form-urlencoded'
+
+button.setAttribute('formEnctype', 'MULTIPART/FORM-DATA')
+button.getAttribute('formEnctype') == 'MULTIPART/FORM-DATA'
+button.formEnctype == 'multipart/form-data'
+
+button.setAttribute('formEnctype', 'tEXt/PlAIn')
+button.getAttribute('formEnctype') == 'tEXt/PlAIn'
+button.formEnctype == 'text/plain'
+
+button.setAttribute('formEnctype', 'text/plain ')
+button.getAttribute('formEnctype') == 'text/plain '
+button.formEnctype == 'application/x-www-form-urlencoded'
+
+button.setAttribute('formEnctype', '7')
+button.getAttribute('formEnctype') == '7'
+button.formEnctype == 'application/x-www-form-urlencoded'
+
+button.setAttribute('formEnctype', '5%')
+button.getAttribute('formEnctype') == '5%'
+button.formEnctype == 'application/x-www-form-urlencoded'
+
+input: unset
+input.getAttribute('formEnctype') == 'null'
+input.formEnctype == ''
+
+input.setAttribute('formEnctype', '')
+input.getAttribute('formEnctype') == ''
+input.formEnctype == 'application/x-www-form-urlencoded'
+
+input.setAttribute('formEnctype', 'undefined')
+input.getAttribute('formEnctype') == 'undefined'
+input.formEnctype == 'application/x-www-form-urlencoded'
+
+input.setAttribute('formEnctype', 'null')
+input.getAttribute('formEnctype') == 'null'
+input.formEnctype == 'application/x-www-form-urlencoded'
+
+input.setAttribute('formEnctype', 'application/x-www-form-urlencoded')
+input.getAttribute('formEnctype') == 'application/x-www-form-urlencoded'
+input.formEnctype == 'application/x-www-form-urlencoded'
+
+input.setAttribute('formEnctype', 'multipart/form-data')
+input.getAttribute('formEnctype') == 'multipart/form-data'
+input.formEnctype == 'multipart/form-data'
+
+input.setAttribute('formEnctype', 'text/plain')
+input.getAttribute('formEnctype') == 'text/plain'
+input.formEnctype == 'text/plain'
+
+input.setAttribute('formEnctype', 'APPLICATION/X-WWW-FORM-URLENCODED')
+input.getAttribute('formEnctype') == 'APPLICATION/X-WWW-FORM-URLENCODED'
+input.formEnctype == 'application/x-www-form-urlencoded'
+
+input.setAttribute('formEnctype', 'MULTIPART/FORM-DATA')
+input.getAttribute('formEnctype') == 'MULTIPART/FORM-DATA'
+input.formEnctype == 'multipart/form-data'
+
+input.setAttribute('formEnctype', 'tEXt/PlAIn')
+input.getAttribute('formEnctype') == 'tEXt/PlAIn'
+input.formEnctype == 'text/plain'
+
+input.setAttribute('formEnctype', 'text/plain ')
+input.getAttribute('formEnctype') == 'text/plain '
+input.formEnctype == 'application/x-www-form-urlencoded'
+
+input.setAttribute('formEnctype', '7')
+input.getAttribute('formEnctype') == '7'
+input.formEnctype == 'application/x-www-form-urlencoded'
+
+input.setAttribute('formEnctype', '5%')
+input.getAttribute('formEnctype') == '5%'
+input.formEnctype == 'application/x-www-form-urlencoded'

+ 103 - 0
Tests/LibWeb/Text/expected/form-formMethod-attribute.txt

@@ -0,0 +1,103 @@
+button: unset
+button.getAttribute('formMethod') == 'null'
+button.formMethod == ''
+
+button.setAttribute('formMethod', '')
+button.getAttribute('formMethod') == ''
+button.formMethod == 'get'
+
+button.setAttribute('formMethod', 'undefined')
+button.getAttribute('formMethod') == 'undefined'
+button.formMethod == 'get'
+
+button.setAttribute('formMethod', 'null')
+button.getAttribute('formMethod') == 'null'
+button.formMethod == 'get'
+
+button.setAttribute('formMethod', 'get')
+button.getAttribute('formMethod') == 'get'
+button.formMethod == 'get'
+
+button.setAttribute('formMethod', 'post')
+button.getAttribute('formMethod') == 'post'
+button.formMethod == 'post'
+
+button.setAttribute('formMethod', 'dialog')
+button.getAttribute('formMethod') == 'dialog'
+button.formMethod == 'dialog'
+
+button.setAttribute('formMethod', 'GeT')
+button.getAttribute('formMethod') == 'GeT'
+button.formMethod == 'get'
+
+button.setAttribute('formMethod', 'POST')
+button.getAttribute('formMethod') == 'POST'
+button.formMethod == 'post'
+
+button.setAttribute('formMethod', 'DIAlog')
+button.getAttribute('formMethod') == 'DIAlog'
+button.formMethod == 'dialog'
+
+button.setAttribute('formMethod', 'foo')
+button.getAttribute('formMethod') == 'foo'
+button.formMethod == 'get'
+
+button.setAttribute('formMethod', 'xpost')
+button.getAttribute('formMethod') == 'xpost'
+button.formMethod == 'get'
+
+button.setAttribute('formMethod', '5%')
+button.getAttribute('formMethod') == '5%'
+button.formMethod == 'get'
+
+input: unset
+input.getAttribute('formMethod') == 'null'
+input.formMethod == ''
+
+input.setAttribute('formMethod', '')
+input.getAttribute('formMethod') == ''
+input.formMethod == 'get'
+
+input.setAttribute('formMethod', 'undefined')
+input.getAttribute('formMethod') == 'undefined'
+input.formMethod == 'get'
+
+input.setAttribute('formMethod', 'null')
+input.getAttribute('formMethod') == 'null'
+input.formMethod == 'get'
+
+input.setAttribute('formMethod', 'get')
+input.getAttribute('formMethod') == 'get'
+input.formMethod == 'get'
+
+input.setAttribute('formMethod', 'post')
+input.getAttribute('formMethod') == 'post'
+input.formMethod == 'post'
+
+input.setAttribute('formMethod', 'dialog')
+input.getAttribute('formMethod') == 'dialog'
+input.formMethod == 'dialog'
+
+input.setAttribute('formMethod', 'GeT')
+input.getAttribute('formMethod') == 'GeT'
+input.formMethod == 'get'
+
+input.setAttribute('formMethod', 'POST')
+input.getAttribute('formMethod') == 'POST'
+input.formMethod == 'post'
+
+input.setAttribute('formMethod', 'DIAlog')
+input.getAttribute('formMethod') == 'DIAlog'
+input.formMethod == 'dialog'
+
+input.setAttribute('formMethod', 'foo')
+input.getAttribute('formMethod') == 'foo'
+input.formMethod == 'get'
+
+input.setAttribute('formMethod', 'xpost')
+input.getAttribute('formMethod') == 'xpost'
+input.formMethod == 'get'
+
+input.setAttribute('formMethod', '5%')
+input.getAttribute('formMethod') == '5%'
+input.formMethod == 'get'

+ 51 - 0
Tests/LibWeb/Text/expected/form-method-attribute.txt

@@ -0,0 +1,51 @@
+form: unset
+form.getAttribute('method') == 'null'
+form.method == 'get'
+
+form.setAttribute('method', '')
+form.getAttribute('method') == ''
+form.method == 'get'
+
+form.setAttribute('method', 'undefined')
+form.getAttribute('method') == 'undefined'
+form.method == 'get'
+
+form.setAttribute('method', 'null')
+form.getAttribute('method') == 'null'
+form.method == 'get'
+
+form.setAttribute('method', 'get')
+form.getAttribute('method') == 'get'
+form.method == 'get'
+
+form.setAttribute('method', 'post')
+form.getAttribute('method') == 'post'
+form.method == 'post'
+
+form.setAttribute('method', 'dialog')
+form.getAttribute('method') == 'dialog'
+form.method == 'dialog'
+
+form.setAttribute('method', 'GeT')
+form.getAttribute('method') == 'GeT'
+form.method == 'get'
+
+form.setAttribute('method', 'POST')
+form.getAttribute('method') == 'POST'
+form.method == 'post'
+
+form.setAttribute('method', 'DIAlog')
+form.getAttribute('method') == 'DIAlog'
+form.method == 'dialog'
+
+form.setAttribute('method', 'foo')
+form.getAttribute('method') == 'foo'
+form.method == 'get'
+
+form.setAttribute('method', 'xpost')
+form.getAttribute('method') == 'xpost'
+form.method == 'get'
+
+form.setAttribute('method', '5%')
+form.getAttribute('method') == '5%'
+form.method == 'get'

+ 1 - 0
Tests/LibWeb/Text/expected/query-scroll-size-of-inline-node.txt

@@ -0,0 +1 @@
+Scroll Height: 0px, Scroll Width: 0px

+ 18 - 0
Tests/LibWeb/Text/input/DOM/CharacterData-substringData-break-surrogate-pair.html

@@ -0,0 +1,18 @@
+<script src="../include.js"></script>
+<script>
+    test(() => {
+        const str = '\uD83D\uDC36'; // 🐶
+        let t = document.createTextNode(str);
+        println("Before substringData:");
+        for (let i = 0; i < t.length; ++i) {
+            println("[" + i + "]: " + t.data.charCodeAt(i));
+        }
+
+        // Break the surrogate pair
+        const invalidSubstring = t.substringData(0, 1);
+        println("After substringData(0, 1):");
+        for (let i = 0; i < invalidSubstring.length; ++i) {
+            println("[" + i + "]: " + invalidSubstring.charCodeAt(i));
+        }
+    });
+</script>

+ 12 - 0
Tests/LibWeb/Text/input/HTML/set-innerText.html

@@ -0,0 +1,12 @@
+<script src="../include.js"></script>
+<script>
+    test(() => {
+        let element = document.createElement('div');
+        element.innerText = "";
+        println('Empty string: "' + element.innerHTML + '"');
+        element.innerText = "foo";
+        println('Simple text: "' + element.innerHTML + '"');
+        element.innerText = "\rfoo\nbar\r\nbaz\n\rfoobar\n";
+        println('Text with newlines: "' + element.innerHTML + '"');
+    });
+</script>

+ 21 - 0
Tests/LibWeb/Text/input/ShadowDOM/css-media-queries.html

@@ -0,0 +1,21 @@
+<div id="myShadowHost">
+<template shadowrootmode="open">
+<style>
+span { color: red; }
+@media (min-width: 0px) {
+span { color: lime; }
+}
+@media (max-width: 0px) {
+span { color: blue !important; }
+}
+</style>
+<span></span>
+</template>
+</div>
+<script src="../include.js"></script>
+<script>
+    test(() => {
+        let span = myShadowHost.shadowRoot.firstElementChild.nextElementSibling;
+        println(getComputedStyle(span).color);
+    });
+</script>

+ 14 - 0
Tests/LibWeb/Text/input/XHR/XMLHttpRequest-request-is-blob.html

@@ -0,0 +1,14 @@
+<script src="../include.js"></script>
+<script>
+    asyncTest(done => {
+        const xhr = new XMLHttpRequest();
+        const data = "PASS";
+        const url = URL.createObjectURL(new Blob([data]));
+        xhr.open("GET", url);
+        xhr.onload = function () {
+            println(xhr.responseText);
+            done();
+        };
+        xhr.send();
+    });
+</script>

+ 40 - 0
Tests/LibWeb/Text/input/form-formEnctype-attribute.html

@@ -0,0 +1,40 @@
+<script src="./include.js"></script>
+<script>
+    test(() => {
+        const btn = document.createElement('button');
+        const input = document.createElement('input');
+        const values = [
+            '', undefined, null,
+            'application/x-www-form-urlencoded',
+            'multipart/form-data',
+            'text/plain',
+            'APPLICATION/X-WWW-FORM-URLENCODED',
+            'MULTIPART/FORM-DATA',
+            'tEXt/PlAIn',
+            'text/plain ', '7', '5%'
+        ];
+
+        println('button: unset');
+        println(`button.getAttribute('formEnctype') == '${btn.getAttribute('formEnctype')}'`);
+        println(`button.formEnctype == '${btn.formEnctype}'`);
+        for (value of values) {
+            btn.setAttribute('formEnctype', value);
+            println('');
+            println(`button.setAttribute('formEnctype', '${value}')`);
+            println(`button.getAttribute('formEnctype') == '${btn.getAttribute('formEnctype')}'`);
+            println(`button.formEnctype == '${btn.formEnctype}'`);
+        }
+
+        println('');
+        println('input: unset');
+        println(`input.getAttribute('formEnctype') == '${input.getAttribute('formEnctype')}'`);
+        println(`input.formEnctype == '${input.formEnctype}'`);
+        for (value of values) {
+            input.setAttribute('formEnctype', value);
+            println('');
+            println(`input.setAttribute('formEnctype', '${value}')`);
+            println(`input.getAttribute('formEnctype') == '${input.getAttribute('formEnctype')}'`);
+            println(`input.formEnctype == '${input.formEnctype}'`);
+        }
+    });
+</script>

+ 36 - 0
Tests/LibWeb/Text/input/form-formMethod-attribute.html

@@ -0,0 +1,36 @@
+<script src="./include.js"></script>
+<script>
+    test(() => {
+        const btn = document.createElement('button');
+        const input = document.createElement('input');
+        const values = [
+            '', undefined, null,
+            'get', 'post', 'dialog',
+            'GeT', 'POST', 'DIAlog',
+            'foo', 'xpost', '5%'
+        ];
+
+        println('button: unset');
+        println(`button.getAttribute('formMethod') == '${btn.getAttribute('formMethod')}'`);
+        println(`button.formMethod == '${btn.formMethod}'`);
+        for (value of values) {
+            btn.setAttribute('formMethod', value);
+            println('');
+            println(`button.setAttribute('formMethod', '${value}')`);
+            println(`button.getAttribute('formMethod') == '${btn.getAttribute('formMethod')}'`);
+            println(`button.formMethod == '${btn.formMethod}'`);
+        }
+
+        println('');
+        println('input: unset')
+        println(`input.getAttribute('formMethod') == '${input.getAttribute('formMethod')}'`);
+        println(`input.formMethod == '${input.formMethod}'`);
+        for (value of values) {
+            input.setAttribute('formMethod', value);
+            println('');
+            println(`input.setAttribute('formMethod', '${value}')`);
+            println(`input.getAttribute('formMethod') == '${input.getAttribute('formMethod')}'`);
+            println(`input.formMethod == '${input.formMethod}'`);
+        }
+    });
+</script>

+ 23 - 0
Tests/LibWeb/Text/input/form-method-attribute.html

@@ -0,0 +1,23 @@
+<script src="./include.js"></script>
+<script>
+    test(() => {
+        const form = document.createElement('form');
+        const values = [
+            '', undefined, null,
+            'get', 'post', 'dialog',
+            'GeT', 'POST', 'DIAlog',
+            'foo', 'xpost', '5%'
+        ];
+
+        println('form: unset');
+        println(`form.getAttribute('method') == '${form.getAttribute('method')}'`);
+        println(`form.method == '${form.method}'`);
+        for (value of values) {
+            form.setAttribute('method', value);
+            println('');
+            println(`form.setAttribute('method', '${value}')`);
+            println(`form.getAttribute('method') == '${form.getAttribute('method')}'`);
+            println(`form.method == '${form.method}'`);
+        }
+    });
+</script>

+ 11 - 0
Tests/LibWeb/Text/input/query-scroll-size-of-inline-node.html

@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<span id="empty-span"></span>
+<script src="include.js"></script>
+<script>
+test(() => {
+    const hiddenDiv = document.getElementById('empty-span');
+    const scrollHeight = hiddenDiv.scrollHeight;
+    const scrollWidth = hiddenDiv.scrollWidth;
+    println(`Scroll Height: ${scrollHeight}px, Scroll Width: ${scrollWidth}px`);
+});
+</script>

+ 2 - 1
Userland/Libraries/LibCore/CMakeLists.txt

@@ -8,7 +8,6 @@ set(SOURCES
     Environment.cpp
     Environment.cpp
     File.cpp
     File.cpp
     StandardPaths.cpp
     StandardPaths.cpp
-    System.cpp
     Version.cpp
     Version.cpp
 )
 )
 
 
@@ -16,11 +15,13 @@ if (WIN32)
     list(APPEND SOURCES
     list(APPEND SOURCES
         DirectoryEntryWindows.cpp
         DirectoryEntryWindows.cpp
         DirIteratorWindows.cpp
         DirIteratorWindows.cpp
+        SystemWindows.cpp
     )
     )
 else()
 else()
     list(APPEND SOURCES
     list(APPEND SOURCES
         DirectoryEntry.cpp
         DirectoryEntry.cpp
         DirIterator.cpp
         DirIterator.cpp
+        System.cpp
     )
     )
 endif()
 endif()
 
 

+ 16 - 2
Userland/Libraries/LibCore/File.cpp

@@ -8,7 +8,15 @@
 #include <LibCore/File.h>
 #include <LibCore/File.h>
 #include <LibCore/System.h>
 #include <LibCore/System.h>
 #include <fcntl.h>
 #include <fcntl.h>
-#include <unistd.h>
+
+#if !defined(AK_OS_WINDOWS)
+#    include <unistd.h>
+#else
+#    include <WinSock2.h>
+#    define STDIN_FILENO _fileno(stdin)
+#    define STDOUT_FILENO _fileno(stdout)
+#    define STDERR_FILENO _fileno(stderr)
+#endif
 
 
 namespace Core {
 namespace Core {
 
 
@@ -85,8 +93,14 @@ int File::open_mode_to_options(OpenMode mode)
         flags |= O_EXCL;
         flags |= O_EXCL;
     if (!has_flag(mode, OpenMode::KeepOnExec))
     if (!has_flag(mode, OpenMode::KeepOnExec))
         flags |= O_CLOEXEC;
         flags |= O_CLOEXEC;
-    if (has_flag(mode, OpenMode::Nonblocking))
+    if (has_flag(mode, OpenMode::Nonblocking)) {
+#if !defined(AK_OS_WINDOWS)
         flags |= O_NONBLOCK;
         flags |= O_NONBLOCK;
+#else
+        dbgln("Core::File::OpenMode::Nonblocking is not implemented");
+        VERIFY_NOT_REACHED();
+#endif
+    }
 
 
     // Some open modes, like `ReadWrite` imply the ability to create the file if it doesn't exist.
     // Some open modes, like `ReadWrite` imply the ability to create the file if it doesn't exist.
     // Certain applications may not want this privledge, and for compability reasons, this is
     // Certain applications may not want this privledge, and for compability reasons, this is

+ 37 - 25
Userland/Libraries/LibCore/System.h

@@ -14,23 +14,30 @@
 #include <AK/StringView.h>
 #include <AK/StringView.h>
 #include <AK/Vector.h>
 #include <AK/Vector.h>
 #include <fcntl.h>
 #include <fcntl.h>
-#include <netdb.h>
-#include <poll.h>
-#include <pwd.h>
 #include <signal.h>
 #include <signal.h>
-#include <spawn.h>
-#include <sys/ioctl.h>
-#include <sys/resource.h>
-#include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/utsname.h>
-#include <sys/wait.h>
-#include <termios.h>
 #include <time.h>
 #include <time.h>
-#include <utime.h>
 
 
-#if !defined(AK_OS_BSD_GENERIC) && !defined(AK_OS_ANDROID)
+#if !defined(AK_OS_WINDOWS)
+#    include <netdb.h>
+#    include <poll.h>
+#    include <pwd.h>
+#    include <spawn.h>
+#    include <sys/ioctl.h>
+#    include <sys/resource.h>
+#    include <sys/socket.h>
+#    include <sys/time.h>
+#    include <sys/utsname.h>
+#    include <sys/wait.h>
+#    include <termios.h>
+#    include <utime.h>
+#else
+#    define O_CLOEXEC O_NOINHERIT
+using sighandler_t = void (*)(int);
+using socklen_t = int;
+#endif
+
+#if !defined(AK_OS_BSD_GENERIC) && !defined(AK_OS_ANDROID) && !defined(AK_OS_WINDOWS)
 #    include <shadow.h>
 #    include <shadow.h>
 #endif
 #endif
 
 
@@ -71,7 +78,6 @@ ErrorOr<struct stat> stat(StringView path);
 ErrorOr<struct stat> lstat(StringView path);
 ErrorOr<struct stat> lstat(StringView path);
 ErrorOr<ssize_t> read(int fd, Bytes buffer);
 ErrorOr<ssize_t> read(int fd, Bytes buffer);
 ErrorOr<ssize_t> write(int fd, ReadonlyBytes buffer);
 ErrorOr<ssize_t> write(int fd, ReadonlyBytes buffer);
-ErrorOr<void> kill(pid_t, int signal);
 ErrorOr<int> dup(int source_fd);
 ErrorOr<int> dup(int source_fd);
 ErrorOr<int> dup2(int source_fd, int destination_fd);
 ErrorOr<int> dup2(int source_fd, int destination_fd);
 ErrorOr<ByteString> getcwd();
 ErrorOr<ByteString> getcwd();
@@ -79,17 +85,8 @@ ErrorOr<void> ioctl(int fd, unsigned request, ...);
 ErrorOr<struct termios> tcgetattr(int fd);
 ErrorOr<struct termios> tcgetattr(int fd);
 ErrorOr<void> tcsetattr(int fd, int optional_actions, struct termios const&);
 ErrorOr<void> tcsetattr(int fd, int optional_actions, struct termios const&);
 ErrorOr<void> chmod(StringView pathname, mode_t mode);
 ErrorOr<void> chmod(StringView pathname, mode_t mode);
-ErrorOr<void> chown(StringView pathname, uid_t uid, gid_t gid);
-
-ErrorOr<pid_t> posix_spawn(StringView path, posix_spawn_file_actions_t const* file_actions, posix_spawnattr_t const* attr, char* const arguments[], char* const envp[]);
-ErrorOr<pid_t> posix_spawnp(StringView path, posix_spawn_file_actions_t* const file_actions, posix_spawnattr_t* const attr, char* const arguments[], char* const envp[]);
 ErrorOr<off_t> lseek(int fd, off_t, int whence);
 ErrorOr<off_t> lseek(int fd, off_t, int whence);
 
 
-struct WaitPidResult {
-    pid_t pid;
-    int status;
-};
-ErrorOr<WaitPidResult> waitpid(pid_t waitee, int options = 0);
 ErrorOr<bool> isatty(int fd);
 ErrorOr<bool> isatty(int fd);
 ErrorOr<void> link(StringView old_path, StringView new_path);
 ErrorOr<void> link(StringView old_path, StringView new_path);
 ErrorOr<void> symlink(StringView target, StringView link_path);
 ErrorOr<void> symlink(StringView target, StringView link_path);
@@ -99,11 +96,9 @@ ErrorOr<void> rmdir(StringView path);
 ErrorOr<int> mkstemp(Span<char> pattern);
 ErrorOr<int> mkstemp(Span<char> pattern);
 ErrorOr<String> mkdtemp(Span<char> pattern);
 ErrorOr<String> mkdtemp(Span<char> pattern);
 ErrorOr<void> fchmod(int fd, mode_t mode);
 ErrorOr<void> fchmod(int fd, mode_t mode);
-ErrorOr<void> fchown(int fd, uid_t, gid_t);
 ErrorOr<void> rename(StringView old_path, StringView new_path);
 ErrorOr<void> rename(StringView old_path, StringView new_path);
 ErrorOr<void> unlink(StringView path);
 ErrorOr<void> unlink(StringView path);
 ErrorOr<void> utimensat(int fd, StringView path, struct timespec const times[2], int flag);
 ErrorOr<void> utimensat(int fd, StringView path, struct timespec const times[2], int flag);
-ErrorOr<struct utsname> uname();
 ErrorOr<Array<int, 2>> pipe2(int flags);
 ErrorOr<Array<int, 2>> pipe2(int flags);
 
 
 ErrorOr<int> socket(int domain, int type, int protocol);
 ErrorOr<int> socket(int domain, int type, int protocol);
@@ -127,6 +122,20 @@ ErrorOr<void> access(StringView pathname, int mode, int flags = 0);
 ErrorOr<ByteString> readlink(StringView pathname);
 ErrorOr<ByteString> readlink(StringView pathname);
 ErrorOr<int> poll(Span<struct pollfd>, int timeout);
 ErrorOr<int> poll(Span<struct pollfd>, int timeout);
 
 
+#if !defined(AK_OS_WINDOWS)
+ErrorOr<void> kill(pid_t, int signal);
+ErrorOr<void> chown(StringView pathname, uid_t uid, gid_t gid);
+ErrorOr<pid_t> posix_spawn(StringView path, posix_spawn_file_actions_t const* file_actions, posix_spawnattr_t const* attr, char* const arguments[], char* const envp[]);
+ErrorOr<pid_t> posix_spawnp(StringView path, posix_spawn_file_actions_t* const file_actions, posix_spawnattr_t* const attr, char* const arguments[], char* const envp[]);
+
+struct WaitPidResult {
+    pid_t pid;
+    int status;
+};
+ErrorOr<WaitPidResult> waitpid(pid_t waitee, int options = 0);
+ErrorOr<void> fchown(int fd, uid_t, gid_t);
+ErrorOr<struct utsname> uname();
+
 class AddressInfoVector {
 class AddressInfoVector {
     AK_MAKE_NONCOPYABLE(AddressInfoVector);
     AK_MAKE_NONCOPYABLE(AddressInfoVector);
     AK_MAKE_DEFAULT_MOVABLE(AddressInfoVector);
     AK_MAKE_DEFAULT_MOVABLE(AddressInfoVector);
@@ -158,13 +167,16 @@ private:
 };
 };
 
 
 ErrorOr<AddressInfoVector> getaddrinfo(char const* nodename, char const* servname, struct addrinfo const& hints);
 ErrorOr<AddressInfoVector> getaddrinfo(char const* nodename, char const* servname, struct addrinfo const& hints);
+#endif
 
 
 unsigned hardware_concurrency();
 unsigned hardware_concurrency();
 u64 physical_memory_bytes();
 u64 physical_memory_bytes();
 
 
 ErrorOr<ByteString> current_executable_path();
 ErrorOr<ByteString> current_executable_path();
 
 
+#if !defined(AK_OS_WINDOWS)
 ErrorOr<rlimit> get_resource_limits(int resource);
 ErrorOr<rlimit> get_resource_limits(int resource);
 ErrorOr<void> set_resource_limits(int resource, rlim_t limit);
 ErrorOr<void> set_resource_limits(int resource, rlim_t limit);
+#endif
 
 
 }
 }

+ 91 - 0
Userland/Libraries/LibCore/SystemWindows.cpp

@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2021-2022, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2021-2022, Kenneth Myhra <kennethmyhra@serenityos.org>
+ * Copyright (c) 2021-2022, Sam Atkins <atkinssj@serenityos.org>
+ * Copyright (c) 2022, Matthias Zimmerman <matthias291999@gmail.com>
+ * Copyright (c) 2023, Cameron Youell <cameronyouell@gmail.com>
+ * Copyright (c) 2024, stasoid <stasoid@yahoo.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/ByteString.h>
+#include <AK/ScopeGuard.h>
+#include <LibCore/System.h>
+#include <WinSock2.h>
+#include <io.h>
+
+namespace Core::System {
+
+ErrorOr<int> open(StringView path, int options, mode_t mode)
+{
+    ByteString string_path = path;
+    int rc = _open(string_path.characters(), options, mode);
+    if (rc < 0)
+        return Error::from_syscall("open"sv, -errno);
+    return rc;
+}
+
+ErrorOr<void> close(int fd)
+{
+    if (_close(fd) < 0)
+        return Error::from_syscall("close"sv, -errno);
+    return {};
+}
+
+ErrorOr<ssize_t> read(int fd, Bytes buffer)
+{
+    int rc = _read(fd, buffer.data(), buffer.size());
+    if (rc < 0)
+        return Error::from_syscall("read"sv, -errno);
+    return rc;
+}
+
+ErrorOr<ssize_t> write(int fd, ReadonlyBytes buffer)
+{
+    int rc = _write(fd, buffer.data(), buffer.size());
+    if (rc < 0)
+        return Error::from_syscall("write"sv, -errno);
+    return rc;
+}
+
+ErrorOr<off_t> lseek(int fd, off_t offset, int whence)
+{
+    long rc = _lseek(fd, offset, whence);
+    if (rc < 0)
+        return Error::from_syscall("lseek"sv, -errno);
+    return rc;
+}
+
+ErrorOr<void> ftruncate(int fd, off_t length)
+{
+    long position = _tell(fd);
+    if (position == -1)
+        return Error::from_errno(errno);
+
+    ScopeGuard restore_position { [&] { _lseek(fd, position, SEEK_SET); } };
+
+    auto result = lseek(fd, length, SEEK_SET);
+    if (result.is_error())
+        return result.release_error();
+
+    if (SetEndOfFile((HANDLE)_get_osfhandle(fd)) == 0)
+        return Error::from_windows_error(GetLastError());
+    return {};
+}
+
+ErrorOr<struct stat> fstat(int fd)
+{
+    struct stat st = {};
+    if (::fstat(fd, &st) < 0)
+        return Error::from_syscall("fstat"sv, -errno);
+    return st;
+}
+
+ErrorOr<void> ioctl(int, unsigned, ...)
+{
+    dbgln("Core::System::ioctl() is not implemented");
+    VERIFY_NOT_REACHED();
+}
+
+}

+ 4 - 4
Userland/Libraries/LibWeb/CSS/CountersSet.cpp

@@ -11,7 +11,7 @@
 namespace Web::CSS {
 namespace Web::CSS {
 
 
 // https://drafts.csswg.org/css-lists-3/#instantiate-counter
 // https://drafts.csswg.org/css-lists-3/#instantiate-counter
-Counter& CountersSet::instantiate_a_counter(FlyString name, i32 originating_element_id, bool reversed, Optional<CounterValue> value)
+Counter& CountersSet::instantiate_a_counter(FlyString name, UniqueNodeID originating_element_id, bool reversed, Optional<CounterValue> value)
 {
 {
     // 1. Let counters be element’s CSS counters set.
     // 1. Let counters be element’s CSS counters set.
     auto* element = DOM::Node::from_unique_id(originating_element_id);
     auto* element = DOM::Node::from_unique_id(originating_element_id);
@@ -48,7 +48,7 @@ Counter& CountersSet::instantiate_a_counter(FlyString name, i32 originating_elem
 }
 }
 
 
 // https://drafts.csswg.org/css-lists-3/#propdef-counter-set
 // https://drafts.csswg.org/css-lists-3/#propdef-counter-set
-void CountersSet::set_a_counter(FlyString name, i32 originating_element_id, CounterValue value)
+void CountersSet::set_a_counter(FlyString name, UniqueNodeID originating_element_id, CounterValue value)
 {
 {
     if (auto existing_counter = last_counter_with_name(name); existing_counter.has_value()) {
     if (auto existing_counter = last_counter_with_name(name); existing_counter.has_value()) {
         existing_counter->value = value;
         existing_counter->value = value;
@@ -63,7 +63,7 @@ void CountersSet::set_a_counter(FlyString name, i32 originating_element_id, Coun
 }
 }
 
 
 // https://drafts.csswg.org/css-lists-3/#propdef-counter-increment
 // https://drafts.csswg.org/css-lists-3/#propdef-counter-increment
-void CountersSet::increment_a_counter(FlyString name, i32 originating_element_id, CounterValue amount)
+void CountersSet::increment_a_counter(FlyString name, UniqueNodeID originating_element_id, CounterValue amount)
 {
 {
     if (auto existing_counter = last_counter_with_name(name); existing_counter.has_value()) {
     if (auto existing_counter = last_counter_with_name(name); existing_counter.has_value()) {
         // FIXME: How should we handle existing counters with no value? Can that happen?
         // FIXME: How should we handle existing counters with no value? Can that happen?
@@ -88,7 +88,7 @@ Optional<Counter&> CountersSet::last_counter_with_name(FlyString const& name)
     return {};
     return {};
 }
 }
 
 
-Optional<Counter&> CountersSet::counter_with_same_name_and_creator(FlyString const& name, i32 originating_element_id)
+Optional<Counter&> CountersSet::counter_with_same_name_and_creator(FlyString const& name, UniqueNodeID originating_element_id)
 {
 {
     return m_counters.first_matching([&](auto& it) {
     return m_counters.first_matching([&](auto& it) {
         return it.name == name && it.originating_element_id == originating_element_id;
         return it.name == name && it.originating_element_id == originating_element_id;

+ 6 - 5
Userland/Libraries/LibWeb/CSS/CountersSet.h

@@ -9,6 +9,7 @@
 #include <AK/Checked.h>
 #include <AK/Checked.h>
 #include <AK/FlyString.h>
 #include <AK/FlyString.h>
 #include <AK/Optional.h>
 #include <AK/Optional.h>
+#include <LibWeb/Forward.h>
 
 
 namespace Web::CSS {
 namespace Web::CSS {
 
 
@@ -21,7 +22,7 @@ using CounterValue = Checked<i32>;
 // https://drafts.csswg.org/css-lists-3/#counter
 // https://drafts.csswg.org/css-lists-3/#counter
 struct Counter {
 struct Counter {
     FlyString name;
     FlyString name;
-    i32 originating_element_id; // "creator"
+    UniqueNodeID originating_element_id; // "creator"
     bool reversed { false };
     bool reversed { false };
     Optional<CounterValue> value;
     Optional<CounterValue> value;
 };
 };
@@ -32,13 +33,13 @@ public:
     CountersSet() = default;
     CountersSet() = default;
     ~CountersSet() = default;
     ~CountersSet() = default;
 
 
-    Counter& instantiate_a_counter(FlyString name, i32 originating_element_id, bool reversed, Optional<CounterValue>);
-    void set_a_counter(FlyString name, i32 originating_element_id, CounterValue value);
-    void increment_a_counter(FlyString name, i32 originating_element_id, CounterValue amount);
+    Counter& instantiate_a_counter(FlyString name, UniqueNodeID originating_element_id, bool reversed, Optional<CounterValue>);
+    void set_a_counter(FlyString name, UniqueNodeID originating_element_id, CounterValue value);
+    void increment_a_counter(FlyString name, UniqueNodeID originating_element_id, CounterValue amount);
     void append_copy(Counter const&);
     void append_copy(Counter const&);
 
 
     Optional<Counter&> last_counter_with_name(FlyString const& name);
     Optional<Counter&> last_counter_with_name(FlyString const& name);
-    Optional<Counter&> counter_with_same_name_and_creator(FlyString const& name, i32 originating_element_id);
+    Optional<Counter&> counter_with_same_name_and_creator(FlyString const& name, UniqueNodeID originating_element_id);
 
 
     Vector<Counter> const& counters() const { return m_counters; }
     Vector<Counter> const& counters() const { return m_counters; }
     bool is_empty() const { return m_counters.is_empty(); }
     bool is_empty() const { return m_counters.is_empty(); }

+ 2 - 8
Userland/Libraries/LibWeb/CSS/StyleComputer.cpp

@@ -328,14 +328,8 @@ void StyleComputer::for_each_stylesheet(CascadeOrigin cascade_origin, Callback c
             callback(*m_user_style_sheet, {});
             callback(*m_user_style_sheet, {});
     }
     }
     if (cascade_origin == CascadeOrigin::Author) {
     if (cascade_origin == CascadeOrigin::Author) {
-        document().for_each_active_css_style_sheet([&](CSSStyleSheet& sheet) {
-            callback(sheet, {});
-        });
-
-        const_cast<DOM::Document&>(document()).for_each_shadow_root([&](DOM::ShadowRoot& shadow_root) {
-            shadow_root.for_each_css_style_sheet([&](CSSStyleSheet& sheet) {
-                callback(sheet, &shadow_root);
-            });
+        document().for_each_active_css_style_sheet([&](auto& sheet, auto shadow_root) {
+            callback(sheet, shadow_root);
         });
         });
     }
     }
 }
 }

+ 4 - 3
Userland/Libraries/LibWeb/CSS/StyleSheetIdentifier.cpp

@@ -50,7 +50,8 @@ template<>
 ErrorOr<void> encode(Encoder& encoder, Web::CSS::StyleSheetIdentifier const& style_sheet_source)
 ErrorOr<void> encode(Encoder& encoder, Web::CSS::StyleSheetIdentifier const& style_sheet_source)
 {
 {
     TRY(encoder.encode(style_sheet_source.type));
     TRY(encoder.encode(style_sheet_source.type));
-    TRY(encoder.encode(style_sheet_source.dom_element_unique_id));
+    Optional<i64> dom_element_unique_id = style_sheet_source.dom_element_unique_id.has_value() ? Optional<i64>(style_sheet_source.dom_element_unique_id.value()) : Optional<i64> {};
+    TRY(encoder.encode(dom_element_unique_id));
     TRY(encoder.encode(style_sheet_source.url));
     TRY(encoder.encode(style_sheet_source.url));
 
 
     return {};
     return {};
@@ -60,12 +61,12 @@ template<>
 ErrorOr<Web::CSS::StyleSheetIdentifier> decode(Decoder& decoder)
 ErrorOr<Web::CSS::StyleSheetIdentifier> decode(Decoder& decoder)
 {
 {
     auto type = TRY(decoder.decode<Web::CSS::StyleSheetIdentifier::Type>());
     auto type = TRY(decoder.decode<Web::CSS::StyleSheetIdentifier::Type>());
-    auto dom_element_unique_id = TRY(decoder.decode<Optional<i32>>());
+    auto dom_element_unique_id = TRY(decoder.decode<Optional<i64>>());
     auto url = TRY(decoder.decode<Optional<String>>());
     auto url = TRY(decoder.decode<Optional<String>>());
 
 
     return Web::CSS::StyleSheetIdentifier {
     return Web::CSS::StyleSheetIdentifier {
         .type = type,
         .type = type,
-        .dom_element_unique_id = move(dom_element_unique_id),
+        .dom_element_unique_id = dom_element_unique_id.has_value() ? Web::UniqueNodeID(dom_element_unique_id.value()) : Optional<Web::UniqueNodeID> {},
         .url = move(url),
         .url = move(url),
     };
     };
 }
 }

+ 2 - 1
Userland/Libraries/LibWeb/CSS/StyleSheetIdentifier.h

@@ -8,6 +8,7 @@
 
 
 #include <LibIPC/Forward.h>
 #include <LibIPC/Forward.h>
 #include <LibURL/URL.h>
 #include <LibURL/URL.h>
+#include <LibWeb/Forward.h>
 
 
 namespace Web::CSS {
 namespace Web::CSS {
 
 
@@ -19,7 +20,7 @@ struct StyleSheetIdentifier {
         UserAgent,
         UserAgent,
         UserStyle,
         UserStyle,
     } type;
     } type;
-    Optional<i32> dom_element_unique_id {};
+    Optional<UniqueNodeID> dom_element_unique_id {};
     Optional<String> url {};
     Optional<String> url {};
 };
 };
 
 

+ 1 - 1
Userland/Libraries/LibWeb/DOM/AccessibilityTreeNode.cpp

@@ -42,7 +42,7 @@ void AccessibilityTreeNode::serialize_tree_as_json(JsonObjectSerializer<StringBu
             MUST(object.add("name"sv, name));
             MUST(object.add("name"sv, name));
             auto description = MUST(element->accessible_description(document));
             auto description = MUST(element->accessible_description(document));
             MUST(object.add("description"sv, description));
             MUST(object.add("description"sv, description));
-            MUST(object.add("id"sv, element->unique_id()));
+            MUST(object.add("id"sv, element->unique_id().value()));
 
 
             if (has_role)
             if (has_role)
                 MUST(object.add("role"sv, ARIA::role_name(*role)));
                 MUST(object.add("role"sv, ARIA::role_name(*role)));

+ 2 - 2
Userland/Libraries/LibWeb/DOM/CharacterData.cpp

@@ -57,10 +57,10 @@ WebIDL::ExceptionOr<String> CharacterData::substring_data(size_t offset, size_t
     // 3. If offset plus count is greater than length, return a string whose value is the code units from the offsetth code unit
     // 3. If offset plus count is greater than length, return a string whose value is the code units from the offsetth code unit
     //    to the end of node’s data, and then return.
     //    to the end of node’s data, and then return.
     if (offset + count > length)
     if (offset + count > length)
-        return MUST(utf16_view.substring_view(offset).to_utf8());
+        return MUST(utf16_view.substring_view(offset).to_utf8(Utf16View::AllowInvalidCodeUnits::Yes));
 
 
     // 4. Return a string whose value is the code units from the offsetth code unit to the offset+countth code unit in node’s data.
     // 4. Return a string whose value is the code units from the offsetth code unit to the offset+countth code unit in node’s data.
-    return MUST(utf16_view.substring_view(offset, count).to_utf8());
+    return MUST(utf16_view.substring_view(offset, count).to_utf8(Utf16View::AllowInvalidCodeUnits::Yes));
 }
 }
 
 
 // https://dom.spec.whatwg.org/#concept-cd-replace
 // https://dom.spec.whatwg.org/#concept-cd-replace

+ 18 - 5
Userland/Libraries/LibWeb/DOM/Document.cpp

@@ -680,7 +680,7 @@ WebIDL::ExceptionOr<JS::GCPtr<HTML::WindowProxy>> Document::open(StringView url,
         return WebIDL::InvalidAccessError::create(realm(), "Cannot perform open on a document that isn't fully active."_string);
         return WebIDL::InvalidAccessError::create(realm(), "Cannot perform open on a document that isn't fully active."_string);
 
 
     // 2. Return the result of running the window open steps with url, name, and features.
     // 2. Return the result of running the window open steps with url, name, and features.
-    return window()->open_impl(url, name, features);
+    return window()->window_open_steps(url, name, features);
 }
 }
 
 
 // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#closing-the-input-stream
 // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#closing-the-input-stream
@@ -2784,7 +2784,7 @@ void Document::evaluate_media_rules()
         return;
         return;
 
 
     bool any_media_queries_changed_match_state = false;
     bool any_media_queries_changed_match_state = false;
-    for_each_active_css_style_sheet([&](CSS::CSSStyleSheet& style_sheet) {
+    for_each_active_css_style_sheet([&](CSS::CSSStyleSheet& style_sheet, auto) {
         if (style_sheet.evaluate_media_queries(*window))
         if (style_sheet.evaluate_media_queries(*window))
             any_media_queries_changed_match_state = true;
             any_media_queries_changed_match_state = true;
     });
     });
@@ -5173,21 +5173,28 @@ WebIDL::ExceptionOr<void> Document::set_adopted_style_sheets(JS::Value new_value
     return {};
     return {};
 }
 }
 
 
-void Document::for_each_active_css_style_sheet(Function<void(CSS::CSSStyleSheet&)>&& callback) const
+void Document::for_each_active_css_style_sheet(Function<void(CSS::CSSStyleSheet&, JS::GCPtr<DOM::ShadowRoot>)>&& callback) const
 {
 {
     if (m_style_sheets) {
     if (m_style_sheets) {
         for (auto& style_sheet : m_style_sheets->sheets()) {
         for (auto& style_sheet : m_style_sheets->sheets()) {
             if (!(style_sheet->is_alternate() && style_sheet->disabled()))
             if (!(style_sheet->is_alternate() && style_sheet->disabled()))
-                callback(*style_sheet);
+                callback(*style_sheet, {});
         }
         }
     }
     }
 
 
     if (m_adopted_style_sheets) {
     if (m_adopted_style_sheets) {
         m_adopted_style_sheets->for_each<CSS::CSSStyleSheet>([&](auto& style_sheet) {
         m_adopted_style_sheets->for_each<CSS::CSSStyleSheet>([&](auto& style_sheet) {
             if (!style_sheet.disabled())
             if (!style_sheet.disabled())
-                callback(style_sheet);
+                callback(style_sheet, {});
         });
         });
     }
     }
+
+    for_each_shadow_root([&](auto& shadow_root) {
+        shadow_root.for_each_css_style_sheet([&](auto& style_sheet) {
+            if (!style_sheet.disabled())
+                callback(style_sheet, &shadow_root);
+        });
+    });
 }
 }
 
 
 static Optional<CSS::CSSStyleSheet&> find_style_sheet_with_url(String const& url, CSS::CSSStyleSheet& style_sheet)
 static Optional<CSS::CSSStyleSheet&> find_style_sheet_with_url(String const& url, CSS::CSSStyleSheet& style_sheet)
@@ -5277,6 +5284,12 @@ void Document::for_each_shadow_root(Function<void(DOM::ShadowRoot&)>&& callback)
         callback(shadow_root);
         callback(shadow_root);
 }
 }
 
 
+void Document::for_each_shadow_root(Function<void(DOM::ShadowRoot&)>&& callback) const
+{
+    for (auto& shadow_root : m_shadow_roots)
+        callback(shadow_root);
+}
+
 bool Document::is_decoded_svg() const
 bool Document::is_decoded_svg() const
 {
 {
     return is<Web::SVG::SVGDecodedImageData::SVGPageClient>(page().client());
     return is<Web::SVG::SVGDecodedImageData::SVGPageClient>(page().client());

+ 2 - 1
Userland/Libraries/LibWeb/DOM/Document.h

@@ -162,7 +162,7 @@ public:
     CSS::StyleSheetList& style_sheets();
     CSS::StyleSheetList& style_sheets();
     CSS::StyleSheetList const& style_sheets() const;
     CSS::StyleSheetList const& style_sheets() const;
 
 
-    void for_each_active_css_style_sheet(Function<void(CSS::CSSStyleSheet&)>&& callback) const;
+    void for_each_active_css_style_sheet(Function<void(CSS::CSSStyleSheet&, JS::GCPtr<DOM::ShadowRoot>)>&& callback) const;
 
 
     CSS::StyleSheetList* style_sheets_for_bindings() { return &style_sheets(); }
     CSS::StyleSheetList* style_sheets_for_bindings() { return &style_sheets(); }
 
 
@@ -675,6 +675,7 @@ public:
     void register_shadow_root(Badge<DOM::ShadowRoot>, DOM::ShadowRoot&);
     void register_shadow_root(Badge<DOM::ShadowRoot>, DOM::ShadowRoot&);
     void unregister_shadow_root(Badge<DOM::ShadowRoot>, DOM::ShadowRoot&);
     void unregister_shadow_root(Badge<DOM::ShadowRoot>, DOM::ShadowRoot&);
     void for_each_shadow_root(Function<void(DOM::ShadowRoot&)>&& callback);
     void for_each_shadow_root(Function<void(DOM::ShadowRoot&)>&& callback);
+    void for_each_shadow_root(Function<void(DOM::ShadowRoot&)>&& callback) const;
 
 
     void add_an_element_to_the_top_layer(JS::NonnullGCPtr<Element>);
     void add_an_element_to_the_top_layer(JS::NonnullGCPtr<Element>);
     void request_an_element_to_be_remove_from_the_top_layer(JS::NonnullGCPtr<Element>);
     void request_an_element_to_be_remove_from_the_top_layer(JS::NonnullGCPtr<Element>);

+ 9 - 3
Userland/Libraries/LibWeb/DOM/Element.cpp

@@ -1169,7 +1169,7 @@ void Element::serialize_pseudo_elements_as_json(JsonArraySerializer<StringBuilde
         auto object = MUST(children_array.add_object());
         auto object = MUST(children_array.add_object());
         MUST(object.add("name"sv, MUST(String::formatted("::{}", CSS::Selector::PseudoElement::name(static_cast<CSS::Selector::PseudoElement::Type>(i))))));
         MUST(object.add("name"sv, MUST(String::formatted("::{}", CSS::Selector::PseudoElement::name(static_cast<CSS::Selector::PseudoElement::Type>(i))))));
         MUST(object.add("type"sv, "pseudo-element"));
         MUST(object.add("type"sv, "pseudo-element"));
-        MUST(object.add("parent-id"sv, unique_id()));
+        MUST(object.add("parent-id"sv, unique_id().value()));
         MUST(object.add("pseudo-element"sv, i));
         MUST(object.add("pseudo-element"sv, i));
         MUST(object.finish());
         MUST(object.finish());
     }
     }
@@ -1455,7 +1455,10 @@ int Element::scroll_width() const
         return 0;
         return 0;
 
 
     // 7. Return the width of the element’s scrolling area.
     // 7. Return the width of the element’s scrolling area.
-    return paintable_box()->scrollable_overflow_rect()->width().to_int();
+    if (auto scrollable_overflow_rect = paintable_box()->scrollable_overflow_rect(); scrollable_overflow_rect.has_value()) {
+        return scrollable_overflow_rect->width().to_int();
+    }
+    return 0;
 }
 }
 
 
 // https://drafts.csswg.org/cssom-view/#dom-element-scrollheight
 // https://drafts.csswg.org/cssom-view/#dom-element-scrollheight
@@ -1491,7 +1494,10 @@ int Element::scroll_height() const
         return 0;
         return 0;
 
 
     // 7. Return the height of the element’s scrolling area.
     // 7. Return the height of the element’s scrolling area.
-    return paintable_box()->scrollable_overflow_rect()->height().to_int();
+    if (auto scrollable_overflow_rect = paintable_box()->scrollable_overflow_rect(); scrollable_overflow_rect.has_value()) {
+        return scrollable_overflow_rect->height().to_int();
+    }
+    return 0;
 }
 }
 
 
 // https://html.spec.whatwg.org/multipage/semantics-other.html#concept-element-disabled
 // https://html.spec.whatwg.org/multipage/semantics-other.html#concept-element-disabled

+ 29 - 13
Userland/Libraries/LibWeb/DOM/Node.cpp

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2018-2023, Andreas Kling <andreas@ladybird.org>
+ * Copyright (c) 2018-2024, Andreas Kling <andreas@ladybird.org>
  * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
  * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
  * Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
  * Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
  *
  *
@@ -7,7 +7,6 @@
  */
  */
 
 
 #include <AK/HashTable.h>
 #include <AK/HashTable.h>
-#include <AK/IDAllocator.h>
 #include <AK/StringBuilder.h>
 #include <AK/StringBuilder.h>
 #include <LibJS/Heap/DeferGC.h>
 #include <LibJS/Heap/DeferGC.h>
 #include <LibJS/Runtime/FunctionObject.h>
 #include <LibJS/Runtime/FunctionObject.h>
@@ -50,24 +49,24 @@
 
 
 namespace Web::DOM {
 namespace Web::DOM {
 
 
-static IDAllocator s_unique_id_allocator;
-static HashMap<i32, Node*> s_node_directory;
+static UniqueNodeID s_next_unique_id;
+static HashMap<UniqueNodeID, Node*> s_node_directory;
 
 
-static i32 allocate_unique_id(Node* node)
+static UniqueNodeID allocate_unique_id(Node* node)
 {
 {
-    i32 id = s_unique_id_allocator.allocate();
+    auto id = s_next_unique_id;
+    ++s_next_unique_id;
     s_node_directory.set(id, node);
     s_node_directory.set(id, node);
     return id;
     return id;
 }
 }
 
 
-static void deallocate_unique_id(i32 node_id)
+static void deallocate_unique_id(UniqueNodeID node_id)
 {
 {
     if (!s_node_directory.remove(node_id))
     if (!s_node_directory.remove(node_id))
         VERIFY_NOT_REACHED();
         VERIFY_NOT_REACHED();
-    s_unique_id_allocator.deallocate(node_id);
 }
 }
 
 
-Node* Node::from_unique_id(i32 unique_id)
+Node* Node::from_unique_id(UniqueNodeID unique_id)
 {
 {
     return s_node_directory.get(unique_id).value_or(nullptr);
     return s_node_directory.get(unique_id).value_or(nullptr);
 }
 }
@@ -1386,7 +1385,7 @@ bool Node::is_uninteresting_whitespace_node() const
 void Node::serialize_tree_as_json(JsonObjectSerializer<StringBuilder>& object) const
 void Node::serialize_tree_as_json(JsonObjectSerializer<StringBuilder>& object) const
 {
 {
     MUST(object.add("name"sv, node_name()));
     MUST(object.add("name"sv, node_name()));
-    MUST(object.add("id"sv, unique_id()));
+    MUST(object.add("id"sv, unique_id().value()));
     if (is_document()) {
     if (is_document()) {
         MUST(object.add("type"sv, "document"));
         MUST(object.add("type"sv, "document"));
     } else if (is_element()) {
     } else if (is_element()) {
@@ -2184,7 +2183,7 @@ void Node::build_accessibility_tree(AccessibilityTreeNode& parent)
 }
 }
 
 
 // https://www.w3.org/TR/accname-1.2/#mapping_additional_nd_te
 // https://www.w3.org/TR/accname-1.2/#mapping_additional_nd_te
-ErrorOr<String> Node::name_or_description(NameOrDescription target, Document const& document, HashTable<i32>& visited_nodes) const
+ErrorOr<String> Node::name_or_description(NameOrDescription target, Document const& document, HashTable<UniqueNodeID>& visited_nodes) const
 {
 {
     // The text alternative for a given element is computed as follows:
     // The text alternative for a given element is computed as follows:
     // 1. Set the root node to the given element, the current node to the root node, and the total accumulated text to the empty string (""). If the root node's role prohibits naming, return the empty string ("").
     // 1. Set the root node to the given element, the current node to the root node, and the total accumulated text to the empty string (""). If the root node's role prohibits naming, return the empty string ("").
@@ -2321,7 +2320,7 @@ ErrorOr<String> Node::name_or_description(NameOrDescription target, Document con
 // https://www.w3.org/TR/accname-1.2/#mapping_additional_nd_name
 // https://www.w3.org/TR/accname-1.2/#mapping_additional_nd_name
 ErrorOr<String> Node::accessible_name(Document const& document) const
 ErrorOr<String> Node::accessible_name(Document const& document) const
 {
 {
-    HashTable<i32> visited_nodes;
+    HashTable<UniqueNodeID> visited_nodes;
     // User agents MUST compute an accessible name using the rules outlined below in the section titled Accessible Name and Description Computation.
     // User agents MUST compute an accessible name using the rules outlined below in the section titled Accessible Name and Description Computation.
     return name_or_description(NameOrDescription::Name, document, visited_nodes);
     return name_or_description(NameOrDescription::Name, document, visited_nodes);
 }
 }
@@ -2339,7 +2338,7 @@ ErrorOr<String> Node::accessible_description(Document const& document) const
     if (!described_by.has_value())
     if (!described_by.has_value())
         return String {};
         return String {};
 
 
-    HashTable<i32> visited_nodes;
+    HashTable<UniqueNodeID> visited_nodes;
     StringBuilder builder;
     StringBuilder builder;
     auto id_list = described_by->bytes_as_string_view().split_view_if(Infra::is_ascii_whitespace);
     auto id_list = described_by->bytes_as_string_view().split_view_if(Infra::is_ascii_whitespace);
     for (auto const& id : id_list) {
     for (auto const& id : id_list) {
@@ -2434,3 +2433,20 @@ void Node::add_registered_observer(RegisteredObserver& registered_observer)
 }
 }
 
 
 }
 }
+
+namespace IPC {
+
+template<>
+ErrorOr<void> encode(Encoder& encoder, Web::UniqueNodeID const& value)
+{
+    return encode(encoder, value.value());
+}
+
+template<>
+ErrorOr<Web::UniqueNodeID> decode(Decoder& decoder)
+{
+    auto value = TRY(decoder.decode<i64>());
+    return Web::UniqueNodeID(value);
+}
+
+}

+ 6 - 5
Userland/Libraries/LibWeb/DOM/Node.h

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2018-2023, Andreas Kling <andreas@ladybird.org>
+ * Copyright (c) 2018-2024, Andreas Kling <andreas@ladybird.org>
  *
  *
  * SPDX-License-Identifier: BSD-2-Clause
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
@@ -7,6 +7,7 @@
 #pragma once
 #pragma once
 
 
 #include <AK/Badge.h>
 #include <AK/Badge.h>
+#include <AK/DistinctNumeric.h>
 #include <AK/FlyString.h>
 #include <AK/FlyString.h>
 #include <AK/GenericShorthands.h>
 #include <AK/GenericShorthands.h>
 #include <AK/JsonObjectSerializer.h>
 #include <AK/JsonObjectSerializer.h>
@@ -287,8 +288,8 @@ public:
     bool is_shadow_including_ancestor_of(Node const&) const;
     bool is_shadow_including_ancestor_of(Node const&) const;
     bool is_shadow_including_inclusive_ancestor_of(Node const&) const;
     bool is_shadow_including_inclusive_ancestor_of(Node const&) const;
 
 
-    i32 unique_id() const { return m_unique_id; }
-    static Node* from_unique_id(i32);
+    [[nodiscard]] UniqueNodeID unique_id() const { return m_unique_id; }
+    static Node* from_unique_id(UniqueNodeID);
 
 
     WebIDL::ExceptionOr<String> serialize_fragment(DOMParsing::RequireWellFormed, FragmentSerializationMode = FragmentSerializationMode::Inner) const;
     WebIDL::ExceptionOr<String> serialize_fragment(DOMParsing::RequireWellFormed, FragmentSerializationMode = FragmentSerializationMode::Inner) const;
 
 
@@ -752,7 +753,7 @@ protected:
     bool m_needs_style_update { false };
     bool m_needs_style_update { false };
     bool m_child_needs_style_update { false };
     bool m_child_needs_style_update { false };
 
 
-    i32 m_unique_id {};
+    UniqueNodeID m_unique_id;
 
 
     // https://dom.spec.whatwg.org/#registered-observer-list
     // https://dom.spec.whatwg.org/#registered-observer-list
     // "Nodes have a strong reference to registered observers in their registered observer list." https://dom.spec.whatwg.org/#garbage-collection
     // "Nodes have a strong reference to registered observers in their registered observer list." https://dom.spec.whatwg.org/#garbage-collection
@@ -760,7 +761,7 @@ protected:
 
 
     void build_accessibility_tree(AccessibilityTreeNode& parent);
     void build_accessibility_tree(AccessibilityTreeNode& parent);
 
 
-    ErrorOr<String> name_or_description(NameOrDescription, Document const&, HashTable<i32>&) const;
+    ErrorOr<String> name_or_description(NameOrDescription, Document const&, HashTable<UniqueNodeID>&) const;
 
 
 private:
 private:
     void queue_tree_mutation_record(Vector<JS::Handle<Node>> added_nodes, Vector<JS::Handle<Node>> removed_nodes, Node* previous_sibling, Node* next_sibling);
     void queue_tree_mutation_record(Vector<JS::Handle<Node>> added_nodes, Vector<JS::Handle<Node>> removed_nodes, Node* previous_sibling, Node* next_sibling);

+ 1 - 1
Userland/Libraries/LibWeb/DOMParsing/XMLSerializer.cpp

@@ -839,7 +839,7 @@ static WebIDL::ExceptionOr<String> serialize_document_type(DOM::DocumentType con
     }
     }
 
 
     // 8. If the node's systemId is not the empty string and the node's publicId is set to the empty string, then append the following, in the order listed, to markup:
     // 8. If the node's systemId is not the empty string and the node's publicId is set to the empty string, then append the following, in the order listed, to markup:
-    if (!document_type.system_id().is_empty() && !document_type.public_id().is_empty()) {
+    if (!document_type.system_id().is_empty() && document_type.public_id().is_empty()) {
         // 1. " " (U+0020 SPACE);
         // 1. " " (U+0020 SPACE);
         // 2. The string "SYSTEM".
         // 2. The string "SYSTEM".
         markup.append(" SYSTEM"sv);
         markup.append(" SYSTEM"sv);

+ 15 - 1
Userland/Libraries/LibWeb/Forward.h

@@ -1,5 +1,5 @@
 /*
 /*
- * Copyright (c) 2020-2023, Andreas Kling <andreas@ladybird.org>
+ * Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
  * Copyright (c) 2021-2023, the SerenityOS developers.
  * Copyright (c) 2021-2023, the SerenityOS developers.
  *
  *
  * SPDX-License-Identifier: BSD-2-Clause
  * SPDX-License-Identifier: BSD-2-Clause
@@ -7,7 +7,9 @@
 
 
 #pragma once
 #pragma once
 
 
+#include <AK/DistinctNumeric.h>
 #include <AK/Variant.h>
 #include <AK/Variant.h>
+#include <LibIPC/Forward.h>
 #include <LibJS/Forward.h>
 #include <LibJS/Forward.h>
 
 
 namespace Web {
 namespace Web {
@@ -24,6 +26,8 @@ class XMLDocumentBuilder;
 
 
 enum class InvalidateDisplayList;
 enum class InvalidateDisplayList;
 enum class TraversalDecision;
 enum class TraversalDecision;
+
+AK_TYPEDEF_DISTINCT_NUMERIC_GENERAL(i64, UniqueNodeID, Comparison, Increment, CastToUnderlying);
 }
 }
 
 
 namespace Web::Painting {
 namespace Web::Painting {
@@ -829,3 +833,13 @@ class XMLHttpRequestUpload;
 
 
 struct FormDataEntry;
 struct FormDataEntry;
 }
 }
+
+namespace IPC {
+
+template<>
+ErrorOr<void> encode(Encoder&, Web::UniqueNodeID const&);
+
+template<>
+ErrorOr<Web::UniqueNodeID> decode(Decoder&);
+
+}

+ 2 - 2
Userland/Libraries/LibWeb/HTML/HTMLButtonElement.idl

@@ -16,8 +16,8 @@ interface HTMLButtonElement : HTMLElement {
     [CEReactions, Reflect] attribute boolean disabled;
     [CEReactions, Reflect] attribute boolean disabled;
     readonly attribute HTMLFormElement? form;
     readonly attribute HTMLFormElement? form;
     [CEReactions] attribute USVString formAction;
     [CEReactions] attribute USVString formAction;
-    [CEReactions, Reflect=formenctype] attribute DOMString formEnctype;
-    [CEReactions, Reflect=formmethod] attribute DOMString formMethod;
+    [CEReactions, Reflect=formenctype, Enumerated=FormEnctypeAttribute] attribute DOMString formEnctype;
+    [CEReactions, Reflect=formmethod, Enumerated=FormMethodAttribute] attribute DOMString formMethod;
     [CEReactions, Reflect=formnovalidate] attribute boolean formNoValidate;
     [CEReactions, Reflect=formnovalidate] attribute boolean formNoValidate;
     [CEReactions, Reflect=formtarget] attribute DOMString formTarget;
     [CEReactions, Reflect=formtarget] attribute DOMString formTarget;
     [CEReactions, Reflect] attribute DOMString name;
     [CEReactions, Reflect] attribute DOMString name;

+ 45 - 1
Userland/Libraries/LibWeb/HTML/HTMLElement.cpp

@@ -9,6 +9,7 @@
 #include <LibWeb/Bindings/ExceptionOrUtils.h>
 #include <LibWeb/Bindings/ExceptionOrUtils.h>
 #include <LibWeb/Bindings/HTMLElementPrototype.h>
 #include <LibWeb/Bindings/HTMLElementPrototype.h>
 #include <LibWeb/DOM/Document.h>
 #include <LibWeb/DOM/Document.h>
+#include <LibWeb/DOM/ElementFactory.h>
 #include <LibWeb/DOM/IDLEventListener.h>
 #include <LibWeb/DOM/IDLEventListener.h>
 #include <LibWeb/DOM/LiveNodeList.h>
 #include <LibWeb/DOM/LiveNodeList.h>
 #include <LibWeb/DOM/ShadowRoot.h>
 #include <LibWeb/DOM/ShadowRoot.h>
@@ -32,6 +33,7 @@
 #include <LibWeb/Layout/Box.h>
 #include <LibWeb/Layout/Box.h>
 #include <LibWeb/Layout/BreakNode.h>
 #include <LibWeb/Layout/BreakNode.h>
 #include <LibWeb/Layout/TextNode.h>
 #include <LibWeb/Layout/TextNode.h>
+#include <LibWeb/Namespace.h>
 #include <LibWeb/Painting/PaintableBox.h>
 #include <LibWeb/Painting/PaintableBox.h>
 #include <LibWeb/UIEvents/EventNames.h>
 #include <LibWeb/UIEvents/EventNames.h>
 #include <LibWeb/UIEvents/FocusEvent.h>
 #include <LibWeb/UIEvents/FocusEvent.h>
@@ -150,10 +152,13 @@ WebIDL::ExceptionOr<void> HTMLElement::set_content_editable(StringView content_e
     return WebIDL::SyntaxError::create(realm(), "Invalid contentEditable value, must be 'true', 'false', or 'inherit'"_string);
     return WebIDL::SyntaxError::create(realm(), "Invalid contentEditable value, must be 'true', 'false', or 'inherit'"_string);
 }
 }
 
 
+// https://html.spec.whatwg.org/multipage/dom.html#set-the-inner-text-steps
 void HTMLElement::set_inner_text(StringView text)
 void HTMLElement::set_inner_text(StringView text)
 {
 {
+    // 1. Let fragment be the rendered text fragment for value given element's node document.
+    // 2. Replace all with fragment within element.
     remove_all_children();
     remove_all_children();
-    MUST(append_child(document().create_text_node(MUST(String::from_utf8(text)))));
+    append_rendered_text_fragment(text);
 
 
     set_needs_style_update(true);
     set_needs_style_update(true);
 }
 }
@@ -165,6 +170,45 @@ WebIDL::ExceptionOr<void> HTMLElement::set_outer_text(String)
     return {};
     return {};
 }
 }
 
 
+// https://html.spec.whatwg.org/multipage/dom.html#rendered-text-fragment
+void HTMLElement::append_rendered_text_fragment(StringView input)
+{
+    // FIXME: 1. Let fragment be a new DocumentFragment whose node document is document.
+    //      Instead of creating a DocumentFragment the nodes are appended directly.
+
+    // 2. Let position be a position variable for input, initially pointing at the start of input.
+    // 3. Let text be the empty string.
+    // 4. While position is not past the end of input:
+    while (!input.is_empty()) {
+        // 1. Collect a sequence of code points that are not U+000A LF or U+000D CR from input given position, and set text to the result.
+        auto newline_index = input.find_any_of("\n\r"sv);
+        size_t const sequence_end_index = newline_index.value_or(input.length());
+        StringView const text = input.substring_view(0, sequence_end_index);
+        input = input.substring_view_starting_after_substring(text);
+
+        // 2. If text is not the empty string, then append a new Text node whose data is text and node document is document to fragment.
+        if (!text.is_empty()) {
+            MUST(append_child(document().create_text_node(MUST(String::from_utf8(text)))));
+        }
+
+        // 3. While position is not past the end of input, and the code point at position is either U+000A LF or U+000D CR:
+        while (input.starts_with('\n') || input.starts_with('\r')) {
+            // 1. If the code point at position is U+000D CR and the next code point is U+000A LF, then advance position to the next code point in input.
+            if (input.starts_with("\r\n"sv)) {
+                // 2. Advance position to the next code point in input.
+                input = input.substring_view(2);
+            } else {
+                // 2. Advance position to the next code point in input.
+                input = input.substring_view(1);
+            }
+
+            // 3. Append the result of creating an element given document, br, and the HTML namespace to fragment.
+            auto br_element = DOM::create_element(document(), HTML::TagNames::br, Namespace::HTML).release_value();
+            MUST(append_child(br_element));
+        }
+    }
+}
+
 // https://html.spec.whatwg.org/multipage/dom.html#get-the-text-steps
 // https://html.spec.whatwg.org/multipage/dom.html#get-the-text-steps
 String HTMLElement::get_the_text_steps()
 String HTMLElement::get_the_text_steps()
 {
 {

+ 1 - 0
Userland/Libraries/LibWeb/HTML/HTMLElement.h

@@ -97,6 +97,7 @@ private:
     virtual void did_receive_focus() override;
     virtual void did_receive_focus() override;
 
 
     [[nodiscard]] String get_the_text_steps();
     [[nodiscard]] String get_the_text_steps();
+    void append_rendered_text_fragment(StringView input);
 
 
     JS::GCPtr<DOMStringMap> m_dataset;
     JS::GCPtr<DOMStringMap> m_dataset;
 
 

+ 0 - 17
Userland/Libraries/LibWeb/HTML/HTMLFormElement.cpp

@@ -565,23 +565,6 @@ Vector<JS::NonnullGCPtr<DOM::Element>> HTMLFormElement::get_submittable_elements
     return submittable_elements;
     return submittable_elements;
 }
 }
 
 
-// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-fs-method
-StringView HTMLFormElement::method() const
-{
-    // The method and enctype IDL attributes must reflect the respective content attributes of the same name, limited to only known values.
-    // FIXME: This should probably be `Reflect` in the IDL.
-    auto method_state = method_state_from_form_element(*this);
-    switch (method_state) {
-    case MethodAttributeState::GET:
-        return "get"sv;
-    case MethodAttributeState::POST:
-        return "post"sv;
-    case MethodAttributeState::Dialog:
-        return "dialog"sv;
-    }
-    VERIFY_NOT_REACHED();
-}
-
 // https://html.spec.whatwg.org/multipage/forms.html#dom-form-rellist
 // https://html.spec.whatwg.org/multipage/forms.html#dom-form-rellist
 JS::NonnullGCPtr<DOM::DOMTokenList> HTMLFormElement::rel_list()
 JS::NonnullGCPtr<DOM::DOMTokenList> HTMLFormElement::rel_list()
 {
 {

+ 0 - 1
Userland/Libraries/LibWeb/HTML/HTMLFormElement.h

@@ -88,7 +88,6 @@ public:
     bool constructing_entry_list() const { return m_constructing_entry_list; }
     bool constructing_entry_list() const { return m_constructing_entry_list; }
     void set_constructing_entry_list(bool value) { m_constructing_entry_list = value; }
     void set_constructing_entry_list(bool value) { m_constructing_entry_list = value; }
 
 
-    StringView method() const;
     WebIDL::ExceptionOr<void> set_method(String const&);
     WebIDL::ExceptionOr<void> set_method(String const&);
 
 
     JS::NonnullGCPtr<DOM::DOMTokenList> rel_list();
     JS::NonnullGCPtr<DOM::DOMTokenList> rel_list();

+ 25 - 1
Userland/Libraries/LibWeb/HTML/HTMLFormElement.idl

@@ -16,6 +16,30 @@ enum EnctypeAttribute {
     "text/plain"
     "text/plain"
 };
 };
 
 
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fs-method
+[MissingValueDefault=get, InvalidValueDefault=get]
+enum MethodAttribute {
+    "get",
+    "post",
+    "dialog"
+};
+
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fs-formenctype
+[InvalidValueDefault=application/x-www-form-urlencoded]
+enum FormEnctypeAttribute {
+    "application/x-www-form-urlencoded",
+    "multipart/form-data",
+    "text/plain"
+};
+
+// https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fs-formmethod
+[InvalidValueDefault=get]
+enum FormMethodAttribute {
+    "get",
+    "post",
+    "dialog"
+};
+
 // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#selectionmode
 // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#selectionmode
 enum SelectionMode {
 enum SelectionMode {
     "select",
     "select",
@@ -35,7 +59,7 @@ interface HTMLFormElement : HTMLElement {
     [CEReactions, Enumerated=Autocomplete, Reflect] attribute DOMString autocomplete;
     [CEReactions, Enumerated=Autocomplete, Reflect] attribute DOMString autocomplete;
     [CEReactions, Enumerated=EnctypeAttribute, Reflect] attribute DOMString enctype;
     [CEReactions, Enumerated=EnctypeAttribute, Reflect] attribute DOMString enctype;
     [CEReactions, Enumerated=EnctypeAttribute, Reflect=enctype] attribute DOMString encoding;
     [CEReactions, Enumerated=EnctypeAttribute, Reflect=enctype] attribute DOMString encoding;
-    [CEReactions] attribute DOMString method;
+    [CEReactions, Enumerated=MethodAttribute, Reflect] attribute DOMString method;
     [CEReactions, Reflect] attribute DOMString name;
     [CEReactions, Reflect] attribute DOMString name;
     [CEReactions, Reflect=novalidate] attribute boolean noValidate;
     [CEReactions, Reflect=novalidate] attribute boolean noValidate;
     [CEReactions, Reflect] attribute DOMString target;
     [CEReactions, Reflect] attribute DOMString target;

+ 14 - 3
Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp

@@ -103,12 +103,23 @@ JS::GCPtr<Layout::Node> HTMLInputElement::create_layout_node(NonnullRefPtr<CSS::
     if (type_state() == TypeAttributeState::Hidden)
     if (type_state() == TypeAttributeState::Hidden)
         return nullptr;
         return nullptr;
 
 
+    // NOTE: Image inputs are `appearance: none` per the default UA style,
+    //       but we still need to create an ImageBox for them, or no image will get loaded.
+    if (type_state() == TypeAttributeState::ImageButton) {
+        return heap().allocate_without_realm<Layout::ImageBox>(document(), *this, move(style), *this);
+    }
+
+    // https://drafts.csswg.org/css-ui/#appearance-switching
+    // This specification introduces the appearance property to provide some control over this behavior.
+    // In particular, using appearance: none allows authors to suppress the native appearance of widgets,
+    // giving them a primitive appearance where CSS can be used to restyle them.
+    if (style->appearance() == CSS::Appearance::None) {
+        return Element::create_layout_node_for_display_type(document(), style->display(), style, this);
+    }
+
     if (type_state() == TypeAttributeState::SubmitButton || type_state() == TypeAttributeState::Button || type_state() == TypeAttributeState::ResetButton)
     if (type_state() == TypeAttributeState::SubmitButton || type_state() == TypeAttributeState::Button || type_state() == TypeAttributeState::ResetButton)
         return heap().allocate_without_realm<Layout::BlockContainer>(document(), this, move(style));
         return heap().allocate_without_realm<Layout::BlockContainer>(document(), this, move(style));
 
 
-    if (type_state() == TypeAttributeState::ImageButton)
-        return heap().allocate_without_realm<Layout::ImageBox>(document(), *this, move(style), *this);
-
     if (type_state() == TypeAttributeState::Checkbox)
     if (type_state() == TypeAttributeState::Checkbox)
         return heap().allocate_without_realm<Layout::CheckBox>(document(), *this, move(style));
         return heap().allocate_without_realm<Layout::CheckBox>(document(), *this, move(style));
 
 

+ 2 - 2
Userland/Libraries/LibWeb/HTML/HTMLInputElement.idl

@@ -18,8 +18,8 @@ interface HTMLInputElement : HTMLElement {
     readonly attribute HTMLFormElement? form;
     readonly attribute HTMLFormElement? form;
     attribute FileList? files;
     attribute FileList? files;
     [CEReactions] attribute USVString formAction;
     [CEReactions] attribute USVString formAction;
-    [FIXME, CEReactions] attribute DOMString formEnctype;
-    [FIXME, CEReactions] attribute DOMString formMethod;
+    [CEReactions, Reflect=formenctype, Enumerated=FormEnctypeAttribute] attribute DOMString formEnctype;
+    [CEReactions, Reflect=formmethod, Enumerated=FormMethodAttribute] attribute DOMString formMethod;
     [CEReactions, Reflect=formnovalidate] attribute boolean formNoValidate;
     [CEReactions, Reflect=formnovalidate] attribute boolean formNoValidate;
     [CEReactions, Reflect=formtarget] attribute DOMString formTarget;
     [CEReactions, Reflect=formtarget] attribute DOMString formTarget;
     [FIXME, CEReactions] attribute unsigned long height;
     [FIXME, CEReactions] attribute unsigned long height;

+ 20 - 10
Userland/Libraries/LibWeb/HTML/Window.cpp

@@ -139,11 +139,26 @@ void Window::finalize()
 Window::~Window() = default;
 Window::~Window() = default;
 
 
 // https://html.spec.whatwg.org/multipage/window-object.html#window-open-steps
 // https://html.spec.whatwg.org/multipage/window-object.html#window-open-steps
-WebIDL::ExceptionOr<JS::GCPtr<WindowProxy>> Window::open_impl(StringView url, StringView target, StringView features)
+WebIDL::ExceptionOr<JS::GCPtr<WindowProxy>> Window::window_open_steps(StringView url, StringView target, StringView features)
+{
+    auto [target_navigable, no_opener, window_type] = TRY(window_open_steps_internal(url, target, features));
+    if (target_navigable == nullptr)
+        return nullptr;
+
+    // 14. If noopener is true or windowType is "new with no opener", then return null.
+    if (no_opener == TokenizedFeature::NoOpener::Yes || window_type == Navigable::WindowType::NewWithNoOpener)
+        return nullptr;
+
+    // 15. Return targetNavigable's active WindowProxy.
+    return target_navigable->active_window_proxy();
+}
+
+// https://html.spec.whatwg.org/multipage/window-object.html#window-open-steps
+WebIDL::ExceptionOr<Window::OpenedWindow> Window::window_open_steps_internal(StringView url, StringView target, StringView features)
 {
 {
     // 1. If the event loop's termination nesting level is nonzero, return null.
     // 1. If the event loop's termination nesting level is nonzero, return null.
     if (main_thread_event_loop().termination_nesting_level() != 0)
     if (main_thread_event_loop().termination_nesting_level() != 0)
-        return nullptr;
+        return OpenedWindow {};
 
 
     // FIXME: Spec-issue: https://github.com/whatwg/html/issues/10681
     // FIXME: Spec-issue: https://github.com/whatwg/html/issues/10681
     // We need to check for an invalid URL before running the 'rules for choosing a navigable' otherwise
     // We need to check for an invalid URL before running the 'rules for choosing a navigable' otherwise
@@ -206,7 +221,7 @@ WebIDL::ExceptionOr<JS::GCPtr<WindowProxy>> Window::open_impl(StringView url, St
 
 
     // 11. If targetNavigable is null, then return null.
     // 11. If targetNavigable is null, then return null.
     if (target_navigable == nullptr)
     if (target_navigable == nullptr)
-        return nullptr;
+        return OpenedWindow {};
 
 
     // 12. If windowType is either "new and unrestricted" or "new with no opener", then:
     // 12. If windowType is either "new and unrestricted" or "new with no opener", then:
     if (window_type == Navigable::WindowType::NewAndUnrestricted || window_type == Navigable::WindowType::NewWithNoOpener) {
     if (window_type == Navigable::WindowType::NewAndUnrestricted || window_type == Navigable::WindowType::NewWithNoOpener) {
@@ -253,12 +268,7 @@ WebIDL::ExceptionOr<JS::GCPtr<WindowProxy>> Window::open_impl(StringView url, St
             target_navigable->active_browsing_context()->set_opener_browsing_context(source_document.browsing_context());
             target_navigable->active_browsing_context()->set_opener_browsing_context(source_document.browsing_context());
     }
     }
 
 
-    // 14. If noopener is true or windowType is "new with no opener", then return null.
-    if (no_opener == TokenizedFeature::NoOpener::Yes || window_type == Navigable::WindowType::NewWithNoOpener)
-        return nullptr;
-
-    // 15. Return targetNavigable's active WindowProxy.
-    return target_navigable->active_window_proxy();
+    return OpenedWindow { target_navigable, no_opener, window_type };
 }
 }
 
 
 bool Window::dispatch_event(DOM::Event& event)
 bool Window::dispatch_event(DOM::Event& event)
@@ -1007,7 +1017,7 @@ JS::GCPtr<DOM::Element const> Window::frame_element() const
 WebIDL::ExceptionOr<JS::GCPtr<WindowProxy>> Window::open(Optional<String> const& url, Optional<String> const& target, Optional<String> const& features)
 WebIDL::ExceptionOr<JS::GCPtr<WindowProxy>> Window::open(Optional<String> const& url, Optional<String> const& target, Optional<String> const& features)
 {
 {
     // The open(url, target, features) method steps are to run the window open steps with url, target, and features.
     // The open(url, target, features) method steps are to run the window open steps with url, target, and features.
-    return open_impl(*url, *target, *features);
+    return window_open_steps(*url, *target, *features);
 }
 }
 
 
 // https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator
 // https://html.spec.whatwg.org/multipage/system-state.html#dom-navigator

+ 9 - 1
Userland/Libraries/LibWeb/HTML/Window.h

@@ -101,7 +101,15 @@ public:
     bool import_maps_allowed() const { return m_import_maps_allowed; }
     bool import_maps_allowed() const { return m_import_maps_allowed; }
     void set_import_maps_allowed(bool import_maps_allowed) { m_import_maps_allowed = import_maps_allowed; }
     void set_import_maps_allowed(bool import_maps_allowed) { m_import_maps_allowed = import_maps_allowed; }
 
 
-    WebIDL::ExceptionOr<JS::GCPtr<WindowProxy>> open_impl(StringView url, StringView target, StringView features);
+    WebIDL::ExceptionOr<JS::GCPtr<WindowProxy>> window_open_steps(StringView url, StringView target, StringView features);
+
+    struct OpenedWindow {
+        JS::GCPtr<Navigable> navigable;
+        TokenizedFeature::NoOpener no_opener { TokenizedFeature::NoOpener::No };
+        Navigable::WindowType window_type { Navigable::WindowType::ExistingOrNone };
+    };
+    WebIDL::ExceptionOr<OpenedWindow> window_open_steps_internal(StringView url, StringView target, StringView features);
+
     bool has_animation_frame_callbacks() const { return m_animation_frame_callback_driver.has_callbacks(); }
     bool has_animation_frame_callbacks() const { return m_animation_frame_callback_driver.has_callbacks(); }
 
 
     DOM::Event* current_event() { return m_current_event.ptr(); }
     DOM::Event* current_event() { return m_current_event.ptr(); }

+ 12 - 8
Userland/Libraries/LibWeb/Internals/Inspector.cpp

@@ -41,7 +41,7 @@ void Inspector::inspector_loaded()
     inspector_page_client().inspector_did_load();
     inspector_page_client().inspector_did_load();
 }
 }
 
 
-void Inspector::inspect_dom_node(i32 node_id, Optional<i32> const& pseudo_element)
+void Inspector::inspect_dom_node(i64 node_id, Optional<i32> const& pseudo_element)
 {
 {
     inspector_page_client().inspector_did_select_dom_node(node_id, pseudo_element.map([](auto value) {
     inspector_page_client().inspector_did_select_dom_node(node_id, pseudo_element.map([](auto value) {
         VERIFY(value < to_underlying(Web::CSS::Selector::PseudoElement::Type::KnownPseudoElementCount));
         VERIFY(value < to_underlying(Web::CSS::Selector::PseudoElement::Type::KnownPseudoElementCount));
@@ -49,27 +49,27 @@ void Inspector::inspect_dom_node(i32 node_id, Optional<i32> const& pseudo_elemen
     }));
     }));
 }
 }
 
 
-void Inspector::set_dom_node_text(i32 node_id, String const& text)
+void Inspector::set_dom_node_text(i64 node_id, String const& text)
 {
 {
     inspector_page_client().inspector_did_set_dom_node_text(node_id, text);
     inspector_page_client().inspector_did_set_dom_node_text(node_id, text);
 }
 }
 
 
-void Inspector::set_dom_node_tag(i32 node_id, String const& tag)
+void Inspector::set_dom_node_tag(i64 node_id, String const& tag)
 {
 {
     inspector_page_client().inspector_did_set_dom_node_tag(node_id, tag);
     inspector_page_client().inspector_did_set_dom_node_tag(node_id, tag);
 }
 }
 
 
-void Inspector::add_dom_node_attributes(i32 node_id, JS::NonnullGCPtr<DOM::NamedNodeMap> attributes)
+void Inspector::add_dom_node_attributes(i64 node_id, JS::NonnullGCPtr<DOM::NamedNodeMap> attributes)
 {
 {
     inspector_page_client().inspector_did_add_dom_node_attributes(node_id, attributes);
     inspector_page_client().inspector_did_add_dom_node_attributes(node_id, attributes);
 }
 }
 
 
-void Inspector::replace_dom_node_attribute(i32 node_id, WebIDL::UnsignedLongLong attribute_index, JS::NonnullGCPtr<DOM::NamedNodeMap> replacement_attributes)
+void Inspector::replace_dom_node_attribute(i64 node_id, WebIDL::UnsignedLongLong attribute_index, JS::NonnullGCPtr<DOM::NamedNodeMap> replacement_attributes)
 {
 {
     inspector_page_client().inspector_did_replace_dom_node_attribute(node_id, static_cast<size_t>(attribute_index), replacement_attributes);
     inspector_page_client().inspector_did_replace_dom_node_attribute(node_id, static_cast<size_t>(attribute_index), replacement_attributes);
 }
 }
 
 
-void Inspector::request_dom_tree_context_menu(i32 node_id, i32 client_x, i32 client_y, String const& type, Optional<String> const& tag, Optional<WebIDL::UnsignedLongLong> const& attribute_index)
+void Inspector::request_dom_tree_context_menu(i64 node_id, i32 client_x, i32 client_y, String const& type, Optional<String> const& tag, Optional<WebIDL::UnsignedLongLong> const& attribute_index)
 {
 {
     inspector_page_client().inspector_did_request_dom_tree_context_menu(node_id, { client_x, client_y }, type, tag, attribute_index.map([](auto index) { return static_cast<size_t>(index); }));
     inspector_page_client().inspector_did_request_dom_tree_context_menu(node_id, { client_x, client_y }, type, tag, attribute_index.map([](auto index) { return static_cast<size_t>(index); }));
 }
 }
@@ -79,14 +79,18 @@ void Inspector::request_cookie_context_menu(WebIDL::UnsignedLongLong cookie_inde
     inspector_page_client().inspector_did_request_cookie_context_menu(cookie_index, { client_x, client_y });
     inspector_page_client().inspector_did_request_cookie_context_menu(cookie_index, { client_x, client_y });
 }
 }
 
 
-void Inspector::request_style_sheet_source(String const& type_string, Optional<i32> const& dom_node_unique_id, Optional<String> const& url)
+void Inspector::request_style_sheet_source(String const& type_string, Optional<i64> const& dom_node_unique_id, Optional<String> const& url)
 {
 {
     auto type = CSS::style_sheet_identifier_type_from_string(type_string);
     auto type = CSS::style_sheet_identifier_type_from_string(type_string);
     VERIFY(type.has_value());
     VERIFY(type.has_value());
 
 
+    Optional<UniqueNodeID> dom_node_unique_id_opt;
+    if (dom_node_unique_id.has_value())
+        dom_node_unique_id_opt = dom_node_unique_id.value();
+
     inspector_page_client().inspector_did_request_style_sheet_source({
     inspector_page_client().inspector_did_request_style_sheet_source({
         .type = type.value(),
         .type = type.value(),
-        .dom_element_unique_id = dom_node_unique_id,
+        .dom_element_unique_id = dom_node_unique_id_opt,
         .url = url,
         .url = url,
     });
     });
 }
 }

+ 7 - 7
Userland/Libraries/LibWeb/Internals/Inspector.h

@@ -20,18 +20,18 @@ public:
     virtual ~Inspector() override;
     virtual ~Inspector() override;
 
 
     void inspector_loaded();
     void inspector_loaded();
-    void inspect_dom_node(i32 node_id, Optional<i32> const& pseudo_element);
+    void inspect_dom_node(i64 node_id, Optional<i32> const& pseudo_element);
 
 
-    void set_dom_node_text(i32 node_id, String const& text);
-    void set_dom_node_tag(i32 node_id, String const& tag);
-    void add_dom_node_attributes(i32 node_id, JS::NonnullGCPtr<DOM::NamedNodeMap> attributes);
-    void replace_dom_node_attribute(i32 node_id, WebIDL::UnsignedLongLong attribute_index, JS::NonnullGCPtr<DOM::NamedNodeMap> replacement_attributes);
+    void set_dom_node_text(i64 node_id, String const& text);
+    void set_dom_node_tag(i64 node_id, String const& tag);
+    void add_dom_node_attributes(i64 node_id, JS::NonnullGCPtr<DOM::NamedNodeMap> attributes);
+    void replace_dom_node_attribute(i64 node_id, WebIDL::UnsignedLongLong attribute_index, JS::NonnullGCPtr<DOM::NamedNodeMap> replacement_attributes);
 
 
-    void request_dom_tree_context_menu(i32 node_id, i32 client_x, i32 client_y, String const& type, Optional<String> const& tag, Optional<WebIDL::UnsignedLongLong> const& attribute_index);
+    void request_dom_tree_context_menu(i64 node_id, i32 client_x, i32 client_y, String const& type, Optional<String> const& tag, Optional<WebIDL::UnsignedLongLong> const& attribute_index);
 
 
     void request_cookie_context_menu(WebIDL::UnsignedLongLong cookie_index, i32 client_x, i32 client_y);
     void request_cookie_context_menu(WebIDL::UnsignedLongLong cookie_index, i32 client_x, i32 client_y);
 
 
-    void request_style_sheet_source(String const& type, Optional<i32> const& dom_node_unique_id, Optional<String> const& url);
+    void request_style_sheet_source(String const& type, Optional<i64> const& dom_node_unique_id, Optional<String> const& url);
 
 
     void execute_console_script(String const& script);
     void execute_console_script(String const& script);
 
 

+ 7 - 7
Userland/Libraries/LibWeb/Internals/Inspector.idl

@@ -3,18 +3,18 @@
 [Exposed=Nobody] interface Inspector {
 [Exposed=Nobody] interface Inspector {
 
 
     undefined inspectorLoaded();
     undefined inspectorLoaded();
-    undefined inspectDOMNode(long nodeID, optional long pseudoElement);
+    undefined inspectDOMNode(long long nodeID, optional long pseudoElement);
 
 
-    undefined setDOMNodeText(long nodeID, DOMString text);
-    undefined setDOMNodeTag(long nodeID, DOMString tag);
-    undefined addDOMNodeAttributes(long nodeID, NamedNodeMap attributes);
-    undefined replaceDOMNodeAttribute(long nodeID, unsigned long long attributeIndex, NamedNodeMap replacementAttributes);
+    undefined setDOMNodeText(long long nodeID, DOMString text);
+    undefined setDOMNodeTag(long long nodeID, DOMString tag);
+    undefined addDOMNodeAttributes(long long nodeID, NamedNodeMap attributes);
+    undefined replaceDOMNodeAttribute(long long nodeID, unsigned long long attributeIndex, NamedNodeMap replacementAttributes);
 
 
-    undefined requestDOMTreeContextMenu(long nodeID, long clientX, long clientY, DOMString type, DOMString? tag, unsigned long long? attributeIndex);
+    undefined requestDOMTreeContextMenu(long long nodeID, long clientX, long clientY, DOMString type, DOMString? tag, unsigned long long? attributeIndex);
 
 
     undefined requestCookieContextMenu(unsigned long long cookieIndex, long clientX, long clientY);
     undefined requestCookieContextMenu(unsigned long long cookieIndex, long clientX, long clientY);
 
 
-    undefined requestStyleSheetSource(DOMString type, long? domNodeID, DOMString? url);
+    undefined requestStyleSheetSource(DOMString type, long long? domNodeID, DOMString? url);
 
 
     undefined executeConsoleScript(DOMString script);
     undefined executeConsoleScript(DOMString script);
 
 

+ 3 - 3
Userland/Libraries/LibWeb/Page/Page.cpp

@@ -432,19 +432,19 @@ void Page::select_dropdown_closed(Optional<u32> const& selected_item_id)
     }
     }
 }
 }
 
 
-void Page::register_media_element(Badge<HTML::HTMLMediaElement>, int media_id)
+void Page::register_media_element(Badge<HTML::HTMLMediaElement>, UniqueNodeID media_id)
 {
 {
     m_media_elements.append(media_id);
     m_media_elements.append(media_id);
 }
 }
 
 
-void Page::unregister_media_element(Badge<HTML::HTMLMediaElement>, int media_id)
+void Page::unregister_media_element(Badge<HTML::HTMLMediaElement>, UniqueNodeID media_id)
 {
 {
     m_media_elements.remove_all_matching([&](auto candidate_id) {
     m_media_elements.remove_all_matching([&](auto candidate_id) {
         return candidate_id == media_id;
         return candidate_id == media_id;
     });
     });
 }
 }
 
 
-void Page::did_request_media_context_menu(i32 media_id, CSSPixelPoint position, ByteString const& target, unsigned modifiers, MediaContextMenu menu)
+void Page::did_request_media_context_menu(UniqueNodeID media_id, CSSPixelPoint position, ByteString const& target, unsigned modifiers, MediaContextMenu menu)
 {
 {
     m_media_context_menu_element_id = media_id;
     m_media_context_menu_element_id = media_id;
     client().page_did_request_media_context_menu(position, target, modifiers, move(menu));
     client().page_did_request_media_context_menu(position, target, modifiers, move(menu));

+ 11 - 11
Userland/Libraries/LibWeb/Page/Page.h

@@ -164,8 +164,8 @@ public:
         Select,
         Select,
     };
     };
 
 
-    void register_media_element(Badge<HTML::HTMLMediaElement>, int media_id);
-    void unregister_media_element(Badge<HTML::HTMLMediaElement>, int media_id);
+    void register_media_element(Badge<HTML::HTMLMediaElement>, UniqueNodeID media_id);
+    void unregister_media_element(Badge<HTML::HTMLMediaElement>, UniqueNodeID media_id);
 
 
     struct MediaContextMenu {
     struct MediaContextMenu {
         URL::URL media_url;
         URL::URL media_url;
@@ -175,7 +175,7 @@ public:
         bool has_user_agent_controls { false };
         bool has_user_agent_controls { false };
         bool is_looping { false };
         bool is_looping { false };
     };
     };
-    void did_request_media_context_menu(i32 media_id, CSSPixelPoint, ByteString const& target, unsigned modifiers, MediaContextMenu);
+    void did_request_media_context_menu(UniqueNodeID media_id, CSSPixelPoint, ByteString const& target, unsigned modifiers, MediaContextMenu);
     WebIDL::ExceptionOr<void> toggle_media_play_state();
     WebIDL::ExceptionOr<void> toggle_media_play_state();
     void toggle_media_mute_state();
     void toggle_media_mute_state();
     WebIDL::ExceptionOr<void> toggle_media_loop_state();
     WebIDL::ExceptionOr<void> toggle_media_loop_state();
@@ -253,8 +253,8 @@ private:
     PendingNonBlockingDialog m_pending_non_blocking_dialog { PendingNonBlockingDialog::None };
     PendingNonBlockingDialog m_pending_non_blocking_dialog { PendingNonBlockingDialog::None };
     WeakPtr<HTML::HTMLElement> m_pending_non_blocking_dialog_target;
     WeakPtr<HTML::HTMLElement> m_pending_non_blocking_dialog_target;
 
 
-    Vector<int> m_media_elements;
-    Optional<int> m_media_context_menu_element_id;
+    Vector<UniqueNodeID> m_media_elements;
+    Optional<UniqueNodeID> m_media_context_menu_element_id;
 
 
     Web::HTML::MuteState m_mute_state { Web::HTML::MuteState::Unmuted };
     Web::HTML::MuteState m_mute_state { Web::HTML::MuteState::Unmuted };
 
 
@@ -373,12 +373,12 @@ public:
     virtual IPC::File request_worker_agent() { return IPC::File {}; }
     virtual IPC::File request_worker_agent() { return IPC::File {}; }
 
 
     virtual void inspector_did_load() { }
     virtual void inspector_did_load() { }
-    virtual void inspector_did_select_dom_node([[maybe_unused]] i32 node_id, [[maybe_unused]] Optional<CSS::Selector::PseudoElement::Type> const& pseudo_element) { }
-    virtual void inspector_did_set_dom_node_text([[maybe_unused]] i32 node_id, [[maybe_unused]] String const& text) { }
-    virtual void inspector_did_set_dom_node_tag([[maybe_unused]] i32 node_id, [[maybe_unused]] String const& tag) { }
-    virtual void inspector_did_add_dom_node_attributes([[maybe_unused]] i32 node_id, [[maybe_unused]] JS::NonnullGCPtr<DOM::NamedNodeMap> attributes) { }
-    virtual void inspector_did_replace_dom_node_attribute([[maybe_unused]] i32 node_id, [[maybe_unused]] size_t attribute_index, [[maybe_unused]] JS::NonnullGCPtr<DOM::NamedNodeMap> replacement_attributes) { }
-    virtual void inspector_did_request_dom_tree_context_menu([[maybe_unused]] i32 node_id, [[maybe_unused]] CSSPixelPoint position, [[maybe_unused]] String const& type, [[maybe_unused]] Optional<String> const& tag, [[maybe_unused]] Optional<size_t> const& attribute_index) { }
+    virtual void inspector_did_select_dom_node([[maybe_unused]] UniqueNodeID node_id, [[maybe_unused]] Optional<CSS::Selector::PseudoElement::Type> const& pseudo_element) { }
+    virtual void inspector_did_set_dom_node_text([[maybe_unused]] UniqueNodeID node_id, [[maybe_unused]] String const& text) { }
+    virtual void inspector_did_set_dom_node_tag([[maybe_unused]] UniqueNodeID node_id, [[maybe_unused]] String const& tag) { }
+    virtual void inspector_did_add_dom_node_attributes([[maybe_unused]] UniqueNodeID node_id, [[maybe_unused]] JS::NonnullGCPtr<DOM::NamedNodeMap> attributes) { }
+    virtual void inspector_did_replace_dom_node_attribute([[maybe_unused]] UniqueNodeID node_id, [[maybe_unused]] size_t attribute_index, [[maybe_unused]] JS::NonnullGCPtr<DOM::NamedNodeMap> replacement_attributes) { }
+    virtual void inspector_did_request_dom_tree_context_menu([[maybe_unused]] UniqueNodeID node_id, [[maybe_unused]] CSSPixelPoint position, [[maybe_unused]] String const& type, [[maybe_unused]] Optional<String> const& tag, [[maybe_unused]] Optional<size_t> const& attribute_index) { }
     virtual void inspector_did_request_cookie_context_menu([[maybe_unused]] size_t cookie_index, [[maybe_unused]] CSSPixelPoint position) { }
     virtual void inspector_did_request_cookie_context_menu([[maybe_unused]] size_t cookie_index, [[maybe_unused]] CSSPixelPoint position) { }
     virtual void inspector_did_request_style_sheet_source([[maybe_unused]] CSS::StyleSheetIdentifier const& identifier) { }
     virtual void inspector_did_request_style_sheet_source([[maybe_unused]] CSS::StyleSheetIdentifier const& identifier) { }
     virtual void inspector_did_execute_console_script([[maybe_unused]] String const& script) { }
     virtual void inspector_did_execute_console_script([[maybe_unused]] String const& script) { }

+ 1 - 1
Userland/Libraries/LibWeb/Painting/PaintableBox.cpp

@@ -432,7 +432,7 @@ void PaintableBox::paint(PaintContext& context, PaintPhase phase) const
         auto size_text_device_rect = context.enclosing_device_rect(size_text_rect).to_type<int>();
         auto size_text_device_rect = context.enclosing_device_rect(size_text_rect).to_type<int>();
         context.display_list_recorder().fill_rect(size_text_device_rect, context.palette().color(Gfx::ColorRole::Tooltip));
         context.display_list_recorder().fill_rect(size_text_device_rect, context.palette().color(Gfx::ColorRole::Tooltip));
         context.display_list_recorder().draw_rect(size_text_device_rect, context.palette().threed_shadow1());
         context.display_list_recorder().draw_rect(size_text_device_rect, context.palette().threed_shadow1());
-        context.display_list_recorder().draw_text(size_text_device_rect, size_text, font, Gfx::TextAlignment::Center, context.palette().color(Gfx::ColorRole::TooltipText));
+        context.display_list_recorder().draw_text(size_text_device_rect, size_text, font.with_size(font.point_size() * context.device_pixels_per_css_pixel()), Gfx::TextAlignment::Center, context.palette().color(Gfx::ColorRole::TooltipText));
     }
     }
 }
 }
 
 

+ 15 - 0
Userland/Libraries/LibWeb/UIEvents/PointerEvent.cpp

@@ -25,7 +25,15 @@ PointerEvent::PointerEvent(JS::Realm& realm, FlyString const& type, PointerEvent
     , m_azimuth_angle(event_init.azimuth_angle.value_or(0))
     , m_azimuth_angle(event_init.azimuth_angle.value_or(0))
     , m_pointer_type(event_init.pointer_type)
     , m_pointer_type(event_init.pointer_type)
     , m_is_primary(event_init.is_primary)
     , m_is_primary(event_init.is_primary)
+    , m_persistent_device_id(event_init.persistent_device_id)
 {
 {
+    m_coalesced_events.ensure_capacity(event_init.coalesced_events.size());
+    for (auto const& coalesced_event : event_init.coalesced_events)
+        m_coalesced_events.unchecked_append(*coalesced_event);
+
+    m_predicted_events.ensure_capacity(event_init.predicted_events.size());
+    for (auto const& predicted_event : event_init.predicted_events)
+        m_predicted_events.unchecked_append(*predicted_event);
 }
 }
 
 
 PointerEvent::~PointerEvent() = default;
 PointerEvent::~PointerEvent() = default;
@@ -36,6 +44,13 @@ void PointerEvent::initialize(JS::Realm& realm)
     WEB_SET_PROTOTYPE_FOR_INTERFACE(PointerEvent);
     WEB_SET_PROTOTYPE_FOR_INTERFACE(PointerEvent);
 }
 }
 
 
+void PointerEvent::visit_edges(Cell::Visitor& visitor)
+{
+    Base::visit_edges(visitor);
+    visitor.visit(m_coalesced_events);
+    visitor.visit(m_predicted_events);
+}
+
 JS::NonnullGCPtr<PointerEvent> PointerEvent::create(JS::Realm& realm, FlyString const& type, PointerEventInit const& event_init, double page_x, double page_y, double offset_x, double offset_y)
 JS::NonnullGCPtr<PointerEvent> PointerEvent::create(JS::Realm& realm, FlyString const& type, PointerEventInit const& event_init, double page_x, double page_y, double offset_x, double offset_y)
 {
 {
     return realm.heap().allocate<PointerEvent>(realm, realm, type, event_init, page_x, page_y, offset_x, offset_y);
     return realm.heap().allocate<PointerEvent>(realm, realm, type, event_init, page_x, page_y, offset_x, offset_y);

+ 17 - 0
Userland/Libraries/LibWeb/UIEvents/PointerEvent.h

@@ -24,6 +24,9 @@ struct PointerEventInit : public MouseEventInit {
     Optional<double> azimuth_angle;
     Optional<double> azimuth_angle;
     String pointer_type;
     String pointer_type;
     bool is_primary { false };
     bool is_primary { false };
+    WebIDL::Long persistent_device_id { 0 };
+    AK::Vector<JS::Handle<PointerEvent>> coalesced_events;
+    AK::Vector<JS::Handle<PointerEvent>> predicted_events;
 };
 };
 
 
 // https://w3c.github.io/pointerevents/#pointerevent-interface
 // https://w3c.github.io/pointerevents/#pointerevent-interface
@@ -49,6 +52,9 @@ public:
     double azimuth_angle() const { return m_azimuth_angle; }
     double azimuth_angle() const { return m_azimuth_angle; }
     String const& pointer_type() const { return m_pointer_type; }
     String const& pointer_type() const { return m_pointer_type; }
     bool is_primary() const { return m_is_primary; }
     bool is_primary() const { return m_is_primary; }
+    WebIDL::Long persistent_device_id() const { return m_persistent_device_id; }
+    AK::ReadonlySpan<JS::NonnullGCPtr<PointerEvent>> get_coalesced_events() const { return m_coalesced_events; }
+    AK::ReadonlySpan<JS::NonnullGCPtr<PointerEvent>> get_predicted_events() const { return m_predicted_events; }
 
 
     // https://w3c.github.io/pointerevents/#dom-pointerevent-pressure
     // https://w3c.github.io/pointerevents/#dom-pointerevent-pressure
     // For hardware and platforms that do not support pressure, the value MUST be 0.5 when in the active buttons state and 0 otherwise.
     // For hardware and platforms that do not support pressure, the value MUST be 0.5 when in the active buttons state and 0 otherwise.
@@ -58,6 +64,7 @@ protected:
     PointerEvent(JS::Realm&, FlyString const& type, PointerEventInit const&, double page_x, double page_y, double offset_x, double offset_y);
     PointerEvent(JS::Realm&, FlyString const& type, PointerEventInit const&, double page_x, double page_y, double offset_x, double offset_y);
 
 
     virtual void initialize(JS::Realm&) override;
     virtual void initialize(JS::Realm&) override;
+    virtual void visit_edges(Cell::Visitor&) override;
 
 
 private:
 private:
     virtual bool is_pointer_event() const final { return true; }
     virtual bool is_pointer_event() const final { return true; }
@@ -119,6 +126,16 @@ private:
     // Indicates if the pointer represents the primary pointer of this pointer type
     // Indicates if the pointer represents the primary pointer of this pointer type
     // https://w3c.github.io/pointerevents/#dom-pointerevent-isprimary
     // https://w3c.github.io/pointerevents/#dom-pointerevent-isprimary
     bool m_is_primary { false };
     bool m_is_primary { false };
+
+    // A unique identifier for the pointing device.
+    // https://w3c.github.io/pointerevents/#dom-pointerevent-persistentdeviceid
+    WebIDL::Long m_persistent_device_id { 0 };
+
+    // https://w3c.github.io/pointerevents/#dom-pointerevent-getcoalescedevents
+    AK::Vector<JS::NonnullGCPtr<PointerEvent>> m_coalesced_events;
+
+    // https://w3c.github.io/pointerevents/#dom-pointerevent-getpredictedevents
+    AK::Vector<JS::NonnullGCPtr<PointerEvent>> m_predicted_events;
 };
 };
 
 
 }
 }

+ 7 - 4
Userland/Libraries/LibWeb/UIEvents/PointerEvent.idl

@@ -1,5 +1,6 @@
 #import <UIEvents/MouseEvent.idl>
 #import <UIEvents/MouseEvent.idl>
 
 
+// https://w3c.github.io/pointerevents/#ref-for-dom-pointereventinit-1
 dictionary PointerEventInit : MouseEventInit {
 dictionary PointerEventInit : MouseEventInit {
     long pointerId = 0;
     long pointerId = 0;
     double width = 1;
     double width = 1;
@@ -13,8 +14,9 @@ dictionary PointerEventInit : MouseEventInit {
     double azimuthAngle;
     double azimuthAngle;
     DOMString pointerType = "";
     DOMString pointerType = "";
     boolean isPrimary = false;
     boolean isPrimary = false;
-    // FIXME: sequence<PointerEvent> coalescedEvents = [];
-    // FIXME: sequence<PointerEvent> predictedEvents = [];
+    long persistentDeviceId = 0;
+    sequence<PointerEvent> coalescedEvents = [];
+    sequence<PointerEvent> predictedEvents = [];
 };
 };
 
 
 // https://w3c.github.io/pointerevents/#pointerevent-interface
 // https://w3c.github.io/pointerevents/#pointerevent-interface
@@ -33,6 +35,7 @@ interface PointerEvent : MouseEvent {
     readonly attribute double azimuthAngle;
     readonly attribute double azimuthAngle;
     readonly attribute DOMString pointerType;
     readonly attribute DOMString pointerType;
     readonly attribute boolean isPrimary;
     readonly attribute boolean isPrimary;
-    [FIXME, SecureContext] sequence<PointerEvent> getCoalescedEvents();
-    [FIXME] sequence<PointerEvent> getPredictedEvents();
+    readonly attribute long persistentDeviceId;
+    [SecureContext] sequence<PointerEvent> getCoalescedEvents();
+    sequence<PointerEvent> getPredictedEvents();
 };
 };

+ 8 - 1
Userland/Libraries/LibWeb/WebDriver/Capabilities.cpp

@@ -72,6 +72,13 @@ static ErrorOr<JsonObject, Error> deserialize_as_a_proxy(JsonValue parameter)
     return proxy;
     return proxy;
 }
 }
 
 
+static InterfaceMode default_interface_mode { InterfaceMode::Graphical };
+
+void set_default_interface_mode(InterfaceMode interface_mode)
+{
+    default_interface_mode = interface_mode;
+}
+
 static Response deserialize_as_ladybird_options(JsonValue value)
 static Response deserialize_as_ladybird_options(JsonValue value)
 {
 {
     if (!value.is_object())
     if (!value.is_object())
@@ -88,7 +95,7 @@ static Response deserialize_as_ladybird_options(JsonValue value)
 static JsonObject default_ladybird_options()
 static JsonObject default_ladybird_options()
 {
 {
     JsonObject options;
     JsonObject options;
-    options.set("headless"sv, false);
+    options.set("headless"sv, default_interface_mode == InterfaceMode::Headless);
 
 
     return options;
     return options;
 }
 }

+ 6 - 0
Userland/Libraries/LibWeb/WebDriver/Capabilities.h

@@ -54,6 +54,12 @@ constexpr UnhandledPromptBehavior unhandled_prompt_behavior_from_string(StringVi
     VERIFY_NOT_REACHED();
     VERIFY_NOT_REACHED();
 }
 }
 
 
+enum class InterfaceMode {
+    Graphical,
+    Headless,
+};
+void set_default_interface_mode(InterfaceMode);
+
 struct LadybirdOptions {
 struct LadybirdOptions {
     explicit LadybirdOptions(JsonObject const& capabilities);
     explicit LadybirdOptions(JsonObject const& capabilities);
 
 

+ 6 - 6
Userland/Libraries/LibWeb/WebDriver/ElementReference.cpp

@@ -35,7 +35,7 @@ ByteString get_or_create_a_web_element_reference(Web::DOM::Node const& element)
     // FIXME: 2. Add element to the list of known elements of the current browsing context.
     // FIXME: 2. Add element to the list of known elements of the current browsing context.
     // FIXME: 3. Return success with the element’s web element reference.
     // FIXME: 3. Return success with the element’s web element reference.
 
 
-    return ByteString::number(element.unique_id());
+    return ByteString::number(element.unique_id().value());
 }
 }
 
 
 // https://w3c.github.io/webdriver/#dfn-web-element-reference-object
 // https://w3c.github.io/webdriver/#dfn-web-element-reference-object
@@ -104,12 +104,12 @@ ErrorOr<JS::NonnullGCPtr<Web::DOM::Element>, Web::WebDriver::Error> get_known_el
 
 
     // 1. If not node reference is known with session, session's current browsing context, and reference return error
     // 1. If not node reference is known with session, session's current browsing context, and reference return error
     //    with error code no such element.
     //    with error code no such element.
-    auto element = element_id.to_number<int>();
+    auto element = element_id.to_number<i64>();
     if (!element.has_value())
     if (!element.has_value())
         return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, "Element ID is not an integer");
         return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, "Element ID is not an integer");
 
 
     // 2. Let node be the result of get a node with session, session's current browsing context, and reference.
     // 2. Let node be the result of get a node with session, session's current browsing context, and reference.
-    auto* node = Web::DOM::Node::from_unique_id(*element);
+    auto* node = Web::DOM::Node::from_unique_id(UniqueNodeID(*element));
 
 
     // 3. If node is not null and node does not implement Element return error with error code no such element.
     // 3. If node is not null and node does not implement Element return error with error code no such element.
     if (node && !node->is_element())
     if (node && !node->is_element())
@@ -264,7 +264,7 @@ ByteString get_or_create_a_shadow_root_reference(Web::DOM::ShadowRoot const& sha
     // FIXME: 2. Add shadow to the list of known shadow roots of the current browsing context.
     // FIXME: 2. Add shadow to the list of known shadow roots of the current browsing context.
     // FIXME: 3. Return success with the shadow’s shadow root reference.
     // FIXME: 3. Return success with the shadow’s shadow root reference.
 
 
-    return ByteString::number(shadow_root.unique_id());
+    return ByteString::number(shadow_root.unique_id().value());
 }
 }
 
 
 // https://w3c.github.io/webdriver/#dfn-shadow-root-reference-object
 // https://w3c.github.io/webdriver/#dfn-shadow-root-reference-object
@@ -287,11 +287,11 @@ ErrorOr<JS::NonnullGCPtr<Web::DOM::ShadowRoot>, Web::WebDriver::Error> get_known
 {
 {
     // NOTE: The whole concept of "known shadow roots" is not implemented yet. See get_or_create_a_shadow_root_reference().
     // NOTE: The whole concept of "known shadow roots" is not implemented yet. See get_or_create_a_shadow_root_reference().
     //       For now the shadow root is only represented by its ID.
     //       For now the shadow root is only represented by its ID.
-    auto shadow_root = shadow_id.to_number<int>();
+    auto shadow_root = shadow_id.to_number<i64>();
     if (!shadow_root.has_value())
     if (!shadow_root.has_value())
         return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Shadow ID is not an integer");
         return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Shadow ID is not an integer");
 
 
-    auto* node = Web::DOM::Node::from_unique_id(*shadow_root);
+    auto* node = Web::DOM::Node::from_unique_id(UniqueNodeID(*shadow_root));
 
 
     if (!node || !node->is_shadow_root())
     if (!node || !node->is_shadow_root())
         return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, ByteString::formatted("Could not find shadow root with ID: {}", shadow_id));
         return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, ByteString::formatted("Could not find shadow root with ID: {}", shadow_id));

+ 6 - 3
Userland/Libraries/LibWeb/XHR/XMLHttpRequest.cpp

@@ -25,6 +25,7 @@
 #include <LibWeb/DOM/EventDispatcher.h>
 #include <LibWeb/DOM/EventDispatcher.h>
 #include <LibWeb/DOM/IDLEventListener.h>
 #include <LibWeb/DOM/IDLEventListener.h>
 #include <LibWeb/DOM/XMLDocument.h>
 #include <LibWeb/DOM/XMLDocument.h>
+#include <LibWeb/DOMURL/DOMURL.h>
 #include <LibWeb/Fetch/BodyInit.h>
 #include <LibWeb/Fetch/BodyInit.h>
 #include <LibWeb/Fetch/Fetching/Fetching.h>
 #include <LibWeb/Fetch/Fetching/Fetching.h>
 #include <LibWeb/Fetch/Infrastructure/FetchAlgorithms.h>
 #include <LibWeb/Fetch/Infrastructure/FetchAlgorithms.h>
@@ -105,7 +106,7 @@ static void fire_progress_event(XMLHttpRequestEventTarget& target, FlyString con
     // with the loaded attribute initialized to transmitted, and if length is not 0, with the lengthComputable attribute initialized to true
     // with the loaded attribute initialized to transmitted, and if length is not 0, with the lengthComputable attribute initialized to true
     // and the total attribute initialized to length.
     // and the total attribute initialized to length.
     ProgressEventInit event_init {};
     ProgressEventInit event_init {};
-    event_init.length_computable = true;
+    event_init.length_computable = length;
     event_init.loaded = transmitted;
     event_init.loaded = transmitted;
     event_init.total = length;
     event_init.total = length;
     // FIXME: If we're in an async context, this will propagate to a callback context which can't propagate it anywhere else and does not expect this to fail.
     // FIXME: If we're in an async context, this will propagate to a callback context which can't propagate it anywhere else and does not expect this to fail.
@@ -480,8 +481,10 @@ WebIDL::ExceptionOr<void> XMLHttpRequest::open(String const& method_string, Stri
     auto normalized_method = Fetch::Infrastructure::normalize_method(method);
     auto normalized_method = Fetch::Infrastructure::normalize_method(method);
 
 
     // 5. Let parsedURL be the result of parsing url with this’s relevant settings object’s API base URL and this’s relevant settings object’s API URL character encoding.
     // 5. Let parsedURL be the result of parsing url with this’s relevant settings object’s API base URL and this’s relevant settings object’s API URL character encoding.
-    // FIXME: Pass in this’s relevant settings object’s API URL character encoding.
-    auto parsed_url = HTML::relevant_settings_object(*this).api_base_url().complete_url(url);
+    auto& relevant_settings_object = HTML::relevant_settings_object(*this);
+    auto api_base_url = relevant_settings_object.api_base_url();
+    auto api_url_character_encoding = relevant_settings_object.api_url_character_encoding();
+    auto parsed_url = DOMURL::parse(url, api_base_url, api_url_character_encoding);
 
 
     // 6. If parsedURL is failure, then throw a "SyntaxError" DOMException.
     // 6. If parsedURL is failure, then throw a "SyntaxError" DOMException.
     if (!parsed_url.is_valid())
     if (!parsed_url.is_valid())

+ 5 - 5
Userland/Libraries/LibWebView/InspectorClient.cpp

@@ -40,7 +40,7 @@ static String style_sheet_identifier_to_json(Web::CSS::StyleSheetIdentifier cons
 {
 {
     return MUST(String::formatted("{{ type: '{}', domNodeId: {}, url: '{}' }}"sv,
     return MUST(String::formatted("{{ type: '{}', domNodeId: {}, url: '{}' }}"sv,
         Web::CSS::style_sheet_identifier_type_to_string(identifier.type),
         Web::CSS::style_sheet_identifier_type_to_string(identifier.type),
-        identifier.dom_element_unique_id.map([](auto& it) { return String::number(it); }).value_or("undefined"_string),
+        identifier.dom_element_unique_id.map([](auto& it) { return String::number(it.value()); }).value_or("undefined"_string),
         identifier.url.value_or("undefined"_string)));
         identifier.url.value_or("undefined"_string)));
 }
 }
 
 
@@ -128,8 +128,8 @@ InspectorClient::InspectorClient(ViewImplementation& content_web_view, ViewImple
         m_inspector_web_view.run_javascript(builder.string_view());
         m_inspector_web_view.run_javascript(builder.string_view());
     };
     };
 
 
-    m_content_web_view.on_received_style_sheet_source = [this](Web::CSS::StyleSheetIdentifier const& identifier, String const& source) {
-        auto html = highlight_source(identifier.url.value_or({}), source, Syntax::Language::CSS, HighlightOutputMode::SourceOnly);
+    m_content_web_view.on_received_style_sheet_source = [this](Web::CSS::StyleSheetIdentifier const& identifier, auto const& base_url, String const& source) {
+        auto html = highlight_source(identifier.url.value_or({}), base_url, source, Syntax::Language::CSS, HighlightOutputMode::SourceOnly);
         auto script = MUST(String::formatted("inspector.setStyleSheetSource({}, \"{}\");",
         auto script = MUST(String::formatted("inspector.setStyleSheetSource({}, \"{}\");",
             style_sheet_identifier_to_json(identifier),
             style_sheet_identifier_to_json(identifier),
             MUST(encode_base64(html.bytes()))));
             MUST(encode_base64(html.bytes()))));
@@ -343,14 +343,14 @@ void InspectorClient::clear_selection()
     m_inspector_web_view.run_javascript(script);
     m_inspector_web_view.run_javascript(script);
 }
 }
 
 
-void InspectorClient::select_node(i32 node_id)
+void InspectorClient::select_node(Web::UniqueNodeID node_id)
 {
 {
     if (!m_dom_tree_loaded) {
     if (!m_dom_tree_loaded) {
         m_pending_selection = node_id;
         m_pending_selection = node_id;
         return;
         return;
     }
     }
 
 
-    auto script = MUST(String::formatted("inspector.inspectDOMNodeID({});", node_id));
+    auto script = MUST(String::formatted("inspector.inspectDOMNodeID({});", node_id.value()));
     m_inspector_web_view.run_javascript(script);
     m_inspector_web_view.run_javascript(script);
 }
 }
 
 

+ 5 - 5
Userland/Libraries/LibWebView/InspectorClient.h

@@ -52,7 +52,7 @@ private:
 
 
     String generate_dom_tree(JsonObject const&);
     String generate_dom_tree(JsonObject const&);
     String generate_accessibility_tree(JsonObject const&);
     String generate_accessibility_tree(JsonObject const&);
-    void select_node(i32 node_id);
+    void select_node(Web::UniqueNodeID);
 
 
     void load_cookies();
     void load_cookies();
 
 
@@ -72,20 +72,20 @@ private:
     ViewImplementation& m_content_web_view;
     ViewImplementation& m_content_web_view;
     ViewImplementation& m_inspector_web_view;
     ViewImplementation& m_inspector_web_view;
 
 
-    Optional<i32> m_body_node_id;
-    Optional<i32> m_pending_selection;
+    Optional<Web::UniqueNodeID> m_body_node_id;
+    Optional<Web::UniqueNodeID> m_pending_selection;
 
 
     bool m_inspector_loaded { false };
     bool m_inspector_loaded { false };
     bool m_dom_tree_loaded { false };
     bool m_dom_tree_loaded { false };
 
 
     struct ContextMenuData {
     struct ContextMenuData {
-        i32 dom_node_id { 0 };
+        Web::UniqueNodeID dom_node_id;
         Optional<String> tag;
         Optional<String> tag;
         Optional<Attribute> attribute;
         Optional<Attribute> attribute;
     };
     };
     Optional<ContextMenuData> m_context_menu_data;
     Optional<ContextMenuData> m_context_menu_data;
 
 
-    HashMap<int, Vector<Attribute>> m_dom_node_attributes;
+    HashMap<Web::UniqueNodeID, Vector<Attribute>> m_dom_node_attributes;
 
 
     Vector<Web::Cookie::Cookie> m_cookies;
     Vector<Web::Cookie::Cookie> m_cookies;
     Optional<size_t> m_cookie_context_menu_index;
     Optional<size_t> m_cookie_context_menu_index;

+ 37 - 4
Userland/Libraries/LibWebView/SourceHighlighter.cpp

@@ -11,6 +11,7 @@
 #include <LibURL/URL.h>
 #include <LibURL/URL.h>
 #include <LibWeb/CSS/Parser/Token.h>
 #include <LibWeb/CSS/Parser/Token.h>
 #include <LibWeb/CSS/SyntaxHighlighter/SyntaxHighlighter.h>
 #include <LibWeb/CSS/SyntaxHighlighter/SyntaxHighlighter.h>
+#include <LibWeb/DOMURL/DOMURL.h>
 #include <LibWeb/HTML/SyntaxHighlighter/SyntaxHighlighter.h>
 #include <LibWeb/HTML/SyntaxHighlighter/SyntaxHighlighter.h>
 #include <LibWebView/SourceHighlighter.h>
 #include <LibWebView/SourceHighlighter.h>
 
 
@@ -113,10 +114,10 @@ void SourceHighlighterClient::highlighter_did_set_folding_regions(Vector<Syntax:
     document().set_folding_regions(move(folding_regions));
     document().set_folding_regions(move(folding_regions));
 }
 }
 
 
-String highlight_source(String const& url, StringView source, Syntax::Language language, HighlightOutputMode mode)
+String highlight_source(URL::URL const& url, URL::URL const& base_url, StringView source, Syntax::Language language, HighlightOutputMode mode)
 {
 {
     SourceHighlighterClient highlighter_client { source, language };
     SourceHighlighterClient highlighter_client { source, language };
-    return highlighter_client.to_html_string(url, mode);
+    return highlighter_client.to_html_string(url, base_url, mode);
 }
 }
 
 
 StringView SourceHighlighterClient::class_for_token(u64 token_type) const
 StringView SourceHighlighterClient::class_for_token(u64 token_type) const
@@ -232,7 +233,7 @@ StringView SourceHighlighterClient::class_for_token(u64 token_type) const
     }
     }
 }
 }
 
 
-String SourceHighlighterClient::to_html_string(String const& url, HighlightOutputMode mode) const
+String SourceHighlighterClient::to_html_string(URL::URL const& url, URL::URL const& base_url, HighlightOutputMode mode) const
 {
 {
     StringBuilder builder;
     StringBuilder builder;
 
 
@@ -266,7 +267,7 @@ String SourceHighlighterClient::to_html_string(String const& url, HighlightOutpu
 <head>
 <head>
     <meta name="color-scheme" content="dark light">)~~~"sv);
     <meta name="color-scheme" content="dark light">)~~~"sv);
 
 
-        builder.appendff("<title>View Source - {}</title>", escape_html_entities(url));
+        builder.appendff("<title>View Source - {}</title>", escape_html_entities(url.serialize_for_display()));
         builder.appendff("<style type=\"text/css\">{}</style>", HTML_HIGHLIGHTER_STYLE);
         builder.appendff("<style type=\"text/css\">{}</style>", HTML_HIGHLIGHTER_STYLE);
         builder.append(R"~~~(
         builder.append(R"~~~(
 </head>
 </head>
@@ -274,6 +275,22 @@ String SourceHighlighterClient::to_html_string(String const& url, HighlightOutpu
     }
     }
     builder.append("<pre class=\"html\">"sv);
     builder.append("<pre class=\"html\">"sv);
 
 
+    static constexpr auto href = to_array<u32>({ 'h', 'r', 'e', 'f' });
+    static constexpr auto src = to_array<u32>({ 's', 'r', 'c' });
+    bool linkify_attribute = false;
+
+    auto resolve_url_for_attribute = [&](Utf32View const& attribute_value) -> Optional<URL::URL> {
+        if (!linkify_attribute)
+            return {};
+
+        auto attribute_url = MUST(String::formatted("{}", attribute_value));
+        auto attribute_url_without_quotes = attribute_url.bytes_as_string_view().trim("\""sv);
+
+        if (auto resolved = Web::DOMURL::parse(attribute_url_without_quotes, base_url); resolved.is_valid())
+            return resolved;
+        return {};
+    };
+
     size_t span_index = 0;
     size_t span_index = 0;
     for (size_t line_index = 0; line_index < document().line_count(); ++line_index) {
     for (size_t line_index = 0; line_index < document().line_count(); ++line_index) {
         auto& line = document().line(line_index);
         auto& line = document().line(line_index);
@@ -286,11 +303,27 @@ String SourceHighlighterClient::to_html_string(String const& url, HighlightOutpu
             size_t length = end - start;
             size_t length = end - start;
             if (length == 0)
             if (length == 0)
                 return;
                 return;
+
             auto text = line_view.substring_view(start, length);
             auto text = line_view.substring_view(start, length);
+
             if (span.has_value()) {
             if (span.has_value()) {
+                bool append_anchor_close = false;
+
+                if (span->data == to_underlying(Web::HTML::AugmentedTokenKind::AttributeName)) {
+                    linkify_attribute = text == Utf32View { href } || text == Utf32View { src };
+                } else if (span->data == to_underlying(Web::HTML::AugmentedTokenKind::AttributeValue)) {
+                    if (auto href = resolve_url_for_attribute(text); href.has_value()) {
+                        builder.appendff("<a href=\"{}\">", *href);
+                        append_anchor_close = true;
+                    }
+                }
+
                 start_token(span->data);
                 start_token(span->data);
                 append_escaped(text);
                 append_escaped(text);
                 end_token();
                 end_token();
+
+                if (append_anchor_close)
+                    builder.append("</a>"sv);
             } else {
             } else {
                 append_escaped(text);
                 append_escaped(text);
             }
             }

+ 4 - 2
Userland/Libraries/LibWebView/SourceHighlighter.h

@@ -7,11 +7,13 @@
 
 
 #pragma once
 #pragma once
 
 
+#include <AK/OwnPtr.h>
 #include <AK/String.h>
 #include <AK/String.h>
 #include <AK/StringView.h>
 #include <AK/StringView.h>
 #include <LibSyntax/Document.h>
 #include <LibSyntax/Document.h>
 #include <LibSyntax/HighlighterClient.h>
 #include <LibSyntax/HighlighterClient.h>
 #include <LibSyntax/Language.h>
 #include <LibSyntax/Language.h>
+#include <LibURL/Forward.h>
 
 
 namespace WebView {
 namespace WebView {
 
 
@@ -50,7 +52,7 @@ public:
     SourceHighlighterClient(StringView source, Syntax::Language);
     SourceHighlighterClient(StringView source, Syntax::Language);
     virtual ~SourceHighlighterClient() = default;
     virtual ~SourceHighlighterClient() = default;
 
 
-    String to_html_string(String const&, HighlightOutputMode) const;
+    String to_html_string(URL::URL const& url, URL::URL const& base_url, HighlightOutputMode) const;
 
 
 private:
 private:
     // ^ Syntax::HighlighterClient
     // ^ Syntax::HighlighterClient
@@ -73,7 +75,7 @@ private:
     OwnPtr<Syntax::Highlighter> m_highlighter;
     OwnPtr<Syntax::Highlighter> m_highlighter;
 };
 };
 
 
-String highlight_source(String const&, StringView, Syntax::Language, HighlightOutputMode);
+String highlight_source(URL::URL const& url, URL::URL const& base_url, StringView, Syntax::Language, HighlightOutputMode);
 
 
 constexpr inline StringView HTML_HIGHLIGHTER_STYLE = R"~~~(
 constexpr inline StringView HTML_HIGHLIGHTER_STYLE = R"~~~(
     @media (prefers-color-scheme: dark) {
     @media (prefers-color-scheme: dark) {

+ 11 - 11
Userland/Libraries/LibWebView/ViewImplementation.cpp

@@ -250,7 +250,7 @@ void ViewImplementation::inspect_dom_tree()
     client().async_inspect_dom_tree(page_id());
     client().async_inspect_dom_tree(page_id());
 }
 }
 
 
-void ViewImplementation::inspect_dom_node(i32 node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element)
+void ViewImplementation::inspect_dom_node(Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element)
 {
 {
     client().async_inspect_dom_node(page_id(), node_id, move(pseudo_element));
     client().async_inspect_dom_node(page_id(), node_id, move(pseudo_element));
 }
 }
@@ -270,47 +270,47 @@ void ViewImplementation::get_hovered_node_id()
     client().async_get_hovered_node_id(page_id());
     client().async_get_hovered_node_id(page_id());
 }
 }
 
 
-void ViewImplementation::set_dom_node_text(i32 node_id, String text)
+void ViewImplementation::set_dom_node_text(Web::UniqueNodeID node_id, String text)
 {
 {
     client().async_set_dom_node_text(page_id(), node_id, move(text));
     client().async_set_dom_node_text(page_id(), node_id, move(text));
 }
 }
 
 
-void ViewImplementation::set_dom_node_tag(i32 node_id, String name)
+void ViewImplementation::set_dom_node_tag(Web::UniqueNodeID node_id, String name)
 {
 {
     client().async_set_dom_node_tag(page_id(), node_id, move(name));
     client().async_set_dom_node_tag(page_id(), node_id, move(name));
 }
 }
 
 
-void ViewImplementation::add_dom_node_attributes(i32 node_id, Vector<Attribute> attributes)
+void ViewImplementation::add_dom_node_attributes(Web::UniqueNodeID node_id, Vector<Attribute> attributes)
 {
 {
     client().async_add_dom_node_attributes(page_id(), node_id, move(attributes));
     client().async_add_dom_node_attributes(page_id(), node_id, move(attributes));
 }
 }
 
 
-void ViewImplementation::replace_dom_node_attribute(i32 node_id, String name, Vector<Attribute> replacement_attributes)
+void ViewImplementation::replace_dom_node_attribute(Web::UniqueNodeID node_id, String name, Vector<Attribute> replacement_attributes)
 {
 {
     client().async_replace_dom_node_attribute(page_id(), node_id, move(name), move(replacement_attributes));
     client().async_replace_dom_node_attribute(page_id(), node_id, move(name), move(replacement_attributes));
 }
 }
 
 
-void ViewImplementation::create_child_element(i32 node_id)
+void ViewImplementation::create_child_element(Web::UniqueNodeID node_id)
 {
 {
     client().async_create_child_element(page_id(), node_id);
     client().async_create_child_element(page_id(), node_id);
 }
 }
 
 
-void ViewImplementation::create_child_text_node(i32 node_id)
+void ViewImplementation::create_child_text_node(Web::UniqueNodeID node_id)
 {
 {
     client().async_create_child_text_node(page_id(), node_id);
     client().async_create_child_text_node(page_id(), node_id);
 }
 }
 
 
-void ViewImplementation::clone_dom_node(i32 node_id)
+void ViewImplementation::clone_dom_node(Web::UniqueNodeID node_id)
 {
 {
     client().async_clone_dom_node(page_id(), node_id);
     client().async_clone_dom_node(page_id(), node_id);
 }
 }
 
 
-void ViewImplementation::remove_dom_node(i32 node_id)
+void ViewImplementation::remove_dom_node(Web::UniqueNodeID node_id)
 {
 {
     client().async_remove_dom_node(page_id(), node_id);
     client().async_remove_dom_node(page_id(), node_id);
 }
 }
 
 
-void ViewImplementation::get_dom_node_html(i32 node_id)
+void ViewImplementation::get_dom_node_html(Web::UniqueNodeID node_id)
 {
 {
     client().async_get_dom_node_html(page_id(), node_id);
     client().async_get_dom_node_html(page_id(), node_id);
 }
 }
@@ -563,7 +563,7 @@ NonnullRefPtr<Core::Promise<LexicalPath>> ViewImplementation::take_screenshot(Sc
     return promise;
     return promise;
 }
 }
 
 
-NonnullRefPtr<Core::Promise<LexicalPath>> ViewImplementation::take_dom_node_screenshot(i32 node_id)
+NonnullRefPtr<Core::Promise<LexicalPath>> ViewImplementation::take_dom_node_screenshot(Web::UniqueNodeID node_id)
 {
 {
     auto promise = Core::Promise<LexicalPath>::construct();
     auto promise = Core::Promise<LexicalPath>::construct();
 
 

+ 21 - 21
Userland/Libraries/LibWebView/ViewImplementation.h

@@ -86,20 +86,20 @@ public:
     void get_source();
     void get_source();
 
 
     void inspect_dom_tree();
     void inspect_dom_tree();
-    void inspect_dom_node(i32 node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element);
+    void inspect_dom_node(Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element);
     void inspect_accessibility_tree();
     void inspect_accessibility_tree();
     void clear_inspected_dom_node();
     void clear_inspected_dom_node();
     void get_hovered_node_id();
     void get_hovered_node_id();
 
 
-    void set_dom_node_text(i32 node_id, String text);
-    void set_dom_node_tag(i32 node_id, String name);
-    void add_dom_node_attributes(i32 node_id, Vector<Attribute> attributes);
-    void replace_dom_node_attribute(i32 node_id, String name, Vector<Attribute> replacement_attributes);
-    void create_child_element(i32 node_id);
-    void create_child_text_node(i32 node_id);
-    void clone_dom_node(i32 node_id);
-    void remove_dom_node(i32 node_id);
-    void get_dom_node_html(i32 node_id);
+    void set_dom_node_text(Web::UniqueNodeID node_id, String text);
+    void set_dom_node_tag(Web::UniqueNodeID node_id, String name);
+    void add_dom_node_attributes(Web::UniqueNodeID node_id, Vector<Attribute> attributes);
+    void replace_dom_node_attribute(Web::UniqueNodeID node_id, String name, Vector<Attribute> replacement_attributes);
+    void create_child_element(Web::UniqueNodeID node_id);
+    void create_child_text_node(Web::UniqueNodeID node_id);
+    void clone_dom_node(Web::UniqueNodeID node_id);
+    void remove_dom_node(Web::UniqueNodeID node_id);
+    void get_dom_node_html(Web::UniqueNodeID node_id);
 
 
     void list_style_sheets();
     void list_style_sheets();
     void request_style_sheet_source(Web::CSS::StyleSheetIdentifier const&);
     void request_style_sheet_source(Web::CSS::StyleSheetIdentifier const&);
@@ -140,7 +140,7 @@ public:
         Full,
         Full,
     };
     };
     NonnullRefPtr<Core::Promise<LexicalPath>> take_screenshot(ScreenshotType);
     NonnullRefPtr<Core::Promise<LexicalPath>> take_screenshot(ScreenshotType);
-    NonnullRefPtr<Core::Promise<LexicalPath>> take_dom_node_screenshot(i32);
+    NonnullRefPtr<Core::Promise<LexicalPath>> take_dom_node_screenshot(Web::UniqueNodeID);
     virtual void did_receive_screenshot(Badge<WebContentClient>, Gfx::ShareableBitmap const&);
     virtual void did_receive_screenshot(Badge<WebContentClient>, Gfx::ShareableBitmap const&);
 
 
     NonnullRefPtr<Core::Promise<String>> request_internal_page_info(PageInfoType);
     NonnullRefPtr<Core::Promise<String>> request_internal_page_info(PageInfoType);
@@ -185,15 +185,15 @@ public:
     Function<void(String const& message)> on_request_set_prompt_text;
     Function<void(String const& message)> on_request_set_prompt_text;
     Function<void()> on_request_accept_dialog;
     Function<void()> on_request_accept_dialog;
     Function<void()> on_request_dismiss_dialog;
     Function<void()> on_request_dismiss_dialog;
-    Function<void(URL::URL const&, ByteString const&)> on_received_source;
+    Function<void(URL::URL const&, URL::URL const&, String const&)> on_received_source;
     Function<void(ByteString const&)> on_received_dom_tree;
     Function<void(ByteString const&)> on_received_dom_tree;
     Function<void(Optional<DOMNodeProperties>)> on_received_dom_node_properties;
     Function<void(Optional<DOMNodeProperties>)> on_received_dom_node_properties;
     Function<void(ByteString const&)> on_received_accessibility_tree;
     Function<void(ByteString const&)> on_received_accessibility_tree;
     Function<void(Vector<Web::CSS::StyleSheetIdentifier>)> on_received_style_sheet_list;
     Function<void(Vector<Web::CSS::StyleSheetIdentifier>)> on_received_style_sheet_list;
     Function<void(Web::CSS::StyleSheetIdentifier const&)> on_inspector_requested_style_sheet_source;
     Function<void(Web::CSS::StyleSheetIdentifier const&)> on_inspector_requested_style_sheet_source;
-    Function<void(Web::CSS::StyleSheetIdentifier const&, String const&)> on_received_style_sheet_source;
-    Function<void(i32 node_id)> on_received_hovered_node_id;
-    Function<void(Optional<i32> const& node_id)> on_finshed_editing_dom_node;
+    Function<void(Web::CSS::StyleSheetIdentifier const&, URL::URL const&, String const&)> on_received_style_sheet_source;
+    Function<void(Web::UniqueNodeID)> on_received_hovered_node_id;
+    Function<void(Optional<Web::UniqueNodeID> const& node_id)> on_finshed_editing_dom_node;
     Function<void(String const&)> on_received_dom_node_html;
     Function<void(String const&)> on_received_dom_node_html;
     Function<void(i32 message_id)> on_received_console_message;
     Function<void(i32 message_id)> on_received_console_message;
     Function<void(i32 start_index, Vector<ByteString> const& message_types, Vector<ByteString> const& messages)> on_received_console_messages;
     Function<void(i32 start_index, Vector<ByteString> const& message_types, Vector<ByteString> const& messages)> on_received_console_messages;
@@ -216,12 +216,12 @@ public:
     Function<void(Web::HTML::AudioPlayState)> on_audio_play_state_changed;
     Function<void(Web::HTML::AudioPlayState)> on_audio_play_state_changed;
     Function<void(bool, bool)> on_navigation_buttons_state_changed;
     Function<void(bool, bool)> on_navigation_buttons_state_changed;
     Function<void()> on_inspector_loaded;
     Function<void()> on_inspector_loaded;
-    Function<void(i32, Optional<Web::CSS::Selector::PseudoElement::Type> const&)> on_inspector_selected_dom_node;
-    Function<void(i32, String const&)> on_inspector_set_dom_node_text;
-    Function<void(i32, String const&)> on_inspector_set_dom_node_tag;
-    Function<void(i32, Vector<Attribute> const&)> on_inspector_added_dom_node_attributes;
-    Function<void(i32, size_t, Vector<Attribute> const&)> on_inspector_replaced_dom_node_attribute;
-    Function<void(i32, Gfx::IntPoint, String const&, Optional<String> const&, Optional<size_t> const&)> on_inspector_requested_dom_tree_context_menu;
+    Function<void(Web::UniqueNodeID, Optional<Web::CSS::Selector::PseudoElement::Type> const&)> on_inspector_selected_dom_node;
+    Function<void(Web::UniqueNodeID, String const&)> on_inspector_set_dom_node_text;
+    Function<void(Web::UniqueNodeID, String const&)> on_inspector_set_dom_node_tag;
+    Function<void(Web::UniqueNodeID, Vector<Attribute> const&)> on_inspector_added_dom_node_attributes;
+    Function<void(Web::UniqueNodeID, size_t, Vector<Attribute> const&)> on_inspector_replaced_dom_node_attribute;
+    Function<void(Web::UniqueNodeID, Gfx::IntPoint, String const&, Optional<String> const&, Optional<size_t> const&)> on_inspector_requested_dom_tree_context_menu;
     Function<void(size_t, Gfx::IntPoint)> on_inspector_requested_cookie_context_menu;
     Function<void(size_t, Gfx::IntPoint)> on_inspector_requested_cookie_context_menu;
     Function<void(String const&)> on_inspector_executed_console_script;
     Function<void(String const&)> on_inspector_executed_console_script;
     Function<void(String const&)> on_inspector_exported_inspector_html;
     Function<void(String const&)> on_inspector_exported_inspector_html;

+ 12 - 12
Userland/Libraries/LibWebView/WebContentClient.cpp

@@ -261,11 +261,11 @@ void WebContentClient::did_request_media_context_menu(u64 page_id, Gfx::IntPoint
     }
     }
 }
 }
 
 
-void WebContentClient::did_get_source(u64 page_id, URL::URL const& url, ByteString const& source)
+void WebContentClient::did_get_source(u64 page_id, URL::URL const& url, URL::URL const& base_url, String const& source)
 {
 {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
         if (view->on_received_source)
         if (view->on_received_source)
-            view->on_received_source(url, source);
+            view->on_received_source(url, base_url, source);
     }
     }
 }
 }
 
 
@@ -307,7 +307,7 @@ void WebContentClient::did_inspect_accessibility_tree(u64 page_id, ByteString co
     }
     }
 }
 }
 
 
-void WebContentClient::did_get_hovered_node_id(u64 page_id, i32 node_id)
+void WebContentClient::did_get_hovered_node_id(u64 page_id, Web::UniqueNodeID const& node_id)
 {
 {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
         if (view->on_received_hovered_node_id)
         if (view->on_received_hovered_node_id)
@@ -315,7 +315,7 @@ void WebContentClient::did_get_hovered_node_id(u64 page_id, i32 node_id)
     }
     }
 }
 }
 
 
-void WebContentClient::did_finish_editing_dom_node(u64 page_id, Optional<i32> const& node_id)
+void WebContentClient::did_finish_editing_dom_node(u64 page_id, Optional<Web::UniqueNodeID> const& node_id)
 {
 {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
         if (view->on_finshed_editing_dom_node)
         if (view->on_finshed_editing_dom_node)
@@ -622,7 +622,7 @@ void WebContentClient::inspector_did_load(u64 page_id)
     }
     }
 }
 }
 
 
-void WebContentClient::inspector_did_select_dom_node(u64 page_id, i32 node_id, Optional<Web::CSS::Selector::PseudoElement::Type> const& pseudo_element)
+void WebContentClient::inspector_did_select_dom_node(u64 page_id, Web::UniqueNodeID const& node_id, Optional<Web::CSS::Selector::PseudoElement::Type> const& pseudo_element)
 {
 {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
         if (view->on_inspector_selected_dom_node)
         if (view->on_inspector_selected_dom_node)
@@ -630,7 +630,7 @@ void WebContentClient::inspector_did_select_dom_node(u64 page_id, i32 node_id, O
     }
     }
 }
 }
 
 
-void WebContentClient::inspector_did_set_dom_node_text(u64 page_id, i32 node_id, String const& text)
+void WebContentClient::inspector_did_set_dom_node_text(u64 page_id, Web::UniqueNodeID const& node_id, String const& text)
 {
 {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
         if (view->on_inspector_set_dom_node_text)
         if (view->on_inspector_set_dom_node_text)
@@ -638,7 +638,7 @@ void WebContentClient::inspector_did_set_dom_node_text(u64 page_id, i32 node_id,
     }
     }
 }
 }
 
 
-void WebContentClient::inspector_did_set_dom_node_tag(u64 page_id, i32 node_id, String const& tag)
+void WebContentClient::inspector_did_set_dom_node_tag(u64 page_id, Web::UniqueNodeID const& node_id, String const& tag)
 {
 {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
         if (view->on_inspector_set_dom_node_tag)
         if (view->on_inspector_set_dom_node_tag)
@@ -646,7 +646,7 @@ void WebContentClient::inspector_did_set_dom_node_tag(u64 page_id, i32 node_id,
     }
     }
 }
 }
 
 
-void WebContentClient::inspector_did_add_dom_node_attributes(u64 page_id, i32 node_id, Vector<Attribute> const& attributes)
+void WebContentClient::inspector_did_add_dom_node_attributes(u64 page_id, Web::UniqueNodeID const& node_id, Vector<Attribute> const& attributes)
 {
 {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
         if (view->on_inspector_added_dom_node_attributes)
         if (view->on_inspector_added_dom_node_attributes)
@@ -654,7 +654,7 @@ void WebContentClient::inspector_did_add_dom_node_attributes(u64 page_id, i32 no
     }
     }
 }
 }
 
 
-void WebContentClient::inspector_did_replace_dom_node_attribute(u64 page_id, i32 node_id, size_t attribute_index, Vector<Attribute> const& replacement_attributes)
+void WebContentClient::inspector_did_replace_dom_node_attribute(u64 page_id, Web::UniqueNodeID const& node_id, size_t attribute_index, Vector<Attribute> const& replacement_attributes)
 {
 {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
         if (view->on_inspector_replaced_dom_node_attribute)
         if (view->on_inspector_replaced_dom_node_attribute)
@@ -662,7 +662,7 @@ void WebContentClient::inspector_did_replace_dom_node_attribute(u64 page_id, i32
     }
     }
 }
 }
 
 
-void WebContentClient::inspector_did_request_dom_tree_context_menu(u64 page_id, i32 node_id, Gfx::IntPoint position, String const& type, Optional<String> const& tag, Optional<size_t> const& attribute_index)
+void WebContentClient::inspector_did_request_dom_tree_context_menu(u64 page_id, Web::UniqueNodeID const& node_id, Gfx::IntPoint position, String const& type, Optional<String> const& tag, Optional<size_t> const& attribute_index)
 {
 {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
         if (view->on_inspector_requested_dom_tree_context_menu)
         if (view->on_inspector_requested_dom_tree_context_menu)
@@ -720,11 +720,11 @@ void WebContentClient::inspector_did_request_style_sheet_source(u64 page_id, Web
     }
     }
 }
 }
 
 
-void WebContentClient::did_request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier const& identifier, String const& source)
+void WebContentClient::did_get_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier const& identifier, URL::URL const& base_url, String const& source)
 {
 {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
     if (auto view = view_for_page_id(page_id); view.has_value()) {
         if (view->on_received_style_sheet_source)
         if (view->on_received_style_sheet_source)
-            view->on_received_style_sheet_source(identifier, source);
+            view->on_received_style_sheet_source(identifier, base_url, source);
     }
     }
 }
 }
 
 

+ 10 - 10
Userland/Libraries/LibWebView/WebContentClient.h

@@ -70,12 +70,12 @@ private:
     virtual void did_request_link_context_menu(u64 page_id, Gfx::IntPoint, URL::URL const&, ByteString const&, unsigned) override;
     virtual void did_request_link_context_menu(u64 page_id, Gfx::IntPoint, URL::URL const&, ByteString const&, unsigned) override;
     virtual void did_request_image_context_menu(u64 page_id, Gfx::IntPoint, URL::URL const&, ByteString const&, unsigned, Gfx::ShareableBitmap const&) override;
     virtual void did_request_image_context_menu(u64 page_id, Gfx::IntPoint, URL::URL const&, ByteString const&, unsigned, Gfx::ShareableBitmap const&) override;
     virtual void did_request_media_context_menu(u64 page_id, Gfx::IntPoint, ByteString const&, unsigned, Web::Page::MediaContextMenu const&) override;
     virtual void did_request_media_context_menu(u64 page_id, Gfx::IntPoint, ByteString const&, unsigned, Web::Page::MediaContextMenu const&) override;
-    virtual void did_get_source(u64 page_id, URL::URL const&, ByteString const&) override;
+    virtual void did_get_source(u64 page_id, URL::URL const&, URL::URL const&, String const&) override;
     virtual void did_inspect_dom_tree(u64 page_id, ByteString const&) override;
     virtual void did_inspect_dom_tree(u64 page_id, ByteString const&) override;
     virtual void did_inspect_dom_node(u64 page_id, bool has_style, ByteString const& computed_style, ByteString const& resolved_style, ByteString const& custom_properties, ByteString const& node_box_sizing, ByteString const& aria_properties_state, ByteString const& fonts) override;
     virtual void did_inspect_dom_node(u64 page_id, bool has_style, ByteString const& computed_style, ByteString const& resolved_style, ByteString const& custom_properties, ByteString const& node_box_sizing, ByteString const& aria_properties_state, ByteString const& fonts) override;
     virtual void did_inspect_accessibility_tree(u64 page_id, ByteString const&) override;
     virtual void did_inspect_accessibility_tree(u64 page_id, ByteString const&) override;
-    virtual void did_get_hovered_node_id(u64 page_id, i32 node_id) override;
-    virtual void did_finish_editing_dom_node(u64 page_id, Optional<i32> const& node_id) override;
+    virtual void did_get_hovered_node_id(u64 page_id, Web::UniqueNodeID const& node_id) override;
+    virtual void did_finish_editing_dom_node(u64 page_id, Optional<Web::UniqueNodeID> const& node_id) override;
     virtual void did_get_dom_node_html(u64 page_id, String const& html) override;
     virtual void did_get_dom_node_html(u64 page_id, String const& html) override;
     virtual void did_take_screenshot(u64 page_id, Gfx::ShareableBitmap const& screenshot) override;
     virtual void did_take_screenshot(u64 page_id, Gfx::ShareableBitmap const& screenshot) override;
     virtual void did_get_internal_page_info(u64 page_id, PageInfoType, String const&) override;
     virtual void did_get_internal_page_info(u64 page_id, PageInfoType, String const&) override;
@@ -117,19 +117,19 @@ private:
     virtual void did_update_navigation_buttons_state(u64 page_id, bool back_enabled, bool forward_enabled) override;
     virtual void did_update_navigation_buttons_state(u64 page_id, bool back_enabled, bool forward_enabled) override;
     virtual void did_allocate_backing_stores(u64 page_id, i32 front_bitmap_id, Gfx::ShareableBitmap const&, i32 back_bitmap_id, Gfx::ShareableBitmap const&) override;
     virtual void did_allocate_backing_stores(u64 page_id, i32 front_bitmap_id, Gfx::ShareableBitmap const&, i32 back_bitmap_id, Gfx::ShareableBitmap const&) override;
     virtual void inspector_did_load(u64 page_id) override;
     virtual void inspector_did_load(u64 page_id) override;
-    virtual void inspector_did_select_dom_node(u64 page_id, i32 node_id, Optional<Web::CSS::Selector::PseudoElement::Type> const& pseudo_element) override;
-    virtual void inspector_did_set_dom_node_text(u64 page_id, i32 node_id, String const& text) override;
-    virtual void inspector_did_set_dom_node_tag(u64 page_id, i32 node_id, String const& tag) override;
-    virtual void inspector_did_add_dom_node_attributes(u64 page_id, i32 node_id, Vector<Attribute> const& attributes) override;
-    virtual void inspector_did_replace_dom_node_attribute(u64 page_id, i32 node_id, size_t attribute_index, Vector<Attribute> const& replacement_attributes) override;
-    virtual void inspector_did_request_dom_tree_context_menu(u64 page_id, i32 node_id, Gfx::IntPoint position, String const& type, Optional<String> const& tag, Optional<size_t> const& attribute_index) override;
+    virtual void inspector_did_select_dom_node(u64 page_id, Web::UniqueNodeID const& node_id, Optional<Web::CSS::Selector::PseudoElement::Type> const& pseudo_element) override;
+    virtual void inspector_did_set_dom_node_text(u64 page_id, Web::UniqueNodeID const& node_id, String const& text) override;
+    virtual void inspector_did_set_dom_node_tag(u64 page_id, Web::UniqueNodeID const& node_id, String const& tag) override;
+    virtual void inspector_did_add_dom_node_attributes(u64 page_id, Web::UniqueNodeID const& node_id, Vector<Attribute> const& attributes) override;
+    virtual void inspector_did_replace_dom_node_attribute(u64 page_id, Web::UniqueNodeID const& node_id, size_t attribute_index, Vector<Attribute> const& replacement_attributes) override;
+    virtual void inspector_did_request_dom_tree_context_menu(u64 page_id, Web::UniqueNodeID const& node_id, Gfx::IntPoint position, String const& type, Optional<String> const& tag, Optional<size_t> const& attribute_index) override;
     virtual void inspector_did_request_cookie_context_menu(u64 page_id, size_t cookie_index, Gfx::IntPoint position) override;
     virtual void inspector_did_request_cookie_context_menu(u64 page_id, size_t cookie_index, Gfx::IntPoint position) override;
     virtual void inspector_did_execute_console_script(u64 page_id, String const& script) override;
     virtual void inspector_did_execute_console_script(u64 page_id, String const& script) override;
     virtual void inspector_did_export_inspector_html(u64 page_id, String const& html) override;
     virtual void inspector_did_export_inspector_html(u64 page_id, String const& html) override;
     virtual Messages::WebContentClient::RequestWorkerAgentResponse request_worker_agent(u64 page_id) override;
     virtual Messages::WebContentClient::RequestWorkerAgentResponse request_worker_agent(u64 page_id) override;
     virtual void inspector_did_list_style_sheets(u64 page_id, Vector<Web::CSS::StyleSheetIdentifier> const& stylesheets) override;
     virtual void inspector_did_list_style_sheets(u64 page_id, Vector<Web::CSS::StyleSheetIdentifier> const& stylesheets) override;
     virtual void inspector_did_request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier const& identifier) override;
     virtual void inspector_did_request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier const& identifier) override;
-    virtual void did_request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier const& identifier, String const& source) override;
+    virtual void did_get_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier const& identifier, URL::URL const&, String const& source) override;
 
 
     Optional<ViewImplementation&> view_for_page_id(u64, SourceLocation = SourceLocation::current());
     Optional<ViewImplementation&> view_for_page_id(u64, SourceLocation = SourceLocation::current());
 
 

+ 16 - 17
Userland/Services/WebContent/ConnectionFromClient.cpp

@@ -432,7 +432,7 @@ void ConnectionFromClient::get_source(u64 page_id)
 {
 {
     if (auto page = this->page(page_id); page.has_value()) {
     if (auto page = this->page(page_id); page.has_value()) {
         if (auto* doc = page->page().top_level_browsing_context().active_document())
         if (auto* doc = page->page().top_level_browsing_context().active_document())
-            async_did_get_source(page_id, doc->url(), doc->source().to_byte_string());
+            async_did_get_source(page_id, doc->url(), doc->base_url(), doc->source());
     }
     }
 }
 }
 
 
@@ -444,7 +444,7 @@ void ConnectionFromClient::inspect_dom_tree(u64 page_id)
     }
     }
 }
 }
 
 
-void ConnectionFromClient::inspect_dom_node(u64 page_id, i32 node_id, Optional<Web::CSS::Selector::PseudoElement::Type> const& pseudo_element)
+void ConnectionFromClient::inspect_dom_node(u64 page_id, Web::UniqueNodeID const& node_id, Optional<Web::CSS::Selector::PseudoElement::Type> const& pseudo_element)
 {
 {
     auto page = this->page(page_id);
     auto page = this->page(page_id);
     if (!page.has_value())
     if (!page.has_value())
@@ -459,7 +459,7 @@ void ConnectionFromClient::inspect_dom_node(u64 page_id, i32 node_id, Optional<W
         return Web::TraversalDecision::Continue;
         return Web::TraversalDecision::Continue;
     });
     });
 
 
-    Web::DOM::Node* node = Web::DOM::Node::from_unique_id(node_id);
+    auto* node = Web::DOM::Node::from_unique_id(node_id);
     // Note: Nodes without layout (aka non-visible nodes, don't have style computed)
     // Note: Nodes without layout (aka non-visible nodes, don't have style computed)
     if (!node || !node->layout_node()) {
     if (!node || !node->layout_node()) {
         async_did_inspect_dom_node(page_id, false, {}, {}, {}, {}, {}, {});
         async_did_inspect_dom_node(page_id, false, {}, {}, {}, {}, {}, {});
@@ -618,7 +618,7 @@ void ConnectionFromClient::get_hovered_node_id(u64 page_id)
     if (!page.has_value())
     if (!page.has_value())
         return;
         return;
 
 
-    i32 node_id = 0;
+    Web::UniqueNodeID node_id = 0;
 
 
     if (auto* document = page->page().top_level_browsing_context().active_document()) {
     if (auto* document = page->page().top_level_browsing_context().active_document()) {
         if (auto* hovered_node = document->hovered_node())
         if (auto* hovered_node = document->hovered_node())
@@ -644,13 +644,12 @@ void ConnectionFromClient::request_style_sheet_source(u64 page_id, Web::CSS::Sty
         return;
         return;
 
 
     if (auto* document = page->page().top_level_browsing_context().active_document()) {
     if (auto* document = page->page().top_level_browsing_context().active_document()) {
-        auto stylesheet = document->get_style_sheet_source(identifier);
-        if (stylesheet.has_value())
-            async_did_request_style_sheet_source(page_id, identifier, stylesheet.value());
+        if (auto stylesheet = document->get_style_sheet_source(identifier); stylesheet.has_value())
+            async_did_get_style_sheet_source(page_id, identifier, document->base_url(), stylesheet.value());
     }
     }
 }
 }
 
 
-void ConnectionFromClient::set_dom_node_text(u64 page_id, i32 node_id, String const& text)
+void ConnectionFromClient::set_dom_node_text(u64 page_id, Web::UniqueNodeID const& node_id, String const& text)
 {
 {
     auto* dom_node = Web::DOM::Node::from_unique_id(node_id);
     auto* dom_node = Web::DOM::Node::from_unique_id(node_id);
     if (!dom_node || (!dom_node->is_text() && !dom_node->is_comment())) {
     if (!dom_node || (!dom_node->is_text() && !dom_node->is_comment())) {
@@ -664,7 +663,7 @@ void ConnectionFromClient::set_dom_node_text(u64 page_id, i32 node_id, String co
     async_did_finish_editing_dom_node(page_id, character_data.unique_id());
     async_did_finish_editing_dom_node(page_id, character_data.unique_id());
 }
 }
 
 
-void ConnectionFromClient::set_dom_node_tag(u64 page_id, i32 node_id, String const& name)
+void ConnectionFromClient::set_dom_node_tag(u64 page_id, Web::UniqueNodeID const& node_id, String const& name)
 {
 {
     auto* dom_node = Web::DOM::Node::from_unique_id(node_id);
     auto* dom_node = Web::DOM::Node::from_unique_id(node_id);
     if (!dom_node || !dom_node->is_element() || !dom_node->parent()) {
     if (!dom_node || !dom_node->is_element() || !dom_node->parent()) {
@@ -688,7 +687,7 @@ void ConnectionFromClient::set_dom_node_tag(u64 page_id, i32 node_id, String con
     async_did_finish_editing_dom_node(page_id, new_element->unique_id());
     async_did_finish_editing_dom_node(page_id, new_element->unique_id());
 }
 }
 
 
-void ConnectionFromClient::add_dom_node_attributes(u64 page_id, i32 node_id, Vector<WebView::Attribute> const& attributes)
+void ConnectionFromClient::add_dom_node_attributes(u64 page_id, Web::UniqueNodeID const& node_id, Vector<WebView::Attribute> const& attributes)
 {
 {
     auto* dom_node = Web::DOM::Node::from_unique_id(node_id);
     auto* dom_node = Web::DOM::Node::from_unique_id(node_id);
     if (!dom_node || !dom_node->is_element()) {
     if (!dom_node || !dom_node->is_element()) {
@@ -706,7 +705,7 @@ void ConnectionFromClient::add_dom_node_attributes(u64 page_id, i32 node_id, Vec
     async_did_finish_editing_dom_node(page_id, element.unique_id());
     async_did_finish_editing_dom_node(page_id, element.unique_id());
 }
 }
 
 
-void ConnectionFromClient::replace_dom_node_attribute(u64 page_id, i32 node_id, String const& name, Vector<WebView::Attribute> const& replacement_attributes)
+void ConnectionFromClient::replace_dom_node_attribute(u64 page_id, Web::UniqueNodeID const& node_id, String const& name, Vector<WebView::Attribute> const& replacement_attributes)
 {
 {
     auto* dom_node = Web::DOM::Node::from_unique_id(node_id);
     auto* dom_node = Web::DOM::Node::from_unique_id(node_id);
     if (!dom_node || !dom_node->is_element()) {
     if (!dom_node || !dom_node->is_element()) {
@@ -731,7 +730,7 @@ void ConnectionFromClient::replace_dom_node_attribute(u64 page_id, i32 node_id,
     async_did_finish_editing_dom_node(page_id, element.unique_id());
     async_did_finish_editing_dom_node(page_id, element.unique_id());
 }
 }
 
 
-void ConnectionFromClient::create_child_element(u64 page_id, i32 node_id)
+void ConnectionFromClient::create_child_element(u64 page_id, Web::UniqueNodeID const& node_id)
 {
 {
     auto* dom_node = Web::DOM::Node::from_unique_id(node_id);
     auto* dom_node = Web::DOM::Node::from_unique_id(node_id);
     if (!dom_node) {
     if (!dom_node) {
@@ -745,7 +744,7 @@ void ConnectionFromClient::create_child_element(u64 page_id, i32 node_id)
     async_did_finish_editing_dom_node(page_id, element->unique_id());
     async_did_finish_editing_dom_node(page_id, element->unique_id());
 }
 }
 
 
-void ConnectionFromClient::create_child_text_node(u64 page_id, i32 node_id)
+void ConnectionFromClient::create_child_text_node(u64 page_id, Web::UniqueNodeID const& node_id)
 {
 {
     auto* dom_node = Web::DOM::Node::from_unique_id(node_id);
     auto* dom_node = Web::DOM::Node::from_unique_id(node_id);
     if (!dom_node) {
     if (!dom_node) {
@@ -759,7 +758,7 @@ void ConnectionFromClient::create_child_text_node(u64 page_id, i32 node_id)
     async_did_finish_editing_dom_node(page_id, text_node->unique_id());
     async_did_finish_editing_dom_node(page_id, text_node->unique_id());
 }
 }
 
 
-void ConnectionFromClient::clone_dom_node(u64 page_id, i32 node_id)
+void ConnectionFromClient::clone_dom_node(u64 page_id, Web::UniqueNodeID const& node_id)
 {
 {
     auto* dom_node = Web::DOM::Node::from_unique_id(node_id);
     auto* dom_node = Web::DOM::Node::from_unique_id(node_id);
     if (!dom_node || !dom_node->parent_node()) {
     if (!dom_node || !dom_node->parent_node()) {
@@ -773,7 +772,7 @@ void ConnectionFromClient::clone_dom_node(u64 page_id, i32 node_id)
     async_did_finish_editing_dom_node(page_id, dom_node_clone->unique_id());
     async_did_finish_editing_dom_node(page_id, dom_node_clone->unique_id());
 }
 }
 
 
-void ConnectionFromClient::remove_dom_node(u64 page_id, i32 node_id)
+void ConnectionFromClient::remove_dom_node(u64 page_id, Web::UniqueNodeID const& node_id)
 {
 {
     auto page = this->page(page_id);
     auto page = this->page(page_id);
     if (!page.has_value())
     if (!page.has_value())
@@ -800,7 +799,7 @@ void ConnectionFromClient::remove_dom_node(u64 page_id, i32 node_id)
     async_did_finish_editing_dom_node(page_id, previous_dom_node->unique_id());
     async_did_finish_editing_dom_node(page_id, previous_dom_node->unique_id());
 }
 }
 
 
-void ConnectionFromClient::get_dom_node_html(u64 page_id, i32 node_id)
+void ConnectionFromClient::get_dom_node_html(u64 page_id, Web::UniqueNodeID const& node_id)
 {
 {
     auto* dom_node = Web::DOM::Node::from_unique_id(node_id);
     auto* dom_node = Web::DOM::Node::from_unique_id(node_id);
     if (!dom_node)
     if (!dom_node)
@@ -830,7 +829,7 @@ void ConnectionFromClient::take_document_screenshot(u64 page_id)
     page->queue_screenshot_task({});
     page->queue_screenshot_task({});
 }
 }
 
 
-void ConnectionFromClient::take_dom_node_screenshot(u64 page_id, i32 node_id)
+void ConnectionFromClient::take_dom_node_screenshot(u64 page_id, Web::UniqueNodeID const& node_id)
 {
 {
     auto page = this->page(page_id);
     auto page = this->page(page_id);
     if (!page.has_value())
     if (!page.has_value())

+ 11 - 11
Userland/Services/WebContent/ConnectionFromClient.h

@@ -74,22 +74,22 @@ private:
     virtual void debug_request(u64 page_id, ByteString const&, ByteString const&) override;
     virtual void debug_request(u64 page_id, ByteString const&, ByteString const&) override;
     virtual void get_source(u64 page_id) override;
     virtual void get_source(u64 page_id) override;
     virtual void inspect_dom_tree(u64 page_id) override;
     virtual void inspect_dom_tree(u64 page_id) override;
-    virtual void inspect_dom_node(u64 page_id, i32 node_id, Optional<Web::CSS::Selector::PseudoElement::Type> const& pseudo_element) override;
+    virtual void inspect_dom_node(u64 page_id, Web::UniqueNodeID const& node_id, Optional<Web::CSS::Selector::PseudoElement::Type> const& pseudo_element) override;
     virtual void inspect_accessibility_tree(u64 page_id) override;
     virtual void inspect_accessibility_tree(u64 page_id) override;
     virtual void get_hovered_node_id(u64 page_id) override;
     virtual void get_hovered_node_id(u64 page_id) override;
 
 
     virtual void list_style_sheets(u64 page_id) override;
     virtual void list_style_sheets(u64 page_id) override;
     virtual void request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier const& identifier) override;
     virtual void request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier const& identifier) override;
 
 
-    virtual void set_dom_node_text(u64 page_id, i32 node_id, String const& text) override;
-    virtual void set_dom_node_tag(u64 page_id, i32 node_id, String const& name) override;
-    virtual void add_dom_node_attributes(u64 page_id, i32 node_id, Vector<WebView::Attribute> const& attributes) override;
-    virtual void replace_dom_node_attribute(u64 page_id, i32 node_id, String const& name, Vector<WebView::Attribute> const& replacement_attributes) override;
-    virtual void create_child_element(u64 page_id, i32 node_id) override;
-    virtual void create_child_text_node(u64 page_id, i32 node_id) override;
-    virtual void clone_dom_node(u64 page_id, i32 node_id) override;
-    virtual void remove_dom_node(u64 page_id, i32 node_id) override;
-    virtual void get_dom_node_html(u64 page_id, i32 node_id) override;
+    virtual void set_dom_node_text(u64 page_id, Web::UniqueNodeID const& node_id, String const& text) override;
+    virtual void set_dom_node_tag(u64 page_id, Web::UniqueNodeID const& node_id, String const& name) override;
+    virtual void add_dom_node_attributes(u64 page_id, Web::UniqueNodeID const& node_id, Vector<WebView::Attribute> const& attributes) override;
+    virtual void replace_dom_node_attribute(u64 page_id, Web::UniqueNodeID const& node_id, String const& name, Vector<WebView::Attribute> const& replacement_attributes) override;
+    virtual void create_child_element(u64 page_id, Web::UniqueNodeID const& node_id) override;
+    virtual void create_child_text_node(u64 page_id, Web::UniqueNodeID const& node_id) override;
+    virtual void clone_dom_node(u64 page_id, Web::UniqueNodeID const& node_id) override;
+    virtual void remove_dom_node(u64 page_id, Web::UniqueNodeID const& node_id) override;
+    virtual void get_dom_node_html(u64 page_id, Web::UniqueNodeID const& node_id) override;
 
 
     virtual void set_content_filters(u64 page_id, Vector<String> const&) override;
     virtual void set_content_filters(u64 page_id, Vector<String> const&) override;
     virtual void set_autoplay_allowed_on_all_websites(u64 page_id) override;
     virtual void set_autoplay_allowed_on_all_websites(u64 page_id) override;
@@ -131,7 +131,7 @@ private:
     virtual void enable_inspector_prototype(u64 page_id) override;
     virtual void enable_inspector_prototype(u64 page_id) override;
 
 
     virtual void take_document_screenshot(u64 page_id) override;
     virtual void take_document_screenshot(u64 page_id) override;
-    virtual void take_dom_node_screenshot(u64 page_id, i32 node_id) override;
+    virtual void take_dom_node_screenshot(u64 page_id, Web::UniqueNodeID const& node_id) override;
 
 
     virtual void request_internal_page_info(u64 page_id, WebView::PageInfoType) override;
     virtual void request_internal_page_info(u64 page_id, WebView::PageInfoType) override;
 
 

+ 7 - 7
Userland/Services/WebContent/PageClient.cpp

@@ -612,17 +612,17 @@ void PageClient::inspector_did_load()
     client().async_inspector_did_load(m_id);
     client().async_inspector_did_load(m_id);
 }
 }
 
 
-void PageClient::inspector_did_select_dom_node(i32 node_id, Optional<Web::CSS::Selector::PseudoElement::Type> const& pseudo_element)
+void PageClient::inspector_did_select_dom_node(Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> const& pseudo_element)
 {
 {
     client().async_inspector_did_select_dom_node(m_id, node_id, pseudo_element);
     client().async_inspector_did_select_dom_node(m_id, node_id, pseudo_element);
 }
 }
 
 
-void PageClient::inspector_did_set_dom_node_text(i32 node_id, String const& text)
+void PageClient::inspector_did_set_dom_node_text(Web::UniqueNodeID node_id, String const& text)
 {
 {
     client().async_inspector_did_set_dom_node_text(m_id, node_id, text);
     client().async_inspector_did_set_dom_node_text(m_id, node_id, text);
 }
 }
 
 
-void PageClient::inspector_did_set_dom_node_tag(i32 node_id, String const& tag)
+void PageClient::inspector_did_set_dom_node_tag(Web::UniqueNodeID node_id, String const& tag)
 {
 {
     client().async_inspector_did_set_dom_node_tag(m_id, node_id, tag);
     client().async_inspector_did_set_dom_node_tag(m_id, node_id, tag);
 }
 }
@@ -642,17 +642,17 @@ static Vector<WebView::Attribute> named_node_map_to_vector(JS::NonnullGCPtr<Web:
     return attributes;
     return attributes;
 }
 }
 
 
-void PageClient::inspector_did_add_dom_node_attributes(i32 node_id, JS::NonnullGCPtr<Web::DOM::NamedNodeMap> attributes)
+void PageClient::inspector_did_add_dom_node_attributes(Web::UniqueNodeID node_id, JS::NonnullGCPtr<Web::DOM::NamedNodeMap> attributes)
 {
 {
     client().async_inspector_did_add_dom_node_attributes(m_id, node_id, named_node_map_to_vector(attributes));
     client().async_inspector_did_add_dom_node_attributes(m_id, node_id, named_node_map_to_vector(attributes));
 }
 }
 
 
-void PageClient::inspector_did_replace_dom_node_attribute(i32 node_id, size_t attribute_index, JS::NonnullGCPtr<Web::DOM::NamedNodeMap> replacement_attributes)
+void PageClient::inspector_did_replace_dom_node_attribute(Web::UniqueNodeID node_id, size_t attribute_index, JS::NonnullGCPtr<Web::DOM::NamedNodeMap> replacement_attributes)
 {
 {
     client().async_inspector_did_replace_dom_node_attribute(m_id, node_id, attribute_index, named_node_map_to_vector(replacement_attributes));
     client().async_inspector_did_replace_dom_node_attribute(m_id, node_id, attribute_index, named_node_map_to_vector(replacement_attributes));
 }
 }
 
 
-void PageClient::inspector_did_request_dom_tree_context_menu(i32 node_id, Web::CSSPixelPoint position, String const& type, Optional<String> const& tag, Optional<size_t> const& attribute_index)
+void PageClient::inspector_did_request_dom_tree_context_menu(Web::UniqueNodeID node_id, Web::CSSPixelPoint position, String const& type, Optional<String> const& tag, Optional<size_t> const& attribute_index)
 {
 {
     client().async_inspector_did_request_dom_tree_context_menu(m_id, node_id, page().css_to_device_point(position).to_type<int>(), type, tag, attribute_index);
     client().async_inspector_did_request_dom_tree_context_menu(m_id, node_id, page().css_to_device_point(position).to_type<int>(), type, tag, attribute_index);
 }
 }
@@ -846,7 +846,7 @@ Web::DisplayListPlayerType PageClient::display_list_player_type() const
     }
     }
 }
 }
 
 
-void PageClient::queue_screenshot_task(Optional<i32> node_id)
+void PageClient::queue_screenshot_task(Optional<Web::UniqueNodeID> node_id)
 {
 {
     m_screenshot_tasks.enqueue({ node_id });
     m_screenshot_tasks.enqueue({ node_id });
     page().top_level_traversable()->set_needs_display();
     page().top_level_traversable()->set_needs_display();

+ 8 - 8
Userland/Services/WebContent/PageClient.h

@@ -89,7 +89,7 @@ public:
 
 
     virtual Web::DisplayListPlayerType display_list_player_type() const override;
     virtual Web::DisplayListPlayerType display_list_player_type() const override;
 
 
-    void queue_screenshot_task(Optional<i32> node_id);
+    void queue_screenshot_task(Optional<Web::UniqueNodeID> node_id);
 
 
     friend class BackingStoreManager;
     friend class BackingStoreManager;
 
 
@@ -163,12 +163,12 @@ private:
     virtual void page_did_allocate_backing_stores(i32 front_bitmap_id, Gfx::ShareableBitmap front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap back_bitmap) override;
     virtual void page_did_allocate_backing_stores(i32 front_bitmap_id, Gfx::ShareableBitmap front_bitmap, i32 back_bitmap_id, Gfx::ShareableBitmap back_bitmap) override;
     virtual IPC::File request_worker_agent() override;
     virtual IPC::File request_worker_agent() override;
     virtual void inspector_did_load() override;
     virtual void inspector_did_load() override;
-    virtual void inspector_did_select_dom_node(i32 node_id, Optional<Web::CSS::Selector::PseudoElement::Type> const& pseudo_element) override;
-    virtual void inspector_did_set_dom_node_text(i32 node_id, String const& text) override;
-    virtual void inspector_did_set_dom_node_tag(i32 node_id, String const& tag) override;
-    virtual void inspector_did_add_dom_node_attributes(i32 node_id, JS::NonnullGCPtr<Web::DOM::NamedNodeMap> attributes) override;
-    virtual void inspector_did_replace_dom_node_attribute(i32 node_id, size_t attribute_index, JS::NonnullGCPtr<Web::DOM::NamedNodeMap> replacement_attributes) override;
-    virtual void inspector_did_request_dom_tree_context_menu(i32 node_id, Web::CSSPixelPoint position, String const& type, Optional<String> const& tag, Optional<size_t> const& attribute_index) override;
+    virtual void inspector_did_select_dom_node(Web::UniqueNodeID, Optional<Web::CSS::Selector::PseudoElement::Type> const& pseudo_element) override;
+    virtual void inspector_did_set_dom_node_text(Web::UniqueNodeID, String const& text) override;
+    virtual void inspector_did_set_dom_node_tag(Web::UniqueNodeID, String const& tag) override;
+    virtual void inspector_did_add_dom_node_attributes(Web::UniqueNodeID, JS::NonnullGCPtr<Web::DOM::NamedNodeMap> attributes) override;
+    virtual void inspector_did_replace_dom_node_attribute(Web::UniqueNodeID, size_t attribute_index, JS::NonnullGCPtr<Web::DOM::NamedNodeMap> replacement_attributes) override;
+    virtual void inspector_did_request_dom_tree_context_menu(Web::UniqueNodeID, Web::CSSPixelPoint position, String const& type, Optional<String> const& tag, Optional<size_t> const& attribute_index) override;
     virtual void inspector_did_request_cookie_context_menu(size_t cookie_index, Web::CSSPixelPoint position) override;
     virtual void inspector_did_request_cookie_context_menu(size_t cookie_index, Web::CSSPixelPoint position) override;
     virtual void inspector_did_request_style_sheet_source(Web::CSS::StyleSheetIdentifier const& stylesheet_source) override;
     virtual void inspector_did_request_style_sheet_source(Web::CSS::StyleSheetIdentifier const& stylesheet_source) override;
     virtual void inspector_did_execute_console_script(String const& script) override;
     virtual void inspector_did_execute_console_script(String const& script) override;
@@ -196,7 +196,7 @@ private:
     PaintState m_paint_state { PaintState::Ready };
     PaintState m_paint_state { PaintState::Ready };
 
 
     struct ScreenshotTask {
     struct ScreenshotTask {
-        Optional<i32> node_id;
+        Optional<Web::UniqueNodeID> node_id;
     };
     };
     Queue<ScreenshotTask> m_screenshot_tasks;
     Queue<ScreenshotTask> m_screenshot_tasks;
 
 

+ 10 - 10
Userland/Services/WebContent/WebContentClient.ipc

@@ -48,18 +48,18 @@ endpoint WebContentClient
     did_request_set_prompt_text(u64 page_id, String message) =|
     did_request_set_prompt_text(u64 page_id, String message) =|
     did_request_accept_dialog(u64 page_id) =|
     did_request_accept_dialog(u64 page_id) =|
     did_request_dismiss_dialog(u64 page_id) =|
     did_request_dismiss_dialog(u64 page_id) =|
-    did_get_source(u64 page_id, URL::URL url, ByteString source) =|
+    did_get_source(u64 page_id, URL::URL url, URL::URL base_url, String source) =|
 
 
     did_inspect_dom_tree(u64 page_id, ByteString dom_tree) =|
     did_inspect_dom_tree(u64 page_id, ByteString dom_tree) =|
     did_inspect_dom_node(u64 page_id, bool has_style, ByteString computed_style,  ByteString resolved_style,  ByteString custom_properties, ByteString node_box_sizing, ByteString aria_properties_state, ByteString fonts) =|
     did_inspect_dom_node(u64 page_id, bool has_style, ByteString computed_style,  ByteString resolved_style,  ByteString custom_properties, ByteString node_box_sizing, ByteString aria_properties_state, ByteString fonts) =|
     did_inspect_accessibility_tree(u64 page_id, ByteString accessibility_tree) =|
     did_inspect_accessibility_tree(u64 page_id, ByteString accessibility_tree) =|
-    did_get_hovered_node_id(u64 page_id, i32 node_id) =|
-    did_finish_editing_dom_node(u64 page_id, Optional<i32> node_id) =|
+    did_get_hovered_node_id(u64 page_id, Web::UniqueNodeID node_id) =|
+    did_finish_editing_dom_node(u64 page_id, Optional<Web::UniqueNodeID> node_id) =|
     did_get_dom_node_html(u64 page_id, String html) =|
     did_get_dom_node_html(u64 page_id, String html) =|
 
 
     inspector_did_list_style_sheets(u64 page_id, Vector<Web::CSS::StyleSheetIdentifier> style_sheets) =|
     inspector_did_list_style_sheets(u64 page_id, Vector<Web::CSS::StyleSheetIdentifier> style_sheets) =|
     inspector_did_request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier) =|
     inspector_did_request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier) =|
-    did_request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier, String source) =|
+    did_get_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier, URL::URL base_url, String source) =|
 
 
     did_take_screenshot(u64 page_id, Gfx::ShareableBitmap screenshot) =|
     did_take_screenshot(u64 page_id, Gfx::ShareableBitmap screenshot) =|
 
 
@@ -104,12 +104,12 @@ endpoint WebContentClient
     request_worker_agent(u64 page_id) => (IPC::File socket) // FIXME: Add required attributes to select a SharedWorker Agent
     request_worker_agent(u64 page_id) => (IPC::File socket) // FIXME: Add required attributes to select a SharedWorker Agent
 
 
     inspector_did_load(u64 page_id) =|
     inspector_did_load(u64 page_id) =|
-    inspector_did_select_dom_node(u64 page_id, i32 node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element) =|
-    inspector_did_set_dom_node_text(u64 page_id, i32 node_id, String text) =|
-    inspector_did_set_dom_node_tag(u64 page_id, i32 node_id, String tag) =|
-    inspector_did_add_dom_node_attributes(u64 page_id, i32 node_id, Vector<WebView::Attribute> attributes) =|
-    inspector_did_replace_dom_node_attribute(u64 page_id, i32 node_id, size_t attribute_index, Vector<WebView::Attribute> replacement_attributes) =|
-    inspector_did_request_dom_tree_context_menu(u64 page_id, i32 node_id, Gfx::IntPoint position, String type, Optional<String> tag, Optional<size_t> attribute_index) =|
+    inspector_did_select_dom_node(u64 page_id, Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element) =|
+    inspector_did_set_dom_node_text(u64 page_id, Web::UniqueNodeID node_id, String text) =|
+    inspector_did_set_dom_node_tag(u64 page_id, Web::UniqueNodeID node_id, String tag) =|
+    inspector_did_add_dom_node_attributes(u64 page_id, Web::UniqueNodeID node_id, Vector<WebView::Attribute> attributes) =|
+    inspector_did_replace_dom_node_attribute(u64 page_id, Web::UniqueNodeID node_id, size_t attribute_index, Vector<WebView::Attribute> replacement_attributes) =|
+    inspector_did_request_dom_tree_context_menu(u64 page_id, Web::UniqueNodeID node_id, Gfx::IntPoint position, String type, Optional<String> tag, Optional<size_t> attribute_index) =|
     inspector_did_request_cookie_context_menu(u64 page_id, size_t cookie_index, Gfx::IntPoint position) =|
     inspector_did_request_cookie_context_menu(u64 page_id, size_t cookie_index, Gfx::IntPoint position) =|
     inspector_did_execute_console_script(u64 page_id, String script) =|
     inspector_did_execute_console_script(u64 page_id, String script) =|
     inspector_did_export_inspector_html(u64 page_id, String html) =|
     inspector_did_export_inspector_html(u64 page_id, String html) =|

+ 11 - 11
Userland/Services/WebContent/WebContentServer.ipc

@@ -42,7 +42,7 @@ endpoint WebContentServer
     debug_request(u64 page_id, ByteString request, ByteString argument) =|
     debug_request(u64 page_id, ByteString request, ByteString argument) =|
     get_source(u64 page_id) =|
     get_source(u64 page_id) =|
     inspect_dom_tree(u64 page_id) =|
     inspect_dom_tree(u64 page_id) =|
-    inspect_dom_node(u64 page_id, i32 node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element) =|
+    inspect_dom_node(u64 page_id, Web::UniqueNodeID node_id, Optional<Web::CSS::Selector::PseudoElement::Type> pseudo_element) =|
     inspect_accessibility_tree(u64 page_id) =|
     inspect_accessibility_tree(u64 page_id) =|
     get_hovered_node_id(u64 page_id) =|
     get_hovered_node_id(u64 page_id) =|
     js_console_input(u64 page_id, ByteString js_source) =|
     js_console_input(u64 page_id, ByteString js_source) =|
@@ -50,18 +50,18 @@ endpoint WebContentServer
     list_style_sheets(u64 page_id) =|
     list_style_sheets(u64 page_id) =|
     request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier) =|
     request_style_sheet_source(u64 page_id, Web::CSS::StyleSheetIdentifier identifier) =|
 
 
-    set_dom_node_text(u64 page_id, i32 node_id, String text) =|
-    set_dom_node_tag(u64 page_id, i32 node_id, String name) =|
-    add_dom_node_attributes(u64 page_id, i32 node_id, Vector<WebView::Attribute> attributes) =|
-    replace_dom_node_attribute(u64 page_id, i32 node_id, String name, Vector<WebView::Attribute> replacement_attributes) =|
-    create_child_element(u64 page_id, i32 node_id) =|
-    create_child_text_node(u64 page_id, i32 node_id) =|
-    clone_dom_node(u64 page_id, i32 node_id) =|
-    remove_dom_node(u64 page_id, i32 node_id) =|
-    get_dom_node_html(u64 page_id, i32 node_id) =|
+    set_dom_node_text(u64 page_id, Web::UniqueNodeID node_id, String text) =|
+    set_dom_node_tag(u64 page_id, Web::UniqueNodeID node_id, String name) =|
+    add_dom_node_attributes(u64 page_id, Web::UniqueNodeID node_id, Vector<WebView::Attribute> attributes) =|
+    replace_dom_node_attribute(u64 page_id, Web::UniqueNodeID node_id, String name, Vector<WebView::Attribute> replacement_attributes) =|
+    create_child_element(u64 page_id, Web::UniqueNodeID node_id) =|
+    create_child_text_node(u64 page_id, Web::UniqueNodeID node_id) =|
+    clone_dom_node(u64 page_id, Web::UniqueNodeID node_id) =|
+    remove_dom_node(u64 page_id, Web::UniqueNodeID node_id) =|
+    get_dom_node_html(u64 page_id, Web::UniqueNodeID node_id) =|
 
 
     take_document_screenshot(u64 page_id) =|
     take_document_screenshot(u64 page_id) =|
-    take_dom_node_screenshot(u64 page_id, i32 node_id) =|
+    take_dom_node_screenshot(u64 page_id, Web::UniqueNodeID node_id) =|
 
 
     request_internal_page_info(u64 page_id, WebView::PageInfoType type) =|
     request_internal_page_info(u64 page_id, WebView::PageInfoType type) =|
 
 

+ 17 - 5
Userland/Services/WebContent/WebDriverConnection.cpp

@@ -45,6 +45,7 @@
 #include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
 #include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
 #include <LibWeb/HTML/SelectedFile.h>
 #include <LibWeb/HTML/SelectedFile.h>
 #include <LibWeb/HTML/TraversableNavigable.h>
 #include <LibWeb/HTML/TraversableNavigable.h>
+#include <LibWeb/HTML/WindowProxy.h>
 #include <LibWeb/Page/Page.h>
 #include <LibWeb/Page/Page.h>
 #include <LibWeb/Platform/EventLoopPlugin.h>
 #include <LibWeb/Platform/EventLoopPlugin.h>
 #include <LibWeb/Platform/Timer.h>
 #include <LibWeb/Platform/Timer.h>
@@ -464,7 +465,7 @@ Messages::WebDriverClient::SwitchToWindowResponse WebDriverConnection::switch_to
 }
 }
 
 
 // 11.5 New Window, https://w3c.github.io/webdriver/#dfn-new-window
 // 11.5 New Window, https://w3c.github.io/webdriver/#dfn-new-window
-Messages::WebDriverClient::NewWindowResponse WebDriverConnection::new_window(JsonValue const&)
+Messages::WebDriverClient::NewWindowResponse WebDriverConnection::new_window(JsonValue const& payload)
 {
 {
     // 1. If the implementation does not support creating new top-level browsing contexts, return error with error code unsupported operation.
     // 1. If the implementation does not support creating new top-level browsing contexts, return error with error code unsupported operation.
 
 
@@ -474,7 +475,14 @@ Messages::WebDriverClient::NewWindowResponse WebDriverConnection::new_window(Jso
     // 3. Handle any user prompts and return its value if it is an error.
     // 3. Handle any user prompts and return its value if it is an error.
     TRY(handle_any_user_prompts());
     TRY(handle_any_user_prompts());
 
 
-    // FIXME: 4. Let type hint be the result of getting the property "type" from the parameters argument.
+    // 4. Let type hint be the result of getting the property "type" from the parameters argument.
+    if (!payload.is_object())
+        return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Payload is not a JSON object");
+
+    // FIXME: Actually use this value to decide between an OS window or tab.
+    auto type_hint = payload.as_object().get("type"sv);
+    if (type_hint.has_value() && !type_hint->is_null() && !type_hint->is_string())
+        return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::InvalidArgument, "Payload property `type` is not null or a string"sv);
 
 
     // 5. Create a new top-level browsing context by running the window open steps with url set to "about:blank",
     // 5. Create a new top-level browsing context by running the window open steps with url set to "about:blank",
     //    target set to the empty string, and features set to "noopener" and the user agent configured to create a new
     //    target set to the empty string, and features set to "noopener" and the user agent configured to create a new
@@ -484,10 +492,14 @@ Messages::WebDriverClient::NewWindowResponse WebDriverConnection::new_window(Jso
     //    is "window", and the implementation supports multiple browsing contexts in separate OS windows, the
     //    is "window", and the implementation supports multiple browsing contexts in separate OS windows, the
     //    created browsing context should be in a new OS window. In all other cases the details of how the browsing
     //    created browsing context should be in a new OS window. In all other cases the details of how the browsing
     //    context is presented to the user are implementation defined.
     //    context is presented to the user are implementation defined.
-    auto [navigable, window_type] = current_browsing_context().top_level_traversable()->choose_a_navigable("_blank"sv, Web::HTML::TokenizedFeature::NoOpener::Yes, Web::HTML::ActivateTab::No);
+    auto* active_window = current_browsing_context().active_window();
+    VERIFY(active_window);
+
+    Web::HTML::TemporaryExecutionContext execution_context { active_window->document()->relevant_settings_object() };
+    auto [target_navigable, no_opener, window_type] = MUST(active_window->window_open_steps_internal("about:blank"sv, ""sv, "noopener"sv));
 
 
     // 6. Let handle be the associated window handle of the newly created window.
     // 6. Let handle be the associated window handle of the newly created window.
-    auto handle = navigable->traversable_navigable()->window_handle();
+    auto handle = target_navigable->traversable_navigable()->window_handle();
 
 
     // 7. Let type be "tab" if the newly created window shares an OS-level window with the current browsing context, or "window" otherwise.
     // 7. Let type be "tab" if the newly created window shares an OS-level window with the current browsing context, or "window" otherwise.
     auto type = "tab"sv;
     auto type = "tab"sv;
@@ -1012,7 +1024,7 @@ Messages::WebDriverClient::GetActiveElementResponse WebDriverConnection::get_act
     // 4. If active element is a non-null element, return success with data set to web element reference object for active element.
     // 4. If active element is a non-null element, return success with data set to web element reference object for active element.
     //    Otherwise, return error with error code no such element.
     //    Otherwise, return error with error code no such element.
     if (active_element)
     if (active_element)
-        return ByteString::number(active_element->unique_id());
+        return ByteString::number(active_element->unique_id().value());
 
 
     return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, "The current document does not have an active element"sv);
     return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::NoSuchElement, "The current document does not have an active element"sv);
 }
 }

+ 17 - 1
Userland/Services/WebDriver/Client.cpp

@@ -11,6 +11,8 @@
 #include <AK/Debug.h>
 #include <AK/Debug.h>
 #include <AK/JsonObject.h>
 #include <AK/JsonObject.h>
 #include <AK/JsonValue.h>
 #include <AK/JsonValue.h>
+#include <LibCore/EventLoop.h>
+#include <LibCore/Timer.h>
 #include <LibWeb/WebDriver/Capabilities.h>
 #include <LibWeb/WebDriver/Capabilities.h>
 #include <LibWeb/WebDriver/TimeoutsConfiguration.h>
 #include <LibWeb/WebDriver/TimeoutsConfiguration.h>
 #include <WebDriver/Client.h>
 #include <WebDriver/Client.h>
@@ -345,7 +347,21 @@ Web::WebDriver::Response Client::new_window(Web::WebDriver::Parameters parameter
 {
 {
     dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/window/new");
     dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/window/new");
     auto session = TRY(find_session_with_id(parameters[0]));
     auto session = TRY(find_session_with_id(parameters[0]));
-    return session->web_content_connection().new_window(payload);
+    auto handle = TRY(session->web_content_connection().new_window(payload));
+
+    constexpr u32 CONNECTION_TIMEOUT_MS = 5000;
+    auto timeout_fired = false;
+    auto timer = Core::Timer::create_single_shot(CONNECTION_TIMEOUT_MS, [&timeout_fired] { timeout_fired = true; });
+    timer->start();
+
+    Core::EventLoop::current().spin_until([&session, &timeout_fired, handle = handle.as_object().get("handle"sv)->as_string()]() {
+        return session->has_window_handle(handle) || timeout_fired;
+    });
+
+    if (timeout_fired)
+        return Web::WebDriver::Error::from_code(Web::WebDriver::ErrorCode::Timeout, "Timed out waiting for window handle");
+
+    return handle;
 }
 }
 
 
 // 11.6 Switch To Frame, https://w3c.github.io/webdriver/#dfn-switch-to-frame
 // 11.6 Switch To Frame, https://w3c.github.io/webdriver/#dfn-switch-to-frame

+ 2 - 0
Userland/Services/WebDriver/Session.h

@@ -49,6 +49,8 @@ public:
         return m_current_window_handle;
         return m_current_window_handle;
     }
     }
 
 
+    bool has_window_handle(StringView handle) const { return m_windows.contains(handle); }
+
     ErrorOr<void> start(LaunchBrowserCallbacks const&);
     ErrorOr<void> start(LaunchBrowserCallbacks const&);
     Web::WebDriver::Response close_window();
     Web::WebDriver::Response close_window();
     Web::WebDriver::Response switch_to_window(StringView);
     Web::WebDriver::Response switch_to_window(StringView);