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(); }