ferridriver_script/bindings/web_error.rs
1//! `WebErrorJs` — QuickJS binding for
2//! [`ferridriver::web_error::WebError`].
3//!
4//! Mirrors Playwright's client-side `WebError` class from
5//! `/tmp/playwright/packages/playwright-core/src/client/webError.ts`
6//! and the public-type contract in
7//! `/tmp/playwright/packages/playwright-core/types/types.d.ts:21658` —
8//! `error(): Error` returning a **native JS `Error`** instance (not a
9//! plain object) so `instanceof Error` holds in script-land. `page()`
10//! is omitted (symmetric with `DownloadJs` / `FileChooserJs` /
11//! `ConsoleMessageJs`); script-land callers have no need for the
12//! page back-reference.
13
14use ferridriver::web_error::{ErrorDetails, WebError as CoreWebError};
15use rquickjs::JsLifetime;
16use rquickjs::class::Trace;
17
18#[derive(JsLifetime, Trace)]
19#[rquickjs::class(rename = "WebError")]
20pub struct WebErrorJs {
21 #[qjs(skip_trace)]
22 inner: CoreWebError,
23}
24
25impl WebErrorJs {
26 #[must_use]
27 pub fn new(inner: CoreWebError) -> Self {
28 Self { inner }
29 }
30}
31
32#[rquickjs::methods]
33impl WebErrorJs {
34 /// Playwright: `webError.error(): Error`. Returns a **native JS
35 /// `Error`** instance constructed via the global `Error`
36 /// constructor so `instanceof Error === true` and the value is a
37 /// throwable object with a real engine-captured `stack`.
38 #[qjs(rename = "error")]
39 pub fn error<'js>(&self, ctx: rquickjs::Ctx<'js>) -> rquickjs::Result<rquickjs::Value<'js>> {
40 build_native_error(&ctx, self.inner.error())
41 }
42}
43
44/// Shared helper: construct a native JS `Error` from an [`ErrorDetails`]
45/// snapshot. Reused by `WebErrorJs::error` (context-scoped) AND by
46/// `PageJs::waitForEvent('pageerror')` (page-scoped, Playwright-parity
47/// `Promise<Error>`).
48///
49/// Constructs the `Error` natively via the global `Error` constructor
50/// (`rquickjs::function::Constructor::construct`, the same call-as-new
51/// path `page.route` uses for `new URL(...)`), then sets `name` and a
52/// non-empty `stack` on the instance. No `ctx.eval` of a JS factory.
53pub fn build_native_error<'js>(
54 ctx: &rquickjs::Ctx<'js>,
55 details: &ErrorDetails,
56) -> rquickjs::Result<rquickjs::Value<'js>> {
57 let err_ctor: rquickjs::function::Constructor<'js> = ctx.globals().get("Error")?;
58 let err: rquickjs::Object<'js> = err_ctor.construct((details.message.clone(),))?;
59 err.set("name", details.name.clone())?;
60 if !details.stack.is_empty() {
61 err.set("stack", details.stack.clone())?;
62 }
63 Ok(err.into_value())
64}