Skip to main content

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}