slicc-cdp-client 1.41.4

Shared CDP connection for slicc-cdp CLIs
Documentation
struct WindowBrowser {
    targets: Vec<Value>,
    children: Vec<Value>,
    window: i64,
    sent: Vec<Value>,
}

struct WindowBackend {
    inbox: VecDeque<String>,
    browser: std::rc::Rc<std::cell::RefCell<WindowBrowser>>,
    attached: usize,
}

impl Transport for WindowBackend {
    fn send_text(&mut self, text: &str) -> Result<(), String> {
        let call: Value = serde_json::from_str(text).unwrap();
        let mut browser = self.browser.borrow_mut();
        browser.sent.push(call.clone());
        let result = match call["method"].as_str().unwrap() {
            "Target.attachToTarget" => {
                self.attached += 1;
                json!({ "sessionId": if self.attached == 1 { "U" } else { "G" } })
            }
            "Target.getTargetInfo" => json!({ "targetInfo": browser.targets.iter().find(|info| info["targetId"] == call["params"]["targetId"]).cloned().unwrap_or(Value::Null) }),
            "Target.getTargets" => json!({ "targetInfos": browser.targets }),
            "Page.getFrameTree" => json!({ "frameTree": {
                "frame": { "id": "T", "loaderId": "LT", "url": "https://ordinary.test/", "securityOrigin": "https://ordinary.test" },
                "childFrames": browser.children,
            }}),
            "Runtime.evaluate" => json!({ "result": { "type": "string", "value": "complete" } }),
            "Browser.getWindowForTarget" => json!({ "windowId": browser.window, "bounds": { "left": 0, "top": 0, "width": 800, "height": 600, "windowState": "normal" } }),
            _ => json!({}),
        };
        self.inbox.push_back(json!({ "id": call["id"], "sessionId": call["sessionId"], "result": result }).to_string());
        Ok(())
    }

    fn recv_text(&mut self, _deadline: Instant) -> Result<Option<String>, String> {
        Ok(self.inbox.pop_front())
    }
}

fn window_page(id: &str, url: &str, context: &str) -> Value {
    json!({ "targetId": id, "type": "page", "url": url, "title": "", "browserContextId": context })
}

fn window_client(extra: Vec<Value>, children: Vec<Value>) -> (Cdp, String, std::rc::Rc<std::cell::RefCell<WindowBrowser>>) {
    let mut targets = vec![window_page("T", "https://ordinary.test/", "CTX")];
    targets.extend(extra);
    let browser = std::rc::Rc::new(std::cell::RefCell::new(WindowBrowser { targets, children, window: 7, sent: Vec::new() }));
    let mut cdp = Cdp::from_transport(Box::new(WindowBackend { inbox: VecDeque::new(), browser: browser.clone(), attached: 0 }));
    let session = super::attach(&mut cdp, "T").unwrap();
    (cdp, session, browser)
}

fn window_resized(browser: &std::rc::Rc<std::cell::RefCell<WindowBrowser>>) -> usize {
    browser.borrow().sent.iter().filter(|call| call["method"] == "Browser.setWindowBounds").count()
}

fn window_bounds(width: u64, height: u64) -> Value {
    json!({ "windowId": 7, "bounds": { "width": width, "height": height } })
}

#[test]
fn window_bounds_are_admitted_for_an_ordinary_page_alone_in_its_profile() {
    for extra in [
        vec![],
        vec![window_page("OTHER", "https://other.test/", "CTX")],
        vec![window_page("SEVEN", "https://seven.sliccy.ai/", "ELSEWHERE")],
        vec![json!({ "targetId": "SW", "type": "service_worker", "url": "https://seven.sliccy.ai/sw.js", "browserContextId": "CTX" })],
        vec![
            json!({ "targetId": "OMNIBOX", "type": "browser_ui", "url": "chrome://omnibox-popup.top-chrome/", "browserContextId": "CTX" }),
            json!({ "targetId": "OMNIBOX2", "type": "browser_ui", "url": "chrome://omnibox-popup.top-chrome/omnibox_popup_aim.html", "browserContextId": "CTX" }),
        ],
    ] {
        let (mut cdp, session, browser) = window_client(extra.clone(), vec![]);
        cdp.call("Browser.setWindowBounds", window_bounds(1024, 768), Some(&session)).unwrap_or_else(|error| panic!("{extra:?}: {error}"));
        assert_eq!(window_resized(&browser), 1, "{extra:?}");
    }
}

