diff --git a/Libraries/LibWeb/HTML/Canvas/CanvasState.h b/Libraries/LibWeb/HTML/Canvas/CanvasState.h index 418c1c956a..a91429f1f0 100644 --- a/Libraries/LibWeb/HTML/Canvas/CanvasState.h +++ b/Libraries/LibWeb/HTML/Canvas/CanvasState.h @@ -42,6 +42,8 @@ public: protected: CanvasState() = default; + void set_context_lost(bool context_lost) { m_context_lost = context_lost; } + private: DrawingState m_drawing_state; Vector m_drawing_state_stack; diff --git a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp index f2c4f483b6..608c53f724 100644 --- a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp +++ b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp @@ -31,9 +31,12 @@ #include #include #include +#include #include #include #include +#include +#include #include #include #include @@ -43,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -323,6 +327,54 @@ Optional CanvasRenderingContext2D::canvas_id() const return m_transport->canvas_id(); } +// https://html.spec.whatwg.org/multipage/canvas.html#context-loss +void CanvasRenderingContext2D::notify_backing_storage_lost() +{ + if (!has_backing_storage()) + return; + + // When the user agent detects that the backing storage associated with a canvas context has been lost, then it + // must queue a global task on the DOM manipulation task source given canvas's relevant global object to run + // these steps: + queue_global_task(HTML::Task::Source::DOMManipulation, relevant_global_object(*this), GC::create_function(heap(), [this] { + // 1. Let canvas be context's canvas element. + // 2. If context's context lost is true, then abort these steps. + if (is_context_lost()) + return; + + // 3. Set context's context lost to true. + set_context_lost(true); + + // AD-HOC: Drop recorded-but-unflushed draw commands; they targeted the lost storage. + discard_backing_storage(); + + // 4. Reset the rendering context to its default state given context. + reset_to_default_state(); + + // 5. Let shouldRestore be the result of firing an event named contextlost at canvas, with the cancelable + // attribute initialized to true. + Bindings::EventInit context_lost_event_init; + context_lost_event_init.cancelable = true; + bool should_restore = m_element->dispatch_event(DOM::Event::create(realm(), HTML::EventNames::contextlost, context_lost_event_init)); + + // 6. If shouldRestore is false, then abort these steps. + if (!should_restore) + return; + + // 7. Attempt to restore context by creating a backing storage using context's attributes and associating + // them with context. If this fails, then abort these steps. + ensure_backing_storage(); + if (!has_backing_storage()) + return; + + // 8. Set context's context lost to false. + set_context_lost(false); + + // 9. Fire an event named contextrestored at canvas. + m_element->dispatch_event(DOM::Event::create(realm(), HTML::EventNames::contextrestored)); + })); +} + void CanvasRenderingContext2D::ensure_backing_storage() { if (has_backing_storage() || m_size.is_empty()) diff --git a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h index 47f1c6c2b9..0e4a855340 100644 --- a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h +++ b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h @@ -130,6 +130,8 @@ public: void discard_backing_storage(); + void notify_backing_storage_lost(); + Optional canvas_id() const; RefPtr read_pixels(Gfx::IntRect const&); diff --git a/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp b/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp index 5f9fd886e0..0d1870a9f9 100644 --- a/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLCanvasElement.cpp @@ -441,6 +441,12 @@ RefPtr HTMLCanvasElement::get_bitmap_from_surface() return bitmap; } +void HTMLCanvasElement::notify_compositor_connection_lost() +{ + if (auto* webgl_context = this->webgl_context()) + webgl_context->lose_context_from_compositor_loss(); +} + void HTMLCanvasElement::set_canvas_content_dirty() { m_canvas_content_dirty = true; @@ -467,6 +473,16 @@ void HTMLCanvasElement::prepare_for_compositing() }); } +void HTMLCanvasElement::notify_compositor_backing_storage_lost() +{ + if (auto* webgl_context = this->webgl_context()) { + webgl_context->restore_context_after_compositor_reconnect(); + return; + } + if (auto context_2d = canvas_rendering_context_2d()) + context_2d->notify_backing_storage_lost(); +} + Optional HTMLCanvasElement::canvas_surface_content_size() const { if (!canvas_id().has_value()) diff --git a/Libraries/LibWeb/HTML/HTMLCanvasElement.h b/Libraries/LibWeb/HTML/HTMLCanvasElement.h index 345bed1ae0..507c832d70 100644 --- a/Libraries/LibWeb/HTML/HTMLCanvasElement.h +++ b/Libraries/LibWeb/HTML/HTMLCanvasElement.h @@ -47,6 +47,7 @@ public: RefPtr get_bitmap_from_surface(); void prepare_for_compositing(); + void notify_compositor_backing_storage_lost(); void set_canvas_content_dirty(); GC::Ptr canvas_rendering_context_2d() const { @@ -61,6 +62,8 @@ public: void ensure_backing_storage(); + void notify_compositor_connection_lost(); + CSS::ComputationContext canvas_font_computation_context(); private: diff --git a/Libraries/LibWeb/Page/Page.cpp b/Libraries/LibWeb/Page/Page.cpp index 3f347f99f3..83396021d8 100644 --- a/Libraries/LibWeb/Page/Page.cpp +++ b/Libraries/LibWeb/Page/Page.cpp @@ -723,6 +723,20 @@ void Page::prepare_canvas_contexts_for_compositing() }); } +void Page::notify_all_canvas_elements_of_lost_backing_storage() +{ + for_each_canvas_element([](auto& canvas_element) { + canvas_element.notify_compositor_backing_storage_lost(); + }); +} + +void Page::notify_all_webgl_contexts_lost() +{ + for_each_canvas_element([](auto& canvas_element) { + canvas_element.notify_compositor_connection_lost(); + }); +} + void Page::did_request_media_context_menu(UniqueNodeID media_id, CSSPixelPoint position, ByteString const& target, unsigned modifiers, MediaContextMenu const& menu) { m_media_context_menu_element_id = media_id; diff --git a/Libraries/LibWeb/Page/Page.h b/Libraries/LibWeb/Page/Page.h index 4012061050..e2b8637cb1 100644 --- a/Libraries/LibWeb/Page/Page.h +++ b/Libraries/LibWeb/Page/Page.h @@ -234,6 +234,8 @@ public: void unregister_canvas_element(Badge, UniqueNodeID canvas_id); void prepare_canvas_contexts_for_compositing(); + void notify_all_canvas_elements_of_lost_backing_storage(); + void notify_all_webgl_contexts_lost(); struct MediaContextMenu { URL::URL media_url; diff --git a/Libraries/LibWeb/WebGL/WebGL2RenderingContext.cpp b/Libraries/LibWeb/WebGL/WebGL2RenderingContext.cpp index 780ac4937c..68226d5d09 100644 --- a/Libraries/LibWeb/WebGL/WebGL2RenderingContext.cpp +++ b/Libraries/LibWeb/WebGL/WebGL2RenderingContext.cpp @@ -70,6 +70,11 @@ void WebGL2RenderingContext::prepare_for_compositing() context().present_canvas_for_compositing(m_context_creation_parameters.preserve_drawing_buffer); } +bool WebGL2RenderingContext::reestablish_remote_context() +{ + return restore_webgl_context_proxy(context(), *m_canvas_element, WebGLVersion::WebGL2, m_actual_context_parameters); +} + GC::Ref WebGL2RenderingContext::canvas_for_binding() const { return *m_canvas_element; diff --git a/Libraries/LibWeb/WebGL/WebGL2RenderingContext.h b/Libraries/LibWeb/WebGL/WebGL2RenderingContext.h index c145e5ab04..6af5891f7e 100644 --- a/Libraries/LibWeb/WebGL/WebGL2RenderingContext.h +++ b/Libraries/LibWeb/WebGL/WebGL2RenderingContext.h @@ -45,6 +45,7 @@ private: WebGL2RenderingContext(JS::Realm&, HTML::HTMLCanvasElement&, NonnullOwnPtr context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters); virtual void visit_edges(Cell::Visitor&) override; + virtual bool reestablish_remote_context() override; GC::Ref m_canvas_element; diff --git a/Libraries/LibWeb/WebGL/WebGLContextProxyBase.cpp b/Libraries/LibWeb/WebGL/WebGLContextProxyBase.cpp index 0bfe8e9562..9a7b392508 100644 --- a/Libraries/LibWeb/WebGL/WebGLContextProxyBase.cpp +++ b/Libraries/LibWeb/WebGL/WebGLContextProxyBase.cpp @@ -25,6 +25,16 @@ WebGLContextProxyBase::~WebGLContextProxyBase() m_transport->destroy_context(); } +void WebGLContextProxyBase::restore(NonnullRefPtr transport, Vector supported_extensions) +{ + m_transport = move(transport); + m_supported_extensions = move(supported_extensions); + m_lost = false; + m_commands.clear_with_capacity(); + m_pending_bitmaps.clear_with_capacity(); + m_string_cache.clear(); +} + void WebGLContextProxyBase::flush_commands() { if (m_commands.is_empty()) diff --git a/Libraries/LibWeb/WebGL/WebGLContextProxyBase.h b/Libraries/LibWeb/WebGL/WebGLContextProxyBase.h index 6f7d66430a..38e51e7ce7 100644 --- a/Libraries/LibWeb/WebGL/WebGLContextProxyBase.h +++ b/Libraries/LibWeb/WebGL/WebGLContextProxyBase.h @@ -35,8 +35,11 @@ public: ~WebGLContextProxyBase(); void flush_commands(); + void set_lost() { m_lost = true; } Optional canvas_id() const { return m_transport->canvas_id(); } + void restore(NonnullRefPtr, Vector supported_extensions); + void make_current() { } void notify_content_will_change() { } u32 default_framebuffer() const { return 0; } diff --git a/Libraries/LibWeb/WebGL/WebGLObject.cpp b/Libraries/LibWeb/WebGL/WebGLObject.cpp index 298176429e..b5114a75c2 100644 --- a/Libraries/LibWeb/WebGL/WebGLObject.cpp +++ b/Libraries/LibWeb/WebGL/WebGLObject.cpp @@ -18,6 +18,7 @@ WebGLObject::WebGLObject(JS::Realm& realm, GC::Ref co : Bindings::PlatformObject(realm) , m_context(context) , m_handle(handle) + , m_context_generation(context->context_generation()) { } @@ -62,8 +63,7 @@ ErrorOr> WebGLObject::handle_for_query(WebGLRenderingContextBas bool WebGLObject::invalidated_for_context(WebGLRenderingContextBase const* context) const { - (void)context; - return m_invalidated; + return m_invalidated || m_context_generation != context->context_generation(); } ErrorOr WebGLObject::validate_context(WebGLRenderingContextBase const* context) const diff --git a/Libraries/LibWeb/WebGL/WebGLObject.h b/Libraries/LibWeb/WebGL/WebGLObject.h index 1fc15f74a8..b3524d8684 100644 --- a/Libraries/LibWeb/WebGL/WebGLObject.h +++ b/Libraries/LibWeb/WebGL/WebGLObject.h @@ -44,6 +44,7 @@ protected: private: GLuint m_handle { 0 }; + u64 m_context_generation { 0 }; bool m_invalidated { false }; String m_label; diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp b/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp index 4cc8d1b9bd..e3cd041ecf 100644 --- a/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContext.cpp @@ -32,6 +32,9 @@ namespace Web::WebGL { GC_DEFINE_ALLOCATOR(WebGLRenderingContext); +// https://www.khronos.org/registry/webgl/specs/latest/1.0/#fire-a-webgl-context-event +// Returns false if the event was canceled (the page called preventDefault), which is how +// webglcontextlost signals that the page wants the context restored. bool fire_webgl_context_event(HTML::HTMLCanvasElement& canvas_element, FlyString const& type) { // To fire a WebGL context event named e means that an event using the WebGLContextEvent interface, with its type attribute [DOM4] initialized to e, its cancelable attribute initialized to true, and its isTrusted attribute [DOM4] initialized to true, is to be dispatched at the given object. @@ -60,7 +63,14 @@ static Gfx::IntSize initial_drawing_buffer_size(HTML::HTMLCanvasElement& canvas_ }; } -OwnPtr create_webgl_context_proxy(HTML::HTMLCanvasElement& canvas_element, WebGLVersion webgl_version, WebGLContextAttributes const& context_attributes) +namespace { + +struct RemoteWebGLContext { + NonnullRefPtr transport; + RemoteWebGLTransport::CreateResult result; +}; + +Optional create_remote_webgl_context(HTML::HTMLCanvasElement& canvas_element, WebGLVersion webgl_version, WebGLContextAttributes const& context_attributes) { auto& page = canvas_element.document().page(); if (!page.has_compositor_host()) @@ -78,7 +88,28 @@ OwnPtr create_webgl_context_proxy(HTML::HTMLCanvasElement& ca if (!result.success) return {}; - return make(transport.release_nonnull(), webgl_version, move(result.supported_extensions)); + return RemoteWebGLContext { transport.release_nonnull(), move(result) }; +} + +} + +OwnPtr create_webgl_context_proxy(HTML::HTMLCanvasElement& canvas_element, WebGLVersion webgl_version, WebGLContextAttributes const& context_attributes) +{ + auto remote = create_remote_webgl_context(canvas_element, webgl_version, context_attributes); + if (!remote.has_value()) + return {}; + + return make(move(remote->transport), webgl_version, move(remote->result.supported_extensions)); +} + +bool restore_webgl_context_proxy(WebGLContextProxy& context, HTML::HTMLCanvasElement& canvas_element, WebGLVersion webgl_version, WebGLContextAttributes const& context_attributes) +{ + auto remote = create_remote_webgl_context(canvas_element, webgl_version, context_attributes); + if (!remote.has_value()) + return false; + + context.restore(move(remote->transport), move(remote->result.supported_extensions)); + return true; } JS::ThrowCompletionOr> WebGLRenderingContext::create(JS::Realm& realm, HTML::HTMLCanvasElement& canvas_element, JS::Value options) @@ -123,6 +154,11 @@ void WebGLRenderingContext::prepare_for_compositing() context().present_canvas_for_compositing(m_context_creation_parameters.preserve_drawing_buffer); } +bool WebGLRenderingContext::reestablish_remote_context() +{ + return restore_webgl_context_proxy(context(), *m_canvas_element, WebGLVersion::WebGL1, m_actual_context_parameters); +} + GC::Ref WebGLRenderingContext::canvas_for_binding() const { return *m_canvas_element; diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContext.h b/Libraries/LibWeb/WebGL/WebGLRenderingContext.h index 8b710efed9..73efadfcc9 100644 --- a/Libraries/LibWeb/WebGL/WebGLRenderingContext.h +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContext.h @@ -44,6 +44,7 @@ private: WebGLRenderingContext(JS::Realm&, HTML::HTMLCanvasElement&, NonnullOwnPtr context, WebGLContextAttributes context_creation_parameters, WebGLContextAttributes actual_context_parameters); virtual void visit_edges(Cell::Visitor&) override; + virtual bool reestablish_remote_context() override; GC::Ref m_canvas_element; @@ -60,5 +61,6 @@ bool fire_webgl_context_event(HTML::HTMLCanvasElement& canvas_element, FlyString void fire_webgl_context_creation_error(HTML::HTMLCanvasElement& canvas_element); OwnPtr create_webgl_context_proxy(HTML::HTMLCanvasElement&, WebGLVersion, WebGLContextAttributes const&); +bool restore_webgl_context_proxy(WebGLContextProxy&, HTML::HTMLCanvasElement&, WebGLVersion, WebGLContextAttributes const&); } diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp b/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp index 39f11aaa77..8ca11618c3 100644 --- a/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.cpp @@ -249,12 +249,19 @@ Optional WebGLRenderingContextBa }; } +// https://registry.khronos.org/webgl/specs/latest/1.0/#CONTEXT_LOST_WEBGL +static constexpr GLenum CONTEXT_LOST_WEBGL = 0x9242; + // TODO: The glGetError spec allows for queueing errors which is something we should probably do, for now // this just keeps track of one error which is also fine by the spec GLenum WebGLRenderingContextBase::get_error_value() { - // A locally-detected failure (currently an upload too large to send over IPC) is reported - // before consulting the host. + if (m_context_lost) { + auto error = m_error; + m_error = GL_NO_ERROR; + return error; + } + if (auto local_error = context().take_pending_local_error(); local_error != GL_NO_ERROR) return local_error; @@ -279,6 +286,52 @@ bool WebGLRenderingContextBase::is_context_lost() const return m_context_lost; } +void WebGLRenderingContextBase::lose_context_from_compositor_loss() +{ + if (m_context_lost) + return; + m_context_lost = true; + context().set_lost(); + + // The next getError() must report CONTEXT_LOST_WEBGL (one-shot) per the spec. + m_error = CONTEXT_LOST_WEBGL; + + HTML::queue_a_task(HTML::Task::Source::WebGL, nullptr, nullptr, GC::create_function(heap(), [this, canvas = canvas_for_binding()] { + // webglcontextlost is cancelable; preventDefault() means the page wants the context + // restored once a compositor is available again. + m_context_restore_requested = !fire_webgl_context_event(canvas, EventNames::webglcontextlost); + })); +} + +void WebGLRenderingContextBase::restore_context_after_compositor_reconnect() +{ + if (!m_context_lost || !m_context_restore_requested) + return; + + // A fresh host context starts with no GL objects; per the spec the page re-creates them + // in its webglcontextrestored handler. + if (!reestablish_remote_context()) + return; + + reset_context_state_after_loss(); + m_context_lost = false; + m_context_restore_requested = false; + m_error = GL_NO_ERROR; + + HTML::queue_a_task(HTML::Task::Source::WebGL, nullptr, nullptr, GC::create_function(heap(), [canvas = canvas_for_binding()] { + fire_webgl_context_event(canvas, EventNames::webglcontextrestored); + })); +} + +void WebGLRenderingContextBase::reset_context_state_after_loss() +{ + ++m_context_generation; + m_unpack_flip_y = false; + m_unpack_premultiply_alpha = false; + m_unpack_colorspace_conversion = BROWSER_DEFAULT_WEBGL; + reset_client_side_webgl_state(); +} + // https://immersive-web.github.io/webxr/#dom-webglrenderingcontextbase-makexrcompatible GC::Ref WebGLRenderingContextBase::make_xr_compatible() { diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.h b/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.h index ac5f3c3ab4..3151c3360c 100644 --- a/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.h +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContextBase.h @@ -48,8 +48,20 @@ public: virtual WebGLContextProxy& context() = 0; virtual GC::Ref canvas_for_binding() const = 0; + u64 context_generation() const { return m_context_generation; } + bool is_context_lost() const; + // https://registry.khronos.org/webgl/specs/latest/1.0/#CONTEXT_LOST + // The compositor process (and with it the GL state) went away: set the context lost + // flag, stop talking to the dead host, and fire webglcontextlost at the canvas. + void lose_context_from_compositor_loss(); + + // The compositor came back. If the page asked for restoration (called preventDefault on + // webglcontextlost), build a fresh remote context and fire webglcontextrestored so the + // page can re-create its now-lost GL resources. + void restore_context_after_compositor_reconnect(); + bool xr_compatible() const { return m_xr_compatible; } void set_xr_compatible(bool xr_compatible) { m_xr_compatible = xr_compatible; } @@ -66,6 +78,12 @@ protected: virtual void visit_edges(Cell::Visitor&) override; + // Builds a fresh transport and host context against the reconnected compositor and + // rebinds the proxy to it. Returns false if no compositor is available. WebGL1/2 + // implement it because they own the version and the actual context parameters. + virtual bool reestablish_remote_context() = 0; + virtual void reset_client_side_webgl_state() = 0; + // FIXME: Make this and any another instance of extension names a FlyString, similarly to HTML::TagNames bool extension_enabled(StringView extension) const; ReadonlySpan enabled_compressed_texture_formats() const; @@ -153,6 +171,7 @@ protected: GLenum get_error_value(); void set_error(GLenum error); + void reset_context_state_after_loss(); // UNPACK_FLIP_Y_WEBGL of type boolean // If set, then during any subsequent calls to texImage2D or texSubImage2D, the source data is flipped along @@ -181,9 +200,11 @@ private: // https://registry.khronos.org/webgl/specs/latest/2.0/#webgl-context-lost-flag // Each WebGLRenderingContext and WebGL2RenderingContext has a webgl context lost flag, which is initially unset. bool m_context_lost { false }; + bool m_context_restore_requested { false }; // https://immersive-web.github.io/webxr/#xr-compatible bool m_xr_compatible { false }; + u64 m_context_generation { 0 }; Vector m_enabled_compressed_texture_formats; diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.cpp b/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.cpp index 6a5cdd2145..c45bbfe3b7 100644 --- a/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.cpp +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.cpp @@ -46,6 +46,30 @@ WebGLRenderingContextImpl::WebGLRenderingContextImpl(JS::Realm& realm, NonnullOw { } +void WebGLRenderingContextImpl::reset_client_side_webgl_state() +{ + m_array_buffer_binding = nullptr; + m_element_array_buffer_binding = nullptr; + m_current_program = nullptr; + m_framebuffer_binding = nullptr; + m_renderbuffer_binding = nullptr; + m_texture_binding_2d = nullptr; + m_texture_binding_cube_map = nullptr; + m_uniform_buffer_binding = nullptr; + m_copy_read_buffer_binding = nullptr; + m_copy_write_buffer_binding = nullptr; + m_transform_feedback_buffer_binding = nullptr; + m_pixel_pack_buffer_binding = nullptr; + m_pixel_unpack_buffer_binding = nullptr; + m_texture_binding_2d_array = nullptr; + m_texture_binding_3d = nullptr; + m_transform_feedback_binding = nullptr; + m_current_vertex_array = nullptr; + m_any_samples_passed = nullptr; + m_any_samples_passed_conservative = nullptr; + m_transform_feedback_primitives_written = nullptr; +} + void WebGLRenderingContextImpl::active_texture(WebIDL::UnsignedLong texture) { m_context->make_current(); diff --git a/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.h b/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.h index 4c27e0e704..3609845579 100644 --- a/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.h +++ b/Libraries/LibWeb/WebGL/WebGLRenderingContextImpl.h @@ -147,6 +147,7 @@ public: protected: virtual void visit_edges(JS::Cell::Visitor&) override; + virtual void reset_client_side_webgl_state() override; GC::Ptr m_array_buffer_binding; GC::Ptr m_element_array_buffer_binding; diff --git a/Services/WebContent/CompositorConnection.cpp b/Services/WebContent/CompositorConnection.cpp index 0074275230..2e88f86fd6 100644 --- a/Services/WebContent/CompositorConnection.cpp +++ b/Services/WebContent/CompositorConnection.cpp @@ -308,6 +308,9 @@ void CompositorConnection::did_lose_compositor() entry.value.callback(); } m_screenshots.clear(); + + if (on_compositor_lost) + on_compositor_lost(); } bool CompositorConnection::can_send_message_to_compositor() const diff --git a/Services/WebContent/CompositorConnection.h b/Services/WebContent/CompositorConnection.h index 22819eb4a9..298c3d8bb9 100644 --- a/Services/WebContent/CompositorConnection.h +++ b/Services/WebContent/CompositorConnection.h @@ -67,6 +67,7 @@ public: void read_webgl_buffer_sub_data(Web::Painting::CanvasId, Web::WebGL::GLenum target, Web::WebGL::GLintptr offset, Web::WebGL::GLintptr size, Core::AnonymousBuffer const& data); Function on_mouse_event; + Function on_compositor_lost; private: struct PendingScreenshot { diff --git a/Services/WebContent/ConnectionFromClient.cpp b/Services/WebContent/ConnectionFromClient.cpp index 24e5b2183f..baf16655a1 100644 --- a/Services/WebContent/ConnectionFromClient.cpp +++ b/Services/WebContent/ConnectionFromClient.cpp @@ -201,6 +201,9 @@ void ConnectionFromClient::connect_to_compositor_process(IPC::TransportHandle ha m_compositor_connection->on_mouse_event = [this](u64 page_id, Web::MouseEvent event) { mouse_event(page_id, move(event)); }; + m_compositor_connection->on_compositor_lost = [this] { + m_page_host->compositor_process_lost(); + }; #ifdef AK_OS_WINDOWS // Perform Windows peer PID handshake before any other IPC diff --git a/Services/WebContent/PageClient.cpp b/Services/WebContent/PageClient.cpp index c91e507247..798847c0ba 100644 --- a/Services/WebContent/PageClient.cpp +++ b/Services/WebContent/PageClient.cpp @@ -250,9 +250,15 @@ void PageClient::set_window_size(Web::DevicePixelSize size) page().set_window_size(size); } +void PageClient::compositor_process_lost() +{ + page().notify_all_webgl_contexts_lost(); +} + void PageClient::compositor_process_reconnected() { page().top_level_traversable()->repaint_after_compositor_process_reconnect(); + page().notify_all_canvas_elements_of_lost_backing_storage(); page().prepare_canvas_contexts_for_compositing(); page().update_all_media_element_video_sinks(); Web::HTML::main_thread_event_loop().queue_task_to_update_the_rendering(); diff --git a/Services/WebContent/PageClient.h b/Services/WebContent/PageClient.h index 2e2bf5f23f..c1bd0393d4 100644 --- a/Services/WebContent/PageClient.h +++ b/Services/WebContent/PageClient.h @@ -90,6 +90,7 @@ public: void set_window_position(Web::DevicePixelPoint); void set_window_size(Web::DevicePixelSize); void compositor_process_reconnected(); + void compositor_process_lost(); void toggle_media_play_state(); void toggle_media_mute_state(); diff --git a/Services/WebContent/PageHost.cpp b/Services/WebContent/PageHost.cpp index 34abe982b0..0fb0d8570e 100644 --- a/Services/WebContent/PageHost.cpp +++ b/Services/WebContent/PageHost.cpp @@ -64,4 +64,10 @@ void PageHost::compositor_process_reconnected() page->compositor_process_reconnected(); } +void PageHost::compositor_process_lost() +{ + for (auto& [_, page] : m_pages) + page->compositor_process_lost(); +} + } diff --git a/Services/WebContent/PageHost.h b/Services/WebContent/PageHost.h index 16fd2a6c5b..89a8d4f0a2 100644 --- a/Services/WebContent/PageHost.h +++ b/Services/WebContent/PageHost.h @@ -43,6 +43,7 @@ public: ConnectionFromClient& client() const { return m_client; } void ensure_compositor_host(); void compositor_process_reconnected(); + void compositor_process_lost(); Web::Compositor::CompositorHost* compositor_host() { return m_compositor_host.ptr(); } Web::Compositor::CompositorHost const* compositor_host() const { return m_compositor_host.ptr(); }