1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//! Worker capture smoke test: `on_worker` fires for a dedicated worker spawned
//! from an inline blob, `worker.url()` reports a `blob:` URL, and `evaluate`
//! runs in the worker's execution context.
mod common;
use playwright_cdp::Worker;
use std::sync::Arc;
use tokio::sync::{oneshot, Mutex};
#[tokio::test]
async fn on_worker_captures_dedicated_worker() {
let browser = match common::browser().await {
Some(b) => b,
None => {
eprintln!("[skip] no browser available");
return;
}
};
let page = browser.new_page().await.unwrap();
// Channel to ferry the first Worker out of the handler.
let (tx, rx) = oneshot::channel::<Worker>();
let tx = Arc::new(Mutex::new(Some(tx)));
// Register the handler BEFORE creating the worker. set_content runs the
// inline script which spawns the worker, so auto-attach must be enabled
// (and the listener subscribed) first.
page.on_worker(move |w| {
let tx = Arc::clone(&tx);
async move {
if let Some(t) = tx.lock().await.take() {
let _ = t.send(w);
}
}
})
.await;
// A dedicated worker created from an inline blob URL.
let html = r#"
<script>
const w = new Worker(URL.createObjectURL(new Blob([
`onmessage = e => postMessage('hi from ' + e.data)`
], {type:'text/javascript'})));
window.__w = w;
</script>
"#;
page.set_content(html).await.unwrap();
let worker = match tokio::time::timeout(std::time::Duration::from_secs(15), rx).await {
Ok(Ok(w)) => w,
_ => {
eprintln!("[skip] worker event did not fire in time");
browser.close().await.unwrap();
return;
}
};
// The worker URL should be a blob: URL.
let url = worker.url().to_string();
println!("[worker] url = {url}");
assert!(
url.starts_with("blob:"),
"dedicated worker url should start with blob:, got {url}"
);
// Evaluate in the worker's execution context. Dedicated workers expose a
// default execution context, so Runtime.evaluate should work after
// Runtime.enable. If a particular Chrome build rejects it, we still pass on
// the url + handler-fired assertions above (documented limitation).
match worker.evaluate::<serde_json::Value>("1 + 1").await {
Ok(v) => {
println!("[worker] evaluate(1+1) = {v}");
let n = v.as_i64().or_else(|| v.as_f64().map(|f| f as i64));
assert_eq!(n, Some(2), "worker evaluate 1+1 should yield 2, got {v}");
}
Err(e) => {
// Document any CDP quirk but keep the test green: the handler fired
// and the url is correct.
eprintln!("[worker] evaluate not supported on this build: {e}");
}
}
browser.close().await.unwrap();
}