mirror of
https://github.com/servo/servo.git
synced 2025-08-02 20:20:14 +01:00
canvas: Make 2D context state creation failable and use dom_canvas_backend
pref for backend selection (#38310)
Before script just crashed in those cases because IPCSender was dropped, now we send `None` to tell script about the failure and fail getContext or registerPainter accordingly. This PR also unifies `dom_canvas_{backends}_enabled` prefs into `dom_canvas_backend` which is more flexible in multi-backends scenarios. Reviewable per commit. Testing: Added servo specific WPT test. --------- Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
parent
93d234d270
commit
ae69646371
17 changed files with 116 additions and 61 deletions
|
@ -85,8 +85,7 @@ impl CanvasPaintThread {
|
||||||
recv(create_receiver) -> msg => {
|
recv(create_receiver) -> msg => {
|
||||||
match msg {
|
match msg {
|
||||||
Ok(ConstellationCanvasMsg::Create { sender: creator, size }) => {
|
Ok(ConstellationCanvasMsg::Create { sender: creator, size }) => {
|
||||||
let canvas_data = canvas_paint_thread.create_canvas(size);
|
creator.send(canvas_paint_thread.create_canvas(size)).unwrap();
|
||||||
creator.send(canvas_data).unwrap();
|
|
||||||
},
|
},
|
||||||
Ok(ConstellationCanvasMsg::Exit(exit_sender)) => {
|
Ok(ConstellationCanvasMsg::Exit(exit_sender)) => {
|
||||||
let _ = exit_sender.send(());
|
let _ = exit_sender.send(());
|
||||||
|
@ -106,15 +105,15 @@ impl CanvasPaintThread {
|
||||||
(create_sender, ipc_sender)
|
(create_sender, ipc_sender)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_canvas(&mut self, size: Size2D<u64>) -> (CanvasId, ImageKey) {
|
pub fn create_canvas(&mut self, size: Size2D<u64>) -> Option<(CanvasId, ImageKey)> {
|
||||||
let canvas_id = self.next_canvas_id;
|
let canvas_id = self.next_canvas_id;
|
||||||
self.next_canvas_id.0 += 1;
|
self.next_canvas_id.0 += 1;
|
||||||
|
|
||||||
let canvas = Canvas::new(size, self.compositor_api.clone(), self.font_context.clone());
|
let canvas = Canvas::new(size, self.compositor_api.clone(), self.font_context.clone())?;
|
||||||
let image_key = canvas.image_key();
|
let image_key = canvas.image_key();
|
||||||
self.canvases.insert(canvas_id, canvas);
|
self.canvases.insert(canvas_id, canvas);
|
||||||
|
|
||||||
(canvas_id, image_key)
|
Some((canvas_id, image_key))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_canvas_2d_message(&mut self, message: Canvas2dMsg, canvas_id: CanvasId) {
|
fn process_canvas_2d_message(&mut self, message: Canvas2dMsg, canvas_id: CanvasId) {
|
||||||
|
@ -305,16 +304,33 @@ impl Canvas {
|
||||||
size: Size2D<u64>,
|
size: Size2D<u64>,
|
||||||
compositor_api: CrossProcessCompositorApi,
|
compositor_api: CrossProcessCompositorApi,
|
||||||
font_context: Arc<FontContext>,
|
font_context: Arc<FontContext>,
|
||||||
) -> Self {
|
) -> Option<Self> {
|
||||||
#[cfg(feature = "vello")]
|
match servo_config::pref!(dom_canvas_backend)
|
||||||
if servo_config::pref!(dom_canvas_vello_enabled) {
|
.to_lowercase()
|
||||||
return Self::Vello(CanvasData::new(size, compositor_api, font_context));
|
.as_str()
|
||||||
|
{
|
||||||
|
#[cfg(feature = "vello")]
|
||||||
|
"vello" => Some(Self::Vello(CanvasData::new(
|
||||||
|
size,
|
||||||
|
compositor_api,
|
||||||
|
font_context,
|
||||||
|
))),
|
||||||
|
#[cfg(feature = "vello_cpu")]
|
||||||
|
"vello_cpu" => Some(Self::VelloCPU(CanvasData::new(
|
||||||
|
size,
|
||||||
|
compositor_api,
|
||||||
|
font_context,
|
||||||
|
))),
|
||||||
|
"" | "auto" | "raqote" => Some(Self::Raqote(CanvasData::new(
|
||||||
|
size,
|
||||||
|
compositor_api,
|
||||||
|
font_context,
|
||||||
|
))),
|
||||||
|
s => {
|
||||||
|
warn!("Unknown 2D canvas backend: `{s}`");
|
||||||
|
None
|
||||||
|
},
|
||||||
}
|
}
|
||||||
#[cfg(feature = "vello_cpu")]
|
|
||||||
if servo_config::pref!(dom_canvas_vello_cpu_enabled) {
|
|
||||||
return Self::VelloCPU(CanvasData::new(size, compositor_api, font_context));
|
|
||||||
}
|
|
||||||
Self::Raqote(CanvasData::new(size, compositor_api, font_context))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn image_key(&self) -> ImageKey {
|
fn image_key(&self) -> ImageKey {
|
||||||
|
|
|
@ -76,10 +76,14 @@ pub struct Preferences {
|
||||||
pub dom_allow_scripts_to_close_windows: bool,
|
pub dom_allow_scripts_to_close_windows: bool,
|
||||||
pub dom_canvas_capture_enabled: bool,
|
pub dom_canvas_capture_enabled: bool,
|
||||||
pub dom_canvas_text_enabled: bool,
|
pub dom_canvas_text_enabled: bool,
|
||||||
/// Uses vello as canvas backend
|
/// Selects canvas backend
|
||||||
pub dom_canvas_vello_enabled: bool,
|
///
|
||||||
/// Uses vello_cpu as canvas backend
|
/// Available values:
|
||||||
pub dom_canvas_vello_cpu_enabled: bool,
|
/// - ` `/`auto`
|
||||||
|
/// - raqote
|
||||||
|
/// - vello
|
||||||
|
/// - vello_cpu
|
||||||
|
pub dom_canvas_backend: String,
|
||||||
pub dom_clipboardevent_enabled: bool,
|
pub dom_clipboardevent_enabled: bool,
|
||||||
pub dom_composition_event_enabled: bool,
|
pub dom_composition_event_enabled: bool,
|
||||||
pub dom_cookiestore_enabled: bool,
|
pub dom_cookiestore_enabled: bool,
|
||||||
|
@ -259,8 +263,7 @@ impl Preferences {
|
||||||
dom_bluetooth_testing_enabled: false,
|
dom_bluetooth_testing_enabled: false,
|
||||||
dom_canvas_capture_enabled: false,
|
dom_canvas_capture_enabled: false,
|
||||||
dom_canvas_text_enabled: true,
|
dom_canvas_text_enabled: true,
|
||||||
dom_canvas_vello_enabled: false,
|
dom_canvas_backend: String::new(),
|
||||||
dom_canvas_vello_cpu_enabled: false,
|
|
||||||
dom_clipboardevent_enabled: true,
|
dom_clipboardevent_enabled: true,
|
||||||
dom_composition_event_enabled: false,
|
dom_composition_event_enabled: false,
|
||||||
dom_cookiestore_enabled: false,
|
dom_cookiestore_enabled: false,
|
||||||
|
|
|
@ -4368,24 +4368,32 @@ where
|
||||||
fn handle_create_canvas_paint_thread_msg(
|
fn handle_create_canvas_paint_thread_msg(
|
||||||
&mut self,
|
&mut self,
|
||||||
size: UntypedSize2D<u64>,
|
size: UntypedSize2D<u64>,
|
||||||
response_sender: IpcSender<(IpcSender<CanvasMsg>, CanvasId, ImageKey)>,
|
response_sender: IpcSender<Option<(IpcSender<CanvasMsg>, CanvasId, ImageKey)>>,
|
||||||
) {
|
) {
|
||||||
let (canvas_data_sender, canvas_data_receiver) = unbounded();
|
let (canvas_data_sender, canvas_data_receiver) = unbounded();
|
||||||
let (canvas_sender, canvas_ipc_sender) = self
|
let (canvas_sender, canvas_ipc_sender) = self
|
||||||
.canvas
|
.canvas
|
||||||
.get_or_init(|| self.create_canvas_paint_thread());
|
.get_or_init(|| self.create_canvas_paint_thread());
|
||||||
|
|
||||||
if let Err(e) = canvas_sender.send(ConstellationCanvasMsg::Create {
|
let response = if let Err(e) = canvas_sender.send(ConstellationCanvasMsg::Create {
|
||||||
sender: canvas_data_sender,
|
sender: canvas_data_sender,
|
||||||
size,
|
size,
|
||||||
}) {
|
}) {
|
||||||
return warn!("Create canvas paint thread failed ({})", e);
|
warn!("Create canvas paint thread failed ({})", e);
|
||||||
}
|
None
|
||||||
let (canvas_id, image_key) = match canvas_data_receiver.recv() {
|
} else {
|
||||||
Ok(canvas_data) => canvas_data,
|
match canvas_data_receiver.recv() {
|
||||||
Err(e) => return warn!("Create canvas paint thread id response failed ({})", e),
|
Ok(Some((canvas_id, image_key))) => {
|
||||||
|
Some((canvas_ipc_sender.clone(), canvas_id, image_key))
|
||||||
|
},
|
||||||
|
Ok(None) => None,
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Create canvas paint thread id response failed ({})", e);
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if let Err(e) = response_sender.send((canvas_ipc_sender.clone(), canvas_id, image_key)) {
|
if let Err(e) = response_sender.send(response) {
|
||||||
warn!("Create canvas paint thread response failed ({})", e);
|
warn!("Create canvas paint thread response failed ({})", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,7 +221,7 @@ pub(crate) struct CanvasState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CanvasState {
|
impl CanvasState {
|
||||||
pub(crate) fn new(global: &GlobalScope, size: Size2D<u64>) -> CanvasState {
|
pub(crate) fn new(global: &GlobalScope, size: Size2D<u64>) -> Option<CanvasState> {
|
||||||
debug!("Creating new canvas rendering context.");
|
debug!("Creating new canvas rendering context.");
|
||||||
let (sender, receiver) =
|
let (sender, receiver) =
|
||||||
profiled_ipc::channel(global.time_profiler_chan().clone()).unwrap();
|
profiled_ipc::channel(global.time_profiler_chan().clone()).unwrap();
|
||||||
|
@ -233,7 +233,7 @@ impl CanvasState {
|
||||||
size, sender,
|
size, sender,
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let (ipc_renderer, canvas_id, image_key) = receiver.recv().unwrap();
|
let (ipc_renderer, canvas_id, image_key) = receiver.recv().ok()??;
|
||||||
debug!("Done.");
|
debug!("Done.");
|
||||||
// Worklets always receive a unique origin. This messes with fetching
|
// Worklets always receive a unique origin. This messes with fetching
|
||||||
// cached images in the case of paint worklets, since the image cache
|
// cached images in the case of paint worklets, since the image cache
|
||||||
|
@ -243,7 +243,7 @@ impl CanvasState {
|
||||||
} else {
|
} else {
|
||||||
global.origin().immutable().clone()
|
global.origin().immutable().clone()
|
||||||
};
|
};
|
||||||
CanvasState {
|
Some(CanvasState {
|
||||||
ipc_renderer,
|
ipc_renderer,
|
||||||
canvas_id,
|
canvas_id,
|
||||||
size: Cell::new(size),
|
size: Cell::new(size),
|
||||||
|
@ -256,7 +256,7 @@ impl CanvasState {
|
||||||
image_key,
|
image_key,
|
||||||
origin,
|
origin,
|
||||||
current_default_path: DomRefCell::new(Path::new()),
|
current_default_path: DomRefCell::new(Path::new()),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_ipc_renderer(&self) -> &IpcSender<CanvasMsg> {
|
pub(crate) fn get_ipc_renderer(&self) -> &IpcSender<CanvasMsg> {
|
||||||
|
|
|
@ -68,12 +68,12 @@ impl CanvasRenderingContext2D {
|
||||||
global: &GlobalScope,
|
global: &GlobalScope,
|
||||||
canvas: HTMLCanvasElementOrOffscreenCanvas,
|
canvas: HTMLCanvasElementOrOffscreenCanvas,
|
||||||
size: Size2D<u32>,
|
size: Size2D<u32>,
|
||||||
) -> 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 ipc_sender = canvas_state.get_ipc_renderer().clone();
|
||||||
let canvas_id = canvas_state.get_canvas_id();
|
let canvas_id = canvas_state.get_canvas_id();
|
||||||
CanvasRenderingContext2D {
|
Some(CanvasRenderingContext2D {
|
||||||
reflector_: Reflector::new(),
|
reflector_: Reflector::new(),
|
||||||
canvas,
|
canvas,
|
||||||
canvas_state,
|
canvas_state,
|
||||||
|
@ -81,21 +81,22 @@ impl CanvasRenderingContext2D {
|
||||||
ipc_sender,
|
ipc_sender,
|
||||||
canvas_id,
|
canvas_id,
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
global: &GlobalScope,
|
global: &GlobalScope,
|
||||||
canvas: &HTMLCanvasElement,
|
canvas: &HTMLCanvasElement,
|
||||||
size: Size2D<u32>,
|
size: Size2D<u32>,
|
||||||
can_gc: CanGc,
|
can_gc: CanGc,
|
||||||
) -> DomRoot<CanvasRenderingContext2D> {
|
) -> Option<DomRoot<CanvasRenderingContext2D>> {
|
||||||
let boxed = Box::new(CanvasRenderingContext2D::new_inherited(
|
let boxed = Box::new(CanvasRenderingContext2D::new_inherited(
|
||||||
global,
|
global,
|
||||||
HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(canvas)),
|
HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(canvas)),
|
||||||
size,
|
size,
|
||||||
));
|
)?);
|
||||||
reflect_dom_object(boxed, global, can_gc)
|
Some(reflect_dom_object(boxed, global, can_gc))
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#reset-the-rendering-context-to-its-default-state
|
// https://html.spec.whatwg.org/multipage/#reset-the-rendering-context-to-its-default-state
|
||||||
|
|
|
@ -205,7 +205,7 @@ impl HTMLCanvasElement {
|
||||||
|
|
||||||
let window = self.owner_window();
|
let window = self.owner_window();
|
||||||
let size = self.get_size();
|
let size = self.get_size();
|
||||||
let context = CanvasRenderingContext2D::new(window.as_global_scope(), self, size, can_gc);
|
let context = CanvasRenderingContext2D::new(window.as_global_scope(), self, size, can_gc)?;
|
||||||
*self.context_mode.borrow_mut() =
|
*self.context_mode.borrow_mut() =
|
||||||
Some(RenderingContext::Context2d(Dom::from_ref(&*context)));
|
Some(RenderingContext::Context2d(Dom::from_ref(&*context)));
|
||||||
Some(context)
|
Some(context)
|
||||||
|
|
|
@ -135,7 +135,7 @@ impl OffscreenCanvas {
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let context = OffscreenCanvasRenderingContext2D::new(&self.global(), self, can_gc);
|
let context = OffscreenCanvasRenderingContext2D::new(&self.global(), self, can_gc)?;
|
||||||
*self.context.borrow_mut() = Some(OffscreenRenderingContext::Context2d(Dom::from_ref(
|
*self.context.borrow_mut() = Some(OffscreenRenderingContext::Context2d(Dom::from_ref(
|
||||||
&*context,
|
&*context,
|
||||||
)));
|
)));
|
||||||
|
|
|
@ -39,29 +39,31 @@ pub(crate) struct OffscreenCanvasRenderingContext2D {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OffscreenCanvasRenderingContext2D {
|
impl OffscreenCanvasRenderingContext2D {
|
||||||
|
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
||||||
fn new_inherited(
|
fn new_inherited(
|
||||||
global: &GlobalScope,
|
global: &GlobalScope,
|
||||||
canvas: &OffscreenCanvas,
|
canvas: &OffscreenCanvas,
|
||||||
) -> OffscreenCanvasRenderingContext2D {
|
) -> Option<OffscreenCanvasRenderingContext2D> {
|
||||||
let size = canvas.get_size().cast();
|
let size = canvas.get_size().cast();
|
||||||
OffscreenCanvasRenderingContext2D {
|
Some(OffscreenCanvasRenderingContext2D {
|
||||||
context: CanvasRenderingContext2D::new_inherited(
|
context: CanvasRenderingContext2D::new_inherited(
|
||||||
global,
|
global,
|
||||||
HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(DomRoot::from_ref(canvas)),
|
HTMLCanvasElementOrOffscreenCanvas::OffscreenCanvas(DomRoot::from_ref(canvas)),
|
||||||
size,
|
size,
|
||||||
),
|
)?,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
global: &GlobalScope,
|
global: &GlobalScope,
|
||||||
canvas: &OffscreenCanvas,
|
canvas: &OffscreenCanvas,
|
||||||
can_gc: CanGc,
|
can_gc: CanGc,
|
||||||
) -> DomRoot<OffscreenCanvasRenderingContext2D> {
|
) -> Option<DomRoot<OffscreenCanvasRenderingContext2D>> {
|
||||||
let boxed = Box::new(OffscreenCanvasRenderingContext2D::new_inherited(
|
let boxed = Box::new(OffscreenCanvasRenderingContext2D::new_inherited(
|
||||||
global, canvas,
|
global, canvas,
|
||||||
));
|
)?);
|
||||||
reflect_dom_object(boxed, global, can_gc)
|
Some(reflect_dom_object(boxed, global, can_gc))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) {
|
pub(crate) fn send_canvas_2d_msg(&self, msg: Canvas2dMsg) {
|
||||||
|
|
|
@ -42,23 +42,25 @@ pub(crate) struct PaintRenderingContext2D {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PaintRenderingContext2D {
|
impl PaintRenderingContext2D {
|
||||||
fn new_inherited(global: &PaintWorkletGlobalScope) -> PaintRenderingContext2D {
|
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
||||||
PaintRenderingContext2D {
|
fn new_inherited(global: &PaintWorkletGlobalScope) -> Option<PaintRenderingContext2D> {
|
||||||
|
Some(PaintRenderingContext2D {
|
||||||
reflector_: Reflector::new(),
|
reflector_: Reflector::new(),
|
||||||
canvas_state: CanvasState::new(global.upcast(), Size2D::zero()),
|
canvas_state: CanvasState::new(global.upcast(), Size2D::zero())?,
|
||||||
device_pixel_ratio: Cell::new(Scale::new(1.0)),
|
device_pixel_ratio: Cell::new(Scale::new(1.0)),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
global: &PaintWorkletGlobalScope,
|
global: &PaintWorkletGlobalScope,
|
||||||
can_gc: CanGc,
|
can_gc: CanGc,
|
||||||
) -> DomRoot<PaintRenderingContext2D> {
|
) -> Option<DomRoot<PaintRenderingContext2D>> {
|
||||||
reflect_dom_object(
|
Some(reflect_dom_object(
|
||||||
Box::new(PaintRenderingContext2D::new_inherited(global)),
|
Box::new(PaintRenderingContext2D::new_inherited(global)?),
|
||||||
global,
|
global,
|
||||||
can_gc,
|
can_gc,
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send update to canvas paint thread and returns [`ImageKey`]
|
/// Send update to canvas paint thread and returns [`ImageKey`]
|
||||||
|
|
|
@ -563,7 +563,9 @@ impl PaintWorkletGlobalScopeMethods<crate::DomTypeHolder> for PaintWorkletGlobal
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 19.
|
// Step 19.
|
||||||
let context = PaintRenderingContext2D::new(self, CanGc::note());
|
let Some(context) = PaintRenderingContext2D::new(self, CanGc::note()) else {
|
||||||
|
return Err(Error::Operation);
|
||||||
|
};
|
||||||
let definition = PaintDefinition::new(
|
let definition = PaintDefinition::new(
|
||||||
paint_val.handle(),
|
paint_val.handle(),
|
||||||
paint_function.handle(),
|
paint_function.handle(),
|
||||||
|
|
|
@ -18,7 +18,7 @@ pub mod webgl;
|
||||||
|
|
||||||
pub enum ConstellationCanvasMsg {
|
pub enum ConstellationCanvasMsg {
|
||||||
Create {
|
Create {
|
||||||
sender: Sender<(CanvasId, ImageKey)>,
|
sender: Sender<Option<(CanvasId, ImageKey)>>,
|
||||||
size: Size2D<u64>,
|
size: Size2D<u64>,
|
||||||
},
|
},
|
||||||
Exit(Sender<()>),
|
Exit(Sender<()>),
|
||||||
|
|
|
@ -535,7 +535,7 @@ pub enum ScriptToConstellationMessage {
|
||||||
/// 2D canvases may use the GPU and we don't want to give untrusted content access to the GPU.)
|
/// 2D canvases may use the GPU and we don't want to give untrusted content access to the GPU.)
|
||||||
CreateCanvasPaintThread(
|
CreateCanvasPaintThread(
|
||||||
UntypedSize2D<u64>,
|
UntypedSize2D<u64>,
|
||||||
IpcSender<(IpcSender<CanvasMsg>, CanvasId, ImageKey)>,
|
IpcSender<Option<(IpcSender<CanvasMsg>, CanvasId, ImageKey)>>,
|
||||||
),
|
),
|
||||||
/// Notifies the constellation that this pipeline is requesting focus.
|
/// Notifies the constellation that this pipeline is requesting focus.
|
||||||
///
|
///
|
||||||
|
|
7
tests/wpt/mozilla/meta/MANIFEST.json
vendored
7
tests/wpt/mozilla/meta/MANIFEST.json
vendored
|
@ -12970,6 +12970,13 @@
|
||||||
null,
|
null,
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
|
],
|
||||||
|
"invalid.html": [
|
||||||
|
"d50ff1030c48794dfc92083b6fecde3f97524b7e",
|
||||||
|
[
|
||||||
|
null,
|
||||||
|
{}
|
||||||
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"canvas-oversize-serialization.html": [
|
"canvas-oversize-serialization.html": [
|
||||||
|
|
3
tests/wpt/mozilla/meta/mozilla/canvas/invalid.html.ini
vendored
Normal file
3
tests/wpt/mozilla/meta/mozilla/canvas/invalid.html.ini
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[invalid.html]
|
||||||
|
prefs: ["dom_canvas_backend:none"]
|
||||||
|
|
11
tests/wpt/mozilla/tests/mozilla/canvas/invalid.html
vendored
Normal file
11
tests/wpt/mozilla/tests/mozilla/canvas/invalid.html
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>empty canvas context with invalid backend pref</title>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<canvas id="c">
|
||||||
|
<script>
|
||||||
|
test(function() {
|
||||||
|
assert_equals(document.getElementById('c').getContext('2d'), null);
|
||||||
|
}, "Invalid canvas backend returns null context");
|
||||||
|
</script>
|
2
tests/wpt/vello_canvas_subsuite.json
vendored
2
tests/wpt/vello_canvas_subsuite.json
vendored
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"vello_canvas": {
|
"vello_canvas": {
|
||||||
"config": {
|
"config": {
|
||||||
"binary_args": ["--pref", "dom_canvas_vello_enabled"]
|
"binary_args": ["--pref", "dom_canvas_backend=vello"]
|
||||||
},
|
},
|
||||||
"include": ["/html/canvas/element"]
|
"include": ["/html/canvas/element"]
|
||||||
}
|
}
|
||||||
|
|
2
tests/wpt/vello_cpu_canvas_subsuite.json
vendored
2
tests/wpt/vello_cpu_canvas_subsuite.json
vendored
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"vello_cpu_canvas": {
|
"vello_cpu_canvas": {
|
||||||
"config": {
|
"config": {
|
||||||
"binary_args": ["--pref", "dom_canvas_vello_cpu_enabled"]
|
"binary_args": ["--pref", "dom_canvas_backend=vello_cpu"]
|
||||||
},
|
},
|
||||||
"include": ["/html/canvas/element"]
|
"include": ["/html/canvas/element"]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue