use chromiumoxide::Page;
use crate::cli::WcagLevel;
use crate::wcag::types::{RuleMetadata, Severity, Violation};
const VIEWPORT_CONTENT_JS: &str =
"document.querySelector('meta[name=\"viewport\"]')?.getAttribute('content') || null";
async fn read_viewport_content(page: &Page) -> Option<String> {
page.evaluate(VIEWPORT_CONTENT_JS)
.await
.ok()
.and_then(|r| r.value().and_then(|v| v.as_str().map(str::to_owned)))
}
fn is_viewport_restricted(content: &str, threshold: f64) -> bool {
let content = content.to_lowercase();
if content.contains("user-scalable=no") || content.contains("user-scalable=0") {
return true;
}
if let Some(pos) = content.find("maximum-scale=") {
let after = &content[pos + "maximum-scale=".len()..];
let value_str: String = after
.chars()
.take_while(|c| c.is_ascii_digit() || *c == '.')
.collect();
if let Ok(val) = value_str.parse::<f64>() {
return val < threshold;
}
}
false
}
pub const RESIZE_TEXT_RULE: RuleMetadata = RuleMetadata {
id: "1.4.4",
name: "Resize Text",
level: WcagLevel::AA,
severity: Severity::High,
description: "Text can be resized up to 200% without loss of content or functionality",
help_url: "https://www.w3.org/WAI/WCAG21/Understanding/resize-text.html",
axe_id: "meta-viewport",
tags: &["wcag2aa", "wcag144", "cat.sensory-and-visual-cues"],
};
pub async fn check_resize_text_with_page(page: &Page) -> Vec<Violation> {
let content = match read_viewport_content(page).await {
Some(c) => c,
None => return vec![],
};
let content_lower = content.to_lowercase();
let mut violations = Vec::new();
if content_lower.contains("user-scalable=no") || content_lower.contains("user-scalable=0") {
violations.push(
Violation::new(
RESIZE_TEXT_RULE.id,
RESIZE_TEXT_RULE.name,
RESIZE_TEXT_RULE.level,
Severity::High,
"Viewport meta tag prevents user scaling (user-scalable=no)",
"meta[name=viewport]",
)
.with_selector("meta[name=viewport]")
.with_fix("Remove user-scalable=no from the viewport meta tag to allow text resizing")
.with_rule_id(RESIZE_TEXT_RULE.axe_id)
.with_help_url(RESIZE_TEXT_RULE.help_url),
);
} else if is_viewport_restricted(&content, 2.0) {
violations.push(
Violation::new(
RESIZE_TEXT_RULE.id,
RESIZE_TEXT_RULE.name,
RESIZE_TEXT_RULE.level,
Severity::Medium,
"Viewport maximum-scale is less than 2.0, limiting text resize",
"meta[name=viewport]",
)
.with_selector("meta[name=viewport]")
.with_fix("Set maximum-scale to at least 2.0 or remove it entirely")
.with_rule_id(RESIZE_TEXT_RULE.axe_id)
.with_help_url(RESIZE_TEXT_RULE.help_url),
);
}
violations
}