Fix :has() invalidation error when removing an element

The InvalidateAncestorsOrSiblingsAffectedByHas(<parent>, <previous-
sibling>, <flag-for-pseudo-change>) method doesn't traverse to
ancestors for a removed element even if the removed element can
affect the :has() state of a :has() anchor element. These are more
specific conditions of the case:
- there is only one :has() pseudo class (has-child or has-descendant
  relationship) that is tested on the :has() anchor element,
- and the :has() anchor element matched the :has() pseudo class from
  the initial style calculation
- and the removed element has a previous sibling element.

The reason of this bug is the lack of cohesiveness of the logic that
determines the traversal direction for :has() invalidation.

The method gets the :has() invalidation traversal direction from the
affected-by flags (AncestorsOrAncestorSiblingsAffectedByHas,
SiblingsAffectedByHas) of the <previous-sibling> element and starts
the traversal from the element if the <previous-sibling> is not null.

This is based on the assumption that, the previous sibling element
of the mutated element will have the affected-by flags set if the
previous sibling is in the :has() argument testing traversal range.

The assumption is true when the :has() pseudo class have tested on the
:has() anchor element but it didn't matched before, because the
SelectorChecker marks the flags of all the elements in the :has()
argument testing traversal range while testing the :has() argument.

But the assumption cannot be true when the :has() pseudo class have
tested and matched on the :has() anchor element, because the
SelectorChecker stops the :has() argument testing traversal at the
first :has() argument matched element. So, the previous sibling of
the :has() argument matched element doesn't have the flags set.

Currently, the has-descendant relationship invalidation for an
attribute change or pseudo state change works well on the :has()
argument matched element even if the element has a previous sibling
and the previous sibling doesn't have the
AncestorsOrAncestorSiblingsAffectedByHas flag set.

It is because of the conditional argument passing logic in the
Invalidate...AffectedByHas(<changed-element>) method. The method calls
the Invalidate...AffectedByHas(<parent>, <previous-sibling>) with
nullptr as the <previous-sibling> if the <changed-element> doesn't
have the SiblingsAffectedByHas flags set. When the <previous-sibling>
is nullptr, the Invalidate...AffectedByHas(<parent>, <previous-
sibling>, <flag-for-pseudo-change>) method can get the
AncestorsOrAncestorSiblingsAffectedByHas flag from the <parent>.

The problem is that, for insertion or removal, StyleEngine calls the
Invalidate...AffectedByHas(<parent>, <previous-sibling>) without the
conditional argument passing logic. So even if the parent has the
Ancestors...AffectedByHas flag set, the Invalidate...AffectedByHas(
<parent>, <previous-sibling>, <flag-for-pseudo-change>) method
determines the ancestor traversal direction from the flag of the
<previous-sibling>. In case that the <previous-sibling> doesn't have
the flag set, the method will not traverse to ancestors.

This CL added PseudoHasInvalidationTraversalContext class
in StyleEngine so that these logic can be collected within the class:
- logic of determining the :has() invalidation traversal direction
- logic of getting the first element of the traversal

To remove unnecessary method overloading, this is moved to the context
class:
- functionality of passing the flag of indicating whether the :has()
  invalidation is only for the pseudo state change or for all mutation
  types

To fix the bug, this CL adds the missing logic of determining the
traversal direction for the insertion and removal:
- Determine the ancestor traversal direction from the flag of <parent>
  for insertion or removing all children.
- Determine the ancestor traversal direction from the flag of
  <removed-element> for removal

The missing tests are also added:
- unittest to check affected-by flags after removal
- web test to check :has() invalidation after removing non-first
  element.