#[test]
fn window_bounds_are_refused_when_a_protected_or_unknown_target_could_share_the_window() {
    for extra in [
        window_page("SEVEN", "https://seven.sliccy.ai/?secret=1", "CTX"),
        window_page("VAULT", "http://vault.sliccy.ai/", "CTX"),
        window_page("BLANK", "", "CTX"),
        window_page("SETTINGS", "chrome://settings/", "CTX"),
        window_page("NOCTX", "https://seven.sliccy.ai/", ""),
        json!({ "targetId": "FRAME", "type": "iframe", "url": "https://vault.sliccy.ai/frame", "browserContextId": "CTX", "parentFrameId": "T" }),
        json!({ "targetId": "PANEL", "type": "other", "url": "chrome-extension://abc/panel.html", "browserContextId": "CTX" }),
        json!({ "targetId": "", "type": "page", "url": "https://other.test/", "browserContextId": "CTX" }),
        window_page("FAKEUI", "chrome://omnibox-popup.top-chrome/", "CTX"),
        json!({ "targetId": "UI", "type": "browser_ui", "url": "chrome://settings/", "browserContextId": "CTX" }),
        json!({ "targetId": "UIEXT", "type": "browser_ui", "url": "chrome-extension://abc.top-chrome/panel.html", "browserContextId": "CTX" }),
        json!({ "targetId": "UIUSER", "type": "browser_ui", "url": "chrome://seven@omnibox-popup.top-chrome/", "browserContextId": "CTX" }),
        json!({ "targetId": "UISEVEN", "type": "browser_ui", "url": "https://seven.sliccy.ai/", "browserContextId": "CTX" }),
    ] {
        let (mut cdp, session, browser) = window_client(vec![extra.clone()], vec![]);
        let refused = cdp.call("Browser.setWindowBounds", window_bounds(1024, 768), Some(&session)).err().unwrap_or_else(|| panic!("{extra} was admitted"));
        assert!(refused.starts_with("slicc: refused:"), "{extra}: {refused}");
        assert!(!refused.contains("secret"));
        assert_eq!(window_resized(&browser), 0, "{extra}");
    }
}

#[test]
fn window_bounds_are_refused_for_a_protected_child_frame_of_the_page() {
    let child = json!({ "frame": { "id": "C", "loaderId": "LC", "parentId": "T", "url": "https://seven.sliccy.ai/", "securityOrigin": "https://seven.sliccy.ai" } });
    let (mut cdp, session, browser) = window_client(vec![], vec![child]);
    assert!(cdp.call("Browser.setWindowBounds", window_bounds(1024, 768), Some(&session)).unwrap_err().starts_with("slicc: refused:"));
    assert_eq!(window_resized(&browser), 0);
}

#[test]
fn window_bounds_params_and_sessions_are_allowlisted() {
    for (params, session) in [
        (window_bounds(1024, 768), None),
        (window_bounds(1024, 768), Some("G")),
        (window_bounds(1024, 768), Some("NOBODY")),
        (json!({ "windowId": 8, "bounds": { "width": 1024, "height": 768 } }), Some("U")),
        (json!({ "windowId": "7", "bounds": { "width": 1024, "height": 768 } }), Some("U")),
        (json!({ "windowId": 7, "bounds": { "width": 1024, "height": 768, "left": 0 } }), Some("U")),
        (json!({ "windowId": 7, "bounds": { "windowState": "fullscreen" } }), Some("U")),
        (json!({ "windowId": 7, "bounds": { "width": 1024 } }), Some("U")),
        (json!({ "windowId": 7, "bounds": { "width": 1024, "height": 768 }, "extra": true }), Some("U")),
        (json!({ "windowId": 7 }), Some("U")),
        (json!([]), Some("U")),
        (window_bounds(0, 768), Some("U")),
        (window_bounds(1024, 10_000_001), Some("U")),
        (json!({ "windowId": 7, "bounds": { "width": 10.5, "height": 768 } }), Some("U")),
        (json!({ "windowId": 7, "bounds": { "width": -1, "height": 768 } }), Some("U")),
    ] {
        let (mut cdp, _, browser) = window_client(vec![], vec![]);
        let refused = cdp.call("Browser.setWindowBounds", params.clone(), session).unwrap_err();
        assert!(refused.starts_with("slicc: refused:"), "{params} {session:?}: {refused}");
        assert!(cdp.notify("Browser.setWindowBounds", params.clone(), session).is_err(), "{params}");
        assert_eq!(window_resized(&browser), 0, "{params} {session:?}");
    }
}

#[test]
fn window_bounds_are_refused_for_a_blocked_or_protected_session() {
    let (mut cdp, session, browser) = window_client(vec![], vec![]);
    cdp.blocked.insert(session.clone());
    assert!(cdp.call("Browser.setWindowBounds", window_bounds(1024, 768), Some(&session)).is_err());
    let (mut cdp, session, _) = window_client(vec![], vec![]);
    cdp.protected_targets.insert("T".into());
    assert!(cdp.call("Browser.setWindowBounds", window_bounds(1024, 768), Some(&session)).is_err());
    assert_eq!(window_resized(&browser), 0);
}

#[test]
fn window_bounds_are_refused_when_the_page_is_not_listed_as_an_ordinary_page() {
    let (mut cdp, session, browser) = window_client(vec![], vec![]);
    browser.borrow_mut().targets[0]["type"] = json!("other");
    assert!(cdp.call("Browser.setWindowBounds", window_bounds(1024, 768), Some(&session)).is_err());
    browser.borrow_mut().targets.clear();
    assert!(cdp.call("Browser.setWindowBounds", window_bounds(1024, 768), Some(&session)).is_err());
    assert_eq!(window_resized(&browser), 0);
}