git.lucas.co / cce-browser
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 }