Skip to content

Commit

Permalink
chore(innate): add accessibility tree building
Browse files Browse the repository at this point in the history
  • Loading branch information
j-mendez committed Sep 29, 2023
1 parent d3aa4a3 commit 27046b6
Show file tree
Hide file tree
Showing 21 changed files with 1,116 additions and 86 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ✨ kayle

The futuristic web accessibility engine.
The blazing fast and accurate web accessibility engine.

## Getting Started

Expand Down Expand Up @@ -218,6 +218,10 @@ Run the following to install on ^node@18

Use the command `yarn build` to compile all the scripts for each locale.

## Rust Runner

We are building a rust based runner called [kayle_innate](./kayle_innate/) that can port to wasm that will take the audits into the nanoseconds - low milliseconds zone.

## Discord

If you want to chat about the project checkout our [Discord](https://discord.gg/ukmJcjQ5).
Expand Down
9 changes: 9 additions & 0 deletions kayle/lib/action.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { RunnerConfig } from "./config";

const failedActionElement = "Failed action: no element matching selector";

export const actions = [
Expand Down Expand Up @@ -290,3 +292,10 @@ export async function runAction(browser, page, options, act, customActions?) {

await action.run(browser, page, options, act.match(action.match));
}

// run actions
export const runActionsList = async (config: RunnerConfig) => {
for (const action of config.actions) {
await runAction(config.browser, config.page, config, action);
}
};
1 change: 1 addition & 0 deletions kayle/lib/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,6 @@ export type Audit = {
meta: MetaInfo;
pageUrl: string;
};

// configs that change how the audit behaves
export type RunnerConf = Partial<RunnerConfig & { html?: string }>;
2 changes: 1 addition & 1 deletion kayle/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ export {
sendCDPPageConfigurationReset,
} from "./utils/cdp-blocking";
export { setLogging, Standard, RunnerConfig, Runner } from "./config";
export { extractLinks } from "./wasm";
export { extractLinks, innateAudit } from "./wasm";
2 changes: 1 addition & 1 deletion kayle/lib/kayle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const auditPage = async (config: RunnerConfig) => {
};

// run actions
const runActionsList = async (config: RunnerConfig) => {
export const runActionsList = async (config: RunnerConfig) => {
for (const action of config.actions) {
await runAction(config.browser, config.page, config, action);
}
Expand Down
6 changes: 5 additions & 1 deletion kayle/lib/option.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ export function extractArgs(o, watcher?: Watcher) {
// default to a runner
if (
!options.runners.some(
(runner) => runner === "axe" || runner === "htmlcs" || runner === "ace"
(runner) =>
runner === "axe" ||
runner === "htmlcs" ||
runner === "ace" ||
runner === "kayle"
)
) {
options.runners.push("htmlcs");
Expand Down
17 changes: 17 additions & 0 deletions kayle/lib/wasm/css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { RunnerConfig } from "../config";

// get all the css of the document to send to rust
export const getAllCss = async (config: RunnerConfig) => {
return await config.page.evaluate(() => {
return [...document.styleSheets]
.map((styleSheet) => {
try {
return [...styleSheet.cssRules].map((rule) => rule.cssText).join("");
} catch (e) {
console.log("Access to stylesheet %s is denied.", styleSheet.href);
}
})
.filter(Boolean)
.join("\n");
});
};
5 changes: 4 additions & 1 deletion kayle/lib/wasm/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,8 @@ import type { RunnerConfig } from "../config";
export async function extractLinks(config: RunnerConfig, target?: string) {
const htmlContent = await config.page.content();
const domain = typeof target === "string" ? target : config.page.url();
return get_document_links(htmlContent, domain !== "about:blank" ? domain : "");
return get_document_links(
htmlContent,
domain !== "about:blank" ? domain : ""
);
}
2 changes: 2 additions & 0 deletions kayle/lib/wasm/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export { extractLinks } from "./extract";
export { getAllCss } from "./css";
export { innateAudit } from "./rust-audit";
31 changes: 31 additions & 0 deletions kayle/lib/wasm/rust-audit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { audit } from "kayle_innate";
import { runActionsList } from "../action";
import { getAllCss } from "./css";
import type { RunnerConf } from "../common";
import { goToPage, setNetworkInterception } from "../utils/go-to-page";
import { RunnerConfig } from "../config";
import { Watcher } from "../watcher";
import { extractArgs } from "../option";

// perform audit using kayle innate @note: should not be used in production
export const innateAudit = async (o: RunnerConf) => {
console.log("NOT READY YET. Do not use.");
const watcher = new Watcher();
const config = extractArgs(o, watcher);

const navigate =
config.page.url() === "about:blank" && (config.origin || o.html);

if (navigate) {
await goToPage(config);
} else if (!config.noIntercept) {
await setNetworkInterception(config);
}

await runActionsList(config as RunnerConfig);
const html = await config.page.content();
const allCss = await getAllCss(config as RunnerConfig);
const results = await audit(html, allCss);

return results;
};
1 change: 1 addition & 0 deletions kayle/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"test:puppeteer:extension": "npm run compile:test && yarn build:extension && node _tests/tests/extension.js",
"test:puppeteer:tables": "npm run compile:test && node _tests/tests/tables.js",
"test:puppeteer:clips": "npm run compile:test && node _tests/tests/clips.js",
"test:puppeteer:innate": "npm run compile:test && node _tests/tests/innate.js",
"test:full": "npm run compile:test && node _tests/tests/full.js",
"test:lint": "node build/lint.js",
"test:unit:unique-selector": "npm run compile:test && node _tests/tests/unit/unique-selector.js",
Expand Down
26 changes: 26 additions & 0 deletions kayle/tests/innate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import puppeteer from "puppeteer";
import { innateAudit } from "kayle";
import { drakeMock } from "./mocks/html-mock";
import { performance } from "perf_hooks";

// setup test for rust wasm auditing
(async () => {
const browser = await puppeteer.launch({ headless: "new" });
const page = await browser.newPage();
if (process.env.LOG_ENABLED) {
page.on("console", (msg) => console.log("PAGE LOG:", msg.text()));
}
const startTime = performance.now();
await innateAudit({
page,
browser,
runners: ["htmlcs"],
includeWarnings: true,
origin: "https://www.drake.com",
html: drakeMock
});
const nextTime = performance.now() - startTime;
console.log("time took", nextTime);

await browser.close();
})();
2 changes: 1 addition & 1 deletion kayle_innate/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions kayle_innate/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "kayle_innate"
version = "0.0.18"
version = "0.0.19"
authors = ["j-mendez"]
edition = "2018"
license = "MIT"
Expand All @@ -11,7 +11,7 @@ repository = "https://github.com/a11ywatch/kayle"
crate-type = ["cdylib", "rlib"]

[features]
default = ["console_error_panic_hook"]
default = ["console_error_panic_hook", "accessibility"]
accessibility = ["select"]

[dependencies]
Expand Down
6 changes: 6 additions & 0 deletions kayle_innate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,9 @@ In order to test the accessibility parser in Rust run.

1. Port expensive functions to wasm with preloading capabilities injection into browsers.
1. Reborn the accessibility testing in rust.

## Notes

For creating a rust based wasm accessibility runner we have a high surface level of getting 35ms audits [medium](./tests/mock.rs) case
from the fastesdt audits `fast_htmlcs` 200-300ms on a large run. This leads us to the possibility of porting this over
to get the drastic benefits from a re-write.
1 change: 1 addition & 0 deletions kayle_innate/src/engine/rules.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// accessibility rules todo:
1 change: 1 addition & 0 deletions kayle_innate/src/i18n/locales.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// support for based locales in readme
68 changes: 34 additions & 34 deletions kayle_innate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ extern crate lazy_static;
mod utils;
use case_insensitive_string::CaseInsensitiveString;
use std::collections::HashSet;
use utils::{convert_abs_path, convert_base_path, set_panic_hook, domain_name};
use utils::{convert_abs_path, convert_base_path, domain_name, set_panic_hook};
use wasm_bindgen::prelude::*;

#[cfg(feature = "wee_alloc")]
Expand Down Expand Up @@ -124,12 +124,14 @@ pub fn get_document_links(res: &str, domain: &str) -> Box<[JsValue]> {
links.into_boxed_slice()
}

#[wasm_bindgen]
// RUST_LOG=info wasm-pack test --firefox --headless --features accessibility --release
#[cfg(feature = "accessibility")]
/// try to fix all possible issues using a spec against the tree.
pub fn parse_accessibility_tree(html: &str) {
pub fn parse_accessibility_tree(
html: &str,
) -> std::collections::BTreeMap<String, Vec<scraper::node::Element>> {
set_panic_hook();
use std::collections::BTreeMap;

#[wasm_bindgen]
extern "C" {
Expand Down Expand Up @@ -158,60 +160,58 @@ pub fn parse_accessibility_tree(html: &str) {
// The chrome browser we can set to ignore all assets and fetch them here but, it would be re-doing the wheel.
// If we can send the Stylesheets from node to rust this could leverage the sheets attached since we just need the node references.

let mut n = 0;
let t = now();
let mut n = 0;
let mut accessibility_tree: BTreeMap<Option<&str>, Vec<_>> = BTreeMap::new();
let d = select::document::Document::from(html);

// measure select parsing doc 1:1 around 34ms - gets slower when using methods possibly due to clones
while let Some(node) = select::document::Document::from(html).nth(n) {
// measure select parsing doc 1:1 around 25ms
while let Some(node) = d.nth(n) {
let element_name = node.name();
console_log!("{:?}", element_name);
// console_log!("{:?}", element_name);
accessibility_tree
.entry(element_name)
.and_modify(|n| n.push(node))
.or_insert(Vec::from([node]));
n += 1;
}

console_log!("Select Parser duration {:?}ms", now() - t);
// console_log!("Tree {:?}", accessibility_tree);

let t = now();

// parse doc will start from html downwards
let h = scraper::Html::parse_document(html);
// accessibility tree for ordered element mappings
let mut accessibility_tree: BTreeMap<String, Vec<_>> = BTreeMap::new();
let mut hh = h.tree.nodes();

// measure select parsing doc 1:1 around 10ms
while let Some(node) = hh.next() {
if let Some(element) = node.value().as_element() {
let element_name = element.name();
console_log!("{:?}", element_name);
// console_log!("{:?}", element_name);
accessibility_tree
.entry(element_name.to_string())
.and_modify(|n| n.push(element.to_owned()))
.or_insert(Vec::from([element.to_owned()]));
}
}
// "html"
// "head"
// "title"
// "meta"
// "link"
// "style"
// "body"
// "header"
// "nav"
// "a"
// "a"
// "main"
// "h1"
// "p"
// "input"
// "footer"
// "ul"
// "li"

console_log!("Scraper Parser: duration {:?}ms", now() - t);
console_log!("Getting tree links {:?}", accessibility_tree.get("a"));

accessibility_tree
// console_log!("Tree {:?}", accessibility_tree);
}

#[wasm_bindgen]
/// use gpu to accelerate layout rendering or workers.
pub fn validate_node() {
todo!("It will validate a node whether accessibility checks should arise.")
/// audit a web page passing the html and css rules.
pub fn audit(html: &str, _css_rules: &str) {
let _tree = parse_accessibility_tree(&html);
}

#[wasm_bindgen]
/// Perform the a judgement against a page to determine effort, access, and more.
pub fn judge() {
todo!("Determine the score of the website after the tree was built.")
/// parse css tree to maps
pub fn parse_css(_css: &str) {
// parse the css to a list of nodes capable of o1 getting results
}
Loading

0 comments on commit 27046b6

Please sign in to comment.