web browser (Servo)
git clone https://git.lucas.co/cce-browser.git
examples/wpe_middle.rs (4.7K)
1 //! A middle-clicked link becomes a background tab, end to end.
2 //!
3 //! Serves a page with an ordinary link and a `target=_blank` one, and checks
4 //! against the real engine:
5 //!
6 //! * a middle-click on either is diverted — queued for a background tab —
7 //! and the page it was clicked on stays where it was;
8 //! * a left-click on the ordinary link still navigates, undiverted;
9 //! * `open_background_tab` adds a tab that loads while the active one
10 //! stays active.
11 //!
12 //! `cce-shadow --instance <n> run ./target/release/examples/wpe_middle`
13
14 #[cfg(not(feature = "wpe"))]
15 fn main() {
16 eprintln!("build with --features wpe");
17 }
18
19 #[cfg(feature = "wpe")]
20 #[derive(Debug, Clone, Copy)]
21 pub enum EditingCommand { Copy, Cut, Paste }
22
23 #[cfg(feature = "wpe")]
24 #[path = "../src/pages.rs"]
25 mod pages;
26 #[cfg(feature = "wpe")]
27 #[path = "../src/downloads.rs"]
28 mod downloads;
29 #[cfg(feature = "wpe")]
30 // The host's vi channel and scripts.
31 #[path = "../src/vi.rs"]
32 #[allow(dead_code)]
33 mod vi;
34
35 #[path = "../src/wpe/mod.rs"]
36 mod wpe;
37
38 #[cfg(feature = "wpe")]
39 const PAGE: &str = "<!doctype html><title>links</title>\
40 <style>a{position:absolute;left:20px;width:200px;height:40px;display:block}</style>\
41 <a id=plain href=/plain style=top:20px>plain</a>\
42 <a id=blank href=/blank target=_blank style=top:100px>blank</a>";
43
44 #[cfg(feature = "wpe")]
45 fn serve() -> u16 {
46 use std::io::{Read, Write};
47 let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
48 let port = listener.local_addr().unwrap().port();
49 std::thread::spawn(move || {
50 for stream in listener.incoming() {
51 let Ok(mut s) = stream else { continue };
52 let mut buf = [0u8; 4096];
53 let n = s.read(&mut buf).unwrap_or(0);
54 let req = String::from_utf8_lossy(&buf[..n]).to_string();
55 let path = req.split_whitespace().nth(1).unwrap_or("/").to_string();
56 let body = if path == "/" {
57 PAGE.to_string()
58 } else {
59 format!("<!doctype html><title>{}</title>", &path[1..])
60 };
61 let _ = write!(
62 s,
63 "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
64 body.len(),
65 body
66 );
67 }
68 });
69 port
70 }
71
72 #[cfg(feature = "wpe")]
73 fn main() {
74 use cce_ui::widget::MouseButton;
75 let port = serve();
76 let base = format!("http://127.0.0.1:{port}");
77 let mut host = wpe::WebKitHost::new(url::Url::parse(&format!("{base}/")).unwrap(), (1200, 800));
78 let settle = |h: &mut wpe::WebKitHost, n: u32| {
79 for _ in 0..n {
80 h.pump(); h.frame_drawn();
81 std::thread::sleep(std::time::Duration::from_millis(50));
82 }
83 };
84 let click = |h: &mut wpe::WebKitHost, b: MouseButton, x: f32, y: f32| {
85 h.mouse_move(x, y);
86 h.mouse_button_ui(b, true, x, y);
87 h.mouse_button_ui(b, false, x, y);
88 };
89 settle(&mut host, 30);
90 println!("loaded: {:?}", host.title());
91 let mut ok = true;
92 let mut check = |name: &str, pass: bool| {
93 println!(" {name:<36} {}", if pass { "OK" } else { "FAIL" });
94 ok &= pass;
95 };
96
97 click(&mut host, MouseButton::Middle, 60.0, 40.0);
98 settle(&mut host, 10);
99 let plain = host.take_background_opens();
100 println!("middle plain -> {plain:?}, title {:?}", host.title());
101 check("middle-click plain link is queued", plain.iter().any(|u| u.path() == "/plain"));
102 check(" and the page stays", host.title().as_deref() == Some("links"));
103
104 click(&mut host, MouseButton::Middle, 60.0, 120.0);
105 settle(&mut host, 10);
106 let blank = host.take_background_opens();
107 println!("middle blank -> {blank:?}, title {:?}", host.title());
108 check("middle-click target=_blank is queued", blank.iter().any(|u| u.path() == "/blank"));
109 check(" and the page stays", host.title().as_deref() == Some("links"));
110
111 let count = host.tab_count();
112 host.open_background_tab(url::Url::parse(&format!("{base}/bg")).unwrap());
113 settle(&mut host, 20);
114 check("background tab added", host.tab_count() == count + 1);
115 check(" active tab unchanged", host.active_index() == 0);
116 check(
117 " and it loaded behind the page",
118 host.tab(count).and_then(|t| t.title.clone()).as_deref() == Some("bg"),
119 );
120
121 click(&mut host, MouseButton::Left, 60.0, 40.0);
122 settle(&mut host, 20);
123 let left = host.take_background_opens();
124 println!("left plain -> {left:?}, title {:?}", host.title());
125 check("left-click is not diverted", left.is_empty());
126 check(" and navigates", host.title().as_deref() == Some("plain"));
127
128 println!("\n{}", if ok { "OK" } else { "FAILED" });
129 std::process::exit(if ok { 0 } else { 1 });
130 }