use anyrender::render_to_buffer;
use anyrender_vello_cpu::VelloCpuImageRenderer;
use blitz_dom::DocumentConfig;
use blitz_html::{HtmlDocument, HtmlProvider};
use blitz_paint::paint_scene;
use blitz_traits::shell::{ColorScheme, Viewport};
use std::sync::Arc;
const BLUE: [u8; 3] = [0, 0, 255];
fn pixel(html: &str, scroll: (f64, f64), x: usize, y: usize) -> [u8; 3] {
pixel_in(html, scroll, x, y, ColorScheme::Light)
}
fn pixel_in(html: &str, scroll: (f64, f64), x: usize, y: usize, scheme: ColorScheme) -> [u8; 3] {
let mut doc = HtmlDocument::from_html(
html,
DocumentConfig {
viewport: Some(Viewport::new(100, 100, 1.0, scheme)),
html_parser_provider: Some(Arc::new(HtmlProvider) as _),
..Default::default()
},
);
doc.resolve(0.0);
let scroller = doc.query_selector("#scroller").unwrap().expect("#scroller");
doc.scroll_by(Some(scroller), -scroll.0, -scroll.1, &mut |_| {});
let buffer = render_to_buffer::<VelloCpuImageRenderer, _>(
|scene| paint_scene(scene, &mut doc, 1.0, 100, 100, 0, 0),
100,
100,
);
let idx = (y * 100 + x) * 4;
[buffer[idx], buffer[idx + 1], buffer[idx + 2]]
}
fn scroller(overflow: &str, child_height: u32) -> String {
format!(
r#"<html><body style="margin:0">
<div id="scroller" style="width:100px; height:100px; overflow-y:{overflow};">
<div style="height:{child_height}px; background:#0000ff;"></div>
</div>
</body></html>"#
)
}
#[test]
fn scrolled_auto_scroller_paints_a_thumb() {
let px = pixel(&scroller("auto", 1000), (0.0, 50.0), 97, 10);
assert_ne!(px, BLUE, "expected a scrollbar thumb over the content");
}
#[test]
fn unscrolled_unhovered_scroller_paints_no_thumb() {
let px = pixel(&scroller("auto", 1000), (0.0, 0.0), 97, 4);
assert_eq!(px, BLUE, "overlay scrollbars are hidden at rest");
}
#[test]
fn non_overflowing_auto_scroller_paints_no_thumb() {
let px = pixel(&scroller("auto", 100), (0.0, 10.0), 97, 4);
assert_eq!(px, BLUE, "no scrollbar for non-overflowing overflow:auto");
}
#[test]
fn hidden_scroller_paints_no_thumb() {
let px = pixel(&scroller("hidden", 1000), (0.0, 50.0), 97, 10);
assert_eq!(px, BLUE, "overflow:hidden must not paint scrollbars");
}
#[test]
fn stale_scroll_offset_alone_paints_no_thumb() {
let mut doc = HtmlDocument::from_html(
&scroller("auto", 1000),
DocumentConfig {
viewport: Some(Viewport::new(100, 100, 1.0, ColorScheme::Light)),
html_parser_provider: Some(Arc::new(HtmlProvider) as _),
..Default::default()
},
);
doc.resolve(0.0);
let scroller = doc.query_selector("#scroller").unwrap().expect("#scroller");
doc.get_node_mut(scroller).unwrap().scroll_offset.y = 50.0;
let buffer = render_to_buffer::<VelloCpuImageRenderer, _>(
|scene| paint_scene(scene, &mut doc, 1.0, 100, 100, 0, 0),
100,
100,
);
let idx = (10 * 100 + 97) * 4;
let px = [buffer[idx], buffer[idx + 1], buffer[idx + 2]];
assert_eq!(px, BLUE, "no scrollbar without scroll activity");
}
#[test]
fn horizontal_scroller_paints_a_thumb() {
let px = pixel(
r#"<html><body style="margin:0">
<div id="scroller" style="width:100px; height:100px; overflow-x:auto; overflow-y:hidden;">
<div style="width:1000px; height:100px; background:#0000ff;"></div>
</div>
</body></html>"#,
(50.0, 0.0),
10,
97,
);
assert_ne!(px, BLUE, "expected a horizontal scrollbar thumb");
}
#[test]
#[ignore = "scrollbar-color is hardcoded to auto until stylo exposes it to the servo engine (servo/stylo#413)"]
fn scrollbar_color_styles_the_thumb() {
let px = pixel(
r#"<html><body style="margin:0">
<div id="scroller" style="width:100px; height:100px; overflow-y:auto; scrollbar-color:#ff0000 transparent;">
<div style="height:1000px; background:#0000ff;"></div>
</div>
</body></html>"#,
(0.0, 50.0),
97,
10,
);
assert!(
px[0] > 200 && px[2] < 100,
"thumb should be the author's red, got {px:?}"
);
}
#[test]
#[ignore = "scrollbar-color is hardcoded to auto until stylo exposes it to the servo engine (servo/stylo#413)"]
fn scrollbar_color_styles_the_track() {
let px = pixel(
r#"<html><body style="margin:0">
<div id="scroller" style="width:100px; height:100px; overflow-y:auto; scrollbar-color:#ff0000 #00ff00;">
<div style="height:1000px; background:#0000ff;"></div>
</div>
</body></html>"#,
(0.0, 50.0),
97,
90,
);
assert!(
px[1] > 200 && px[2] < 100,
"track should be the author's green, got {px:?}"
);
}
#[test]
#[ignore = "scrollbar-width is hardcoded to auto until stylo exposes it to the servo engine (servo/stylo#413)"]
fn scrollbar_width_none_hides_the_scrollbar() {
let px = pixel(
r#"<html><body style="margin:0">
<div id="scroller" style="width:100px; height:100px; overflow-y:auto; scrollbar-width:none;">
<div style="height:1000px; background:#0000ff;"></div>
</div>
</body></html>"#,
(0.0, 50.0),
97,
10,
);
assert_eq!(px, BLUE, "scrollbar-width:none must paint no scrollbar");
}
#[test]
#[ignore = "scrollbar-color is hardcoded to auto until stylo exposes it to the servo engine (servo/stylo#413)"]
fn author_styled_scrollbar_still_hides_at_rest() {
let px = pixel(
r#"<html><body style="margin:0">
<div id="scroller" style="width:100px; height:100px; overflow-y:auto; scrollbar-color:#ff0000 transparent;">
<div style="height:1000px; background:#0000ff;"></div>
</div>
</body></html>"#,
(0.0, 0.0),
97,
4,
);
assert_eq!(px, BLUE, "styled overlay scrollbars still hide at rest");
}
#[test]
fn default_thumb_has_a_contrast_stroke() {
let html = scroller("auto", 1000);
let edge = pixel(&html, (0.0, 50.0), 88, 12);
let fill = pixel(&html, (0.0, 50.0), 93, 12);
assert_ne!(edge, fill, "thumb edge should carry a contrast stroke");
}
#[test]
fn dark_scheme_uses_a_distinct_thumb() {
let html = scroller("auto", 1000);
let light = pixel_in(&html, (0.0, 50.0), 95, 12, ColorScheme::Light);
let dark = pixel_in(&html, (0.0, 50.0), 95, 12, ColorScheme::Dark);
assert_ne!(light, dark, "thumb fill should follow the color scheme");
}