Browse Source

LibWeb+WebContent+WebDriver+UI: Make window min/maximize asynchronous

This follows suit of previous changes to never block the WebContent
process in WebDriver endpoints.
Timothy Flynn 8 tháng trước cách đây
mục cha
commit
3ca2ee9c72

+ 9 - 8
Ladybird/AppKit/UI/LadybirdWebView.mm

@@ -1032,34 +1032,35 @@ static void copy_data_to_clipboard(StringView data, NSPasteboardType pasteboard_
     m_web_view_bridge->on_maximize_window = [weak_self]() {
     m_web_view_bridge->on_maximize_window = [weak_self]() {
         LadybirdWebView* self = weak_self;
         LadybirdWebView* self = weak_self;
         if (self == nil) {
         if (self == nil) {
-            return Gfx::IntRect {};
+            return;
         }
         }
-        auto frame = [[NSScreen mainScreen] frame];
+
+        auto frame = [[[self window] screen] frame];
         [[self window] setFrame:frame display:YES];
         [[self window] setFrame:frame display:YES];
 
 
-        return Ladybird::ns_rect_to_gfx_rect([[self window] frame]);
+        m_web_view_bridge->did_update_window_rect();
     };
     };
 
 
     m_web_view_bridge->on_minimize_window = [weak_self]() {
     m_web_view_bridge->on_minimize_window = [weak_self]() {
         LadybirdWebView* self = weak_self;
         LadybirdWebView* self = weak_self;
         if (self == nil) {
         if (self == nil) {
-            return Gfx::IntRect {};
+            return;
         }
         }
-        [[self window] setIsMiniaturized:YES];
 
 
-        return Ladybird::ns_rect_to_gfx_rect([[self window] frame]);
+        [[self window] setIsMiniaturized:YES];
     };
     };
 
 
     m_web_view_bridge->on_fullscreen_window = [weak_self]() {
     m_web_view_bridge->on_fullscreen_window = [weak_self]() {
         LadybirdWebView* self = weak_self;
         LadybirdWebView* self = weak_self;
         if (self == nil) {
         if (self == nil) {
-            return Gfx::IntRect {};
+            return;
         }
         }
+
         if (([[self window] styleMask] & NSWindowStyleMaskFullScreen) == 0) {
         if (([[self window] styleMask] & NSWindowStyleMaskFullScreen) == 0) {
             [[self window] toggleFullScreen:nil];
             [[self window] toggleFullScreen:nil];
         }
         }
 
 
-        return Ladybird::ns_rect_to_gfx_rect([[self window] frame]);
+        m_web_view_bridge->did_update_window_rect();
     };
     };
 
 
     m_web_view_bridge->on_received_source = [weak_self](auto const& url, auto const& base_url, auto const& source) {
     m_web_view_bridge->on_received_source = [weak_self](auto const& url, auto const& base_url, auto const& source) {

+ 2 - 3
Ladybird/Qt/Tab.cpp

@@ -364,17 +364,16 @@ Tab::Tab(BrowserWindow* window, RefPtr<WebView::WebContentClient> parent_client,
 
 
     view().on_maximize_window = [this]() {
     view().on_maximize_window = [this]() {
         m_window->showMaximized();
         m_window->showMaximized();
-        return Gfx::IntRect { m_window->x(), m_window->y(), m_window->width(), m_window->height() };
+        view().did_update_window_rect();
     };
     };
 
 
     view().on_minimize_window = [this]() {
     view().on_minimize_window = [this]() {
         m_window->showMinimized();
         m_window->showMinimized();
-        return Gfx::IntRect { m_window->x(), m_window->y(), m_window->width(), m_window->height() };
     };
     };
 
 
     view().on_fullscreen_window = [this]() {
     view().on_fullscreen_window = [this]() {
         m_window->showFullScreen();
         m_window->showFullScreen();
-        return Gfx::IntRect { m_window->x(), m_window->y(), m_window->width(), m_window->height() };
+        view().did_update_window_rect();
     };
     };
 
 
     view().on_insert_clipboard_entry = [](auto const& data, auto const&, auto const& mime_type) {
     view().on_insert_clipboard_entry = [](auto const& data, auto const&, auto const& mime_type) {

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

@@ -317,9 +317,9 @@ public:
     virtual void page_did_request_resize_window(Gfx::IntSize) { }
     virtual void page_did_request_resize_window(Gfx::IntSize) { }
     virtual void page_did_request_reposition_window(Gfx::IntPoint) { }
     virtual void page_did_request_reposition_window(Gfx::IntPoint) { }
     virtual void page_did_request_restore_window() { }
     virtual void page_did_request_restore_window() { }
-    virtual Gfx::IntRect page_did_request_maximize_window() { return {}; }
-    virtual Gfx::IntRect page_did_request_minimize_window() { return {}; }
-    virtual Gfx::IntRect page_did_request_fullscreen_window() { return {}; }
+    virtual void page_did_request_maximize_window() { }
+    virtual void page_did_request_minimize_window() { }
+    virtual void page_did_request_fullscreen_window() { }
     virtual void page_did_start_loading(URL::URL const&, bool is_redirect) { (void)is_redirect; }
     virtual void page_did_start_loading(URL::URL const&, bool is_redirect) { (void)is_redirect; }
     virtual void page_did_create_new_document(Web::DOM::Document&) { }
     virtual void page_did_create_new_document(Web::DOM::Document&) { }
     virtual void page_did_change_active_document_in_top_level_browsing_context(Web::DOM::Document&) { }
     virtual void page_did_change_active_document_in_top_level_browsing_context(Web::DOM::Document&) { }

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

@@ -205,9 +205,9 @@ public:
     Function<void()> on_restore_window;
     Function<void()> on_restore_window;
     Function<void(Gfx::IntPoint)> on_reposition_window;
     Function<void(Gfx::IntPoint)> on_reposition_window;
     Function<void(Gfx::IntSize)> on_resize_window;
     Function<void(Gfx::IntSize)> on_resize_window;
-    Function<Gfx::IntRect()> on_maximize_window;
-    Function<Gfx::IntRect()> on_minimize_window;
-    Function<Gfx::IntRect()> on_fullscreen_window;
+    Function<void()> on_maximize_window;
+    Function<void()> on_minimize_window;
+    Function<void()> on_fullscreen_window;
     Function<void(Color current_color)> on_request_color_picker;
     Function<void(Color current_color)> on_request_color_picker;
     Function<void(Web::HTML::FileFilter const& accepted_file_types, Web::HTML::AllowMultipleFiles)> on_request_file_picker;
     Function<void(Web::HTML::FileFilter const& accepted_file_types, Web::HTML::AllowMultipleFiles)> on_request_file_picker;
     Function<void(Gfx::IntPoint content_position, i32 minimum_width, Vector<Web::HTML::SelectItem> items)> on_request_select_dropdown;
     Function<void(Gfx::IntPoint content_position, i32 minimum_width, Vector<Web::HTML::SelectItem> items)> on_request_select_dropdown;

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

@@ -508,34 +508,28 @@ void WebContentClient::did_request_resize_window(u64 page_id, Gfx::IntSize size)
     }
     }
 }
 }
 
 
-Messages::WebContentClient::DidRequestMaximizeWindowResponse WebContentClient::did_request_maximize_window(u64 page_id)
+void WebContentClient::did_request_maximize_window(u64 page_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_maximize_window)
         if (view->on_maximize_window)
-            return view->on_maximize_window();
+            view->on_maximize_window();
     }
     }
