canvas: Send CanvasClose to canvas thread from canvas state (#38315)

Currently we only closed `CanvasRenderingContext2D` (and
`OffscreenCanvasRenderingContext2D` because it wraps
`CanvasRenderingContext2D`), but we didn't close last consumer of
`CanvasState` which is `PaintRenderingContext`. To prevent any future
leaks, let's just send `CanvasClose` in `CanvasState` drop.

Testing: Existing WPT tests

---------

Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
sagudev 2025-07-28 22:09:47 +02:00 committed by GitHub
parent 213b532712
commit d410236c87
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 10 additions and 29 deletions

View file

@ -259,10 +259,6 @@ impl CanvasState {
}) })
} }
pub(crate) fn get_ipc_renderer(&self) -> &IpcSender<CanvasMsg> {
&self.ipc_renderer
}
pub(crate) fn image_key(&self) -> ImageKey { pub(crate) fn image_key(&self) -> ImageKey {
self.image_key self.image_key
} }
@ -2197,6 +2193,14 @@ impl CanvasState {
} }
} }
impl Drop for CanvasState {
fn drop(&mut self) {
if let Err(err) = self.ipc_renderer.send(CanvasMsg::Close(self.canvas_id)) {
warn!("Could not close canvas: {}", err)
}
}
}
pub(crate) fn parse_color( pub(crate) fn parse_color(
canvas: Option<&HTMLCanvasElement>, canvas: Option<&HTMLCanvasElement>,
string: &str, string: &str,

View file

@ -2,10 +2,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use canvas_traits::canvas::{Canvas2dMsg, CanvasId, CanvasMsg}; use canvas_traits::canvas::{Canvas2dMsg, CanvasId};
use dom_struct::dom_struct; use dom_struct::dom_struct;
use euclid::default::Size2D; use euclid::default::Size2D;
use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::ipc;
use pixels::Snapshot; use pixels::Snapshot;
use script_bindings::inheritance::Castable; use script_bindings::inheritance::Castable;
use servo_url::ServoUrl; use servo_url::ServoUrl;
@ -37,29 +37,12 @@ use crate::dom::path2d::Path2D;
use crate::dom::textmetrics::TextMetrics; use crate::dom::textmetrics::TextMetrics;
use crate::script_runtime::CanGc; use crate::script_runtime::CanGc;
#[derive(JSTraceable, MallocSizeOf)]
struct DroppableCanvasRenderingContext2D {
#[no_trace]
ipc_sender: IpcSender<CanvasMsg>,
#[no_trace]
canvas_id: CanvasId,
}
impl Drop for DroppableCanvasRenderingContext2D {
fn drop(&mut self) {
if let Err(err) = self.ipc_sender.send(CanvasMsg::Close(self.canvas_id)) {
warn!("Could not close canvas: {}", err)
}
}
}
// https://html.spec.whatwg.org/multipage/#canvasrenderingcontext2d // https://html.spec.whatwg.org/multipage/#canvasrenderingcontext2d
#[dom_struct] #[dom_struct]
pub(crate) struct CanvasRenderingContext2D { pub(crate) struct CanvasRenderingContext2D {
reflector_: Reflector, reflector_: Reflector,
canvas: HTMLCanvasElementOrOffscreenCanvas, canvas: HTMLCanvasElementOrOffscreenCanvas,
canvas_state: CanvasState, canvas_state: CanvasState,
droppable: DroppableCanvasRenderingContext2D,
} }
impl CanvasRenderingContext2D { impl CanvasRenderingContext2D {
@ -71,16 +54,10 @@ impl CanvasRenderingContext2D {
) -> Option<CanvasRenderingContext2D> { ) -> Option<CanvasRenderingContext2D> {
let canvas_state = let canvas_state =
CanvasState::new(global, Size2D::new(size.width as u64, size.height as u64))?; CanvasState::new(global, Size2D::new(size.width as u64, size.height as u64))?;
let ipc_sender = canvas_state.get_ipc_renderer().clone();
let canvas_id = canvas_state.get_canvas_id();
Some(CanvasRenderingContext2D { Some(CanvasRenderingContext2D {
reflector_: Reflector::new(), reflector_: Reflector::new(),
canvas, canvas,
canvas_state, canvas_state,
droppable: DroppableCanvasRenderingContext2D {
ipc_sender,
canvas_id,
},
}) })
} }