Skip to content

Commit 0a4edd3

Browse files
committed
Low-level error handling in canvas context creation (for WebGPU and WebGL 2).
Part of fixing #3017. This commit changes the handling of `web_sys` errors and nulls from `expect()` to returning `Err`, but it doesn't actually affect the public API — that will be done in the next commit.
1 parent d4b1d57 commit 0a4edd3

File tree

2 files changed

+80
-29
lines changed

2 files changed

+80
-29
lines changed

wgpu-hal/src/gles/web.rs

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,32 +33,53 @@ impl Instance {
3333
&self,
3434
canvas: &web_sys::HtmlCanvasElement,
3535
) -> Result<Surface, crate::InstanceError> {
36-
let webgl2_context = canvas
37-
.get_context_with_context_options("webgl2", &Self::create_context_options())
38-
.expect("Cannot create WebGL2 context")
39-
.and_then(|context| context.dyn_into::<web_sys::WebGl2RenderingContext>().ok())
40-
.expect("Cannot convert into WebGL2 context");
41-
42-
*self.webgl2_context.lock() = Some(webgl2_context.clone());
43-
44-
Ok(Surface {
45-
webgl2_context,
46-
srgb_present_program: None,
47-
swapchain: None,
48-
texture: None,
49-
presentable: true,
50-
})
36+
self.create_surface_from_context(
37+
canvas.get_context_with_context_options("webgl2", &Self::create_context_options()),
38+
)
5139
}
5240

5341
pub fn create_surface_from_offscreen_canvas(
5442
&self,
5543
canvas: &web_sys::OffscreenCanvas,
5644
) -> Result<Surface, crate::InstanceError> {
57-
let webgl2_context = canvas
58-
.get_context_with_context_options("webgl2", &Self::create_context_options())
59-
.expect("Cannot create WebGL2 context")
60-
.and_then(|context| context.dyn_into::<web_sys::WebGl2RenderingContext>().ok())
61-
.expect("Cannot convert into WebGL2 context");
45+
self.create_surface_from_context(
46+
canvas.get_context_with_context_options("webgl2", &Self::create_context_options()),
47+
)
48+
}
49+
50+
/// Common portion of public `create_surface_from_*` functions.
51+
///
52+
/// Note: Analogous code also exists in the WebGPU backend at
53+
/// `wgpu::backend::web::Context`.
54+
fn create_surface_from_context(
55+
&self,
56+
context_result: Result<Option<js_sys::Object>, wasm_bindgen::JsValue>,
57+
) -> Result<Surface, crate::InstanceError> {
58+
let context_object: js_sys::Object = match context_result {
59+
Ok(Some(context)) => context,
60+
Ok(None) => {
61+
// <https://html.spec.whatwg.org/multipage/canvas.html#dom-canvas-getcontext-dev>
62+
// A getContext() call “returns null if contextId is not supported, or if the
63+
// canvas has already been initialized with another context type”. Additionally,
64+
// “not supported” could include “insufficient GPU resources” or “the GPU process
65+
// previously crashed”. So, we must return it as an `Err` since it could occur
66+
// for circumstances outside the application author's control.
67+
return Err(crate::InstanceError);
68+
}
69+
Err(js_error) => {
70+
// <https://html.spec.whatwg.org/multipage/canvas.html#dom-canvas-getcontext>
71+
// A thrown exception indicates misuse of the canvas state. Ideally we wouldn't
72+
// panic in this case, but for now, `InstanceError` conveys no detail, so it
73+
// is more informative to panic with a specific message.
74+
panic!("canvas.getContext() threw {js_error:?}")
75+
}
76+
};
77+
78+
// Not returning this error because it is a type error that shouldn't happen unless
79+
// the browser, JS builtin objects, or wasm bindings are misbehaving somehow.
80+
let webgl2_context: web_sys::WebGl2RenderingContext = context_object
81+
.dyn_into()
82+
.expect("canvas context is not a WebGl2RenderingContext");
6283

6384
*self.webgl2_context.lock() = Some(webgl2_context.clone());
6485

wgpu/src/backend/web.rs

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,22 +1027,52 @@ impl Context {
10271027
&self,
10281028
canvas: &web_sys::HtmlCanvasElement,
10291029
) -> <Self as crate::Context>::SurfaceId {
1030-
let context: wasm_bindgen::JsValue = match canvas.get_context("webgpu") {
1031-
Ok(Some(ctx)) => ctx.into(),
1032-
_ => panic!("expected to get context from canvas"),
1033-
};
1034-
create_identified(context.into())
1030+
self.create_surface_from_context(canvas.get_context("webgpu"))
1031+
.unwrap()
10351032
}
10361033

10371034
pub fn instance_create_surface_from_offscreen_canvas(
10381035
&self,
10391036
canvas: &web_sys::OffscreenCanvas,
10401037
) -> <Self as crate::Context>::SurfaceId {
1041-
let context: wasm_bindgen::JsValue = match canvas.get_context("webgpu") {
1042-
Ok(Some(ctx)) => ctx.into(),
1043-
_ => panic!("expected to get context from canvas"),
1038+
self.create_surface_from_context(canvas.get_context("webgpu"))
1039+
.unwrap()
1040+
}
1041+
1042+
/// Common portion of public `instance_create_surface_from_*` functions.
1043+
///
1044+
/// Note: Analogous code also exists in the WebGL 2 backend at
1045+
/// `wgpu_hal::gles::web::Instance`.
1046+
fn create_surface_from_context(
1047+
&self,
1048+
context_result: Result<Option<js_sys::Object>, wasm_bindgen::JsValue>,
1049+
) -> Result<<Self as crate::Context>::SurfaceId, ()> {
1050+
let context: js_sys::Object = match context_result {
1051+
Ok(Some(context)) => context,
1052+
Ok(None) => {
1053+
// <https://html.spec.whatwg.org/multipage/canvas.html#dom-canvas-getcontext-dev>
1054+
// A getContext() call “returns null if contextId is not supported, or if the
1055+
// canvas has already been initialized with another context type”. Additionally,
1056+
// “not supported” could include “insufficient GPU resources” or “the GPU process
1057+
// previously crashed”. So, we must return it as an `Err` since it could occur
1058+
// for circumstances outside the application author's control.
1059+
return Err(());
1060+
}
1061+
Err(js_error) => {
1062+
// <https://html.spec.whatwg.org/multipage/canvas.html#dom-canvas-getcontext>
1063+
// A thrown exception indicates misuse of the canvas state. Ideally we wouldn't
1064+
// panic in this case ... TODO
1065+
panic!("canvas.getContext() threw {js_error:?}")
1066+
}
10441067
};
1045-
create_identified(context.into())
1068+
1069+
// Not returning this error because it is a type error that shouldn't happen unless
1070+
// the browser, JS builtin objects, or wasm bindings are misbehaving somehow.
1071+
let context: web_sys::GpuCanvasContext = context
1072+
.dyn_into()
1073+
.expect("canvas context is not a GPUCanvasContext");
1074+
1075+
Ok(create_identified(context))
10461076
}
10471077

10481078
pub fn queue_copy_external_image_to_texture(

0 commit comments

Comments
 (0)