-
-    return Gfx::IntRect {};
 }
 }
 
 
-Messages::WebContentClient::DidRequestMinimizeWindowResponse WebContentClient::did_request_minimize_window(u64 page_id)
+void WebContentClient::did_request_minimize_window(u64 page_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_minimize_window)
         if (view->on_minimize_window)
-            return view->on_minimize_window();
+            view->on_minimize_window();
     }
     }
-
-    return Gfx::IntRect {};
 }
 }
 
 
-Messages::WebContentClient::DidRequestFullscreenWindowResponse WebContentClient::did_request_fullscreen_window(u64 page_id)
+void WebContentClient::did_request_fullscreen_window(u64 page_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_fullscreen_window)
         if (view->on_fullscreen_window)
-            return view->on_fullscreen_window();
+            view->on_fullscreen_window();
     }
     }
-
-    return Gfx::IntRect {};
 }
 }
 
 
 void WebContentClient::did_request_file(u64 page_id, ByteString const& path, i32 request_id)
 void WebContentClient::did_request_file(u64 page_id, ByteString const& path, i32 request_id)

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

@@ -102,9 +102,9 @@ private:
     virtual void did_request_restore_window(u64 page_id) override;
     virtual void did_request_restore_window(u64 page_id) override;
     virtual void did_request_reposition_window(u64 page_id, Gfx::IntPoint) override;
     virtual void did_request_reposition_window(u64 page_id, Gfx::IntPoint) override;
     virtual void did_request_resize_window(u64 page_id, Gfx::IntSize) override;
     virtual void did_request_resize_window(u64 page_id, Gfx::IntSize) override;
