From 62b7a91a88d8d6af0b687a639f4b56ce932f6dd9 Mon Sep 17 00:00:00 2001 From: j-mendez Date: Sat, 7 Oct 2023 10:57:57 -0400 Subject: [PATCH] chore(innate): add layout nodes --- kayle_innate/Cargo.lock | 1 + kayle_innate/kayle_innate/Cargo.toml | 1 + .../kayle_innate/src/engine/audit/auditor.rs | 21 +-- .../kayle_innate/src/engine/audit/tree.rs | 134 +++++++++++++----- .../kayle_innate/src/engine/rules/rule.rs | 10 +- .../src/engine/rules/wcag_rule_map.rs | 3 +- .../kayle_innate/src/engine/styles/mod.rs | 2 +- .../victor/src/style/values/length.rs | 10 ++ 8 files changed, 138 insertions(+), 44 deletions(-) diff --git a/kayle_innate/Cargo.lock b/kayle_innate/Cargo.lock index 34789f6..9799759 100644 --- a/kayle_innate/Cargo.lock +++ b/kayle_innate/Cargo.lock @@ -360,6 +360,7 @@ dependencies = [ "selectors", "serde", "serde-wasm-bindgen", + "slotmap", "smallvec 1.11.1", "taffy", "url", diff --git a/kayle_innate/kayle_innate/Cargo.toml b/kayle_innate/kayle_innate/Cargo.toml index 636e549..d5ff194 100644 --- a/kayle_innate/kayle_innate/Cargo.toml +++ b/kayle_innate/kayle_innate/Cargo.toml @@ -33,6 +33,7 @@ ego-tree = { workspace = true } victor_tree = { version = "0.0.7", path = "../kayle_victor/victor" } markup5ever = "0.11.0" cssparser = { workspace = true } +slotmap = "1.0.6" [dev-dependencies] wasm-bindgen-test = "0.3.37" diff --git a/kayle_innate/kayle_innate/src/engine/audit/auditor.rs b/kayle_innate/kayle_innate/src/engine/audit/auditor.rs index d060e53..4eef968 100644 --- a/kayle_innate/kayle_innate/src/engine/audit/auditor.rs +++ b/kayle_innate/kayle_innate/src/engine/audit/auditor.rs @@ -1,21 +1,24 @@ +use super::tree::parse_accessibility_tree; use crate::{console_log, now}; +use markup5ever::local_name; use scraper_forky::ElementRef; use scraper_forky::Html; +use slotmap::DefaultKey; +use taffy::Taffy; use victor_tree::style::StyleSet; -use markup5ever::local_name; - -use super::tree::parse_accessibility_tree; /// the intro to an audit pub struct Auditor<'a> { /// the html document pub document: &'a Html, /// the tree to map to nodes - pub tree: std::collections::BTreeMap<&'a str, Vec>>, + pub tree: std::collections::BTreeMap<&'a str, Vec<(ElementRef<'a>, DefaultKey)>>, /// styles for the audit pub author: StyleSet, - // /// the matching context for css selectors + /// the matching context for css selectors pub match_context: selectors::matching::MatchingContext<'a, scraper_forky::selector::Simple>, + /// layout handling + pub taffy: Taffy, } impl<'a> Auditor<'a> { @@ -25,7 +28,7 @@ impl<'a> Auditor<'a> { match_context: selectors::matching::MatchingContext<'a, scraper_forky::selector::Simple>, ) -> Auditor<'a> { let tt = now(); - + // TODO: make stylesheet building optional and only on first requirement let author = { let mut author = victor_tree::style::StyleSetBuilder::new(); if !css_rules.is_empty() { @@ -46,12 +49,13 @@ impl<'a> Auditor<'a> { } author.finish() }; - // TODO: make stylesheet building optional and only on first requirement console_log!("StyleSheets Build Time {:?}", now() - tt); let t = now(); - let tree = parse_accessibility_tree(&document, &author); + + let (tree, taffy, match_context) = + parse_accessibility_tree(&document, &author, match_context); console_log!("Tree Build Time {:?}", now() - t); Auditor { @@ -59,6 +63,7 @@ impl<'a> Auditor<'a> { tree, author, match_context, + taffy, } } } diff --git a/kayle_innate/kayle_innate/src/engine/audit/tree.rs b/kayle_innate/kayle_innate/src/engine/audit/tree.rs index 0b06fb4..80d2cbc 100644 --- a/kayle_innate/kayle_innate/src/engine/audit/tree.rs +++ b/kayle_innate/kayle_innate/src/engine/audit/tree.rs @@ -1,19 +1,34 @@ +use scraper_forky::selector::Simple; use scraper_forky::ElementRef; -use victor_tree::style::StyleSet; use scraper_forky::Html; +use selectors::matching::MatchingContext; +use slotmap::DefaultKey; use std::collections::BTreeMap; +use std::collections::HashSet; +use taffy::prelude::*; +use victor_tree::style::values::Direction; +use victor_tree::style::values::WritingMode; +use victor_tree::style::StyleSet; + +lazy_static! { + static ref NODE_IGNORE: HashSet<&'static str> = + HashSet::from(["meta", "style", "link", "script", "head", "html"]); +} /// try to fix all possible issues using a spec against the tree. -pub fn parse_accessibility_tree<'a, 'b>( - html: &'a Html, - _author: &StyleSet, - // todo: return the nodes with a tuple of the layout node and the element node -) -> std::collections::BTreeMap<&'a str, Vec>> { - // use taffy::prelude::*; +pub fn parse_accessibility_tree<'a, 'b, 'c>( + document: &'a Html, + author: &StyleSet, + match_context: MatchingContext<'c, Simple>, // todo: return the nodes with a tuple of the layout node and the element node +) -> ( + BTreeMap<&'a str, Vec<(ElementRef<'a>, slotmap::DefaultKey)>>, + Taffy, + MatchingContext<'c, Simple>, +) { // // todo: use optional variable for clips or layout creation - // let mut taffy = Taffy::new(); + let mut taffy = Taffy::new(); - // let header_node = taffy + // let header_node: DefaultKey = taffy // .new_leaf(Style { // size: Size { // width: points(800.0), @@ -34,44 +49,99 @@ pub fn parse_accessibility_tree<'a, 'b>( // }) // .unwrap(); - // let root_node = taffy - // .new_with_children( - // Style { - // flex_direction: FlexDirection::Column, - // size: Size { - // width: points(800.0), - // height: points(600.0), - // }, - // ..Default::default() - // }, - // &[header_node, body_node], - // ) - // .unwrap(); - - // // Call compute_layout on the root of your tree to run the layout algorithm - // taffy.compute_layout(root_node, Size::MAX_CONTENT).unwrap(); - // console_log!("Header Layout {:?}", taffy.layout(header_node).unwrap()); // We can get the x,y, and height, width of the element on proper tree insert // parse doc will start from html downwards // accessibility tree for ordered element mappings - let mut accessibility_tree: BTreeMap<&str, Vec>> = + let mut accessibility_tree: BTreeMap<&str, Vec<(ElementRef<'_>, DefaultKey)>> = BTreeMap::from([("title".into(), Default::default())]); - for node in html.tree.nodes() { + let mut layout_leafs: Vec = vec![]; + + // let mut _match_context = match_context; + + let mut matching_context = match_context; + let writing_direction = (WritingMode::HorizontalTb, Direction::Ltr); + + // push taffy layout in order from elements + for node in document.tree.nodes() { match scraper_forky::element_ref::ElementRef::wrap(node) { Some(element) => { + let name = element.value().name(); + // TODO: determine if children are found to get entire layout of children to vector first + let layout_leaf = taffy + .new_leaf(Style { + // make nodes optional but, for now max out perf drawbacks + size: if NODE_IGNORE.contains(name) { + Size { + width: points(0.0), + height: points(0.0), + } + } else { + // get the layout size from computed styles + let style = victor_tree::style::cascade::style_for_element_ref( + &element, + &author, + &document, + &mut matching_context, + ); + let style = style.as_ref(); + let _physical_size = + style.box_size().size_to_physical(writing_direction); + // TODO: Build physical styles of each element as needed + // crate::console_log!("{name} {:?}", physical_size); + // crate::console_log!("{name} Margin {:?}", style.margin()); + // crate::console_log!("{name} Padding {:?}", style.padding()); + let padding_insent = style.padding().block_start; + // todo: make method to get entire size + let _padding_pxs = padding_insent.inner_px(); + // crate::console_log!("{name} Padding Top {:?}", padding_pxs); + // IF the x and y is empty get the height based on the padding. + // If img has no height get the inherited from style width + // We may have to build the width/height if empty from paddings and margins + + Size { + width: points(800.0), + height: points(100.0), + } + }, + // todo: get entire styles up front + flex_grow: if name == "body" { 1.0 } else { 0.0 }, + ..Default::default() + }) + .unwrap(); + + layout_leafs.push(layout_leaf.clone()); + accessibility_tree - .entry(element.value().name()) - .and_modify(|n| n.push(element)) - .or_insert(Vec::from([element])); + .entry(name) + .and_modify(|n| n.push((element, layout_leaf))) + .or_insert(Vec::from([(element, layout_leaf)])); } _ => (), }; } + // this is slow at the moment for large leafs being mocked + let root_node = taffy + .new_with_children( + Style { + flex_direction: FlexDirection::Column, + // compute the default layout from CDP + size: Size { + width: points(800.0), + height: points(600.0), + }, + ..Default::default() + }, + &layout_leafs, + ) + .unwrap(); + + taffy.compute_layout(root_node, Size::MAX_CONTENT).unwrap(); + // crate::console_log!("Last Element Position {:?}", taffy.layout(layout_leafs[layout_leafs.len() - 1]).unwrap()); // console_log!("Getting tree links {:?}", accessibility_tree.get("a")); // console_log!("Tree {:?}", accessibility_tree); - accessibility_tree + (accessibility_tree, taffy, matching_context) } diff --git a/kayle_innate/kayle_innate/src/engine/rules/rule.rs b/kayle_innate/kayle_innate/src/engine/rules/rule.rs index 4af13d4..fc0d902 100644 --- a/kayle_innate/kayle_innate/src/engine/rules/rule.rs +++ b/kayle_innate/kayle_innate/src/engine/rules/rule.rs @@ -1,3 +1,5 @@ +use slotmap::DefaultKey; + use crate::engine::rules::ids::Techniques; use crate::engine::rules::wcag_base::{Criteria, Guideline, Principle}; use crate::ElementRef; @@ -9,7 +11,8 @@ pub struct Rule { /// the type of rule pub criteria: Criteria, /// validate a test returns (valid, rule, selectors) - pub validate: fn(&str, &Vec>) -> (bool, &'static str, Vec<&'static str>), + pub validate: + fn(&str, &Vec<(ElementRef<'_>, DefaultKey)>) -> (bool, &'static str, Vec<&'static str>), /// the principle type pub principle: Principle, /// the guideline to follow @@ -23,7 +26,10 @@ impl Rule { criteria: Criteria, principle: Principle, guideline: Guideline, - validate: fn(&str, &Vec>) -> (bool, &'static str, Vec<&'static str>), + validate: fn( + &str, + &Vec<(ElementRef<'_>, DefaultKey)>, + ) -> (bool, &'static str, Vec<&'static str>), ) -> Rule { Rule { rule_id, diff --git a/kayle_innate/kayle_innate/src/engine/rules/wcag_rule_map.rs b/kayle_innate/kayle_innate/src/engine/rules/wcag_rule_map.rs index af604bc..d77d07b 100644 --- a/kayle_innate/kayle_innate/src/engine/rules/wcag_rule_map.rs +++ b/kayle_innate/kayle_innate/src/engine/rules/wcag_rule_map.rs @@ -15,7 +15,7 @@ lazy_static! { (!nodes.is_empty(), "1.NoTitleEl", Default::default()) }), Rule::new(Techniques::H25, Criteria::Error, Principle::Understandable, Guideline::Predictable, |_rule, nodes| { - (nodes.is_empty() || nodes[0].html().is_empty(), "2", Default::default()) + (nodes.is_empty() || nodes[0].0.html().is_empty(), "2", Default::default()) }), ])), // missing label @@ -26,6 +26,7 @@ lazy_static! { let selector = unsafe { Selector::parse("button[type=submit]").unwrap_unchecked() }; for ele in nodes { + let ele = ele.0; valid = match ele.select(&selector).next() { Some(_) => true, _ => false diff --git a/kayle_innate/kayle_innate/src/engine/styles/mod.rs b/kayle_innate/kayle_innate/src/engine/styles/mod.rs index aa4c4dc..0fda49b 100644 --- a/kayle_innate/kayle_innate/src/engine/styles/mod.rs +++ b/kayle_innate/kayle_innate/src/engine/styles/mod.rs @@ -1,3 +1,3 @@ pub mod css_cache; pub mod errors; -pub mod rules; \ No newline at end of file +pub mod rules; diff --git a/kayle_innate/kayle_victor/victor/src/style/values/length.rs b/kayle_innate/kayle_victor/victor/src/style/values/length.rs index 2bcb885..5b6df14 100644 --- a/kayle_innate/kayle_victor/victor/src/style/values/length.rs +++ b/kayle_innate/kayle_victor/victor/src/style/values/length.rs @@ -36,6 +36,16 @@ pub enum LengthOrPercentage { Percentage(Percentage), } +impl LengthOrPercentage { + /// get the px values as a float + pub fn inner_px(&self) -> f32 { + match self { + LengthOrPercentage::Length(l) => l.px, + LengthOrPercentage::Percentage(l) => l.unit_value, + } + } +} + #[derive(Clone, Parse, FromVariants)] pub enum SpecifiedLengthOrPercentageOrAuto { Length(SpecifiedLength),