govuk_publishing_components 21.16.3 → 21.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (230) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/component_guide/accessibility-test.js +0 -6
  3. data/app/views/govuk_publishing_components/components/docs/machine_readable_metadata.yml +7 -0
  4. data/lib/govuk_publishing_components/presenters/machine_readable/dataset_schema.rb +34 -0
  5. data/lib/govuk_publishing_components/presenters/schema_org.rb +3 -0
  6. data/lib/govuk_publishing_components/version.rb +1 -1
  7. data/node_modules/axe-core/CHANGELOG.md +166 -2
  8. data/node_modules/axe-core/CONTRIBUTING.md +5 -5
  9. data/node_modules/axe-core/README.md +4 -4
  10. data/node_modules/axe-core/axe.d.ts +27 -11
  11. data/node_modules/axe-core/axe.js +9597 -2431
  12. data/node_modules/axe-core/axe.min.js +2 -2
  13. data/node_modules/axe-core/bower.json +1 -1
  14. data/node_modules/axe-core/doc/API.md +211 -128
  15. data/node_modules/axe-core/doc/accessibility-supported.md +1 -1
  16. data/node_modules/axe-core/doc/aria-supported.md +4 -13
  17. data/node_modules/axe-core/doc/backwards-compatibility-doc.md +93 -0
  18. data/node_modules/axe-core/doc/code-submission-guidelines.md +4 -4
  19. data/node_modules/axe-core/doc/developer-guide.md +27 -13
  20. data/node_modules/axe-core/doc/examples/chrome-debugging-protocol/package.json +5 -2
  21. data/node_modules/axe-core/doc/examples/jasmine/README.md +3 -5
  22. data/node_modules/axe-core/doc/examples/jasmine/karma.conf.js +29 -0
  23. data/node_modules/axe-core/doc/examples/jasmine/package.json +6 -5
  24. data/node_modules/axe-core/doc/examples/jest_react/README.md +1 -1
  25. data/node_modules/axe-core/doc/examples/jest_react/link.test.js +3 -3
  26. data/node_modules/axe-core/doc/examples/jest_react/package.json +9 -9
  27. data/node_modules/axe-core/doc/examples/jsdom/package.json +15 -0
  28. data/node_modules/axe-core/doc/examples/mocha/README.md +5 -7
  29. data/node_modules/axe-core/doc/examples/mocha/karma.conf.js +29 -0
  30. data/node_modules/axe-core/doc/examples/mocha/package.json +7 -6
  31. data/node_modules/axe-core/doc/examples/phantomjs/README.md +3 -3
  32. data/node_modules/axe-core/doc/examples/phantomjs/axe-phantom.js +4 -2
  33. data/node_modules/axe-core/doc/examples/phantomjs/package.json +3 -3
  34. data/node_modules/axe-core/doc/examples/puppeteer/package.json +5 -2
  35. data/node_modules/axe-core/doc/examples/qunit/README.md +2 -2
  36. data/node_modules/axe-core/doc/examples/qunit/package.json +2 -2
  37. data/node_modules/axe-core/doc/examples/test-examples.js +32 -0
  38. data/node_modules/axe-core/doc/plugins.md +10 -10
  39. data/node_modules/axe-core/doc/projects.md +12 -8
  40. data/node_modules/axe-core/doc/rule-descriptions.md +87 -79
  41. data/node_modules/axe-core/doc/rule-development.md +30 -2
  42. data/node_modules/axe-core/lib/checks/aria/allowed-attr.js +1 -1
  43. data/node_modules/axe-core/lib/checks/aria/aria-roledescription.js +14 -0
  44. data/node_modules/axe-core/lib/checks/aria/aria-roledescription.json +23 -0
  45. data/node_modules/axe-core/lib/checks/aria/no-implicit-explicit-label.js +21 -0
  46. data/node_modules/axe-core/lib/checks/aria/no-implicit-explicit-label.json +11 -0
  47. data/node_modules/axe-core/lib/checks/aria/required-attr.js +32 -7
  48. data/node_modules/axe-core/lib/checks/aria/required-children.js +40 -14
  49. data/node_modules/axe-core/lib/checks/aria/required-children.json +12 -1
  50. data/node_modules/axe-core/lib/checks/aria/required-parent.js +1 -1
  51. data/node_modules/axe-core/lib/checks/aria/unsupportedattr.js +1 -1
  52. data/node_modules/axe-core/lib/checks/aria/valid-attr-value.js +50 -17
  53. data/node_modules/axe-core/lib/checks/aria/valid-attr-value.json +2 -1
  54. data/node_modules/axe-core/lib/checks/aria/valid-attr.js +1 -1
  55. data/node_modules/axe-core/lib/checks/color/color-contrast.js +2 -2
  56. data/node_modules/axe-core/lib/checks/forms/autocomplete-appropriate.js +4 -4
  57. data/node_modules/axe-core/lib/checks/forms/autocomplete-valid.js +1 -1
  58. data/node_modules/axe-core/lib/checks/forms/fieldset.json +1 -0
  59. data/node_modules/axe-core/lib/checks/forms/group-labelledby.json +1 -0
  60. data/node_modules/axe-core/lib/checks/keyboard/focusable-content.js +16 -0
  61. data/node_modules/axe-core/lib/checks/keyboard/focusable-content.json +11 -0
  62. data/node_modules/axe-core/lib/checks/keyboard/focusable-element.js +12 -0
  63. data/node_modules/axe-core/lib/checks/keyboard/focusable-element.json +11 -0
  64. data/node_modules/axe-core/lib/checks/keyboard/tabindex.js +6 -1
  65. data/node_modules/axe-core/lib/checks/label/alt-space-value.js +3 -2
  66. data/node_modules/axe-core/lib/checks/label/duplicate-img-label.js +18 -15
  67. data/node_modules/axe-core/lib/checks/label/label-content-name-mismatch.js +13 -3
  68. data/node_modules/axe-core/lib/checks/label/label-content-name-mismatch.json +4 -0
  69. data/node_modules/axe-core/lib/checks/label/multiple-label.js +22 -12
  70. data/node_modules/axe-core/lib/checks/label/multiple-label.json +1 -1
  71. data/node_modules/axe-core/lib/checks/landmarks/landmark-is-unique-after.js +23 -0
  72. data/node_modules/axe-core/lib/checks/landmarks/landmark-is-unique.js +7 -0
  73. data/node_modules/axe-core/lib/checks/landmarks/landmark-is-unique.json +12 -0
  74. data/node_modules/axe-core/lib/checks/lists/listitem.js +1 -0
  75. data/node_modules/axe-core/lib/checks/lists/listitem.json +1 -1
  76. data/node_modules/axe-core/lib/checks/lists/only-listitems.js +0 -4
  77. data/node_modules/axe-core/lib/checks/mobile/css-orientation-lock.js +8 -6
  78. data/node_modules/axe-core/lib/checks/navigation/region.js +2 -19
  79. data/node_modules/axe-core/lib/checks/shared/avoid-inline-spacing.js +18 -0
  80. data/node_modules/axe-core/lib/checks/shared/avoid-inline-spacing.json +11 -0
  81. data/node_modules/axe-core/lib/checks/shared/exists.js +1 -1
  82. data/node_modules/axe-core/lib/checks/shared/exists.json +1 -1
  83. data/node_modules/axe-core/lib/checks/shared/has-alt.js +6 -4
  84. data/node_modules/axe-core/lib/checks/shared/non-empty-alt.js +1 -1
  85. data/node_modules/axe-core/lib/checks/tables/caption-faked.json +1 -1
  86. data/node_modules/axe-core/lib/checks/tables/td-has-header.js +5 -4
  87. data/node_modules/axe-core/lib/checks/tables/th-has-data-cells.js +19 -29
  88. data/node_modules/axe-core/lib/commons/aria/get-owned-virtual.js +1 -1
  89. data/node_modules/axe-core/lib/commons/aria/index.js +50 -46
  90. data/node_modules/axe-core/lib/commons/aria/is-accessible-ref.js +41 -37
  91. data/node_modules/axe-core/lib/commons/aria/label-virtual.js +2 -2
  92. data/node_modules/axe-core/lib/commons/aria/roles.js +1 -1
  93. data/node_modules/axe-core/lib/commons/aria/validate-attr-value.js +0 -1
  94. data/node_modules/axe-core/lib/commons/color/center-point-of-rect.js +30 -0
  95. data/node_modules/axe-core/lib/commons/color/contrast.js +7 -1
  96. data/node_modules/axe-core/lib/commons/color/element-has-image.js +36 -0
  97. data/node_modules/axe-core/lib/commons/color/get-background-color.js +332 -306
  98. data/node_modules/axe-core/lib/commons/color/get-foreground-color.js +35 -6
  99. data/node_modules/axe-core/lib/commons/color/get-own-background-color.js +22 -0
  100. data/node_modules/axe-core/lib/commons/dom/find-up.js +5 -5
  101. data/node_modules/axe-core/lib/commons/dom/get-scroll-offset.js +0 -1
  102. data/node_modules/axe-core/lib/commons/dom/get-tabbable-elements.js +1 -1
  103. data/node_modules/axe-core/lib/commons/dom/has-content-virtual.js +7 -5
  104. data/node_modules/axe-core/lib/commons/dom/is-focusable.js +8 -5
  105. data/node_modules/axe-core/lib/commons/dom/is-hidden-with-css.js +15 -2
  106. data/node_modules/axe-core/lib/commons/dom/is-in-text-block.js +1 -2
  107. data/node_modules/axe-core/lib/commons/dom/is-skip-link.js +45 -0
  108. data/node_modules/axe-core/lib/commons/dom/is-visible.js +43 -17
  109. data/node_modules/axe-core/lib/commons/dom/is-visual-content.js +0 -1
  110. data/node_modules/axe-core/lib/commons/dom/visually-contains.js +0 -1
  111. data/node_modules/axe-core/lib/commons/dom/visually-overlaps.js +0 -1
  112. data/node_modules/axe-core/lib/commons/forms/index.js +8 -0
  113. data/node_modules/axe-core/lib/commons/forms/is-aria-combobox.js +13 -0
  114. data/node_modules/axe-core/lib/commons/forms/is-aria-listbox.js +13 -0
  115. data/node_modules/axe-core/lib/commons/forms/is-aria-range.js +14 -0
  116. data/node_modules/axe-core/lib/commons/forms/is-aria-textbox.js +13 -0
  117. data/node_modules/axe-core/lib/commons/forms/is-native-select.js +13 -0
  118. data/node_modules/axe-core/lib/commons/forms/is-native-textbox.js +28 -0
  119. data/node_modules/axe-core/lib/commons/table/get-cell-position.js +2 -2
  120. data/node_modules/axe-core/lib/commons/table/get-headers.js +55 -11
  121. data/node_modules/axe-core/lib/commons/table/get-scope.js +1 -1
  122. data/node_modules/axe-core/lib/commons/table/is-data-table.js +0 -1
  123. data/node_modules/axe-core/lib/commons/table/to-grid.js +2 -2
  124. data/node_modules/axe-core/lib/commons/text/accessible-text-virtual.js +5 -5
  125. data/node_modules/axe-core/lib/commons/text/form-control-value.js +18 -30
  126. data/node_modules/axe-core/lib/commons/text/is-icon-ligature.js +210 -0
  127. data/node_modules/axe-core/lib/commons/text/is-valid-autocomplete.js +0 -1
  128. data/node_modules/axe-core/lib/commons/text/label-text.js +1 -1
  129. data/node_modules/axe-core/lib/commons/text/label-virtual.js +1 -1
  130. data/node_modules/axe-core/lib/commons/text/native-text-methods.js +1 -1
  131. data/node_modules/axe-core/lib/commons/text/subtree-text.js +3 -3
  132. data/node_modules/axe-core/lib/commons/text/unicode.js +15 -0
  133. data/node_modules/axe-core/lib/commons/text/visible-text-nodes.js +25 -0
  134. data/node_modules/axe-core/lib/commons/text/visible-virtual.js +1 -1
  135. data/node_modules/axe-core/lib/core/base/audit.js +90 -15
  136. data/node_modules/axe-core/lib/core/base/cache.js +33 -0
  137. data/node_modules/axe-core/lib/core/base/check.js +48 -1
  138. data/node_modules/axe-core/lib/core/base/context.js +15 -14
  139. data/node_modules/axe-core/lib/core/base/rule.js +223 -46
  140. data/node_modules/axe-core/lib/core/base/virtual-node/abstract-virtual-node.js +40 -0
  141. data/node_modules/axe-core/lib/core/base/virtual-node/serial-virtual-node.js +86 -0
  142. data/node_modules/axe-core/lib/core/base/virtual-node/virtual-node.js +85 -0
  143. data/node_modules/axe-core/lib/core/constants.js +10 -2
  144. data/node_modules/axe-core/lib/core/imports/index.js +28 -3
  145. data/node_modules/axe-core/lib/core/index.js +2 -4
  146. data/node_modules/axe-core/lib/core/public/configure.js +28 -1
  147. data/node_modules/axe-core/lib/core/public/run-rules.js +2 -0
  148. data/node_modules/axe-core/lib/core/public/run-virtual-rule.js +50 -0
  149. data/node_modules/axe-core/lib/core/public/run.js +13 -2
  150. data/node_modules/axe-core/lib/core/reporters/helpers/process-aggregate.js +1 -1
  151. data/node_modules/axe-core/lib/core/reporters/na.js +4 -0
  152. data/node_modules/axe-core/lib/core/reporters/raw-env.js +12 -0
  153. data/node_modules/axe-core/lib/core/reporters/raw.js +25 -1
  154. data/node_modules/axe-core/lib/core/utils/are-styles-set.js +4 -7
  155. data/node_modules/axe-core/lib/core/utils/assert.js +12 -0
  156. data/node_modules/axe-core/lib/core/utils/collect-results-from-frames.js +2 -2
  157. data/node_modules/axe-core/lib/core/utils/contains.js +27 -13
  158. data/node_modules/axe-core/lib/core/utils/css-parser.js +2 -0
  159. data/node_modules/axe-core/lib/core/utils/element-matches.js +5 -1
  160. data/node_modules/axe-core/lib/core/utils/escape-selector.js +1 -2
  161. data/node_modules/axe-core/lib/core/utils/flattened-tree.js +47 -61
  162. data/node_modules/axe-core/lib/core/utils/get-check-option.js +0 -1
  163. data/node_modules/axe-core/lib/core/utils/get-friendly-uri-end.js +1 -1
  164. data/node_modules/axe-core/lib/core/utils/get-node-attributes.js +21 -0
  165. data/node_modules/axe-core/lib/core/utils/get-scroll.js +39 -0
  166. data/node_modules/axe-core/lib/core/utils/get-selector.js +9 -6
  167. data/node_modules/axe-core/lib/core/utils/get-stylesheet-factory.js +51 -0
  168. data/node_modules/axe-core/lib/core/utils/get-xpath.js +0 -1
  169. data/node_modules/axe-core/lib/core/utils/is-hidden.js +16 -4
  170. data/node_modules/axe-core/lib/core/utils/is-html-element.js +5 -5
  171. data/node_modules/axe-core/lib/core/utils/is-shadow-root.js +3 -3
  172. data/node_modules/axe-core/lib/core/utils/memoize.js +17 -0
  173. data/node_modules/axe-core/lib/core/utils/parse-crossorigin-stylesheet.js +53 -0
  174. data/node_modules/axe-core/lib/core/utils/parse-sameorigin-stylesheet.js +96 -0
  175. data/node_modules/axe-core/lib/core/utils/parse-stylesheet.js +70 -0
  176. data/node_modules/axe-core/lib/core/utils/performance-timer.js +7 -2
  177. data/node_modules/axe-core/lib/core/utils/preload-cssom.js +77 -281
  178. data/node_modules/axe-core/lib/core/utils/preload.js +49 -23
  179. data/node_modules/axe-core/lib/core/utils/qsa.js +39 -50
  180. data/node_modules/axe-core/lib/core/utils/respondable.js +20 -3
  181. data/node_modules/axe-core/lib/core/utils/rule-should-run.js +0 -1
  182. data/node_modules/axe-core/lib/core/utils/scroll-state.js +12 -25
  183. data/node_modules/axe-core/lib/core/utils/select.js +12 -23
  184. data/node_modules/axe-core/lib/core/utils/uuid.js +1 -2
  185. data/node_modules/axe-core/lib/intro.stub +1 -1
  186. data/node_modules/axe-core/lib/misc/incomplete-fallback.json +1 -1
  187. data/node_modules/axe-core/lib/rules/aria-allowed-attr-matches.js +1 -1
  188. data/node_modules/axe-core/lib/rules/aria-form-field-name-matches.js +50 -0
  189. data/node_modules/axe-core/lib/rules/aria-has-attr-matches.js +1 -1
  190. data/node_modules/axe-core/lib/rules/aria-hidden-focus.json +1 -1
  191. data/node_modules/axe-core/lib/rules/aria-input-field-name.json +13 -0
  192. data/node_modules/axe-core/lib/rules/aria-roledescription.json +12 -0
  193. data/node_modules/axe-core/lib/rules/aria-toggle-field-name.json +18 -0
  194. data/node_modules/axe-core/lib/rules/autocomplete-matches.js +14 -10
  195. data/node_modules/axe-core/lib/rules/avoid-inline-spacing.json +12 -0
  196. data/node_modules/axe-core/lib/rules/button-name.json +3 -5
  197. data/node_modules/axe-core/lib/rules/checkboxgroup.json +2 -1
  198. data/node_modules/axe-core/lib/rules/color-contrast-matches.js +37 -22
  199. data/node_modules/axe-core/lib/rules/duplicate-id-active-matches.js +1 -1
  200. data/node_modules/axe-core/lib/rules/duplicate-id-misc-matches.js +2 -2
  201. data/node_modules/axe-core/lib/rules/form-field-multiple-labels.json +1 -1
  202. data/node_modules/axe-core/lib/rules/html-has-lang.json +1 -0
  203. data/node_modules/axe-core/lib/rules/image-alt.json +1 -1
  204. data/node_modules/axe-core/lib/rules/img-redundant-alt.json +3 -3
  205. data/node_modules/axe-core/lib/rules/input-button-name.json +26 -0
  206. data/node_modules/axe-core/lib/rules/landmark-unique-matches.js +41 -0
  207. data/node_modules/axe-core/lib/rules/landmark-unique.json +13 -0
  208. data/node_modules/axe-core/lib/rules/link-name.json +5 -4
  209. data/node_modules/axe-core/lib/rules/meta-refresh.json +8 -1
  210. data/node_modules/axe-core/lib/rules/radiogroup.json +2 -1
  211. data/node_modules/axe-core/lib/rules/role-img-alt.json +18 -0
  212. data/node_modules/axe-core/lib/rules/scrollable-region-focusable-matches.js +30 -0
  213. data/node_modules/axe-core/lib/rules/scrollable-region-focusable.json +12 -0
  214. data/node_modules/axe-core/lib/rules/skip-link-matches.js +1 -1
  215. data/node_modules/axe-core/lib/rules/skip-link.json +1 -1
  216. data/node_modules/axe-core/lib/rules/video-description.json +3 -1
  217. data/node_modules/axe-core/locales/de.json +1 -5
  218. data/node_modules/axe-core/locales/es.json +773 -0
  219. data/node_modules/axe-core/locales/fr.json +15 -19
  220. data/node_modules/axe-core/locales/ja.json +65 -11
  221. data/node_modules/axe-core/locales/ko.json +777 -0
  222. data/node_modules/axe-core/locales/nl.json +35 -35
  223. data/node_modules/axe-core/locales/pt_BR.json +773 -0
  224. data/node_modules/axe-core/package.json +56 -52
  225. data/node_modules/axe-core/sri-history.json +20 -0
  226. data/node_modules/axe-core/typings/axe-core/axe-core-tests.ts +5 -15
  227. data/node_modules/govuk-frontend/package.json +10 -10
  228. metadata +62 -4
  229. data/node_modules/axe-core/doc/axelogo2018.png +0 -0
  230. data/node_modules/axe-core/lib/rules/role-not-button-matches.js +0 -1
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Memoize a function.
3
+ * @method memoize
4
+ * @memberof axe.utils
5
+ * @param {Function} fn Function to memoize
6
+ * @return {Function}
7
+ */
8
+ axe._memoizedFns = [];
9
+ axe.utils.memoize = function(fn) {
10
+ // keep track of each function that is memoized so it can be cleared at
11
+ // the end of a run. each memoized function has its own cache, so there is
12
+ // no method to clear all memoized caches. instead, we have to clear each
13
+ // individual memoized function ourselves.
14
+ const memoized = axe.imports.memoize(fn);
15
+ axe._memoizedFns.push(memoized);
16
+ return memoized;
17
+ };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Parse cross-origin stylesheets
3
+ *
4
+ * @method parseCrossOriginStylesheet
5
+ * @memberof axe.utils
6
+ * @param {String} url url from which to fetch stylesheet
7
+ * @param {Object} options options object from `axe.utils.parseStylesheet`
8
+ * @param {Array<Number>} priority sheet priority
9
+ * @param {Array<String>} importedUrls urls of already imported stylesheets
10
+ * @param {Boolean} isCrossOrigin boolean denoting if a stylesheet is `cross-origin`
11
+ * @returns {Promise}
12
+ */
13
+ axe.utils.parseCrossOriginStylesheet = function parseCrossOriginStylesheet(
14
+ url,
15
+ options,
16
+ priority,
17
+ importedUrls,
18
+ isCrossOrigin
19
+ ) {
20
+ const axiosOptions = {
21
+ method: 'get',
22
+ url
23
+ };
24
+
25
+ /**
26
+ * Add `url` to `importedUrls`
27
+ */
28
+ importedUrls.push(url);
29
+
30
+ /**
31
+ * Fetch `cross-origin stylesheet` via axios
32
+ */
33
+ return axe.imports.axios(axiosOptions).then(({ data }) => {
34
+ const result = options.convertDataToStylesheet({
35
+ data,
36
+ isCrossOrigin,
37
+ priority,
38
+ root: options.rootNode,
39
+ shadowId: options.shadowId
40
+ });
41
+
42
+ /**
43
+ * Parse resolved stylesheet further for any `@import` styles
44
+ */
45
+ return axe.utils.parseStylesheet(
46
+ result.sheet,
47
+ options,
48
+ priority,
49
+ importedUrls,
50
+ result.isCrossOrigin
51
+ );
52
+ });
53
+ };
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Parse non cross-origin stylesheets
3
+ *
4
+ * @method parseSameOriginStylesheet
5
+ * @memberof axe.utils
6
+ * @param {Object} sheet CSSStylesheet object
7
+ * @param {Object} options options object from `axe.utils.parseStylesheet`
8
+ * @param {Array<Number>} priority sheet priority
9
+ * @param {Array<String>} importedUrls urls of already imported stylesheets
10
+ * @param {Boolean} isCrossOrigin boolean denoting if a stylesheet is `cross-origin`
11
+ * @returns {Promise}
12
+ */
13
+ axe.utils.parseSameOriginStylesheet = function parseSameOriginStylesheet(
14
+ sheet,
15
+ options,
16
+ priority,
17
+ importedUrls,
18
+ isCrossOrigin = false
19
+ ) {
20
+ const rules = Array.from(sheet.cssRules);
21
+
22
+ if (!rules) {
23
+ return Promise.resolve();
24
+ }
25
+
26
+ /**
27
+ * reference -> https://developer.mozilla.org/en-US/docs/Web/API/CSSRule#Type_constants
28
+ */
29
+ const cssImportRules = rules.filter(r => r.type === 3); // type === 3 -> CSSRule.IMPORT_RULE
30
+
31
+ /**
32
+ * when no `@import` rules in given sheet -> resolve the current `sheet` & exit
33
+ */
34
+ if (!cssImportRules.length) {
35
+ // exit
36
+ return Promise.resolve({
37
+ isCrossOrigin,
38
+ priority,
39
+ root: options.rootNode,
40
+ shadowId: options.shadowId,
41
+ sheet
42
+ });
43
+ }
44
+
45
+ /**
46
+ * filter rules that are not already fetched
47
+ */
48
+ const cssImportUrlsNotAlreadyImported = cssImportRules
49
+ // ensure rule has a href
50
+ .filter(rule => rule.href)
51
+ // extract href from object
52
+ .map(rule => rule.href)
53
+ // only href that are not already imported
54
+ .filter(url => !importedUrls.includes(url));
55
+
56
+ /**
57
+ * iterate `@import` rules and fetch styles
58
+ */
59
+ const promises = cssImportUrlsNotAlreadyImported.map(
60
+ (importUrl, cssRuleIndex) => {
61
+ const newPriority = [...priority, cssRuleIndex];
62
+ const isCrossOriginRequest = /^https?:\/\/|^\/\//i.test(importUrl);
63
+
64
+ return axe.utils.parseCrossOriginStylesheet(
65
+ importUrl,
66
+ options,
67
+ newPriority,
68
+ importedUrls,
69
+ isCrossOriginRequest
70
+ );
71
+ }
72
+ );
73
+
74
+ const nonImportCSSRules = rules.filter(r => r.type !== 3);
75
+
76
+ // no further rules to process in this sheet
77
+ if (!nonImportCSSRules.length) {
78
+ return Promise.all(promises);
79
+ }
80
+
81
+ // convert all `nonImportCSSRules` style rules into `text` and chain
82
+
83
+ promises.push(
84
+ Promise.resolve(
85
+ options.convertDataToStylesheet({
86
+ data: nonImportCSSRules.map(rule => rule.cssText).join(),
87
+ isCrossOrigin,
88
+ priority,
89
+ root: options.rootNode,
90
+ shadowId: options.shadowId
91
+ })
92
+ )
93
+ );
94
+
95
+ return Promise.all(promises);
96
+ };
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Parse a given stylesheet
3
+ *
4
+ * @method parseStylesheet
5
+ * @memberof axe.utils
6
+ * @param {Object} sheet stylesheet to parse
7
+ * @param {Object} options configuration options object from `axe.utils.parseStylesheets`
8
+ * @param {Array<Number>} priority priority of stylesheet
9
+ * @param {Array<String>} importedUrls list of resolved `@import` urls
10
+ * @param {Boolean} isCrossOrigin boolean denoting if a stylesheet is `cross-origin`, passed for re-parsing `cross-origin` sheets
11
+ * @returns {Promise}
12
+ */
13
+ axe.utils.parseStylesheet = function parseStylesheet(
14
+ sheet,
15
+ options,
16
+ priority,
17
+ importedUrls,
18
+ isCrossOrigin = false
19
+ ) {
20
+ const isSameOrigin = isSameOriginStylesheet(sheet);
21
+ if (isSameOrigin) {
22
+ /**
23
+ * resolve `same-origin` stylesheet
24
+ */
25
+ return axe.utils.parseSameOriginStylesheet(
26
+ sheet,
27
+ options,
28
+ priority,
29
+ importedUrls,
30
+ isCrossOrigin
31
+ );
32
+ }
33
+
34
+ /**
35
+ * resolve `cross-origin` stylesheet
36
+ */
37
+ return axe.utils.parseCrossOriginStylesheet(
38
+ sheet.href,
39
+ options,
40
+ priority,
41
+ importedUrls,
42
+ true // -> isCrossOrigin
43
+ );
44
+ };
45
+
46
+ /**
47
+ * Check if a given stylesheet is from the `same-origin`
48
+ * Note:
49
+ * `sheet.cssRules` throws an error on `cross-origin` stylesheets
50
+ *
51
+ * @param {Object} sheet CSS stylesheet
52
+ * @returns {Boolean}
53
+ */
54
+ function isSameOriginStylesheet(sheet) {
55
+ try {
56
+ /*eslint no-unused-vars: 0*/
57
+ const rules = sheet.cssRules;
58
+
59
+ /**
60
+ * Safari, does not throw an error when accessing cssRules property,
61
+ */
62
+ if (!rules && sheet.href) {
63
+ return false;
64
+ }
65
+
66
+ return true;
67
+ } catch (e) {
68
+ return false;
69
+ }
70
+ }
@@ -27,7 +27,7 @@ utils.performanceTimer = (function() {
27
27
  */
28
28
  return {
29
29
  /**
30
- * @member {Function} start Kicks off performance timing for overall aXe audit
30
+ * @member {Function} start Kicks off performance timing for overall axe audit
31
31
  */
32
32
  start: function() {
33
33
  this.mark('mark_axe_start');
@@ -90,7 +90,12 @@ utils.performanceTimer = (function() {
90
90
  window.performance &&
91
91
  window.performance.getEntriesByType !== undefined
92
92
  ) {
93
- var measures = window.performance.getEntriesByType('measure');
93
+ // only output measures that were started after axe started, otherwise
94
+ // we get measures made by the page before axe ran (which is confusing)
95
+ var axeStart = window.performance.getEntriesByName('mark_axe_start')[0];
96
+ var measures = window.performance
97
+ .getEntriesByType('measure')
98
+ .filter(measure => measure.startTime >= axeStart.startTime);
94
99
  for (var i = 0; i < measures.length; ++i) {
95
100
  var req = measures[i];
96
101
  if (req.name === measureName) {
@@ -1,51 +1,41 @@
1
1
  /**
2
- * NOTE:
2
+ * NOTE:
3
3
  * this `eslint` rule is disabled because of calling `getStyleSheetFactory` before it is defined (further below).
4
4
  */
5
5
  /* eslint no-use-before-define: 0 */
6
6
 
7
-
8
7
  /**
9
8
  * Given a rootNode - construct CSSOM
10
9
  * -> get all source nodes (document & document fragments) within given root node
11
- * -> recursively call `loadCssom` to resolve styles
10
+ * -> recursively call `axe.utils.parseStylesheets` to resolve styles for each node
12
11
  *
13
12
  * @method preloadCssom
14
13
  * @memberof `axe.utils`
15
- *
16
- * @param {Object} object argument which is a composite object, with attributes timeout, treeRoot(optional), resolve & reject
17
- * @property {Number} timeout timeout for any network calls made
18
- * @property {Object} treeRoot the DOM tree to be inspected
19
- * @returns {Object} `axe.utils.queue` with CSSOM assets
14
+ * @param {Object} options composite options object
15
+ * @property {Array<String>} options.assets array of preloaded assets requested, eg: [`cssom`]
16
+ * @property {Number} options.timeout timeout
17
+ * @property {Object} options.treeRoot (optional) the DOM tree to be inspected
18
+ * @returns {Promise}
20
19
  */
21
- axe.utils.preloadCssom = function preloadCssom({
22
- timeout,
23
- treeRoot = axe._tree[0]
24
- }) {
20
+ axe.utils.preloadCssom = function preloadCssom({ treeRoot = axe._tree[0] }) {
25
21
  /**
26
22
  * get all `document` and `documentFragment` with in given `tree`
27
23
  */
28
24
  const rootNodes = getAllRootNodesInTree(treeRoot);
29
25
 
30
- const q = axe.utils.queue();
31
-
32
26
  if (!rootNodes.length) {
33
- return q;
27
+ return Promise.resolve();
34
28
  }
35
29
 
36
- const dynamicDoc = document.implementation.createHTMLDocument();
37
- const convertDataToStylesheet = getStyleSheetFactory(dynamicDoc);
30
+ const dynamicDoc = document.implementation.createHTMLDocument(
31
+ 'Dynamic document for loading cssom'
32
+ );
38
33
 
39
- q.defer((resolve, reject) => {
40
- getCssomForAllRootNodes(rootNodes, convertDataToStylesheet, timeout)
41
- .then(assets => {
42
- const cssom = processCssomAssets(assets);
43
- resolve(cssom);
44
- })
45
- .catch(reject);
46
- });
34
+ const convertDataToStylesheet = axe.utils.getStyleSheetFactory(dynamicDoc);
47
35
 
48
- return q;
36
+ return getCssomForAllRootNodes(rootNodes, convertDataToStylesheet).then(
37
+ assets => flattenAssets(assets)
38
+ );
49
39
  };
50
40
 
51
41
  /**
@@ -76,283 +66,89 @@ function getAllRootNodesInTree(tree) {
76
66
  }
77
67
 
78
68
  /**
79
- * Convert text to CSSStyleSheet
80
- * Is a factory (closure) function, initialized with `document.implementation.createHTMLDocument()` which surfaces DOM API for creating `style` elements.
81
- *
82
- * @param {Object} param `document.implementation.createHTMLDocument()
83
- * @param {Object} arg an object with properties to construct stylesheet
84
- * @property {String} arg.data text content of the stylesheet
85
- * @property {Boolean} arg.isExternal flag to notify if the resource was fetched from the network
86
- * @property {String} arg.shadowId (Optional) shadowId if shadowDOM
87
- * @property {Object} arg.root implementation document to create style elements
88
- * @property {String} arg.priority a number indicating the loaded priority of CSS, to denote specificity of styles contained in the sheet.
89
- */
90
- const getStyleSheetFactory = dynamicDoc => ({
91
- data,
92
- isExternal,
93
- shadowId,
94
- root,
95
- priority,
96
- isLink = false
97
- }) => {
98
- const style = dynamicDoc.createElement('style');
99
- if (isLink) {
100
- // as creating a stylesheet as link will need to be awaited
101
- // till `onload`, it is wise to convert link href to @import statement
102
- const text = dynamicDoc.createTextNode(`@import "${data.href}"`);
103
- style.appendChild(text);
104
- } else {
105
- style.appendChild(dynamicDoc.createTextNode(data));
106
- }
107
- dynamicDoc.head.appendChild(style);
108
- return {
109
- sheet: style.sheet,
110
- isExternal,
111
- shadowId,
112
- root,
113
- priority
114
- };
115
- };
116
-
117
- /**
118
- * Deferred function for CSSOM queue processing on all root nodes
69
+ * Process CSSOM on all root nodes
119
70
  *
120
71
  * @param {Array<Object>} rootNodes array of root nodes, where node is an enhanced `document` or `documentFragment` object returned from `getAllRootNodesInTree`
121
72
  * @param {Function} convertDataToStylesheet fn to convert given data to Stylesheet object
122
- * @returns {Object} `axe.utils.queue`
73
+ * @returns {Promise}
123
74
  */
124
- function getCssomForAllRootNodes(rootNodes, convertDataToStylesheet, timeout) {
125
- const q = axe.utils.queue();
126
-
127
- rootNodes.forEach(({ rootNode, shadowId }, index) =>
128
- q.defer((resolve, reject) =>
129
- loadCssom({
130
- rootNode,
131
- shadowId,
132
- timeout,
133
- convertDataToStylesheet,
134
- rootIndex: index + 1
135
- })
136
- .then(resolve)
137
- .catch(reject)
138
- )
139
- );
140
-
141
- return q;
142
- }
143
-
144
- /**
145
- * Process results from `loadCssom` queues of all root nodes
146
- * NOTE:
147
- * using `axe.utils.queue` from various `loadCssom` paths, returns a nested array of arrays at various depths,
148
- * hence the need to flatten arrays
149
- *
150
- * @param {Array<Object>} assets CSSOM assets for each root
151
- * @returns {Object} CSSOM object
152
- */
153
- function processCssomAssets(nestedAssets) {
154
- const result = [];
155
-
156
- nestedAssets.forEach(item => {
157
- if (Array.isArray(item)) {
158
- result.push(...processCssomAssets(item));
159
- } else {
160
- result.push(item);
161
- }
162
- });
163
-
164
- return result;
165
- }
166
-
167
- /**
168
- * Returns `axe.utils.queue` of CSSStyleSheet(s) for a given root node
169
- *
170
- * @param {Object} options configuration options
171
- * @property {Object} options.rootNode document or document fragment
172
- * @property {Number} options.rootIndex a number representing the index of the document or document fragment, used for priority computation
173
- * @property {String} options.shadowId an id if undefined denotes that given root is a document fragment/ shadowDOM
174
- * @property {Number} options.timeout abort duration for network request
175
- * @property {Function} options.convertDataToStylesheet a utility function to generate a style sheet from given data (text)
176
- * @return {Object} queue
177
- */
178
- function loadCssom(options) {
179
- const { rootIndex } = options;
180
-
181
- const q = axe.utils.queue();
182
-
183
- const sheets = getStylesheetsOfRootNode(options);
184
- if (!sheets) {
185
- return q;
186
- }
187
-
188
- sheets.forEach((sheet, sheetIndex) => {
189
- const priority = [rootIndex, sheetIndex];
190
- try {
191
- const deferredQ = parseNonCrossOriginStylesheet(sheet, options, priority);
192
- q.defer(deferredQ);
193
- } catch (e) {
194
- // cross-origin stylesheet -> make an XHR and q the response
195
- const deferredQ = parseCrossOriginStylesheet(
196
- sheet.href,
197
- options,
198
- priority
199
- );
200
- q.defer(deferredQ);
75
+ function getCssomForAllRootNodes(rootNodes, convertDataToStylesheet) {
76
+ const promises = [];
77
+
78
+ rootNodes.forEach(({ rootNode, shadowId }, index) => {
79
+ const sheets = getStylesheetsOfRootNode(
80
+ rootNode,
81
+ shadowId,
82
+ convertDataToStylesheet
83
+ );
84
+ if (!sheets) {
85
+ return Promise.all(promises);
201
86
  }
202
- });
203
-
204
- return q;
205
- }
206
-
207
- /**
208
- * Parse non cross-origin stylesheets
209
- *
210
- * @param {Object} sheet CSSStylesheet object
211
- * @param {Object} options `loadCssom` options
212
- * @param {Array<Number>} priority sheet priority
213
- */
214
- function parseNonCrossOriginStylesheet(sheet, options, priority) {
215
- const q = axe.utils.queue();
216
-
217
- /**
218
- * `sheet.cssRules` throws an error on `cross-origin` stylesheets
219
- */
220
- const cssRules = sheet.cssRules;
221
87
 
222
- const rules = Array.from(cssRules);
223
- if (!rules) {
224
- return q;
225
- }
226
-
227
- /**
228
- * reference -> https://developer.mozilla.org/en-US/docs/Web/API/CSSRule#Type_constants
229
- */
230
- const cssImportRules = rules.filter(r => r.type === 3); // type === 3 -> CSSRule.IMPORT_RULE
231
-
232
- /**
233
- * when no `@import` rules in given sheet
234
- * -> resolve the current `sheet` & exit
235
- */
236
- if (!cssImportRules.length) {
237
- q.defer(resolve =>
238
- resolve({
239
- isExternal: false,
240
- priority,
241
- root: options.rootNode,
242
- shadowId: options.shadowId,
243
- sheet
88
+ const rootIndex = index + 1;
89
+ const parseOptions = {
90
+ rootNode,
91
+ shadowId,
92
+ convertDataToStylesheet,
93
+ rootIndex
94
+ };
95
+ /**
96
+ * Note:
97
+ * `importedUrls` - keeps urls of already imported stylesheets, to prevent re-fetching
98
+ * eg: nested, cyclic or cross referenced `@import` urls
99
+ */
100
+ const importedUrls = [];
101
+
102
+ const p = Promise.all(
103
+ sheets.map((sheet, sheetIndex) => {
104
+ const priority = [rootIndex, sheetIndex];
105
+
106
+ return axe.utils.parseStylesheet(
107
+ sheet,
108
+ parseOptions,
109
+ priority,
110
+ importedUrls
111
+ );
244
112
  })
245
113
  );
246
114
 
247
- // exit
248
- return q;
249
- }
250
-
251
- /**
252
- * iterate `@import` rules and fetch styles
253
- */
254
- cssImportRules.forEach((importRule, cssRuleIndex) =>
255
- q.defer((resolve, reject) => {
256
- const importUrl = importRule.href;
257
- const newPriority = [...priority, cssRuleIndex];
258
- const axiosOptions = {
259
- method: 'get',
260
- url: importUrl,
261
- timeout: options.timeout
262
- };
263
- axe.imports
264
- .axios(axiosOptions)
265
- .then(({ data }) =>
266
- resolve(
267
- options.convertDataToStylesheet({
268
- data,
269
- isExternal: true,
270
- priority: newPriority,
271
- root: options.rootNode,
272
- shadowId: options.shadowId
273
- })
274
- )
275
- )
276
- .catch(reject);
277
- })
278
- );
279
-
280
- const nonImportCSSRules = rules.filter(r => r.type !== 3);
281
-
282
- // no further rules to process in this sheet
283
- if (!nonImportCSSRules.length) {
284
- return q;
285
- }
286
-
287
- // convert all `nonImportCSSRules` style rules into `text` and defer into queue
288
- q.defer(resolve =>
289
- resolve(
290
- options.convertDataToStylesheet({
291
- data: nonImportCSSRules.map(rule => rule.cssText).join(),
292
- isExternal: false,
293
- priority,
294
- root: options.rootNode,
295
- shadowId: options.shadowId
296
- })
297
- )
298
- );
115
+ promises.push(p);
116
+ });
299
117
 
300
- return q;
118
+ return Promise.all(promises);
301
119
  }
302
120
 
303
121
  /**
304
- * Parse cross-origin stylesheets
122
+ * Flatten CSSOM assets
305
123
  *
306
- * @param {String} url url from which to fetch stylesheet
307
- * @param {Object} options `loadCssom` options
308
- * @param {Array<Number>} priority sheet priority
124
+ * @param {Array.<Object[]>} assets nested assets (varying depth)
125
+ * @returns {Array<Object>} Array of CSSOM object
309
126
  */
310
- function parseCrossOriginStylesheet(url, options, priority) {
311
- const q = axe.utils.queue();
312
-
313
- if (!url) {
314
- return q;
315
- }
316
-
317
- const axiosOptions = {
318
- method: 'get',
319
- url,
320
- timeout: options.timeout
321
- };
322
-
323
- q.defer((resolve, reject) => {
324
- axe.imports
325
- .axios(axiosOptions)
326
- .then(({ data }) =>
327
- resolve(
328
- options.convertDataToStylesheet({
329
- data,
330
- isExternal: true,
331
- priority,
332
- root: options.rootNode,
333
- shadowId: options.shadowId
334
- })
335
- )
336
- )
337
- .catch(reject);
338
- });
339
-
340
- return q;
127
+ function flattenAssets(assets) {
128
+ return assets.reduce(
129
+ (acc, val) =>
130
+ Array.isArray(val) ? acc.concat(flattenAssets(val)) : acc.concat(val),
131
+ []
132
+ );
341
133
  }
342
134
 
343
135
  /**
344
136
  * Get stylesheet(s) for root
345
137
  *
346
- * @param {Object} options configuration options of `loadCssom`
347
- * @returns an array of stylesheets
138
+ * @param {Object} options.rootNode `document` or `documentFragment`
139
+ * @param {String} options.shadowId an id if undefined denotes that given root is a document fragment/ shadowDOM
140
+ * @param {Function} options.convertDataToStylesheet a utility function to generate a style sheet from given data (text)
141
+ * @returns {Array<Object>} an array of stylesheets
348
142
  */
349
- function getStylesheetsOfRootNode(options) {
350
- const { rootNode, shadowId } = options;
143
+ function getStylesheetsOfRootNode(rootNode, shadowId, convertDataToStylesheet) {
351
144
  let sheets;
352
145
 
353
146
  // nodeType === 11 -> DOCUMENT_FRAGMENT
354
147
  if (rootNode.nodeType === 11 && shadowId) {
355
- sheets = getStylesheetsFromDocumentFragment(options);
148
+ sheets = getStylesheetsFromDocumentFragment(
149
+ rootNode,
150
+ convertDataToStylesheet
151
+ );
356
152
  } else {
357
153
  sheets = getStylesheetsFromDocument(rootNode);
358
154
  }
@@ -363,11 +159,11 @@ function getStylesheetsOfRootNode(options) {
363
159
  /**
364
160
  * Get stylesheets from `documentFragment`
365
161
  *
366
- * @param {Object} options configuration options of `loadCssom`
162
+ * @property {Object} options.rootNode `documentFragment`
163
+ * @property {Function} options.convertDataToStylesheet a utility function to generate a stylesheet from given data
367
164
  * @returns {Array<Object>}
368
165
  */
369
- function getStylesheetsFromDocumentFragment(options) {
370
- const { rootNode, convertDataToStylesheet } = options;
166
+ function getStylesheetsFromDocumentFragment(rootNode, convertDataToStylesheet) {
371
167
  return (
372
168
  Array.from(rootNode.children)
373
169
  .filter(filerStyleAndLinkAttributesInDocumentFragment)