-    virtual Messages::WebContentClient::DidRequestMaximizeWindowResponse did_request_maximize_window(u64 page_id) override;
-    virtual Messages::WebContentClient::DidRequestMinimizeWindowResponse did_request_minimize_window(u64 page_id) override;
-    virtual Messages::WebContentClient::DidRequestFullscreenWindowResponse did_request_fullscreen_window(u64 page_id) override;
+    virtual void did_request_maximize_window(u64 page_id) override;
+    virtual void did_request_minimize_window(u64 page_id) override;
+    virtual void did_request_fullscreen_window(u64 page_id) override;
     virtual void did_request_file(u64 page_id, ByteString const& path, i32) override;
     virtual void did_request_file(u64 page_id, ByteString const& path, i32) override;
     virtual void did_request_color_picker(u64 page_id, Color const& current_color) override;
     virtual void did_request_color_picker(u64 page_id, Color const& current_color) override;
     virtual void did_request_file_picker(u64 page_id, Web::HTML::FileFilter const& accepted_file_types, Web::HTML::AllowMultipleFiles) override;
     virtual void did_request_file_picker(u64 page_id, Web::HTML::FileFilter const& accepted_file_types, Web::HTML::AllowMultipleFiles) override;

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

@@ -276,19 +276,19 @@ void PageClient::page_did_request_restore_window()
     client().async_did_request_restore_window(m_id);
     client().async_did_request_restore_window(m_id);
 }
 }
 
 
-Gfx::IntRect PageClient::page_did_request_maximize_window()
+void PageClient::page_did_request_maximize_window()
 {
 {
-    return client().did_request_maximize_window(m_id);
+    client().async_did_request_maximize_window(m_id);
 }
 }
 
 
-Gfx::IntRect PageClient::page_did_request_minimize_window()
+void PageClient::page_did_request_minimize_window()
 {
 {
-    return client().did_request_minimize_window(m_id);
+    client().async_did_request_minimize_window(m_id);
 }
 }
 
 
-Gfx::IntRect PageClient::page_did_request_fullscreen_window()
+void PageClient::page_did_request_fullscreen_window()
 {
 {
-    return client().did_request_fullscreen_window(m_id);
+    client().async_did_request_fullscreen_window(m_id);
 }
 }
 
 
 void PageClient::page_did_request_tooltip_override(Web::CSSPixelPoint position, ByteString const& title)
 void PageClient::page_did_request_tooltip_override(Web::CSSPixelPoint position, ByteString const& title)

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

@@ -115,9 +115,9 @@ private:
     virtual void page_did_request_resize_window(Gfx::IntSize) override;
     virtual void page_did_request_resize_window(Gfx::IntSize) override;
     virtual void page_did_request_reposition_window(Gfx::IntPoint) override;
     virtual void page_did_request_reposition_window(Gfx::IntPoint) override;
     virtual void page_did_request_restore_window() override;
     virtual void page_did_request_restore_window() override;
-    virtual Gfx::IntRect page_did_request_maximize_window() override;
-    virtual Gfx::IntRect page_did_request_minimize_window() override;
-    virtual Gfx::IntRect page_did_request_fullscreen_window() override;
+    virtual void page_did_request_maximize_window() override;
+    virtual void page_did_request_minimize_window() override;
+    virtual void page_did_request_fullscreen_window() override;
     virtual void page_did_request_tooltip_override(Web::CSSPixelPoint, ByteString const&) override;
     virtual void page_did_request_tooltip_override(Web::CSSPixelPoint, ByteString const&) override;
     virtual void page_did_stop_tooltip_override() override;
     virtual void page_did_stop_tooltip_override() override;
     virtual void page_did_enter_tooltip_area(ByteString const&) override;
     virtual void page_did_enter_tooltip_area(ByteString const&) override;

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