Bug: 1375248
Change-Id: Ia08bcdfaadf6e8a2973b1b4ba6b640d51e56516c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3960181
Reviewed-by: Rune Lillesveen <futhark@chromium.org>
Commit-Queue: Byungwoo Lee <blee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#1062093}
1 file changed
tree: 62c3d2d706183f16a99dfb20ea9dff9759ac5681
  1. .github/
  2. .well-known/
  3. accelerometer/
  4. accessibility/
  5. accname/
  6. acid/
  7. ambient-light/
  8. animation-worklet/
  9. annotation-model/
  10. annotation-protocol/
  11. annotation-vocab/
  12. apng/
  13. appmanifest/
  14. attribution-reporting/
  15. audio-output/
  16. avif/
  17. background-fetch/
  18. background-sync/
  19. badging/
  20. battery-status/
  21. beacon/
  22. bluetooth/
  23. clear-site-data/
  24. client-hints/
  25. clipboard-apis/
  26. close-watcher/
  27. common/
  28. compat/
  29. compression/
  30. compute-pressure/
  31. conformance-checkers/
  32. console/
  33. contacts/
  34. content-dpr/
  35. content-index/
  36. content-security-policy/
  37. contenteditable/
  38. cookie-store/
  39. cookies/
  40. core-aam/
  41. cors/
  42. credential-management/
  43. css/
  44. custom-elements/
  45. custom-state-pseudo-class/
  46. delegated-ink/
  47. density-size-correction/
  48. deprecation-reporting/
  49. device-memory/
  50. direct-sockets/
  51. docs/
  52. document-policy/
  53. dom/
  54. domparsing/
  55. domxpath/
  56. dpub-aam/
  57. dpub-aria/
  58. editing/
  59. element-timing/
  60. encoding/
  61. encoding-detection/
  62. encrypted-media/
  63. entries-api/
  64. event-timing/
  65. eventsource/
  66. eyedropper/
  67. feature-policy/
  68. fetch/
  69. file-system-access/
  70. FileAPI/
  71. focus/
  72. font-access/
  73. fonts/
  74. forced-colors-mode/
  75. fs/
  76. fullscreen/
  77. gamepad/
  78. generic-sensor/
  79. geolocation-API/
  80. geolocation-sensor/
  81. graphics-aam/
  82. gyroscope/
  83. hr-time/
  84. html/
  85. html-longdesc/
  86. html-media-capture/
  87. idle-detection/
  88. imagebitmap-renderingcontext/
  89. images/
  90. import-maps/
  91. IndexedDB/
  92. inert/
  93. infrastructure/
  94. input-device-capabilities/
  95. input-events/
  96. installedapp/
  97. interfaces/
  98. intersection-observer/
  99. intervention-reporting/
  100. is-input-pending/
  101. js/
  102. js-self-profiling/
  103. keyboard-lock/
  104. keyboard-map/
  105. largest-contentful-paint/
  106. layout-instability/
  107. lifecycle/
  108. loading/
  109. longtask-timing/
  110. magnetometer/
  111. managed/
  112. mathml/
  113. measure-memory/
  114. media/
  115. media-capabilities/
  116. media-playback-quality/
  117. media-source/
  118. mediacapture-extensions/
  119. mediacapture-fromelement/
  120. mediacapture-handle/
  121. mediacapture-image/
  122. mediacapture-insertable-streams/
  123. mediacapture-record/
  124. mediacapture-region/
  125. mediacapture-streams/
  126. mediasession/
  127. merchant-validation/
  128. mimesniff/
  129. mixed-content/
  130. mst-content-hint/
  131. native-io/
  132. navigation-api/
  133. navigation-timing/
  134. netinfo/
  135. network-error-logging/
  136. notifications/
  137. old-tests/
  138. orientation-event/
  139. orientation-sensor/
  140. page-lifecycle/
  141. page-visibility/
  142. paint-timing/
  143. parakeet/
  144. payment-handler/
  145. payment-method-basic-card/
  146. payment-method-id/
  147. payment-request/
  148. pending-beacon/
  149. performance-timeline/
  150. periodic-background-sync/
  151. permissions/
  152. permissions-policy/
  153. permissions-request/
  154. permissions-revoke/
  155. picture-in-picture/
  156. pointerevents/
  157. pointerlock/
  158. portals/
  159. preload/
  160. presentation-api/
  161. priority-hints/
  162. private-click-measurement/
  163. proximity/
  164. push-api/
  165. quirks/
  166. referrer-policy/
  167. remote-playback/
  168. reporting/
  169. requestidlecallback/
  170. resize-observer/
  171. resource-timing/
  172. resources/
  173. sanitizer-api/
  174. savedata/
  175. scheduler/
  176. screen-capture/
  177. screen-details/
  178. screen-orientation/
  179. screen-wake-lock/
  180. scroll-animations/
  181. scroll-to-text-fragment/
  182. secure-contexts/
  183. secure-payment-confirmation/
  184. selection/
  185. serial/
  186. server-timing/
  187. service-workers/
  188. shadow-dom/
  189. shape-detection/
  190. shared-storage/
  191. signed-exchange/
  192. soft-navigation-heuristics/
  193. speculation-rules/
  194. speech-api/
  195. storage/
  196. storage-access-api/
  197. streams/
  198. subapps/
  199. subresource-integrity/
  200. svg/
  201. svg-aam/
  202. timing-entrytypes-registry/
  203. tools/
  204. touch-events/
  205. trust-tokens/
  206. trusted-types/
  207. ua-client-hints/
  208. uievents/
  209. upgrade-insecure-requests/
  210. url/
  211. urlpattern/
  212. user-timing/
  213. vibration/
  214. video-rvfc/
  215. virtual-keyboard/
  216. visual-viewport/
  217. wai-aria/
  218. wasm/
  219. web-animations/
  220. web-bundle/
  221. web-locks/
  222. web-nfc/
  223. web-otp/
  224. web-share/
  225. webaudio/
  226. webauthn/
  227. webcodecs/
  228. WebCryptoAPI/
  229. webdriver/
  230. webgl/
  231. webgpu/
  232. webhid/
  233. webidl/
  234. webmessaging/
  235. webmidi/
  236. webnn/
  237. webrtc/
  238. webrtc-encoded-transform/
  239. webrtc-extensions/
  240. webrtc-ice/
  241. webrtc-identity/
  242. webrtc-priority/
  243. webrtc-stats/
  244. webrtc-svc/
  245. websockets/
  246. webstorage/
  247. webtransport/
  248. webusb/
  249. webvr/
  250. webvtt/
  251. webxr/
  252. window-placement/
  253. workers/
  254. worklets/
  255. x-frame-options/
  256. xhr/
  257. .azure-pipelines.yml
  258. .gitattributes
  259. .gitignore
  260. .mailmap
  261. .taskcluster.yml
  262. CODE_OF_CONDUCT.md
  263. CODEOWNERS
  264. CONTRIBUTING.md
  265. LICENSE.md
  266. lint.ignore
  267. README.md
  268. wpt
  269. wpt.py
