diff --git a/src/core/xfa/template.js b/src/core/xfa/template.js index d1c7f5e03..9fb3bf6ee 100644 --- a/src/core/xfa/template.js +++ b/src/core/xfa/template.js @@ -118,6 +118,9 @@ const MAX_ATTEMPTS_FOR_LRTB_LAYOUT = 2; // the loop after having MAX_EMPTY_PAGES empty pages. const MAX_EMPTY_PAGES = 3; +// Default value to start with for the tabIndex property. +const DEFAULT_TAB_INDEX = 5000; + function getBorderDims(node) { if (!node || !node.border) { return { w: 0, h: 0 }; @@ -173,7 +176,12 @@ function* getContainedChildren(node) { function setTabIndex(node) { while (node) { - if (!node.traversal || node[$tabIndex]) { + if (!node.traversal) { + node[$tabIndex] = node[$getParent]()[$tabIndex]; + return; + } + + if (node[$tabIndex]) { return; } @@ -186,6 +194,7 @@ function setTabIndex(node) { } if (!next || !next.ref) { + node[$tabIndex] = node[$getParent]()[$tabIndex]; return; } @@ -1755,6 +1764,8 @@ class Draw extends XFAObject { } [$toHTML](availableSpace) { + setTabIndex(this); + if (this.presence === "hidden" || this.presence === "inactive") { return HTMLResult.EMPTY; } @@ -2309,6 +2320,7 @@ class ExclGroup extends XFAObject { } [$toHTML](availableSpace) { + setTabIndex(this); if ( this.presence === "hidden" || this.presence === "inactive" || @@ -2611,6 +2623,8 @@ class Field extends XFAObject { } [$toHTML](availableSpace) { + setTabIndex(this); + if (!this.ui) { // It's allowed to not have an ui, specs say: // If the UI element contains no children or is not present, @@ -2642,7 +2656,6 @@ class Field extends XFAObject { this.ui[$appendChild](node); } - setTabIndex(this); if ( !this.ui || this.presence === "hidden" || @@ -4833,6 +4846,8 @@ class Subform extends XFAObject { } [$toHTML](availableSpace) { + setTabIndex(this); + if (this.break) { // break element is deprecated so plug it on one of its replacement // breakBefore or breakAfter. @@ -5255,7 +5270,7 @@ class Template extends XFAObject { if (this.subform.children.length >= 2) { warn("XFA - Several subforms in template node: please file a bug."); } - this[$tabIndex] = 1000; + this[$tabIndex] = DEFAULT_TAB_INDEX; } [$isSplittable]() { diff --git a/src/display/annotation_layer.js b/src/display/annotation_layer.js index 5bcb5bd82..2fdd7fcab 100644 --- a/src/display/annotation_layer.js +++ b/src/display/annotation_layer.js @@ -33,6 +33,8 @@ import { import { AnnotationStorage } from "./annotation_storage.js"; import { ColorConverters } from "../shared/scripting_utils.js"; +const DEFAULT_TAB_INDEX = 1000; + /** * @typedef {Object} AnnotationElementParameters * @property {Object} data @@ -722,6 +724,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement { element.type = "text"; element.setAttribute("value", textContent); } + element.tabIndex = DEFAULT_TAB_INDEX; elementData.userValue = textContent; element.setAttribute("id", id); @@ -978,6 +981,7 @@ class CheckboxWidgetAnnotationElement extends WidgetAnnotationElement { element.setAttribute("checked", true); } element.setAttribute("id", id); + element.tabIndex = DEFAULT_TAB_INDEX; element.addEventListener("change", function (event) { const name = event.target.name; @@ -1052,6 +1056,7 @@ class RadioButtonWidgetAnnotationElement extends WidgetAnnotationElement { element.setAttribute("checked", true); } element.setAttribute("id", id); + element.tabIndex = DEFAULT_TAB_INDEX; element.addEventListener("change", function (event) { const { target } = event; @@ -1148,6 +1153,7 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { selectElement.disabled = this.data.readOnly; selectElement.name = this.data.fieldName; selectElement.setAttribute("id", id); + selectElement.tabIndex = DEFAULT_TAB_INDEX; selectElement.style.fontSize = `${fontSize}px`; diff --git a/web/annotation_layer_builder.css b/web/annotation_layer_builder.css index 571e7a7f5..f0201cd32 100644 --- a/web/annotation_layer_builder.css +++ b/web/annotation_layer_builder.css @@ -95,6 +95,14 @@ border: 1px solid transparent; } +.annotationLayer .textWidgetAnnotation input :focus, +.annotationLayer .textWidgetAnnotation textarea :focus, +.annotationLayer .choiceWidgetAnnotation select :focus, +.annotationLayer .buttonWidgetAnnotation.checkBox :focus, +.annotationLayer .buttonWidgetAnnotation.radioButton :focus { + outline: auto; +} + .annotationLayer .buttonWidgetAnnotation.checkBox input:checked:before, .annotationLayer .buttonWidgetAnnotation.checkBox input:checked:after, .annotationLayer .buttonWidgetAnnotation.radioButton input:checked:before { diff --git a/web/xfa_layer_builder.css b/web/xfa_layer_builder.css index 9252a7a4c..192d09ec4 100644 --- a/web/xfa_layer_builder.css +++ b/web/xfa_layer_builder.css @@ -155,7 +155,13 @@ .xfaSelect:focus { background-image: none; background-color: transparent; - outline: none; + outline: auto; + outline-offset: -1px; +} + +.xfaCheckbox:focus, +.xfaRadio:focus { + outline: auto; } .xfaTextfield,