@@ -79,9 +79,9 @@ endpoint WebContentClient
     did_request_restore_window(u64 page_id) =|
     did_request_restore_window(u64 page_id) =|
     did_request_reposition_window(u64 page_id, Gfx::IntPoint position) =|
     did_request_reposition_window(u64 page_id, Gfx::IntPoint position) =|
     did_request_resize_window(u64 page_id, Gfx::IntSize size) =|
     did_request_resize_window(u64 page_id, Gfx::IntSize size) =|
-    did_request_maximize_window(u64 page_id) => (Gfx::IntRect window_rect)
-    did_request_minimize_window(u64 page_id) => (Gfx::IntRect window_rect)
-    did_request_fullscreen_window(u64 page_id) => (Gfx::IntRect window_rect)
+    did_request_maximize_window(u64 page_id) =|
+    did_request_minimize_window(u64 page_id) =|
+    did_request_fullscreen_window(u64 page_id) =|
     did_request_file(u64 page_id, ByteString path, i32 request_id) =|
     did_request_file(u64 page_id, ByteString path, i32 request_id) =|
     did_request_color_picker(u64 page_id, Color current_color) =|
     did_request_color_picker(u64 page_id, Color current_color) =|
     did_request_file_picker(u64 page_id, Web::HTML::FileFilter accepted_file_types, Web::HTML::AllowMultipleFiles allow_multiple_files) =|
     did_request_file_picker(u64 page_id, Web::HTML::FileFilter accepted_file_types, Web::HTML::AllowMultipleFiles allow_multiple_files) =|

+ 83 - 69
Userland/Services/WebContent/WebDriverConnection.cpp

@@ -693,27 +693,27 @@ Messages::WebDriverClient::SetWindowRectResponse WebDriverConnection::set_window
     // FIXME: 10. Fully exit fullscreen.
     // FIXME: 10. Fully exit fullscreen.
 
 
     // 11. Restore the window.
     // 11. Restore the window.
-    restore_the_window();
-
-    auto& page = current_top_level_browsing_context()->page();
-
-    // 11. If width and height are not null:
-    if (width.has_value() && height.has_value()) {
-        // a. Set the width, in CSS pixels, of the operating system window containing the current top-level browsing context, including any browser chrome and externally drawn window decorations to a value that is as close as possible to width.
-        // b. Set the height, in CSS pixels, of the operating system window containing the current top-level browsing context, including any browser chrome and externally drawn window decorations to a value that is as close as possible to height.
-        page.client().page_did_request_resize_window({ *width, *height });
-        ++m_pending_window_rect_requests;
-    }
+    restore_the_window(JS::create_heap_function(current_top_level_browsing_context()->heap(), [this, x, y, width, height]() {
+        auto& page = current_top_level_browsing_context()->page();
+
+        // 11. If width and height are not null:
+        if (width.has_value() && height.has_value()) {
+            // a. Set the width, in CSS pixels, of the operating system window containing the current top-level browsing context, including any browser chrome and externally drawn window decorations to a value that is as close as possible to width.
+            // b. Set the height, in CSS pixels, of the operating system window containing the current top-level browsing context, including any browser chrome and externally drawn window decorations to a value that is as close as possible to height.
+            page.client().page_did_request_resize_window({ *width, *height });
+            ++m_pending_window_rect_requests;
+        }
 
 
-    // 12. If x and y are not null:
-    if (x.has_value() && y.has_value()) {
-        // a. Run the implementation-specific steps to set the position of the operating system level window containing the current top-level browsing context to the position given by the x and y coordinates.
-        page.client().page_did_request_reposition_window({ *x, *y });
-        ++m_pending_window_rect_requests;
-    }
+        // 12. If x and y are not null:
+        if (x.has_value() && y.has_value()) {
+            // a. Run the implementation-specific steps to set the position of the operating system level window containing the current top-level browsing context to the position given by the x and y coordinates.
+            page.client().page_did_request_reposition_window({ *x, *y });
+            ++m_pending_window_rect_requests;
+        }
 
 
-    if (m_pending_window_rect_requests == 0)
-        async_window_rect_updated(serialize_rect(compute_window_rect(page)));
+        if (m_pending_window_rect_requests == 0)
+            async_window_rect_updated(serialize_rect(compute_window_rect(page)));
+    }));
 
 
     // 14. Return success with data set to the WindowRect object for the current top-level browsing context.
     // 14. Return success with data set to the WindowRect object for the current top-level browsing context.
     return JsonValue {};
     return JsonValue {};
