Skip to content

Commit

Permalink
Fix shadow engine bug (#745)
Browse files Browse the repository at this point in the history
* Fix shadow engine bug

* Update rule instructions

* Don't mix vars between DOMWalker and NodeWalker
  • Loading branch information
tombrunet authored Mar 17, 2022
1 parent e45608e commit c881497
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 71 deletions.
64 changes: 25 additions & 39 deletions accessibility-checker-engine/README-RULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Multiple objects are needed for a rule to fire and show up in the tool results:

### Rule object

The basic rule format is defined by the Rule type in [src/v2/api/IEngine.ts](src/v2/api/IEngine.ts). Rule implementation is located in [src/v2/checker/accessibility/rules](src/v2/checker/accessibility/rules). The rule context, including DOM object hierarchies, attributes, explicit/implicit CSS and ARIA attributes, that may trigger a rule, are defined in [src/v2/common/Context.ts](src/v2/common/Context.ts). The rule results can be one of:
The basic rule format is defined by the Rule type in [src/v4/api/IRule.ts](src/v4/api/IRule.ts). Rule implementation is located in [src/v4/rules](src/v4/rules). The rule context, including DOM object hierarchies, attributes, explicit/implicit CSS and ARIA attributes, that may trigger a rule, are defined in [src/v2/common/Context.ts](src/v2/common/Context.ts). The rule results can be one of:
* RulePass("MSG_ID")
* RuleFail("MSG_ID")
* RulePotential("MSG_ID")
Expand All @@ -26,8 +26,26 @@ An example rule might look like:
{
id: "TRIGGER_ALL_BODY",
context: "dom:body",
run: (context: RuleContext,
options?: {}): RuleResult | RuleResult[] => {
help: {
"en-US": {
0: `TRIGGER_ALL_BODY.html`,
"Pass_0": `TRIGGER_ALL_BODY.html`
}
},
messages: {
"en-US": {
"group": "Grouping label for the rule",
"Pass_0": "Check the body element for something"
}
},
rulesets: [{
id: [ "IBM_Accessibility", "WCAG_2_0", "WCAG_2_1"],
num: "2.1.1", // num: [ "2.4.4", "x.y.z" ] also allowed
level: eRulePolicy.RECOMMENDATION,
toolkitLevel: eToolkitLevel.LEVEL_FOUR
}],
act: {},
run: (context: RuleContext, options?: {}, contextHierarchies?: RuleContextHierarchy): RuleResult | RuleResult[] => {
const ruleContext = context["dom"].node as Element;
const domAttrs = context["dom"].attributes;
Expand All @@ -36,39 +54,7 @@ An example rule might look like:
}
```

### Ruleset mapping

Rules are mapped to rulesets based on checkpoints. A rule may be mapped to one or more rulesets, and a ruleset may include one or more rules. The ruleset mappings are defined in [src/v2/checker/accessibility/rulesets/index.ts](src/v2/checker/accessibility/rulesets/index.ts). Rules are added to an appropriate checkpoint section with a mapping such as:
```
{
id: "TRIGGER_ALL_BODY",
level: eRulePolicy.VIOLATION,
toolkitLevel: eToolkitLevel.LEVEL_ONE
}
```

### Messages

Each rule message is a short description of the result of a rule execution. Message mappings are defined in [src/v2/checker/accessibility/nls/index.ts](src/v2/checker/accessibility/nls/index.ts). Mappings are defined as:
```
"TRIGGER_ALL_BODY": {
0: "Passive message used for rule groupings",
"Pass_0": "Message with message code PASS_0 and arguments {0}, {1}, etc",
"Fail_1": "Another message with message code Fail_1."
},
```

### Help file

Each rule has its own help file in .mdx format. A help file contains rule description and examples. The rule help files are located in [help](help). The mapping between a rule and its help file is defined in [src/v2/checker/accessibility/help/index.ts](src/v2/checker/accessibility/help/index.ts):

```
"TRIGGER_ALL_BODY": {
0: `${Config.helpRoot}/Rpt_Aria_MultipleApplicationLandmarks`,
"Pass_0": `${Config.helpRoot}/Rpt_Aria_MultipleApplicationLandmarks`,
"Fail_1": `${Config.helpRoot}/Rpt_Aria_MultipleApplicationLandmarks`
}
```
Help files are found in [help-v4](help-v4).

## Test cases

Expand Down Expand Up @@ -123,7 +109,7 @@ Then, run `npm test` again.

You can run test cases to verify a rule implementation, or you can deploy the rules to a local rule server, and then build the browser extension to access the rules deployed in the local server to test. The steps to use a local server are:

* Build and start rule server. In `rule-server` run `npm run start` or without help `npm run start:nohelp`.
* Build and start rule server. In `rule-server` run `npm run start`.
* Load `https://localhost:9445/` in the browser and type `thisisunsafe` to bypass cert warnings.
* Build extension. In `accessibility-checker-extension` run `npm run build:watch:local`.
* Add the extension in the `accessibility-checker-extension/dist` directory to Chrome. It will have the `(local)` label on the DevTools tab.
Expand All @@ -134,7 +120,7 @@ Note: Rule changes are not automatically rebuilt. You will have to kill the rule

* Create a rule id for a new rule.
* Create the rule and ruleset mapping to [src/v2/checker/accessibility/rulesets/index.ts](src/v2/checker/accessibility/rulesets/index.ts).
* Create the <rule id>.mdx help file in [help](help), and add the rule and the help file mapping to [src/v2/checker/accessibility/help/index.ts](src/v2/checker/accessibility/help/index.ts).
* Create the rule implementation in [src/v2/checker/accessibility/rules](src/v2/checker/accessibility/rules). The rule implementation includes the rule context, logic and outcome (Pass or Fail).
* Create the help file in [help-v4](help-v4).
* Create the rule implementation in [src/v4/rules](src/v4/rules). The rule implementation includes the rule context, logic and outcome (Pass or Fail).
* Create test cases for the rule in [test/v2/checker/accessibility/rules](test/v2/checker/accessibility/rules).
* Test the rules with the test cases. You may run the test cases locally, or run with the local rule server.
Original file line number Diff line number Diff line change
Expand Up @@ -1374,7 +1374,7 @@ export class RPTUtil {
while (nw.nextNode()) {
if (formElements.includes(nw.node.nodeName.toLowerCase())) {
if (RPTUtil.isNodeDisabled(nw.node))
return true;
return true;
return false;
}
}
Expand Down Expand Up @@ -2425,14 +2425,14 @@ export class RPTUtil {

// ignore aria-level, aria-setsize or aria-posinset if "row" is not in treegrid
if (permittedRoles.includes("row") && RPTUtil.getAncestorWithRole(ruleContext, "treegrid", true) == null ) {
let index = -1;
if ((index = allowedAttributes.indexOf("aria-level")) > -1)
let index = -1;
if ((index = allowedAttributes.indexOf("aria-level")) > -1)
allowedAttributes.splice(index, 1);

if ((index = allowedAttributes.indexOf("aria-setsize")) > -1)
if ((index = allowedAttributes.indexOf("aria-setsize")) > -1)
allowedAttributes.splice(index, 1);

if ((index = allowedAttributes.indexOf("aria-posinset")) > -1)
if ((index = allowedAttributes.indexOf("aria-posinset")) > -1)
allowedAttributes.splice(index, 1);

}
Expand Down Expand Up @@ -3455,55 +3455,49 @@ export class NodeWalker {
{
let ownerElement = this.node;
this.node = iframeNode.contentDocument.documentElement;
(this.node as any).ownerElement = ownerElement;
(this.node as any).nwOwnerElement = ownerElement;
} else if (this.node.nodeType === 1 /* Node.ELEMENT_NODE */
&& elementNode.shadowRoot
&& elementNode.shadowRoot.firstChild)
{
let ownerElement = this.node;
this.node = elementNode.shadowRoot;
(this.node as any).ownerElement = ownerElement;
(this.node as any).nwOwnerElement = ownerElement;
} else if (this.node.nodeType === 1
&& elementNode.nodeName.toLowerCase() === "slot"
&& slotElement.assignedNodes().length > 0)
{
let slotOwner = this.node;
this.node = slotElement.assignedNodes()[0];
(this.node as any).slotOwner = slotOwner;
(this.node as any).nwSlotOwner = slotOwner;
(this.node as any).nwSlotIndex = 0;
} else if (this.node.firstChild) {
this.node = this.node.firstChild;
} else {
this.bEndTag = true;
return this.nextNode();
}
} else {
if (this.node.nextSibling) {
this.node = this.node.nextSibling;
this.bEndTag = false;
} else if ((this.node as any).ownerElement) {
this.node = (this.node as any).ownerElement;
this.bEndTag = true;
} else if ((this.node as any).slotOwner) {
if (this.node.nodeType !== 1 || !(this.node as HTMLElement).hasAttribute("slot")) {
// If this wasn't a named slot, look for the next unnamed node to put in the slot
let n = this.node.nextSibling;
while (n && this.node.nodeType === 1 && (this.node as HTMLElement).hasAttribute("slot")) {
n = this.node.nextSibling;
}
if (n) {
// We found another unnamed slot
let slotOwner = (this.node as any).slotOwner;
this.node = n;
(this.node as any).slotOwner = slotOwner;
this.bEndTag = false;
} else {
this.node = (this.node as any).slotOwner;
this.bEndTag = true;
}
if ((this.node as any).nwSlotOwner) {
let slotOwner = (this.node as any).nwSlotOwner;
let nextSlotIndex = (this.node as any).nwSlotIndex+1;
delete (this.node as any).nwSlotOwner;
delete (this.node as any).nwSlotIndex;
if (nextSlotIndex < slotOwner.assignedNodes().length) {
this.node = slotOwner.assignedNodes()[nextSlotIndex];
(this.node as any).nwSlotOwner = slotOwner;
(this.node as any).nwSlotIndex = nextSlotIndex;
this.bEndTag = false;
} else {
this.node = (this.node as any).slotOwner;
this.node = slotOwner;
this.bEndTag = true;
}
} else if ((this.node as any).nwOwnerElement) {
this.node = (this.node as any).nwOwnerElement;
this.bEndTag = true;
} else if (this.node.nextSibling) {
this.node = this.node.nextSibling;
this.bEndTag = false;
} else if (this.node.parentNode) {
this.node = this.node.parentNode;
this.bEndTag = true;
Expand Down
2 changes: 2 additions & 0 deletions accessibility-checker-engine/src/v2/dom/DOMWalker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ export class DOMWalker {
} else if ((this.node as any).slotOwner) {
let slotOwner = (this.node as any).slotOwner;
let nextSlotIndex = (this.node as any).slotIndex+1;
delete (this.node as any).slotOwner;
delete (this.node as any).slotIndex;
if (nextSlotIndex < slotOwner.assignedNodes().length) {
this.node = slotOwner.assignedNodes()[nextSlotIndex];
(this.node as any).slotOwner = slotOwner;
Expand Down
1 change: 1 addition & 0 deletions rule-server/gulp/gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ const archivePolicies = () => {
latestArchive.policies = latestPol;
latestArchive.rulesets = latestRS;
latestArchive.version = latestVersion;
latestArchive.latest = true;
}
if (latestVersion !== releaseTag) {
previewArchive.version = releaseTag;
Expand Down

0 comments on commit c881497

Please sign in to comment.