README.md

The web-platform-tests Project

Taskcluster CI Status documentation manifest Python 3

The web-platform-tests Project is a cross-browser test suite for the Web-platform stack. Writing tests in a way that allows them to be run in all browsers gives browser projects confidence that they are shipping software that is compatible with other implementations, and that later implementations will be compatible with their implementations. This in turn gives Web authors/developers confidence that they can actually rely on the Web platform to deliver on the promise of working across browsers and devices without needing extra layers of abstraction to paper over the gaps left by specification editors and implementors.

The most important sources of information and activity are:

  • github.com/web-platform-tests/wpt: the canonical location of the project's source code revision history and the discussion forum for changes to the code
  • web-platform-tests.org: the documentation website; details how to set up the project, how to write tests, how to give and receive peer review, how to serve as an administrator, and more
  • wpt.live: a public deployment of the test suite, allowing anyone to run the tests by visiting from an Internet-enabled browser of their choice
  • wpt.fyi: an archive of test results collected from an array of web browsers on a regular basis
  • Real-time chat room: the wpt:matrix.org matrix channel; includes participants located around the world, but busiest during the European working day.
  • Mailing list: a public and low-traffic discussion list
  • RFCs: a repo for requesting comments on substantial changes that would impact other stakeholders or users; people who work on WPT infra are encouraged to watch the repo.

If you'd like clarification about anything, don't hesitate to ask in the chat room or on the mailing list.

Setting Up the Repo

Clone or otherwise get https://github.com/web-platform-tests/wpt.

Note: because of the frequent creation and deletion of branches in this repo, it is recommended to “prune” stale branches when fetching updates, i.e. use git pull --prune (or git fetch -p && git merge).

Running the Tests

See the documentation website and in particular the system setup for running tests locally.

Command Line Tools

The wpt command provides a frontend to a variety of tools for working with and running web-platform-tests. Some of the most useful commands are:

  • wpt serve - For starting the wpt http server
  • wpt run - For running tests in a browser
  • wpt lint - For running the lint against all tests
  • wpt manifest - For updating or generating a MANIFEST.json test manifest
  • wpt install - For installing the latest release of a browser or webdriver server on the local machine.
  • wpt serve-wave - For starting the wpt http server and the WAVE test runner. For more details on how to use the WAVE test runner see the documentation.

Windows Notes

On Windows wpt commands must be prefixed with python or the path to the python binary (if python is not in your %PATH%).

python wpt [command]

Alternatively, you may also use Bash on Ubuntu on Windows in the Windows 10 Anniversary Update build, then access your windows partition from there to launch wpt commands.

Please make sure git and your text editor do not automatically convert line endings, as it will cause lint errors. For git, please set git config core.autocrlf false in your working tree.

Publication

The master branch is automatically synced to wpt.live and w3c-test.org.

Contributing

Save the Web, Write Some Tests!

Absolutely everyone is welcome to contribute to test development. No test is too small or too simple, especially if it corresponds to something for which you've noted an interoperability bug in a browser.

The way to contribute is just as usual:

  • Fork this repository (and make sure you're still relatively in sync with it if you forked a while ago).
  • Create a branch for your changes: git checkout -b topic.
  • Make your changes.
  • Run ./wpt lint as described above.
  • Commit locally and push that to your repo.
  • Create a pull request based on the above.

Issues with web-platform-tests

If you spot an issue with a test and are not comfortable providing a pull request per above to fix it, please file a new issue. Thank you!