@@ -733,13 +733,13 @@ Messages::WebDriverClient::MaximizeWindowResponse WebDriverConnection::maximize_
     // FIXME: 4. Fully exit fullscreen.
     // FIXME: 4. Fully exit fullscreen.
 
 
     // 5. Restore the window.
     // 5. Restore the window.
-    restore_the_window();
-
-    // 6. Maximize the window of the current top-level browsing context.
-    auto window_rect = maximize_the_window();
+    restore_the_window(JS::create_heap_function(current_top_level_browsing_context()->heap(), [this]() {
+        // 6. Maximize the window of the current top-level browsing context.
+        maximize_the_window();
+    }));
 
 
     // 7. Return success with data set to the WindowRect object for the current top-level browsing context.
     // 7. Return success with data set to the WindowRect object for the current top-level browsing context.
-    return serialize_rect(window_rect);
+    return JsonValue {};
 }
 }
 
 
 // 11.8.4 Minimize Window, https://w3c.github.io/webdriver/#minimize-window
 // 11.8.4 Minimize Window, https://w3c.github.io/webdriver/#minimize-window
@@ -756,10 +756,13 @@ Messages::WebDriverClient::MinimizeWindowResponse WebDriverConnection::minimize_
     // FIXME: 4. Fully exit fullscreen.
     // FIXME: 4. Fully exit fullscreen.
 
 
     // 5. Iconify the window.
     // 5. Iconify the window.
-    auto window_rect = iconify_the_window();
+    iconify_the_window(JS::create_heap_function(current_top_level_browsing_context()->heap(), [this]() {
+        auto& page = current_top_level_browsing_context()->page();
+        async_window_rect_updated(serialize_rect(compute_window_rect(page)));
+    }));
 
 
     // 6. Return success with data set to the WindowRect object for the current top-level browsing context.
     // 6. Return success with data set to the WindowRect object for the current top-level browsing context.
-    return serialize_rect(window_rect);
+    return JsonValue {};
 }
 }
 
 
 // 11.8.5 Fullscreen Window, https://w3c.github.io/webdriver/#dfn-fullscreen-window
 // 11.8.5 Fullscreen Window, https://w3c.github.io/webdriver/#dfn-fullscreen-window
@@ -774,15 +777,16 @@ Messages::WebDriverClient::FullscreenWindowResponse WebDriverConnection::fullscr
     TRY(handle_any_user_prompts());
     TRY(handle_any_user_prompts());
 
 
     // 4. Restore the window.
     // 4. Restore the window.
-    restore_the_window();
-
-    // 5. FIXME: Call fullscreen an element with the current top-level browsing context’s active document’s document element.
-    //           As described in https://fullscreen.spec.whatwg.org/#fullscreen-an-element
-    //    NOTE: What we do here is basically `requestFullscreen(options)` with options["navigationUI"]="show"
-    auto rect = current_top_level_browsing_context()->page().client().page_did_request_fullscreen_window();
+    restore_the_window(JS::create_heap_function(current_top_level_browsing_context()->heap(), [this]() {
+        // 5. FIXME: Call fullscreen an element with the current top-level browsing context’s active document’s document element.
+        //           As described in https://fullscreen.spec.whatwg.org/#fullscreen-an-element
+        //    NOTE: What we do here is basically `requestFullscreen(options)` with options["navigationUI"]="show"
+        current_top_level_browsing_context()->page().client().page_did_request_fullscreen_window();
+        ++m_pending_window_rect_requests;
+    }));
 
 
     // 6. Return success with data set to the WindowRect object for the current top-level browsing context.
     // 6. Return success with data set to the WindowRect object for the current top-level browsing context.
-    return serialize_rect(rect);
+    return JsonValue {};
 }
 }
 
 
 // Extension Consume User Activation, https://html.spec.whatwg.org/multipage/interaction.html#user-activation-user-agent-automation
 // Extension Consume User Activation, https://html.spec.whatwg.org/multipage/interaction.html#user-activation-user-agent-automation
@@ -2521,56 +2525,66 @@ void WebDriverConnection::page_did_open_dialog(Badge<PageClient>)
         m_navigation_timer->stop_and_fire_timeout_handler();
         m_navigation_timer->stop_and_fire_timeout_handler();
 }
 }
 
 
-// https://w3c.github.io/webdriver/#dfn-restore-the-window
-void WebDriverConnection::restore_the_window()
+// https://w3c.github.io/webdriver/#dfn-maximize-the-window
+void WebDriverConnection::maximize_the_window()
 {
 {
-    // To restore the window, given an operating system level window with an associated top-level browsing context, run implementation-specific steps to restore or unhide the window to the visible screen.
-    current_top_level_browsing_context()->page().client().page_did_request_restore_window();
+    // To maximize the window, given an operating system level window with an associated top-level browsing context, run
+    // the implementation-specific steps to transition the operating system level window into the maximized window state.
+    // Return when the window has completed the transition, or within an implementation-defined timeout.
+    current_top_level_browsing_context()->page().client().page_did_request_maximize_window();
+    ++m_pending_window_rect_requests;
+}
 
 
-    // Do not return from this operation until the visibility state of the top-level browsing context’s active document has reached the visible state, or until the operation times out.
-    // FIXME: It isn't clear which timeout should be used here.
-    auto page_load_timeout_fired = false;
-    auto timer = Core::Timer::create_single_shot(m_timeouts_configuration.page_load_timeout.value_or(300'000), [&] {
-        page_load_timeout_fired = true;
-    });
-    timer->start();
+// https://w3c.github.io/webdriver/#dfn-iconify-the-window
+void WebDriverConnection::iconify_the_window(JS::NonnullGCPtr<JS::HeapFunction<void()>> on_complete)
+{
+    // To iconify the window, given an operating system level window with an associated top-level browsing context, run
+    // implementation-specific steps to iconify, minimize, or hide the window from the visible screen.
+    current_top_level_browsing_context()->page().client().page_did_request_minimize_window();
 
 
-    Web::Platform::EventLoopPlugin::the().spin_until([&]() {
-        auto state = current_top_level_browsing_context()->top_level_traversable()->system_visibility_state();
-        return page_load_timeout_fired || state == Web::HTML::VisibilityState::Visible;
-    });
+    // Do not return from this operation until the visibility state of the top-level browsing context’s active document
+    // has reached the hidden state, or until the operation times out.
+    wait_for_visibility_state(on_complete, Web::HTML::VisibilityState::Hidden);
 }
 }
 
 
-// https://w3c.github.io/webdriver/#dfn-maximize-the-window
-Gfx::IntRect WebDriverConnection::maximize_the_window()
+// https://w3c.github.io/webdriver/#dfn-restore-the-window
+void WebDriverConnection::restore_the_window(JS::NonnullGCPtr<JS::HeapFunction<void()>> on_complete)
 {
 {
-    // To maximize the window, given an operating system level window with an associated top-level browsing context, run the implementation-specific steps to transition the operating system level window into the maximized window state.
-    auto rect = current_top_level_browsing_context()->page().client().page_did_request_maximize_window();
+    // To restore the window, given an operating system level window with an associated top-level browsing context, run
+    // implementation-specific steps to restore or unhide the window to the visible screen.
+    current_top_level_browsing_context()->page().client().page_did_request_restore_window();
 
 
-    // Return when the window has completed the transition, or within an implementation-defined timeout.
-    return rect;
+    // Do not return from this operation until the visibility state of the top-level browsing context’s active document
+    // has reached the visible state, or until the operation times out.
+    wait_for_visibility_state(on_complete, Web::HTML::VisibilityState::Visible);
 }
 }
 
 
-// https://w3c.github.io/webdriver/#dfn-iconify-the-window
-Gfx::IntRect WebDriverConnection::iconify_the_window()
+void WebDriverConnection::wait_for_visibility_state(JS::NonnullGCPtr<JS::HeapFunction<void()>> on_complete, Web::HTML::VisibilityState target_visibility_state)
 {
 {
-    // To iconify the window, given an operating system level window with an associated top-level browsing context, run implementation-specific steps to iconify, minimize, or hide the window from the visible screen.
-    auto rect = current_top_level_browsing_context()->page().client().page_did_request_minimize_window();
+    static constexpr auto VISIBILITY_STATE_TIMEOUT_MS = 5'000;
 
 
-    // Do not return from this operation until the visibility state of the top-level browsing context’s active document has reached the hidden state, or until the operation times out.
-    // FIXME: It isn't clear which timeout should be used here.
-    auto page_load_timeout_fired = false;
-    auto timer = Core::Timer::create_single_shot(m_timeouts_configuration.page_load_timeout.value_or(300'000), [&] {
-        page_load_timeout_fired = true;
-    });
-    timer->start();
+    auto* document = current_top_level_browsing_context()->active_document();
+    auto& realm = document->realm();
 
 
-    Web::Platform::EventLoopPlugin::the().spin_until([&]() {
-        auto state = current_top_level_browsing_context()->top_level_traversable()->system_visibility_state();
-        return page_load_timeout_fired || state == Web::HTML::VisibilityState::Hidden;
+    if (document->visibility_state_value() == target_visibility_state) {
+        on_complete->function()();
+        return;
+    }
+
+    auto timer = realm.heap().allocate<Web::WebDriver::HeapTimer>(realm);
+    m_document_observer = realm.heap().allocate<Web::DOM::DocumentObserver>(realm, realm, *document);
+
+    m_document_observer->set_document_visibility_state_observer([timer, target_visibility_state](Web::HTML::VisibilityState visibility_state) {
+        if (visibility_state == target_visibility_state)
+            timer->stop_and_fire_timeout_handler();
     });
     });
 
 
-    return rect;
+    timer->start(VISIBILITY_STATE_TIMEOUT_MS, JS::create_heap_function(realm.heap(), [this, on_complete]() {
+        m_document_observer->set_document_visibility_state_observer({});
+        m_document_observer = nullptr;
+
+        on_complete->function()();
+    }));
 }
 }
 
 
 // https://w3c.github.io/webdriver/#dfn-find
 // https://w3c.github.io/webdriver/#dfn-find

+ 6 - 3
Userland/Services/WebContent/WebDriverConnection.h

@@ -18,6 +18,7 @@
 #include <LibJS/Forward.h>
 #include <LibJS/Forward.h>
 #include <LibJS/Heap/MarkedVector.h>
 #include <LibJS/Heap/MarkedVector.h>
 #include <LibWeb/Forward.h>
 #include <LibWeb/Forward.h>
+#include <LibWeb/HTML/VisibilityState.h>
 #include <LibWeb/WebDriver/ElementLocationStrategies.h>
 #include <LibWeb/WebDriver/ElementLocationStrategies.h>
 #include <LibWeb/WebDriver/Response.h>
 #include <LibWeb/WebDriver/Response.h>
 #include <LibWeb/WebDriver/TimeoutsConfiguration.h>
 #include <LibWeb/WebDriver/TimeoutsConfiguration.h>
@@ -120,9 +121,11 @@ private:
     ErrorOr<void, Web::WebDriver::Error> ensure_current_top_level_browsing_context_is_open();
     ErrorOr<void, Web::WebDriver::Error> ensure_current_top_level_browsing_context_is_open();
 
 
     ErrorOr<void, Web::WebDriver::Error> handle_any_user_prompts();
     ErrorOr<void, Web::WebDriver::Error> handle_any_user_prompts();
-    void restore_the_window();
-    Gfx::IntRect maximize_the_window();
-    Gfx::IntRect iconify_the_window();
+
+    void maximize_the_window();
+    void iconify_the_window(JS::NonnullGCPtr<JS::HeapFunction<void()>>);
+    void restore_the_window(JS::NonnullGCPtr<JS::HeapFunction<void()>>);
+    void wait_for_visibility_state(JS::NonnullGCPtr<JS::HeapFunction<void()>>, Web::HTML::VisibilityState);
 
 
     using OnNavigationComplete = JS::NonnullGCPtr<JS::HeapFunction<void(Web::WebDriver::Response)>>;
     using OnNavigationComplete = JS::NonnullGCPtr<JS::HeapFunction<void(Web::WebDriver::Response)>>;
     void wait_for_navigation_to_complete(OnNavigationComplete);
     void wait_for_navigation_to_complete(OnNavigationComplete);

+ 3 - 3
Userland/Services/WebDriver/Client.cpp

@@ -406,7 +406,7 @@ Web::WebDriver::Response Client::maximize_window(Web::WebDriver::Parameters para
 {
 {
     dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/window/maximize");
     dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/window/maximize");
     auto session = TRY(find_session_with_id(parameters[0]));
     auto session = TRY(find_session_with_id(parameters[0]));
-    return session->web_content_connection().maximize_window();
+    return session->maximize_window();
 }
 }
 
 
 // 11.8.4 Minimize Window, https://w3c.github.io/webdriver/#minimize-window
 // 11.8.4 Minimize Window, https://w3c.github.io/webdriver/#minimize-window
@@ -415,7 +415,7 @@ Web::WebDriver::Response Client::minimize_window(Web::WebDriver::Parameters para
 {
 {
     dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/window/minimize");
     dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/window/minimize");
     auto session = TRY(find_session_with_id(parameters[0]));
     auto session = TRY(find_session_with_id(parameters[0]));
-    return session->web_content_connection().minimize_window();
+    return session->minimize_window();
 }
 }
 
 
 // 11.8.5 Fullscreen Window, https://w3c.github.io/webdriver/#dfn-fullscreen-window
 // 11.8.5 Fullscreen Window, https://w3c.github.io/webdriver/#dfn-fullscreen-window
@@ -424,7 +424,7 @@ Web::WebDriver::Response Client::fullscreen_window(Web::WebDriver::Parameters pa
 {
 {
     dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/window/fullscreen");
     dbgln_if(WEBDRIVER_DEBUG, "Handling POST /session/<session_id>/window/fullscreen");
     auto session = TRY(find_session_with_id(parameters[0]));
     auto session = TRY(find_session_with_id(parameters[0]));
-    return session->web_content_connection().fullscreen_window();
+    return session->fullscreen_window();
 }
 }
 
 
 // Extension: Consume User Activation, https://html.spec.whatwg.org/multipage/interaction.html#user-activation-user-agent-automation
 // Extension: Consume User Activation, https://html.spec.whatwg.org/multipage/interaction.html#user-activation-user-agent-automation

+ 21 - 1
Userland/Services/WebDriver/Session.cpp

@@ -216,6 +216,27 @@ Web::WebDriver::Response Session::set_window_rect(JsonValue payload) const
     });
     });
 }
 }
 
 
+Web::WebDriver::Response Session::maximize_window() const
+{
+    return perform_async_action(web_content_connection().on_window_rect_updated, [&]() {
+        return web_content_connection().maximize_window();
+    });
+}
+
+Web::WebDriver::Response Session::minimize_window() const
+{
+    return perform_async_action(web_content_connection().on_window_rect_updated, [&]() {
+        return web_content_connection().minimize_window();
+    });
+}
+
+Web::WebDriver::Response Session::fullscreen_window() const
+{
+    return perform_async_action(web_content_connection().on_window_rect_updated, [&]() {
+        return web_content_connection().fullscreen_window();
+    });
+}
+
 Web::WebDriver::Response Session::execute_script(JsonValue payload, ScriptMode mode) const
 Web::WebDriver::Response Session::execute_script(JsonValue payload, ScriptMode mode) const
 {
 {
     return perform_async_action(web_content_connection().on_script_executed, [&]() {
     return perform_async_action(web_content_connection().on_script_executed, [&]() {
@@ -263,5 +284,4 @@ Web::WebDriver::Response Session::accept_alert() const
         return web_content_connection().accept_alert();
         return web_content_connection().accept_alert();
     });
     });
 }
 }
-
 }
 }

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

@@ -66,6 +66,9 @@ public:
     Web::WebDriver::Response execute_script(JsonValue, ScriptMode) const;
     Web::WebDriver::Response execute_script(JsonValue, ScriptMode) const;
 
 
     Web::WebDriver::Response set_window_rect(JsonValue) const;
     Web::WebDriver::Response set_window_rect(JsonValue) const;
+    Web::WebDriver::Response maximize_window() const;
+    Web::WebDriver::Response minimize_window() const;
+    Web::WebDriver::Response fullscreen_window() const;
 
 
     Web::WebDriver::Response element_click(String) const;
     Web::WebDriver::Response element_click(String) const;
     Web::WebDriver::Response element_send_keys(String, JsonValue) const;
     Web::WebDriver::Response element_send_keys(String, JsonValue) const;