aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/CODEOWNERS1
-rw-r--r--.github/workflows/dispatch-workflow.yml6
-rw-r--r--.github/workflows/linux.yml6
-rw-r--r--.github/workflows/mac.yml6
-rw-r--r--.github/workflows/main.yml1
-rw-r--r--.github/workflows/ohos.yml14
-rw-r--r--.github/workflows/try-label.yml1
-rw-r--r--.github/workflows/try.yml6
-rw-r--r--.github/workflows/windows.yml6
-rw-r--r--.gitignore3
-rw-r--r--Cargo.lock371
-rw-r--r--Cargo.toml5
-rw-r--r--components/canvas/backend.rs31
-rw-r--r--components/canvas/raqote_backend.rs46
-rw-r--r--components/compositing/compositor.rs24
-rw-r--r--components/compositing/tracing.rs1
-rw-r--r--components/compositing/webview_renderer.rs21
-rw-r--r--components/config/opts.rs2
-rw-r--r--components/config/prefs.rs3
-rw-r--r--components/constellation/constellation.rs79
-rw-r--r--components/constellation/sandboxing.rs1
-rw-r--r--components/constellation/tracing.rs5
-rw-r--r--components/devtools/actors/browsing_context.rs4
-rw-r--r--components/devtools/actors/console.rs10
-rw-r--r--components/devtools/actors/watcher.rs24
-rw-r--r--components/devtools/actors/worker.rs28
-rw-r--r--components/devtools/lib.rs22
-rw-r--r--components/devtools/resource.rs25
-rw-r--r--components/fonts/Cargo.toml1
-rw-r--r--components/fonts/font_store.rs10
-rw-r--r--components/fonts/font_template.rs18
-rw-r--r--components/fonts/system_font_service.rs45
-rw-r--r--components/fonts/tests/font.rs13
-rw-r--r--components/fonts/tests/font_context.rs1
-rw-r--r--components/layout/construct_modern.rs42
-rw-r--r--components/layout/display_list/mod.rs92
-rw-r--r--components/layout/display_list/stacking_context.rs135
-rw-r--r--components/layout/dom.rs141
-rw-r--r--components/layout/dom_traversal.rs147
-rw-r--r--components/layout/flexbox/layout.rs92
-rw-r--r--components/layout/flexbox/mod.rs29
-rw-r--r--components/layout/flow/construct.rs258
-rw-r--r--components/layout/flow/float.rs5
-rw-r--r--components/layout/flow/inline/construct.rs78
-rw-r--r--components/layout/flow/inline/inline_box.rs29
-rw-r--r--components/layout/flow/inline/line.rs21
-rw-r--r--components/layout/flow/inline/mod.rs85
-rw-r--r--components/layout/flow/inline/text_run.rs72
-rw-r--r--components/layout/flow/mod.rs126
-rw-r--r--components/layout/flow/root.rs104
-rw-r--r--components/layout/formatting_contexts.rs50
-rw-r--r--components/layout/fragment_tree/base_fragment.rs6
-rw-r--r--components/layout/fragment_tree/box_fragment.rs18
-rw-r--r--components/layout/fragment_tree/fragment.rs27
-rw-r--r--components/layout/fragment_tree/fragment_tree.rs56
-rw-r--r--components/layout/fragment_tree/positioning_fragment.rs17
-rw-r--r--components/layout/geom.rs86
-rw-r--r--components/layout/layout_box_base.rs8
-rw-r--r--components/layout/layout_impl.rs19
-rw-r--r--components/layout/lib.rs11
-rw-r--r--components/layout/lists.rs10
-rw-r--r--components/layout/positioned.rs305
-rw-r--r--components/layout/query.rs35
-rw-r--r--components/layout/replaced.rs12
-rw-r--r--components/layout/table/construct.rs97
-rw-r--r--components/layout/table/layout.rs9
-rw-r--r--components/layout/table/mod.rs63
-rw-r--r--components/layout/taffy/layout.rs15
-rw-r--r--components/layout/taffy/mod.rs30
-rw-r--r--components/layout/traversal.rs82
-rw-r--r--components/malloc_size_of/Cargo.toml1
-rw-r--r--components/malloc_size_of/lib.rs49
-rw-r--r--components/net/hsts.rs134
-rw-r--r--components/net/http_loader.rs16
-rw-r--r--components/net/image_cache.rs16
-rw-r--r--components/net/resource_thread.rs16
-rw-r--r--components/net/tests/hsts.rs98
-rw-r--r--components/net/tests/http_loader.rs8
-rw-r--r--components/pixels/lib.rs7
-rw-r--r--components/script/Cargo.toml1
-rw-r--r--components/script/body.rs14
-rw-r--r--components/script/canvas_context.rs6
-rw-r--r--components/script/dom/attr.rs10
-rw-r--r--components/script/dom/bindings/buffer_source.rs49
-rw-r--r--components/script/dom/bindings/cell.rs7
-rw-r--r--components/script/dom/bindings/structuredclone.rs7
-rw-r--r--components/script/dom/clipboard.rs146
-rw-r--r--components/script/dom/clipboarditem.rs8
-rw-r--r--components/script/dom/create.rs2
-rw-r--r--components/script/dom/document.rs13
-rw-r--r--components/script/dom/element.rs333
-rw-r--r--components/script/dom/event.rs29
-rw-r--r--components/script/dom/eventsource.rs2
-rw-r--r--components/script/dom/globalscope.rs50
-rw-r--r--components/script/dom/htmlbodyelement.rs19
-rw-r--r--components/script/dom/htmlcanvaselement.rs55
-rw-r--r--components/script/dom/htmlelement.rs23
-rw-r--r--components/script/dom/htmliframeelement.rs26
-rw-r--r--components/script/dom/htmlimageelement.rs10
-rw-r--r--components/script/dom/htmllinkelement.rs7
-rw-r--r--components/script/dom/htmlmediaelement.rs2
-rw-r--r--components/script/dom/htmloptgroupelement.rs4
-rw-r--r--components/script/dom/htmloptionelement.rs24
-rw-r--r--components/script/dom/htmlscriptelement.rs12
-rw-r--r--components/script/dom/htmlselectelement.rs11
-rw-r--r--components/script/dom/htmlstyleelement.rs18
-rw-r--r--components/script/dom/htmlvideoelement.rs2
-rw-r--r--components/script/dom/imagedata.rs49
-rw-r--r--components/script/dom/intersectionobserver.rs14
-rw-r--r--components/script/dom/macros.rs35
-rw-r--r--components/script/dom/mod.rs3
-rw-r--r--components/script/dom/node.rs17
-rw-r--r--components/script/dom/notification.rs2
-rw-r--r--components/script/dom/offscreencanvas.rs20
-rw-r--r--components/script/dom/promise.rs17
-rw-r--r--components/script/dom/raredata.rs1
-rw-r--r--components/script/dom/readablestream.rs7
-rw-r--r--components/script/dom/readablestreamdefaultcontroller.rs2
-rw-r--r--components/script/dom/readablestreamgenericreader.rs1
-rw-r--r--components/script/dom/securitypolicyviolationevent.rs11
-rw-r--r--components/script/dom/servoparser/html.rs44
-rw-r--r--components/script/dom/servoparser/mod.rs3
-rw-r--r--components/script/dom/svgelement.rs41
-rw-r--r--components/script/dom/svgimageelement.rs96
-rw-r--r--components/script/dom/transformstream.rs1105
-rw-r--r--components/script/dom/transformstreamdefaultcontroller.rs420
-rw-r--r--components/script/dom/trustedhtml.rs18
-rw-r--r--components/script/dom/trustedtypepolicyfactory.rs4
-rw-r--r--components/script/dom/underlyingsourcecontainer.rs18
-rw-r--r--components/script/dom/urlpattern.rs204
-rw-r--r--components/script/dom/urlpattern/mod.rs810
-rw-r--r--components/script/dom/urlpattern/pattern_parser.rs473
-rw-r--r--components/script/dom/urlpattern/preprocessing.rs659
-rw-r--r--components/script/dom/urlpattern/tokenizer.rs524
-rw-r--r--components/script/dom/virtualmethods.rs4
-rw-r--r--components/script/dom/websocket.rs2
-rw-r--r--components/script/dom/window.rs28
-rw-r--r--components/script/dom/writablestream.rs3
-rw-r--r--components/script/dom/writablestreamdefaultcontroller.rs31
-rw-r--r--components/script/dom/xmlhttprequest.rs2
-rw-r--r--components/script/fetch.rs2
-rw-r--r--components/script/layout_image.rs2
-rw-r--r--components/script/messaging.rs1
-rw-r--r--components/script/script_module.rs2
-rw-r--r--components/script/script_runtime.rs2
-rw-r--r--components/script/script_thread.rs60
-rw-r--r--components/script/security_manager.rs23
-rw-r--r--components/script/stylesheet_loader.rs2
-rw-r--r--components/script/webdriver_handlers.rs76
-rw-r--r--components/script_bindings/codegen/Bindings.conf6
-rw-r--r--components/script_bindings/codegen/CodegenRust.py97
-rw-r--r--components/script_bindings/conversions.rs6
-rw-r--r--components/script_bindings/import.rs6
-rw-r--r--components/script_bindings/interfaces.rs19
-rw-r--r--components/script_bindings/webidls/CanvasRenderingContext2D.webidl2
-rw-r--r--components/script_bindings/webidls/Clipboard.webidl2
-rw-r--r--components/script_bindings/webidls/Document.webidl1
-rw-r--r--components/script_bindings/webidls/Element.webidl8
-rw-r--r--components/script_bindings/webidls/EventHandler.webidl30
-rw-r--r--components/script_bindings/webidls/HTMLElement.webidl1
-rw-r--r--components/script_bindings/webidls/HTMLIFrameElement.webidl4
-rw-r--r--components/script_bindings/webidls/SVGElement.webidl2
-rw-r--r--components/script_bindings/webidls/SVGImageElement.webidl16
-rw-r--r--components/script_bindings/webidls/TransformStream.webidl18
-rw-r--r--components/script_bindings/webidls/TransformStreamDefaultController.webidl15
-rw-r--r--components/script_bindings/webidls/Transformer.webidl22
-rw-r--r--components/script_bindings/webidls/URLPattern.webidl8
-rw-r--r--components/script_bindings/webidls/Window.webidl4
-rw-r--r--components/servo/javascript_evaluator.rs65
-rw-r--r--components/servo/lib.rs22
-rw-r--r--components/servo/tests/webview.rs82
-rw-r--r--components/servo/webview.rs23
-rw-r--r--components/shared/compositing/lib.rs5
-rw-r--r--components/shared/constellation/from_script_message.rs9
-rw-r--r--components/shared/constellation/lib.rs9
-rw-r--r--components/shared/constellation/structured_data/mod.rs3
-rw-r--r--components/shared/constellation/structured_data/serializable.rs6
-rw-r--r--components/shared/constellation/structured_data/transferable.rs6
-rw-r--r--components/shared/embedder/input_events.rs7
-rw-r--r--components/shared/embedder/lib.rs64
-rw-r--r--components/shared/embedder/webdriver.rs2
-rw-r--r--components/shared/net/request.rs4
-rw-r--r--components/shared/profile/mem.rs6
-rw-r--r--components/shared/script/lib.rs7
-rw-r--r--components/shared/script_layout/lib.rs1
-rw-r--r--components/url/lib.rs2
-rw-r--r--components/webdriver_server/actions.rs308
-rw-r--r--components/webdriver_server/lib.rs54
-rw-r--r--components/webgl/Cargo.toml1
-rw-r--r--components/webgl/webgl_thread.rs23
-rw-r--r--deny.toml5
-rw-r--r--ports/servoshell/Cargo.toml4
-rw-r--r--ports/servoshell/egl/app_state.rs16
-rw-r--r--ports/servoshell/egl/ohos.rs43
-rw-r--r--ports/servoshell/egl/ohos/simpleservo.rs5
-rw-r--r--ports/servoshell/prefs.rs9
-rw-r--r--python/requirements.txt3
-rw-r--r--python/servo/build_commands.py58
-rw-r--r--python/servo/devtools_tests.py207
-rw-r--r--python/servo/devtools_tests/sources/classic.js1
-rw-r--r--python/servo/devtools_tests/sources/module.js2
-rw-r--r--python/servo/devtools_tests/sources/test.html11
-rw-r--r--python/servo/devtools_tests/sources/worker.js1
-rw-r--r--python/servo/testing_commands.py9
-rw-r--r--python/servo/try_parser.py33
-rw-r--r--servobuild.example3
-rw-r--r--tests/wpt/include.ini2
-rw-r--r--tests/wpt/meta/MANIFEST.json1179
-rw-r--r--tests/wpt/meta/WebCryptoAPI/idlharness.https.any.js.ini8
-rw-r--r--tests/wpt/meta/__dir__.ini1
-rw-r--r--tests/wpt/meta/clipboard-apis/idlharness.https.window.js.ini6
-rw-r--r--tests/wpt/meta/content-security-policy/default-src/default-src-sri_hash.sub.html.ini12
-rw-r--r--tests/wpt/meta/content-security-policy/gen/top.http-rp/script-src-self/script-tag.http.html.ini9
-rw-r--r--tests/wpt/meta/content-security-policy/gen/top.http-rp/script-src-self/script-tag.https.html.ini9
-rw-r--r--tests/wpt/meta/content-security-policy/gen/top.meta/script-src-self/script-tag.http.html.ini9
-rw-r--r--tests/wpt/meta/content-security-policy/gen/top.meta/script-src-self/script-tag.https.html.ini9
-rw-r--r--tests/wpt/meta/content-security-policy/navigation/to-javascript-url-script-src.html.ini13
-rw-r--r--tests/wpt/meta/content-security-policy/nonce-hiding/nonces.html.ini24
-rw-r--r--tests/wpt/meta/content-security-policy/nonce-hiding/script-nonces-hidden-meta.sub.html.ini3
-rw-r--r--tests/wpt/meta/content-security-policy/nonce-hiding/script-nonces-hidden.html.ini27
-rw-r--r--tests/wpt/meta/content-security-policy/nonce-hiding/svgscript-nonces-hidden-meta.sub.html.ini6
-rw-r--r--tests/wpt/meta/content-security-policy/nonce-hiding/svgscript-nonces-hidden.html.ini18
-rw-r--r--tests/wpt/meta/content-security-policy/reporting/report-clips-sample.https.html.ini3
-rw-r--r--tests/wpt/meta/content-security-policy/reporting/report-original-url.sub.html.ini6
-rw-r--r--tests/wpt/meta/content-security-policy/script-src/script-src-report-only-policy-works-with-hash-policy.html.ini4
-rw-r--r--tests/wpt/meta/content-security-policy/script-src/script-src-sri_hash.sub.html.ini12
-rw-r--r--tests/wpt/meta/content-security-policy/script-src/scripthash-case-insensitive.sub.html.ini3
-rw-r--r--tests/wpt/meta/content-security-policy/script-src/scriptnonce-basic-blocked.sub.html.ini3
-rw-r--r--tests/wpt/meta/content-security-policy/securitypolicyviolation/blockeduri-eval.html.ini4
-rw-r--r--tests/wpt/meta/content-security-policy/securitypolicyviolation/blockeduri-inline.html.ini3
-rw-r--r--tests/wpt/meta/content-security-policy/securitypolicyviolation/blockeduri-ws-wss-scheme.html.ini12
-rw-r--r--tests/wpt/meta/content-security-policy/securitypolicyviolation/linenumber.tentative.html.ini3
-rw-r--r--tests/wpt/meta/content-security-policy/securitypolicyviolation/script-sample-no-opt-in.html.ini8
-rw-r--r--tests/wpt/meta/content-security-policy/securitypolicyviolation/script-sample.html.ini14
-rw-r--r--tests/wpt/meta/content-security-policy/securitypolicyviolation/source-file-blob-scheme.html.ini3
-rw-r--r--tests/wpt/meta/content-security-policy/securitypolicyviolation/source-file-data-scheme.html.ini3
-rw-r--r--tests/wpt/meta/content-security-policy/securitypolicyviolation/style-sample-no-opt-in.html.ini3
-rw-r--r--tests/wpt/meta/content-security-policy/securitypolicyviolation/style-sample.html.ini3
-rw-r--r--tests/wpt/meta/content-security-policy/securitypolicyviolation/targeting.html.ini5
-rw-r--r--tests/wpt/meta/content-security-policy/style-src-attr-elem/style-src-attr-blocked-src-allowed.html.ini7
-rw-r--r--tests/wpt/meta/content-security-policy/style-src-attr-elem/style-src-elem-allowed-attr-blocked.html.ini7
-rw-r--r--tests/wpt/meta/content-security-policy/style-src-attr-elem/style-src-elem-blocked-attr-allowed.html.ini7
-rw-r--r--tests/wpt/meta/content-security-policy/style-src-attr-elem/style-src-elem-blocked-src-allowed.html.ini7
-rw-r--r--tests/wpt/meta/content-security-policy/style-src/injected-inline-style-blocked.sub.html.ini3
-rw-r--r--tests/wpt/meta/content-security-policy/style-src/inline-style-allowed-while-cloning-objects.sub.html.ini35
-rw-r--r--tests/wpt/meta/content-security-policy/style-src/inline-style-attribute-blocked.sub.html.ini3
-rw-r--r--tests/wpt/meta/content-security-policy/style-src/inline-style-blocked.sub.html.ini7
-rw-r--r--tests/wpt/meta/content-security-policy/style-src/style-src-hash-blocked.html.ini7
-rw-r--r--tests/wpt/meta/content-security-policy/style-src/style-src-imported-style-blocked.html.ini4
-rw-r--r--tests/wpt/meta/content-security-policy/style-src/style-src-injected-inline-style-blocked.html.ini7
-rw-r--r--tests/wpt/meta/content-security-policy/style-src/style-src-injected-stylesheet-blocked.sub.html.ini4
-rw-r--r--tests/wpt/meta/content-security-policy/style-src/style-src-inline-style-attribute-blocked.html.ini7
-rw-r--r--tests/wpt/meta/content-security-policy/style-src/style-src-inline-style-blocked.html.ini7
-rw-r--r--tests/wpt/meta/content-security-policy/style-src/style-src-inline-style-nonce-blocked-error-event.html.ini3
-rw-r--r--tests/wpt/meta/content-security-policy/style-src/style-src-inline-style-nonce-blocked.html.ini7
-rw-r--r--tests/wpt/meta/content-security-policy/style-src/style-src-none-blocked.html.ini4
-rw-r--r--tests/wpt/meta/content-security-policy/style-src/style-src-stylesheet-nonce-blocked.html.ini4
-rw-r--r--tests/wpt/meta/content-security-policy/style-src/stylehash-basic-blocked.sub.html.ini3
-rw-r--r--tests/wpt/meta/content-security-policy/style-src/stylenonce-allowed.sub.html.ini7
-rw-r--r--tests/wpt/meta/content-security-policy/style-src/stylenonce-blocked.sub.html.ini7
-rw-r--r--tests/wpt/meta/content-security-policy/unsafe-hashes/style_attribute_denied_missing_unsafe_hashes.html.ini4
-rw-r--r--tests/wpt/meta/content-security-policy/unsafe-hashes/style_attribute_denied_wrong_hash.html.ini4
-rw-r--r--tests/wpt/meta/css/css-flexbox/flex-container-max-content-002.tentative.html.ini12
-rw-r--r--tests/wpt/meta/css/css-flexbox/flex-container-min-content-002.tentative.html.ini21
-rw-r--r--tests/wpt/meta/css/css-gaps/flex/flex-gap-decorations-022.html.ini2
-rw-r--r--tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-029.html.ini2
-rw-r--r--tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-030.html.ini2
-rw-r--r--tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-031.html.ini2
-rw-r--r--tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-032.html.ini2
-rw-r--r--tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-033.html.ini2
-rw-r--r--tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-38.html.ini2
-rw-r--r--tests/wpt/meta/css/css-gaps/multicol/multicol-gap-decorations-017.html.ini2
-rw-r--r--tests/wpt/meta/css/css-gaps/parsing/gap-decorations-col-rule-width.html.ini9
-rw-r--r--tests/wpt/meta/css/css-images/linear-gradient-body-sibling-index.html.ini2
-rw-r--r--tests/wpt/meta/css/css-images/linear-gradient-sibling-index.html.ini2
-rw-r--r--tests/wpt/meta/css/css-mixins/dashed-function-cycles.html.ini (renamed from tests/wpt/meta/css/css-mixins/dashed-function-cycles.tentative.html.ini)19
-rw-r--r--tests/wpt/meta/css/css-transforms/transform-translate-background-001.html.ini2
-rw-r--r--tests/wpt/meta/css/css-transforms/transform-translate-background-002.html.ini2
-rw-r--r--tests/wpt/meta/css/css-values/tree-counting/sibling-function-descriptors.tentative.html.ini12
-rw-r--r--tests/wpt/meta/css/css-values/tree-counting/sibling-index-keyframe-length-value-dynamic.html.ini6
-rw-r--r--tests/wpt/meta/css/css-values/tree-counting/sibling-index-keyframe-value-dynamic.html.ini6
-rw-r--r--tests/wpt/meta/css/cssom-view/scrollIntoView-container.html.ini15
-rw-r--r--tests/wpt/meta/css/filter-effects/backdrop-filter-scale-transform.html.ini2
-rw-r--r--tests/wpt/meta/css/geometry/DOMMatrix-001.html.ini6
-rw-r--r--tests/wpt/meta/custom-elements/parser/serializing-html-fragments-customized-builtins.html.ini6
-rw-r--r--tests/wpt/meta/dom/events/webkit-animation-end-event.html.ini16
-rw-r--r--tests/wpt/meta/dom/events/webkit-animation-iteration-event.html.ini16
-rw-r--r--tests/wpt/meta/dom/events/webkit-animation-start-event.html.ini22
-rw-r--r--tests/wpt/meta/dom/events/webkit-transition-end-event.html.ini12
-rw-r--r--tests/wpt/meta/fetch/http-cache/pragma-no-cache-with-cache-control.html.ini3
-rw-r--r--tests/wpt/meta/fetch/metadata/generated/css-font-face.https.sub.tentative.html.ini3
-rw-r--r--tests/wpt/meta/fetch/metadata/generated/svg-image.https.sub.html.ini47
-rw-r--r--tests/wpt/meta/fetch/metadata/generated/svg-image.sub.html.ini37
-rw-r--r--tests/wpt/meta/fetch/metadata/report.https.sub.html.ini10
-rw-r--r--tests/wpt/meta/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html.ini3
-rw-r--r--tests/wpt/meta/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html.ini3
-rw-r--r--tests/wpt/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html.ini3
-rw-r--r--tests/wpt/meta/html/browsers/browsing-the-web/scroll-to-fragid/scroll-to-top.html.ini3
-rw-r--r--tests/wpt/meta/html/browsers/history/the-history-interface/traverse_the_history_5.html.ini (renamed from tests/wpt/meta/html/browsers/history/the-history-interface/traverse_the_history_3.html.ini)2
-rw-r--r--tests/wpt/meta/html/browsers/windows/auxiliary-browsing-contexts/named-lookup-noopener.html.ini7
-rw-r--r--tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html.ini60
-rw-r--r--tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html.ini24
-rw-r--r--tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html.ini2
-rw-r--r--tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html.ini36
-rw-r--r--tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html.ini15
-rw-r--r--tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html.ini15
-rw-r--r--tests/wpt/meta/html/canvas/element/manual/text/canvas.2d.disconnected.html.ini2
-rw-r--r--tests/wpt/meta/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.basics.html.ini3
-rw-r--r--tests/wpt/meta/html/dom/idlharness.https.html.ini648
-rw-r--r--tests/wpt/meta/html/rendering/replaced-elements/the-select-element/select-1-block-size.html.ini2
-rw-r--r--tests/wpt/meta/html/rendering/replaced-elements/the-select-element/select-multiple-re-add-option-via-document-fragment.html.ini2
-rw-r--r--tests/wpt/meta/html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html.ini8
-rw-r--r--tests/wpt/meta/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html.ini3
-rw-r--r--tests/wpt/meta/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html.ini3
-rw-r--r--tests/wpt/meta/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html.ini3
-rw-r--r--tests/wpt/meta/html/semantics/forms/form-submission-0/reparent-form-during-planned-navigation-task.html.ini4
-rw-r--r--tests/wpt/meta/html/semantics/forms/form-submission-0/text-plain.window.js.ini3
-rw-r--r--tests/wpt/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/blob-url.any.js.ini3
-rw-r--r--tests/wpt/meta/html/semantics/the-button-element/command-and-commandfor/button-event-dispatch-content-attribute.html.ini3
-rw-r--r--tests/wpt/meta/html/semantics/the-button-element/interest-target/interesttarget-outline-appearance.tentative.html.ini2
-rw-r--r--tests/wpt/meta/html/webappapis/scripting/events/event-handler-all-global-events.html.ini516
-rw-r--r--tests/wpt/meta/html/webappapis/scripting/events/event-handler-attributes-body-window.html.ini183
-rw-r--r--tests/wpt/meta/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html.ini171
-rw-r--r--tests/wpt/meta/html/webappapis/scripting/events/event-handler-attributes-windowless-body.html.ini102
-rw-r--r--tests/wpt/meta/streams/transferable/transfer-with-messageport.window.js.ini12
-rw-r--r--tests/wpt/meta/streams/transferable/transform-stream-members.any.js.ini4
-rw-r--r--tests/wpt/meta/streams/transferable/transform-stream.html.ini12
-rw-r--r--tests/wpt/meta/streams/transferable/writable-stream.html.ini3
-rw-r--r--tests/wpt/meta/streams/transform-streams/backpressure.any.js.ini23
-rw-r--r--tests/wpt/meta/streams/transform-streams/cancel.any.js.ini32
-rw-r--r--tests/wpt/meta/streams/transform-streams/errors.any.js.ini32
-rw-r--r--tests/wpt/meta/streams/transform-streams/flush.any.js.ini23
-rw-r--r--tests/wpt/meta/streams/transform-streams/general.any.js.ini23
-rw-r--r--tests/wpt/meta/streams/transform-streams/lipfuzz.any.js.ini23
-rw-r--r--tests/wpt/meta/streams/transform-streams/patched-global.any.js.ini23
-rw-r--r--tests/wpt/meta/streams/transform-streams/properties.any.js.ini23
-rw-r--r--tests/wpt/meta/streams/transform-streams/reentrant-strategies.any.js.ini23
-rw-r--r--tests/wpt/meta/streams/transform-streams/strategies.any.js.ini24
-rw-r--r--tests/wpt/meta/streams/transform-streams/terminate.any.js.ini23
-rw-r--r--tests/wpt/meta/streams/writable-streams/crashtests/garbage-collection.any.js.ini9
-rw-r--r--tests/wpt/meta/subresource-integrity/tentative/integrity-policy/parsing.https.html.ini9
-rw-r--r--tests/wpt/meta/subresource-integrity/tentative/integrity-policy/script.https.html.ini15
-rw-r--r--tests/wpt/meta/trusted-types/HTMLElement-generic.html.ini24
-rw-r--r--tests/wpt/meta/trusted-types/block-string-assignment-to-Element-insertAdjacentHTML.html.ini12
-rw-r--r--tests/wpt/meta/trusted-types/block-string-assignment-to-Element-outerHTML.html.ini15
-rw-r--r--tests/wpt/meta/trusted-types/block-string-assignment-to-Element-setAttribute.html.ini3
-rw-r--r--tests/wpt/meta/trusted-types/block-string-assignment-to-Element-setHTMLUnsafe.html.ini12
-rw-r--r--tests/wpt/meta/trusted-types/block-string-assignment-to-HTMLElement-generic.html.ini12
-rw-r--r--tests/wpt/meta/trusted-types/block-string-assignment-to-HTMLIFrameElement-srcdoc.html.ini9
-rw-r--r--tests/wpt/meta/trusted-types/block-string-assignment-to-text-and-url-sinks.html.ini9
-rw-r--r--tests/wpt/meta/trusted-types/default-policy.html.ini22
-rw-r--r--tests/wpt/meta/trusted-types/empty-default-policy.html.ini7
-rw-r--r--tests/wpt/meta/trusted-types/require-trusted-types-for-TypeError-belongs-to-the-global-object-realm.html.ini3
-rw-r--r--tests/wpt/meta/trusted-types/require-trusted-types-for-report-only.html.ini3
-rw-r--r--tests/wpt/meta/trusted-types/require-trusted-types-for.html.ini3
-rw-r--r--tests/wpt/meta/trusted-types/should-sink-type-mismatch-violation-be-blocked-by-csp-001.html.ini18
-rw-r--r--tests/wpt/meta/trusted-types/trusted-types-createHTMLDocument.html.ini24
-rw-r--r--tests/wpt/meta/trusted-types/trusted-types-event-handlers.html.ini39
-rw-r--r--tests/wpt/meta/trusted-types/trusted-types-report-only.html.ini6
-rw-r--r--tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-innerHTML.html.ini3
-rw-r--r--tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-insertAdjacentHTML.html.ini3
-rw-r--r--tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-outerHTML.html.ini3
-rw-r--r--tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-setHTMLUnsafe.html.ini3
-rw-r--r--tests/wpt/meta/trusted-types/trusted-types-reporting-for-HTMLIFrameElement-srcdoc.html.ini3
-rw-r--r--tests/wpt/meta/trusted-types/trusted-types-reporting-for-HTMLScriptElement-innerHTML.html.ini3
-rw-r--r--tests/wpt/meta/trusted-types/trusted-types-reporting-for-SVGScriptElement-innerHTML.html.ini3
-rw-r--r--tests/wpt/meta/trusted-types/trusted-types-sandbox-allow-scripts.html.ini3
-rw-r--r--tests/wpt/meta/trusted-types/trusted-types-source-file-path.html.ini9
-rw-r--r--tests/wpt/meta/urlpattern/urlpattern.any.js.ini18
-rw-r--r--tests/wpt/meta/urlpattern/urlpattern.https.any.js.ini18
-rw-r--r--tests/wpt/meta/wasm/webapi/esm-integration/script-src-blocks-wasm.tentative.sub.html.ini3
-rw-r--r--tests/wpt/meta/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html.ini12
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/element_click/click.py.ini6
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/element_click/scroll_into_view.py.ini20
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/element_send_keys/form_controls.py.ini3
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/element_send_keys/scroll_into_view.py.ini3
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/element_send_keys/send_keys.py.ini6
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/execute_async_script/collections.py.ini6
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/execute_script/collections.py.ini6
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/get_computed_role/get.py.ini6
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/get_element_css_value/get.py.ini6
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/get_element_rect/get.py.ini6
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/get_element_tag_name/get.py.ini6
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/get_element_text/get.py.ini6
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/is_element_selected/selected.py.ini6
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/new_window/new_tab.py.ini12
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/perform_actions/perform.py.ini9
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_mouse.py.ini14
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_mouse_drag.py.ini42
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_origin.py.ini21
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_pen.py.ini6
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_touch.py.ini6
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/perform_actions/sequence.py.ini3
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/perform_actions/wheel.py.ini9
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/switch_to_frame/switch_webelement.py.ini6
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/take_element_screenshot/screenshot.py.ini9
-rw-r--r--tests/wpt/meta/webdriver/tests/classic/take_screenshot/iframe.py.ini3
-rw-r--r--tests/wpt/meta/webmessaging/messageerror.html.ini6
-rw-r--r--tests/wpt/meta/webxr/render_state_update.https.html.ini2
-rw-r--r--tests/wpt/meta/workers/WorkerGlobalScope-close.html.ini3
-rw-r--r--tests/wpt/meta/xhr/formdata/append.any.js.ini8
-rw-r--r--tests/wpt/mozilla/meta/MANIFEST.json11
-rw-r--r--tests/wpt/mozilla/meta/__dir__.ini1
-rw-r--r--tests/wpt/mozilla/tests/mozilla/canvas/transfer-control-to-offscreencanvas-then-change-dimensions-crash.html7
-rw-r--r--tests/wpt/mozilla/tests/mozilla/interfaces.https.html3
-rw-r--r--tests/wpt/mozilla/tests/mozilla/interfaces.worker.js2
-rw-r--r--tests/wpt/tests/IndexedDB/idbcursor-request-source.any.js124
-rw-r--r--tests/wpt/tests/WebCryptoAPI/idlharness.https.any.js2
-rw-r--r--tests/wpt/tests/ai/translator/resources/util.js4
-rw-r--r--tests/wpt/tests/ai/translator/translator-bad-input.https.window.js (renamed from tests/wpt/tests/ai/translator/translator-bad-input.tentative.https.window.js)3
-rw-r--r--tests/wpt/tests/ai/translator/translator.optional.https.window.js (renamed from tests/wpt/tests/ai/translator/translator-translate.tentative.https.window.js)11
-rw-r--r--tests/wpt/tests/badging/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/clear-site-data/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/compat/webkit-box-ignores-flex-wrap.tentative.html7
-rw-r--r--tests/wpt/tests/content-security-policy/img-src/icon-blocked.sub.html1
-rw-r--r--tests/wpt/tests/content-security-policy/img-src/img-src-targeting.html24
-rw-r--r--tests/wpt/tests/content-security-policy/style-src/style-src-inline-style-with-csstext.html29
-rw-r--r--tests/wpt/tests/cookie-store/cookieListItem_attributes.https.any.js17
-rw-r--r--tests/wpt/tests/cookie-store/httponly_cookies.https.window.js30
-rw-r--r--tests/wpt/tests/css/compositing/root-element-background-image-opaque-crash.html9
-rw-r--r--tests/wpt/tests/css/css-align/blocks/justify-items-anonymous.tentative.html10
-rw-r--r--tests/wpt/tests/css/css-anchor-position/anchor-position-grid-001.html4
-rw-r--r--tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-002.html8
-rw-r--r--tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-004.html8
-rw-r--r--tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-nested-001.html4
-rw-r--r--tests/wpt/tests/css/css-animations/crashtests/chrome-bug-415627003.html17
-rw-r--r--tests/wpt/tests/css/css-break/root-margin-001-print-ref.html6
-rw-r--r--tests/wpt/tests/css/css-break/root-margin-001-print.html10
-rw-r--r--tests/wpt/tests/css/css-break/table/border-spacing.html11
-rw-r--r--tests/wpt/tests/css/css-break/table/table-parts-offsets-vertical-rl.tentative.html68
-rw-r--r--tests/wpt/tests/css/css-cascade/inline-style-background.html16
-rw-r--r--tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001-ref.html73
-rw-r--r--tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001a.html96
-rw-r--r--tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001b.html96
-rw-r--r--tests/wpt/tests/css/css-display/display-contents-inline-002.html18
-rw-r--r--tests/wpt/tests/css/css-flexbox/flex-container-max-content-002.tentative.html169
-rw-r--r--tests/wpt/tests/css/css-flexbox/flex-container-min-content-002.tentative.html169
-rw-r--r--tests/wpt/tests/css/css-fonts/font-variant-emoji-005-ref.html23
-rw-r--r--tests/wpt/tests/css/css-fonts/font-variant-emoji-005.html29
-rw-r--r--tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-002-ref.html51
-rw-r--r--tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-022.html46
-rw-r--r--tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-029-ref.html70
-rw-r--r--tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-029.html48
-rw-r--r--tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-030-ref.html45
-rw-r--r--tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-030.html37
-rw-r--r--tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-031-ref.html45
-rw-r--r--tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-031.html38
-rw-r--r--tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-032-ref.html71
-rw-r--r--tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-032.html39
-rw-r--r--tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-033-ref.html72
-rw-r--r--tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-033.html38
-rw-r--r--tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-38.html44
-rw-r--r--tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-017-ref.html49
-rw-r--r--tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-017.html48
-rw-r--r--tests/wpt/tests/css/css-gaps/parsing/gap-decorations-col-rule-width.html51
-rw-r--r--tests/wpt/tests/css/css-images/linear-gradient-body-sibling-index-ref.html11
-rw-r--r--tests/wpt/tests/css/css-images/linear-gradient-body-sibling-index.html15
-rw-r--r--tests/wpt/tests/css/css-images/linear-gradient-calc-em-units-ref.html7
-rw-r--r--tests/wpt/tests/css/css-images/linear-gradient-calc-em-units.html19
-rw-r--r--tests/wpt/tests/css/css-images/linear-gradient-sibling-index-ref.html7
-rw-r--r--tests/wpt/tests/css/css-images/linear-gradient-sibling-index.html19
-rw-r--r--tests/wpt/tests/css/css-mixins/dashed-function-cycles.html (renamed from tests/wpt/tests/css/css-mixins/dashed-function-cycles.tentative.html)4
-rw-r--r--tests/wpt/tests/css/css-multicol/getclientrects-005.html139
-rw-r--r--tests/wpt/tests/css/css-multicol/getclientrects-006.html139
-rw-r--r--tests/wpt/tests/css/css-multicol/getclientrects-007.html139
-rw-r--r--tests/wpt/tests/css/css-multicol/getclientrects-008.html67
-rw-r--r--tests/wpt/tests/css/css-overflow/scroll-button-activation-without-scroller.html36
-rw-r--r--tests/wpt/tests/css/css-overflow/scroll-button-disabled-no-focus.html30
-rw-r--r--tests/wpt/tests/css/css-overflow/scroll-buttons-appearance-ref.html2
-rw-r--r--tests/wpt/tests/css/css-scroll-anchoring/table-col-and-dead-row-group-crash.html14
-rw-r--r--tests/wpt/tests/css/css-text/letter-spacing/letter-spacing-cursive-001.html39
-rw-r--r--tests/wpt/tests/css/css-text/letter-spacing/reference/letter-spacing-cursive-001-ref.html32
-rw-r--r--tests/wpt/tests/css/css-transforms/css-scale-of-clip-path-ref.html20
-rw-r--r--tests/wpt/tests/css/css-transforms/css-scale-of-clip-path.html23
-rw-r--r--tests/wpt/tests/css/css-transforms/scale-transform-filtered-text-ref.html16
-rw-r--r--tests/wpt/tests/css/css-transforms/scale-transform-filtered-text.html24
-rw-r--r--tests/wpt/tests/css/css-typed-om/the-stylepropertymap/properties/transform.html4
-rw-r--r--tests/wpt/tests/css/css-values/tree-counting/sibling-function-descriptors.tentative.html110
-rw-r--r--tests/wpt/tests/css/css-values/tree-counting/sibling-index-keyframe-length-value-dynamic.html37
-rw-r--r--tests/wpt/tests/css/css-values/tree-counting/sibling-index-keyframe-value-dynamic.html46
-rw-r--r--tests/wpt/tests/css/css-view-transitions/root-preserve3d-crash.html13
-rw-r--r--tests/wpt/tests/css/css-view-transitions/root-replace-crash.html15
-rw-r--r--tests/wpt/tests/css/css-view-transitions/scoped/crashtests/shadow-dom.html24
-rw-r--r--tests/wpt/tests/css/css-view-transitions/start-skip-start.html87
-rw-r--r--tests/wpt/tests/css/css-view-transitions/view-transition-types-mutable-no-document-element-crashtest.html19
-rw-r--r--tests/wpt/tests/css/cssom-view/getBoundingClientRect-newline.html8
-rw-r--r--tests/wpt/tests/css/cssom-view/resources/scrollIntoView-frame.html16
-rw-r--r--tests/wpt/tests/css/cssom-view/scrollIntoView-container.html84
-rw-r--r--tests/wpt/tests/css/cssom-view/table-client-props.html13
-rw-r--r--tests/wpt/tests/css/filter-effects/backdrop-filter-scale-transform-ref.html20
-rw-r--r--tests/wpt/tests/css/filter-effects/backdrop-filter-scale-transform.html23
-rw-r--r--tests/wpt/tests/css/filter-effects/backdrop-filter-transform-popover-crash.html17
-rw-r--r--tests/wpt/tests/css/filter-effects/css-filters-opacity-hit-testing.html19
-rw-r--r--tests/wpt/tests/css/geometry/DOMMatrix-001.html6
-rw-r--r--tests/wpt/tests/css/mediaqueries/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/docs/writing-tests/testdriver.md8
-rw-r--r--tests/wpt/tests/dom/events/scrolling/overscroll-deltas.tentative.html (renamed from tests/wpt/tests/dom/events/scrolling/overscroll-deltas.html)0
-rw-r--r--tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-document.tentative.html (renamed from tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-document.html)0
-rw-r--r--tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-element-with-overscroll-behavior.tentative.html (renamed from tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-element-with-overscroll-behavior.html)0
-rw-r--r--tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-scrolled-element.tentative.html (renamed from tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-scrolled-element.html)0
-rw-r--r--tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-window.tentative.html (renamed from tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-window.html)0
-rw-r--r--tests/wpt/tests/dom/observable/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/domparsing/XMLSerializer-serializeToString.html4
-rw-r--r--tests/wpt/tests/element-timing/resources/element-timing-helpers.js2
-rw-r--r--tests/wpt/tests/event-timing/orphan-keydown.html34
-rw-r--r--tests/wpt/tests/event-timing/resources/event-timing-test-utils.js11
-rw-r--r--tests/wpt/tests/fedcm/fedcm-disconnect.sub.https.html17
-rw-r--r--tests/wpt/tests/fetch/api/request/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/fetch/http-cache/pragma-no-cache-with-cache-control.html28
-rw-r--r--tests/wpt/tests/fetch/http-cache/resources/cached_pragma_rand.py14
-rw-r--r--tests/wpt/tests/fetch/local-network-access/META.yml5
-rw-r--r--tests/wpt/tests/fetch/local-network-access/README.md11
-rw-r--r--tests/wpt/tests/fetch/local-network-access/fetch.tentative.https.html51
-rw-r--r--tests/wpt/tests/fetch/local-network-access/resources/fetch-private.html52
-rw-r--r--tests/wpt/tests/fetch/local-network-access/resources/support.sub.js186
-rw-r--r--tests/wpt/tests/fetch/local-network-access/resources/target.py105
-rw-r--r--tests/wpt/tests/file-system-access/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/html/browsers/windows/auxiliary-browsing-contexts/named-lookup-noopener.html49
-rw-r--r--tests/wpt/tests/html/canvas/element/manual/wide-gamut-canvas/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/html/canvas/element/path-objects/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/html/canvas/element/wide-gamut-canvas/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/manual/wide-gamut-canvas/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/path-objects/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/html/canvas/offscreen/wide-gamut-canvas/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/html/dom/reflection.js6
-rw-r--r--tests/wpt/tests/html/editing/dnd/platform/pointerdown-add-display-none.html65
-rw-r--r--tests/wpt/tests/html/embedded-content/the-img-element/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/html/scripting/the-script-element/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/html/semantics/forms/the-select-element/customizable-select/select-input-keyboard-behavior.tentative.html58
-rw-r--r--tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-keyboard-invalidation-ref.html9
-rw-r--r--tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-keyboard-invalidation.tentative.html12
-rw-r--r--tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-outline-appearance-ref.html9
-rw-r--r--tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-outline-appearance.tentative.html16
-rw-r--r--tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-partial-interest.tentative.html52
-rw-r--r--tests/wpt/tests/html/semantics/the-link-element/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/infrastructure/testdriver/bidi/emulation/set_geolocation_override.https.html37
-rw-r--r--tests/wpt/tests/input-events/input-events-spin-button-click-on-number-input-delete-document.html9
-rw-r--r--tests/wpt/tests/interfaces/largest-contentful-paint.idl3
-rw-r--r--tests/wpt/tests/interfaces/service-workers.idl2
-rw-r--r--tests/wpt/tests/interfaces/webauthn.idl52
-rw-r--r--tests/wpt/tests/largest-contentful-paint/idlharness.html2
-rw-r--r--tests/wpt/tests/largest-contentful-paint/observe-text.html2
-rw-r--r--tests/wpt/tests/largest-contentful-paint/resources/largest-contentful-paint-helpers.js2
-rw-r--r--tests/wpt/tests/paint-timing/paint-timing-mixin.html2
-rw-r--r--tests/wpt/tests/paint-timing/resources/utils.js2
-rw-r--r--tests/wpt/tests/remote-playback/WEB_FEATURES.yml3
-rw-r--r--tests/wpt/tests/reporting/resources/report-helper.js4
-rw-r--r--tests/wpt/tests/resources/test/tests/functional/api-tests-1.html73
-rw-r--r--tests/wpt/tests/resources/testdriver.js10
-rw-r--r--tests/wpt/tests/resources/testharness.js94
-rw-r--r--tests/wpt/tests/sanitizer-api/sanitizer-basic-filtering.tentative.html9
-rw-r--r--tests/wpt/tests/sanitizer-api/sanitizer-parseHTML.tentative.html145
-rw-r--r--tests/wpt/tests/soft-navigation-heuristics/innertext.tentative.html13
-rw-r--r--tests/wpt/tests/soft-navigation-heuristics/interaction-with-paint-before-back.tentative.html154
-rw-r--r--tests/wpt/tests/soft-navigation-heuristics/smoke/tentative/basic.html67
-rw-r--r--tests/wpt/tests/soft-navigation-heuristics/text-lcp-followed-by-anim-image-softnav-lcp.tentative.html11
-rw-r--r--tests/wpt/tests/soft-navigation-heuristics/visited-link.tentative.html10
-rw-r--r--tests/wpt/tests/speculation-rules/prefetch/resources/basic-service-worker.js7
-rw-r--r--tests/wpt/tests/speculation-rules/prefetch/resources/counting-executor.py5
-rw-r--r--tests/wpt/tests/speculation-rules/prefetch/resources/executor.sub.html36
-rw-r--r--tests/wpt/tests/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https.html122
-rw-r--r--tests/wpt/tests/speculation-rules/speculation-tags/prefetch-eagerness-pointer-down.https.html46
-rw-r--r--tests/wpt/tests/speculation-rules/speculation-tags/prefetch-eagerness-pointer-hover.https.html50
-rw-r--r--tests/wpt/tests/speech-api/SpeechRecognition-installOnDevice.https.html96
-rw-r--r--tests/wpt/tests/streams/readable-streams/crashtests/garbage-collection.any.js29
-rw-r--r--tests/wpt/tests/streams/writable-streams/crashtests/garbage-collection.any.js43
-rw-r--r--tests/wpt/tests/subresource-integrity/tentative/integrity-policy/parsing.https.html119
-rw-r--r--tests/wpt/tests/subresource-integrity/tentative/integrity-policy/script.https.html239
-rw-r--r--tests/wpt/tests/svg/animations/discard-check-removal-order.html52
-rw-r--r--tests/wpt/tests/svg/animations/reftests/reference/green-100x100.svg3
-rw-r--r--tests/wpt/tests/svg/embedded/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/svg/idlharness.window.js3
-rw-r--r--tests/wpt/tests/svg/scripted/WEB_FEATURES.yml4
-rw-r--r--tests/wpt/tests/svg/styling/presentation-attributes-special-cases.html2
-rw-r--r--tests/wpt/tests/tools/webdriver/webdriver/bidi/modules/emulation.py7
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/browsers/android_webview.py3
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/browsers/chrome_android.py41
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/browsers/firefox.py2
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/executors/asyncactions.py90
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/executors/executorwebdriver.py4
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/executors/protocol.py8
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/formatters/chromium.py338
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/formatters/tests/test_chromium.py828
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/testdriver-extra.js7
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/testrunner.py2
-rw-r--r--tests/wpt/tests/tools/wptrunner/wptrunner/wptcommandline.py3
-rw-r--r--tests/wpt/tests/wasm/core/js/simd/simd_select.wast.js24
-rw-r--r--tests/wpt/tests/wasm/core/simd/simd_select.wast.js.html16
-rw-r--r--tests/wpt/tests/webdriver/tests/bidi/__init__.py8
-rw-r--r--tests/wpt/tests/webdriver/tests/bidi/browsing_context/set_viewport/user_contexts.py46
-rw-r--r--tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/__init__.py27
-rw-r--r--tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/conftest.py31
-rw-r--r--tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/contexts.py40
-rw-r--r--tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/coordinates.py44
-rw-r--r--tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/error.py123
-rw-r--r--tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/invalid.py60
-rw-r--r--tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/user_contexts.py83
-rw-r--r--tests/wpt/tests/webnn/conformance_tests/conv2d.https.any.js1005
-rw-r--r--tests/wpt/tests/webnn/conformance_tests/conv_transpose2d.https.any.js1664
-rw-r--r--tests/wpt/tests/webnn/conformance_tests/log.https.any.js7
-rw-r--r--tests/wpt/tests/webnn/conformance_tests/pooling.https.any.js43
-rw-r--r--tests/wpt/tests/webnn/conformance_tests/qdq_subgraph.https.any.js347
-rw-r--r--tests/wpt/tests/webnn/conformance_tests/sign.https.any.js6
-rw-r--r--tests/wpt/tests/webnn/resources/utils.js54
-rw-r--r--tests/wpt/tests/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-clone.https.html2
-rw-r--r--tests/wpt/tests/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-metadata.https.html4
-rw-r--r--tests/wpt/tests/webrtc-encoded-transform/tentative/RTCPeerConnection-insertable-streams.js2
-rw-r--r--tests/wpt/webgl/meta/conformance/context/premultiplyalpha-test.html.ini8
608 files changed, 16767 insertions, 9225 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 3668ac41f48..0bac7800467 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -11,7 +11,6 @@
/components/compositing @mrobinson
# Reviewers for layout-related code
-/components/layout_2020 @mrobinson @Loirooriol @nicoburns
/components/layout @mrobinson @Loirooriol @nicoburns
# Reviewers for Minibrowser related code
diff --git a/.github/workflows/dispatch-workflow.yml b/.github/workflows/dispatch-workflow.yml
index 472f4813cd7..2e4b8ef7076 100644
--- a/.github/workflows/dispatch-workflow.yml
+++ b/.github/workflows/dispatch-workflow.yml
@@ -8,6 +8,9 @@ on:
profile:
required: true
type: string
+ build-args:
+ required: true
+ type: string
wpt-args:
required: true
type: string
@@ -35,6 +38,7 @@ jobs:
secrets: inherit
with:
profile: ${{ inputs.profile }}
+ build-args: ${{ inputs.build-args }}
unit-tests: ${{ inputs.unit-tests }}
build-libservo: ${{ inputs.build-libservo }}
bencher: ${{ inputs.bencher }}
@@ -46,6 +50,7 @@ jobs:
secrets: inherit
with:
profile: ${{ inputs.profile }}
+ build-args: ${{ inputs.build-args }}
wpt: ${{ inputs.wpt }}
unit-tests: ${{ inputs.unit-tests }}
build-libservo: ${{ inputs.build-libservo }}
@@ -59,6 +64,7 @@ jobs:
secrets: inherit
with:
profile: ${{ inputs.profile }}
+ build-args: ${{ inputs.build-args }}
wpt: ${{ inputs.wpt }}
number-of-wpt-chunks: ${{ inputs.number-of-wpt-chunks }}
unit-tests: ${{ inputs.unit-tests }}
diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml
index 033f9795a23..b47f1906160 100644
--- a/.github/workflows/linux.yml
+++ b/.github/workflows/linux.yml
@@ -6,6 +6,10 @@ on:
required: false
default: "release"
type: string
+ build-args:
+ default: ""
+ required: false
+ type: string
wpt-args:
default: ""
required: false
@@ -166,7 +170,7 @@ jobs:
- name: Build (${{ inputs.profile }})
run: |
- ./mach build --use-crown --locked --${{ inputs.profile }}
+ ./mach build --use-crown --locked --${{ inputs.profile }} ${{ inputs.build-args }}
cp -r target/cargo-timings target/cargo-timings-linux
- name: Smoketest
run: xvfb-run ./mach smoketest --${{ inputs.profile }}
diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml
index e27c0a54180..4c19def57d2 100644
--- a/.github/workflows/mac.yml
+++ b/.github/workflows/mac.yml
@@ -7,6 +7,10 @@ on:
required: false
default: "release"
type: string
+ build-args:
+ default: ""
+ required: false
+ type: string
wpt-args:
default: ""
required: false
@@ -146,7 +150,7 @@ jobs:
brew install gnu-tar
- name: Build (${{ inputs.profile }})
run: |
- ./mach build --use-crown --locked --${{ inputs.profile }}
+ ./mach build --use-crown --locked --${{ inputs.profile }} ${{ inputs.build-args }}
cp -r target/cargo-timings target/cargo-timings-macos
- name: Smoketest
uses: nick-fields/retry@v3
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 1545dfa2450..1d11033a326 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -56,6 +56,7 @@ jobs:
unit-tests: ${{ matrix.unit_tests }}
build-libservo: ${{ matrix.build_libservo }}
wpt-args: ${{ matrix.wpt_args }}
+ build-args: ${{ matrix.build_args }}
number-of-wpt-chunks: ${{ matrix.number_of_wpt_chunks }}
bencher: ${{ matrix.bencher }}
diff --git a/.github/workflows/ohos.yml b/.github/workflows/ohos.yml
index 8728d31865c..0fe92dcf5c7 100644
--- a/.github/workflows/ohos.yml
+++ b/.github/workflows/ohos.yml
@@ -36,6 +36,7 @@ env:
RUST_BACKTRACE: 1
SHELL: /bin/bash
CARGO_INCREMENTAL: 0
+ BENCHER_PROJECT: ${{ vars.BENCHER_PROJECT || 'servo' }}
jobs:
build:
@@ -223,10 +224,8 @@ jobs:
hdc shell snapshot_display -f /data/local/tmp/servo.jpeg
hdc file recv /data/local/tmp/servo.jpeg test_output/servo_hos_screenshot.jpeg
hdc file recv /data/local/tmp/ohtrace.txt test_output/servo.ftrace
- # To limit the logsize we only save logs from servo.
- # Todo: investigate giving servo a custom domain tag, so we can also log appspawn etc,
- # since another common error might be the dynamic loader failing to relocate libservoshell.so
- hdc shell hilog --exit --pid=${servo_pid} > test_output/servo.log
+ # To limit the logsize we only save logs from servo.
+ hdc shell hilog --exit -D 0xE0C3 > test_output/servo.log
# todo: Also benchmark some other websites....
- name: Upload artifacts
uses: actions/upload-artifact@v4
@@ -240,3 +239,10 @@ jobs:
[[ $servo_pid =~ ^[0-9]+$ ]] || { echo "It looks like servo crashed!" ; exit 1; }
# If the grep fails, then the trace output for the "page loaded" prompt is missing
grep 'org\.servo\.servo-.* tracing_mark_write.*PageLoadEndedPrompt' test_output/servo.ftrace
+ - name: "Run benchmark"
+ run: hitrace-bench --bencher -b "org.servo.servo" -p "https://www.servo.org" -n 5
+ - name: Getting bencher
+ uses: bencherdev/bencher@main
+ - name: Uploading to bencher.dev
+ run: |
+ bencher run --adapter json --file bench.json --project '${{ env.BENCHER_PROJECT }}' --token '${{ secrets.BENCHER_API_TOKEN }}' --github-actions '${{ secrets.GITHUB_TOKEN }}'
diff --git a/.github/workflows/try-label.yml b/.github/workflows/try-label.yml
index b1ca27a6f58..d4ce3f944f6 100644
--- a/.github/workflows/try-label.yml
+++ b/.github/workflows/try-label.yml
@@ -128,6 +128,7 @@ jobs:
unit-tests: ${{ matrix.unit_tests }}
build-libservo: ${{ matrix.build_libservo }}
wpt-args: ${{ matrix.wpt_args }}
+ build-args: ${{ matrix.build_args }}
number-of-wpt-chunks: ${{ matrix.number_of_wpt_chunks }}
bencher: ${{ matrix.bencher }}
diff --git a/.github/workflows/try.yml b/.github/workflows/try.yml
index cba4fc03e06..c3a49af8857 100644
--- a/.github/workflows/try.yml
+++ b/.github/workflows/try.yml
@@ -10,6 +10,10 @@ on:
default: "release"
type: choice
options: ["release", "debug", "production"]
+ build-args:
+ default: ""
+ required: false
+ type: string
wpt-args:
default: ""
required: false
@@ -79,6 +83,7 @@ jobs:
// WPT-related overrides only affect Linux currently, as tests don't run by default on other platforms.
configuration.matrix[0].wpt = Boolean(${{ inputs.wpt }});
configuration.matrix[0].wpt_args = "${{ inputs.wpt-args }}" || "";
+ configuration.matrix[0].build_args = "${{ inputs.build-args }}" || "";
let unit_tests = Boolean(${{ inputs.unit-tests }});
let profile = '${{ inputs.profile }}';
@@ -107,6 +112,7 @@ jobs:
profile: ${{ matrix.profile }}
unit-tests: ${{ matrix.unit_tests }}
build-libservo: ${{ matrix.build_libservo }}
+ build-args: ${{ matrix.build_args }}
wpt-args: ${{ matrix.wpt_args }}
number-of-wpt-chunks: ${{ matrix.number_of_wpt_chunks }}
bencher: ${{ matrix.bencher }}
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml
index 5cf1e50b853..93e1710e464 100644
--- a/.github/workflows/windows.yml
+++ b/.github/workflows/windows.yml
@@ -7,6 +7,10 @@ on:
required: false
default: "release"
type: string
+ build-args:
+ default: ""
+ required: false
+ type: string
unit-tests:
required: false
default: false
@@ -161,7 +165,7 @@ jobs:
- name: Build (${{ inputs.profile }})
run: |
- .\mach build --use-crown --locked --${{ inputs.profile }}
+ .\mach build --use-crown --locked --${{ inputs.profile }} ${{ inputs.build-args }}
cp C:\a\servo\servo\target\cargo-timings C:\a\servo\servo\target\cargo-timings-windows -Recurse
- name: Copy resources
if: ${{ runner.environment != 'self-hosted' }}
diff --git a/.gitignore b/.gitignore
index 73c2fce6cda..630eb63b9bb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -57,6 +57,9 @@ webrender-captures/
Session.vim
Sessionx.vim
+# Zed
+/.zed
+
/unminified-js
/unminified-css
diff --git a/Cargo.lock b/Cargo.lock
index 6940195ff30..36e579d2921 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -393,9 +393,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "aws-lc-rs"
-version = "1.13.0"
+version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19b756939cb2f8dc900aa6dcd505e6e2428e9cae7ff7b028c49e3946efa70878"
+checksum = "93fcc8f365936c834db5514fc45aee5b1202d677e6b40e48468aaaa8183ca8c7"
dependencies = [
"aws-lc-sys",
"zeroize",
@@ -403,9 +403,9 @@ dependencies = [
[[package]]
name = "aws-lc-sys"
-version = "0.28.2"
+version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa9b6986f250236c27e5a204062434a773a13243d2ffc2955f37bdba4c5c6a1"
+checksum = "61b1d86e7705efe1be1b569bab41d4fa1e14e220b60a160f78de2db687add079"
dependencies = [
"bindgen 0.69.5",
"cc",
@@ -442,9 +442,9 @@ dependencies = [
[[package]]
name = "backtrace"
-version = "0.3.74"
+version = "0.3.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
+checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
dependencies = [
"addr2line",
"cfg-if",
@@ -601,7 +601,7 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f"
dependencies = [
- "objc2",
+ "objc2 0.5.2",
]
[[package]]
@@ -844,9 +844,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.2.21"
+version = "1.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8691782945451c1c383942c4874dbe63814f61cb57ef773cda2972682b7bb3c0"
+checksum = "32db95edf998450acc7881c932f94cd9b05c87b4b2599e8bab064753da4acfd1"
dependencies = [
"jobserver",
"libc",
@@ -964,18 +964,18 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.5.37"
+version = "4.5.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
+checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
-version = "4.5.37"
+version = "4.5.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
+checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
dependencies = [
"anstyle",
"clap_lex",
@@ -1223,7 +1223,7 @@ dependencies = [
[[package]]
name = "content-security-policy"
version = "0.5.4"
-source = "git+https://github.com/servo/rust-content-security-policy/?branch=servo-csp#fcd91e99139ca96629e04e1a8010f96374f0370f"
+source = "git+https://github.com/servo/rust-content-security-policy/?branch=servo-csp#334bfcbf0a3f503b21c90aee6aee30d4b8c9558a"
dependencies = [
"base64 0.22.1",
"bitflags 2.9.0",
@@ -1715,6 +1715,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
[[package]]
+name = "dispatch2"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
+dependencies = [
+ "bitflags 2.9.0",
+ "objc2 0.6.1",
+]
+
+[[package]]
name = "displaydoc"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2027,9 +2037,9 @@ dependencies = [
[[package]]
name = "error-code"
-version = "3.3.1"
+version = "3.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f"
+checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
[[package]]
name = "etagere"
@@ -2194,6 +2204,7 @@ dependencies = [
"net_traits",
"num-traits",
"parking_lot",
+ "profile_traits",
"range",
"serde",
"servo_allocator",
@@ -2227,7 +2238,7 @@ dependencies = [
[[package]]
name = "fontsan"
version = "0.5.2"
-source = "git+https://github.com/servo/fontsan#138bdb0451c4ea02a303caddc1a6c1fd654ae927"
+source = "git+https://github.com/servo/fontsan#c0d0b5333117901e1c31bc3c502c384115b93e6f"
dependencies = [
"cc",
"glob",
@@ -2540,9 +2551,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "gio-sys"
-version = "0.20.9"
+version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "160eb5250a26998c3e1b54e6a3d4ea15c6c7762a6062a19a7b63eff6e2b33f9e"
+checksum = "521e93a7e56fc89e84aea9a52cfc9436816a4b363b030260b699950ff1336c83"
dependencies = [
"glib-sys",
"gobject-sys",
@@ -2573,9 +2584,9 @@ dependencies = [
[[package]]
name = "glib"
-version = "0.20.9"
+version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "707b819af8059ee5395a2de9f2317d87a53dbad8846a2f089f0bb44703f37686"
+checksum = "c501c495842c2b23cdacead803a5a343ca2a5d7a7ddaff14cc5f6cf22cfb92c2"
dependencies = [
"bitflags 2.9.0",
"futures-channel",
@@ -2594,9 +2605,9 @@ dependencies = [
[[package]]
name = "glib-macros"
-version = "0.20.7"
+version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "715601f8f02e71baef9c1f94a657a9a77c192aea6097cf9ae7e5e177cd8cde68"
+checksum = "ebe6dc9ce29887c4b3b74d78d5ba473db160a258ae7ed883d23632ac7fed7bc9"
dependencies = [
"heck",
"proc-macro-crate",
@@ -2607,9 +2618,9 @@ dependencies = [
[[package]]
name = "glib-sys"
-version = "0.20.9"
+version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8928869a44cfdd1fccb17d6746e4ff82c8f82e41ce705aa026a52ca8dc3aefb"
+checksum = "8ab79e1ed126803a8fb827e3de0e2ff95191912b8db65cee467edb56fc4cc215"
dependencies = [
"libc",
"system-deps",
@@ -2653,9 +2664,9 @@ dependencies = [
[[package]]
name = "gobject-sys"
-version = "0.20.9"
+version = "0.20.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c773a3cb38a419ad9c26c81d177d96b4b08980e8bdbbf32dace883e96e96e7e3"
+checksum = "ec9aca94bb73989e3cfdbf8f2e0f1f6da04db4d291c431f444838925c4c63eda"
dependencies = [
"glib-sys",
"libc",
@@ -2721,9 +2732,9 @@ checksum = "36119f3a540b086b4e436bb2b588cf98a68863470e0e880f4d0842f112a3183a"
[[package]]
name = "gstreamer"
-version = "0.23.5"
+version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2188fe829b0ebe12e4cf2bbcf6658470a936269daba7afae92847a2af32c9105"
+checksum = "50ab4c88f731596a2511a6f14cabdd666e0d8efab62a1d58e6ddb57faa96e22e"
dependencies = [
"cfg-if",
"futures-channel",
@@ -2731,7 +2742,7 @@ dependencies = [
"futures-util",
"glib",
"gstreamer-sys",
- "itertools 0.13.0",
+ "itertools 0.14.0",
"libc",
"muldiv",
"num-integer",
@@ -2774,9 +2785,9 @@ dependencies = [
[[package]]
name = "gstreamer-audio"
-version = "0.23.5"
+version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49118ca684e2fc42207509fcac8497d91079c2ffe8ff2b4ae99e71dbafef1ede"
+checksum = "2e7ec7e0374298897e669db7c79544bc44df12011985e7dd5f38644edaf2caf4"
dependencies = [
"cfg-if",
"glib",
@@ -2790,9 +2801,9 @@ dependencies = [
[[package]]
name = "gstreamer-audio-sys"
-version = "0.23.5"
+version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d469526ecf30811b50a6460fd285ee40d189c46048b3d0c69b67a04b414fb51"
+checksum = "2b5f3e09e7c04ec91d78c2a6ca78d50b574b9ed49fdf5e72f3693adca4306a87"
dependencies = [
"glib-sys",
"gobject-sys",
@@ -2804,9 +2815,9 @@ dependencies = [
[[package]]
name = "gstreamer-base"
-version = "0.23.5"
+version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad33dd444db0d215ac363164f900f800ffb93361ad8a60840e95e14b7de985e8"
+checksum = "f19a74fd04ffdcb847dd322640f2cf520897129d00a7bcb92fd62a63f3e27404"
dependencies = [
"atomic_refcell",
"cfg-if",
@@ -2818,9 +2829,9 @@ dependencies = [
[[package]]
name = "gstreamer-base-sys"
-version = "0.23.5"
+version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "114b2a704f19a70f20c54b00e54f5d5376bbf78bd2791e6beb0776c997d8bf24"
+checksum = "87f2fb0037b6d3c5b51f60dea11e667910f33be222308ca5a101450018a09840"
dependencies = [
"glib-sys",
"gobject-sys",
@@ -2831,9 +2842,9 @@ dependencies = [
[[package]]
name = "gstreamer-gl"
-version = "0.23.5"
+version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02818bd81028abc4ee7b0106c21625be9a2f86ba5fd41ccff58359537637db59"
+checksum = "34aa19feafc4da2c7635abce0e0768892ff97ad73586bef02d9a60b251d9fe09"
dependencies = [
"glib",
"gstreamer",
@@ -2846,9 +2857,9 @@ dependencies = [
[[package]]
name = "gstreamer-gl-egl"
-version = "0.23.5"
+version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc5fad98961d18ed5dba4be44787d4735b78a1f53b7db392be004b96f1f2430b"
+checksum = "8de1f4247cf2d009b41ab5efb03e4d826b7ccaafb9a75d3ea10e68e46f65e8aa"
dependencies = [
"glib",
"gstreamer",
@@ -2859,9 +2870,9 @@ dependencies = [
[[package]]
name = "gstreamer-gl-egl-sys"
-version = "0.23.5"
+version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7ebdc94dc34e2b135b2610676b47d30ce88b80862f01e2acf7e29b9b42a14e4"
+checksum = "dda4d852ed107cc48692af4e109e5e4775b6ce1044d13df79f6f431c195096d7"
dependencies = [
"glib-sys",
"gstreamer-gl-sys",
@@ -2871,9 +2882,9 @@ dependencies = [
[[package]]
name = "gstreamer-gl-sys"
-version = "0.23.5"
+version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c2984f8c246407fabbecf852c4595dd1487f4cc495386e17ad31acb69db7d39"
+checksum = "a832c21d4522ed5e1b8dfc676a45361969216b144fc03af413a38c471f38bcf7"
dependencies = [
"glib-sys",
"gobject-sys",
@@ -2961,9 +2972,9 @@ dependencies = [
[[package]]
name = "gstreamer-sys"
-version = "0.23.5"
+version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe159238834058725808cf6604a7c5d9e4a50e1eacd7b0c63bce2fe3a067dbd1"
+checksum = "feea73b4d92dbf9c24a203c9cd0bcc740d584f6b5960d5faf359febf288919b2"
dependencies = [
"glib-sys",
"gobject-sys",
@@ -2973,9 +2984,9 @@ dependencies = [
[[package]]
name = "gstreamer-video"
-version = "0.23.5"
+version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad242d388b63c91652c8157de3b0c1f709e49c941a0aae1952455f6ee326ca2d"
+checksum = "1318b599d77ca4f7702ecbdeac1672d6304cb16b7e5752fabb3ee8260449a666"
dependencies = [
"cfg-if",
"futures-channel",
@@ -2990,9 +3001,9 @@ dependencies = [
[[package]]
name = "gstreamer-video-sys"
-version = "0.23.5"
+version = "0.23.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "465ff496889fb38be47f5e821163c2e83414d87c4aa55f5aae62dc7200971d4d"
+checksum = "0a70f0947f12d253b9de9bc3fd92f981e4d025336c18389c7f08cdf388a99f5c"
dependencies = [
"glib-sys",
"gobject-sys",
@@ -4010,15 +4021,6 @@ dependencies = [
[[package]]
name = "itertools"
-version = "0.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
-dependencies = [
- "either",
-]
-
-[[package]]
-name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
@@ -4034,9 +4036,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "jiff"
-version = "0.2.12"
+version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d07d8d955d798e7a4d6f9c58cd1f1916e790b42b092758a9ef6e16fef9f1b3fd"
+checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806"
dependencies = [
"jiff-static",
"log",
@@ -4047,9 +4049,9 @@ dependencies = [
[[package]]
name = "jiff-static"
-version = "0.2.12"
+version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f244cfe006d98d26f859c7abd1318d85327e1882dc9cef80f62daeeb0adcf300"
+checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48"
dependencies = [
"proc-macro2",
"quote",
@@ -4247,9 +4249,9 @@ dependencies = [
[[package]]
name = "libloading"
-version = "0.8.6"
+version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
+checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c"
dependencies = [
"cfg-if",
"windows-targets 0.52.6",
@@ -4257,9 +4259,9 @@ dependencies = [
[[package]]
name = "libm"
-version = "0.2.14"
+version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a25169bd5913a4b437588a7e3d127cd6e90127b60e0ffbd834a38f1599e016b8"
+checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
[[package]]
name = "libredox"
@@ -4644,7 +4646,7 @@ dependencies = [
[[package]]
name = "mozjs"
version = "0.14.1"
-source = "git+https://github.com/servo/mozjs#d1525dfaee22cc1ea9ee16c552cdeedaa9f20741"
+source = "git+https://github.com/servo/mozjs#728acdf3d4ce0604e9f75dd1d539dc6f291ccec7"
dependencies = [
"bindgen 0.71.1",
"cc",
@@ -4655,8 +4657,8 @@ dependencies = [
[[package]]
name = "mozjs_sys"
-version = "0.128.9-1"
-source = "git+https://github.com/servo/mozjs#d1525dfaee22cc1ea9ee16c552cdeedaa9f20741"
+version = "0.128.9-2"
+source = "git+https://github.com/servo/mozjs#728acdf3d4ce0604e9f75dd1d539dc6f291ccec7"
dependencies = [
"bindgen 0.71.1",
"cc",
@@ -5073,6 +5075,15 @@ dependencies = [
]
[[package]]
+name = "objc2"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551"
+dependencies = [
+ "objc2-encode",
+]
+
+[[package]]
name = "objc2-app-kit"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5081,14 +5092,25 @@ dependencies = [
"bitflags 2.9.0",
"block2",
"libc",
- "objc2",
+ "objc2 0.5.2",
"objc2-core-data",
"objc2-core-image",
- "objc2-foundation",
+ "objc2-foundation 0.2.2",
"objc2-quartz-core",
]
[[package]]
+name = "objc2-app-kit"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc"
+dependencies = [
+ "bitflags 2.9.0",
+ "objc2 0.6.1",
+ "objc2-foundation 0.3.1",
+]
+
+[[package]]
name = "objc2-cloud-kit"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5096,9 +5118,9 @@ checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009"
dependencies = [
"bitflags 2.9.0",
"block2",
- "objc2",
+ "objc2 0.5.2",
"objc2-core-location",
- "objc2-foundation",
+ "objc2-foundation 0.2.2",
]
[[package]]
@@ -5108,8 +5130,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889"
dependencies = [
"block2",
- "objc2",
- "objc2-foundation",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
]
[[package]]
@@ -5120,8 +5142,19 @@ checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
dependencies = [
"bitflags 2.9.0",
"block2",
- "objc2",
- "objc2-foundation",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-core-foundation"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
+dependencies = [
+ "bitflags 2.9.0",
+ "dispatch2",
+ "objc2 0.6.1",
]
[[package]]
@@ -5131,8 +5164,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
dependencies = [
"block2",
- "objc2",
- "objc2-foundation",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
"objc2-metal",
]
@@ -5143,9 +5176,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781"
dependencies = [
"block2",
- "objc2",
+ "objc2 0.5.2",
"objc2-contacts",
- "objc2-foundation",
+ "objc2-foundation 0.2.2",
]
[[package]]
@@ -5164,7 +5197,18 @@ dependencies = [
"block2",
"dispatch",
"libc",
- "objc2",
+ "objc2 0.5.2",
+]
+
+[[package]]
+name = "objc2-foundation"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c"
+dependencies = [
+ "bitflags 2.9.0",
+ "objc2 0.6.1",
+ "objc2-core-foundation",
]
[[package]]
@@ -5174,9 +5218,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398"
dependencies = [
"block2",
- "objc2",
- "objc2-app-kit",
- "objc2-foundation",
+ "objc2 0.5.2",
+ "objc2-app-kit 0.2.2",
+ "objc2-foundation 0.2.2",
]
[[package]]
@@ -5187,8 +5231,8 @@ checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
dependencies = [
"bitflags 2.9.0",
"block2",
- "objc2",
- "objc2-foundation",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
]
[[package]]
@@ -5199,8 +5243,8 @@ checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
dependencies = [
"bitflags 2.9.0",
"block2",
- "objc2",
- "objc2-foundation",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
"objc2-metal",
]
@@ -5210,8 +5254,8 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc"
dependencies = [
- "objc2",
- "objc2-foundation",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
]
[[package]]
@@ -5222,12 +5266,12 @@ checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f"
dependencies = [
"bitflags 2.9.0",
"block2",
- "objc2",
+ "objc2 0.5.2",
"objc2-cloud-kit",
"objc2-core-data",
"objc2-core-image",
"objc2-core-location",
- "objc2-foundation",
+ "objc2-foundation 0.2.2",
"objc2-link-presentation",
"objc2-quartz-core",
"objc2-symbols",
@@ -5242,8 +5286,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe"
dependencies = [
"block2",
- "objc2",
- "objc2-foundation",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
]
[[package]]
@@ -5254,9 +5298,9 @@ checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3"
dependencies = [
"bitflags 2.9.0",
"block2",
- "objc2",
+ "objc2 0.5.2",
"objc2-core-location",
- "objc2-foundation",
+ "objc2-foundation 0.2.2",
]
[[package]]
@@ -6143,9 +6187,9 @@ dependencies = [
[[package]]
name = "rustls"
-version = "0.23.26"
+version = "0.23.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0"
+checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321"
dependencies = [
"aws-lc-rs",
"log",
@@ -6167,15 +6211,18 @@ dependencies = [
[[package]]
name = "rustls-pki-types"
-version = "1.11.0"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c"
+checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
+dependencies = [
+ "zeroize",
+]
[[package]]
name = "rustls-webpki"
-version = "0.103.1"
+version = "0.103.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03"
+checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435"
dependencies = [
"aws-lc-rs",
"ring",
@@ -6323,6 +6370,7 @@ dependencies = [
"unicode-bidi",
"unicode-segmentation",
"url",
+ "urlpattern",
"utf-8",
"uuid",
"webdriver",
@@ -6467,7 +6515,7 @@ dependencies = [
[[package]]
name = "selectors"
version = "0.28.0"
-source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf"
+source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea"
dependencies = [
"bitflags 2.9.0",
"cssparser",
@@ -6762,7 +6810,7 @@ dependencies = [
[[package]]
name = "servo_arc"
version = "0.4.1"
-source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf"
+source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea"
dependencies = [
"serde",
"stable_deref_trait",
@@ -6828,6 +6876,7 @@ dependencies = [
"unicode-bidi",
"unicode-script",
"url",
+ "urlpattern",
"uuid",
"webrender_api",
"wr_malloc_size_of",
@@ -6898,8 +6947,8 @@ dependencies = [
"net",
"net_traits",
"nix",
- "objc2-app-kit",
- "objc2-foundation",
+ "objc2-app-kit 0.3.1",
+ "objc2-foundation 0.3.1",
"ohos-ime",
"ohos-ime-sys",
"ohos-vsync",
@@ -7222,7 +7271,7 @@ dependencies = [
[[package]]
name = "stylo"
version = "0.3.0"
-source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf"
+source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea"
dependencies = [
"app_units",
"arrayvec",
@@ -7280,7 +7329,7 @@ dependencies = [
[[package]]
name = "stylo_atoms"
version = "0.3.0"
-source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf"
+source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea"
dependencies = [
"string_cache",
"string_cache_codegen",
@@ -7289,12 +7338,12 @@ dependencies = [
[[package]]
name = "stylo_config"
version = "0.3.0"
-source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf"
+source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea"
[[package]]
name = "stylo_derive"
version = "0.3.0"
-source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf"
+source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea"
dependencies = [
"darling",
"proc-macro2",
@@ -7306,7 +7355,7 @@ dependencies = [
[[package]]
name = "stylo_dom"
version = "0.3.0"
-source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf"
+source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea"
dependencies = [
"bitflags 2.9.0",
"stylo_malloc_size_of",
@@ -7315,7 +7364,7 @@ dependencies = [
[[package]]
name = "stylo_malloc_size_of"
version = "0.3.0"
-source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf"
+source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea"
dependencies = [
"app_units",
"cssparser",
@@ -7332,12 +7381,12 @@ dependencies = [
[[package]]
name = "stylo_static_prefs"
version = "0.3.0"
-source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf"
+source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea"
[[package]]
name = "stylo_traits"
version = "0.3.0"
-source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf"
+source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea"
dependencies = [
"app_units",
"bitflags 2.9.0",
@@ -7720,7 +7769,7 @@ dependencies = [
[[package]]
name = "to_shmem"
version = "0.2.0"
-source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf"
+source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea"
dependencies = [
"cssparser",
"servo_arc",
@@ -7733,7 +7782,7 @@ dependencies = [
[[package]]
name = "to_shmem_derive"
version = "0.1.0"
-source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf"
+source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea"
dependencies = [
"darling",
"proc-macro2",
@@ -7744,9 +7793,9 @@ dependencies = [
[[package]]
name = "tokio"
-version = "1.44.2"
+version = "1.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
+checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165"
dependencies = [
"backtrace",
"bytes",
@@ -7944,9 +7993,9 @@ checksum = "ce607aae8ab0ab3abf3a2723a9ab6f09bb8639ed83fdd888d857b8e556c868d8"
[[package]]
name = "truetype"
-version = "0.47.8"
+version = "0.47.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d9ffbd4cf26797938aa36b2d03ec051800e6886fbed4bf70333d96b230a575d"
+checksum = "05ce9543b570c6e8a392274b67e1001816bce953aa89724e52a4639db02a10e0"
dependencies = [
"typeface",
]
@@ -8011,6 +8060,47 @@ dependencies = [
]
[[package]]
+name = "unic-char-property"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
+dependencies = [
+ "unic-char-range",
+]
+
+[[package]]
+name = "unic-char-range"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
+
+[[package]]
+name = "unic-common"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
+
+[[package]]
+name = "unic-ucd-ident"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987"
+dependencies = [
+ "unic-char-property",
+ "unic-char-range",
+ "unic-ucd-version",
+]
+
+[[package]]
+name = "unic-ucd-version"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
+dependencies = [
+ "unic-common",
+]
+
+[[package]]
name = "unicase"
version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -8091,6 +8181,18 @@ dependencies = [
]
[[package]]
+name = "urlpattern"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d"
+dependencies = [
+ "regex",
+ "serde",
+ "unic-ucd-ident",
+ "url",
+]
+
+[[package]]
name = "utf-8"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -8423,9 +8525,9 @@ dependencies = [
[[package]]
name = "web_atoms"
-version = "0.1.1"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08bcbdcad8fb2e316072ba6bbe09419afdb550285668ac2534f4230a6f2da0ee"
+checksum = "0b9c5f0bc545ea3b20b423e33b9b457764de0b3730cd957f6c6aa6c301785f6e"
dependencies = [
"phf",
"phf_codegen",
@@ -8435,11 +8537,11 @@ dependencies = [
[[package]]
name = "webdriver"
-version = "0.51.0"
+version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "310ce9d3648c5ff1915ca7dd09c44eabb7eb17f9ff4a6e7e5f4a902c8d1e269f"
+checksum = "91d53921e1bef27512fa358179c9a22428d55778d2c2ae3c5c37a52b82ce6e92"
dependencies = [
- "base64 0.21.7",
+ "base64 0.22.1",
"bytes",
"cookie 0.16.2",
"http 0.2.12",
@@ -8498,6 +8600,7 @@ dependencies = [
"glow",
"half",
"ipc-channel",
+ "itertools 0.14.0",
"log",
"pixels",
"snapshot",
@@ -9173,9 +9276,9 @@ dependencies = [
"libc",
"memmap2",
"ndk",
- "objc2",
- "objc2-app-kit",
- "objc2-foundation",
+ "objc2 0.5.2",
+ "objc2-app-kit 0.2.2",
+ "objc2-foundation 0.2.2",
"objc2-ui-kit",
"orbclient",
"percent-encoding",
@@ -9204,9 +9307,9 @@ dependencies = [
[[package]]
name = "winnow"
-version = "0.7.9"
+version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3"
+checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
dependencies = [
"memchr",
]
diff --git a/Cargo.toml b/Cargo.toml
index a82fb8aea73..4045a0ffcb9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -115,7 +115,7 @@ rayon = "1"
regex = "1.11"
rustls = { version = "0.23", default-features = false, features = ["logging", "std", "tls12"] }
rustls-pemfile = "2.0"
-rustls-pki-types = "1.11"
+rustls-pki-types = "1.12"
script_layout_interface = { path = "components/shared/script_layout" }
script_traits = { path = "components/shared/script" }
selectors = { git = "https://github.com/servo/stylo", branch = "2025-05-01" }
@@ -162,8 +162,9 @@ unicode-properties = { version = "0.1.3", features = ["emoji"] }
unicode-script = "0.5"
unicode-segmentation = "1.12.0"
url = "2.5"
+urlpattern = "0.3"
uuid = { version = "1.12.1", features = ["v4"] }
-webdriver = "0.51.0"
+webdriver = "0.53.0"
webgpu_traits = { path = "components/shared/webgpu" }
webpki-roots = "0.26"
webrender = { git = "https://github.com/servo/webrender", branch = "0.67", features = ["capture"] }
diff --git a/components/canvas/backend.rs b/components/canvas/backend.rs
index 53acbea8b8d..7e348fbc9b9 100644
--- a/components/canvas/backend.rs
+++ b/components/canvas/backend.rs
@@ -134,7 +134,18 @@ pub(crate) trait GenericPathBuilder<B: Backend> {
start_angle: f32,
end_angle: f32,
anticlockwise: bool,
- );
+ ) {
+ Self::ellipse(
+ self,
+ origin,
+ radius,
+ radius,
+ 0.,
+ start_angle,
+ end_angle,
+ anticlockwise,
+ );
+ }
fn bezier_curve_to(
&mut self,
control_point1: &Point2D<f32>,
@@ -212,7 +223,23 @@ pub(crate) trait GenericPathBuilder<B: Backend> {
large_arc: bool,
sweep: bool,
end_point: Point2D<f32>,
- );
+ ) {
+ let Some(start) = self.get_current_point() else {
+ return;
+ };
+
+ let arc = lyon_geom::SvgArc {
+ from: start,
+ to: end_point,
+ radii: lyon_geom::vector(radius_x, radius_y),
+ x_rotation: lyon_geom::Angle::degrees(rotation_angle),
+ flags: lyon_geom::ArcFlags { large_arc, sweep },
+ };
+
+ arc.for_each_quadratic_bezier(&mut |q| {
+ self.quadratic_curve_to(&q.ctrl, &q.to);
+ });
+ }
fn finish(&mut self) -> B::Path;
}
diff --git a/components/canvas/raqote_backend.rs b/components/canvas/raqote_backend.rs
index efe0ffd05b8..ecf780c36d5 100644
--- a/components/canvas/raqote_backend.rs
+++ b/components/canvas/raqote_backend.rs
@@ -680,26 +680,6 @@ impl PathBuilder {
}
impl GenericPathBuilder<RaqoteBackend> for PathBuilder {
- fn arc(
- &mut self,
- origin: Point2D<f32>,
- radius: f32,
- start_angle: f32,
- end_angle: f32,
- anticlockwise: bool,
- ) {
- <PathBuilder as GenericPathBuilder<RaqoteBackend>>::ellipse(
- self,
- origin,
- radius,
- radius,
- 0.,
- start_angle,
- end_angle,
- anticlockwise,
- );
- }
-
fn bezier_curve_to(
&mut self,
control_point1: &Point2D<f32>,
@@ -720,32 +700,6 @@ impl GenericPathBuilder<RaqoteBackend> for PathBuilder {
self.0.as_mut().unwrap().close();
}
- fn svg_arc(
- &mut self,
- radius_x: f32,
- radius_y: f32,
- rotation_angle: f32,
- large_arc: bool,
- sweep: bool,
- end_point: Point2D<f32>,
- ) {
- let Some(start) = self.get_current_point() else {
- return;
- };
-
- let arc = lyon_geom::SvgArc {
- from: start,
- to: end_point,
- radii: lyon_geom::vector(radius_x, radius_y),
- x_rotation: lyon_geom::Angle::degrees(rotation_angle),
- flags: lyon_geom::ArcFlags { large_arc, sweep },
- };
-
- arc.for_each_quadratic_bezier(&mut |q| {
- self.quadratic_curve_to(&q.ctrl, &q.to);
- });
- }
-
fn get_current_point(&mut self) -> Option<Point2D<f32>> {
let path = self.finish();
self.0 = Some(path.clone().into());
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs
index 41286a2760a..b1669277ba1 100644
--- a/components/compositing/compositor.rs
+++ b/components/compositing/compositor.rs
@@ -26,9 +26,9 @@ use crossbeam_channel::{Receiver, Sender};
use dpi::PhysicalSize;
use embedder_traits::{
CompositorHitTestResult, Cursor, InputEvent, MouseButtonEvent, MouseMoveEvent, ShutdownState,
- TouchEventType, UntrustedNodeAddress, ViewportDetails,
+ TouchEventType, UntrustedNodeAddress, ViewportDetails, WheelDelta, WheelEvent, WheelMode,
};
-use euclid::{Point2D, Rect, Scale, Size2D, Transform3D};
+use euclid::{Point2D, Rect, Scale, Size2D, Transform3D, Vector2D};
use fnv::FnvHashMap;
use ipc_channel::ipc::{self, IpcSharedMemory};
use libc::c_void;
@@ -646,6 +646,26 @@ impl IOCompositor {
.dispatch_input_event(InputEvent::MouseMove(MouseMoveEvent { point }));
},
+ CompositorMsg::WebDriverWheelScrollEvent(webview_id, x, y, delta_x, delta_y) => {
+ let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
+ warn!("Handling input event for unknown webview: {webview_id}");
+ return;
+ };
+ let delta = WheelDelta {
+ x: delta_x,
+ y: delta_y,
+ z: 0.0,
+ mode: WheelMode::DeltaPixel,
+ };
+ let dppx = webview_renderer.device_pixels_per_page_pixel();
+ let point = dppx.transform_point(Point2D::new(x, y));
+ let scroll_delta =
+ dppx.transform_vector(Vector2D::new(delta_x as f32, delta_y as f32));
+ webview_renderer
+ .dispatch_input_event(InputEvent::Wheel(WheelEvent { delta, point }));
+ webview_renderer.on_webdriver_wheel_action(scroll_delta, point);
+ },
+
CompositorMsg::SendInitialTransaction(pipeline) => {
let mut txn = Transaction::new();
txn.set_display_list(WebRenderEpoch(0), (pipeline, Default::default()));
diff --git a/components/compositing/tracing.rs b/components/compositing/tracing.rs
index ae7338106d0..a8bb8b42bb8 100644
--- a/components/compositing/tracing.rs
+++ b/components/compositing/tracing.rs
@@ -42,6 +42,7 @@ mod from_constellation {
Self::LoadComplete(..) => target!("LoadComplete"),
Self::WebDriverMouseButtonEvent(..) => target!("WebDriverMouseButtonEvent"),
Self::WebDriverMouseMoveEvent(..) => target!("WebDriverMouseMoveEvent"),
+ Self::WebDriverWheelScrollEvent(..) => target!("WebDriverWheelScrollEvent"),
Self::SendInitialTransaction(..) => target!("SendInitialTransaction"),
Self::SendScrollNode(..) => target!("SendScrollNode"),
Self::SendDisplayList { .. } => target!("SendDisplayList"),
diff --git a/components/compositing/webview_renderer.rs b/components/compositing/webview_renderer.rs
index 614ef0ff4c3..a51dd5f8cda 100644
--- a/components/compositing/webview_renderer.rs
+++ b/components/compositing/webview_renderer.rs
@@ -689,11 +689,6 @@ impl WebViewRenderer {
action: MouseButtonAction::Up,
point,
}));
- self.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent {
- button,
- action: MouseButtonAction::Click,
- point,
- }));
}
pub(crate) fn notify_scroll_event(
@@ -726,6 +721,22 @@ impl WebViewRenderer {
}));
}
+ /// Push scroll pending event when receiving wheel action from webdriver
+ pub(crate) fn on_webdriver_wheel_action(
+ &mut self,
+ scroll_delta: Vector2D<f32, DevicePixel>,
+ point: Point2D<f32, DevicePixel>,
+ ) {
+ if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
+ return;
+ }
+
+ let scroll_location =
+ ScrollLocation::Delta(LayoutVector2D::from_untyped(scroll_delta.to_untyped()));
+ let cursor = DeviceIntPoint::new(point.x as i32, point.y as i32);
+ self.on_scroll_window_event(scroll_location, cursor)
+ }
+
pub(crate) fn process_pending_scroll_events(&mut self, compositor: &mut IOCompositor) {
if self.pending_scroll_zoom_events.is_empty() {
return;
diff --git a/components/config/opts.rs b/components/config/opts.rs
index 785b43b0acd..3db866a7443 100644
--- a/components/config/opts.rs
+++ b/components/config/opts.rs
@@ -104,7 +104,7 @@ pub struct DebugOptions {
/// Dumps the rule tree.
pub dump_rule_tree: bool,
- /// Print the flow tree (Layout 2013) or fragment tree (Layout 2020) after each layout.
+ /// Print the fragment tree after each layout.
pub dump_flow_tree: bool,
/// Print the stacking context tree after each layout.
diff --git a/components/config/prefs.rs b/components/config/prefs.rs
index 96c40c91360..a9ec112e3eb 100644
--- a/components/config/prefs.rs
+++ b/components/config/prefs.rs
@@ -236,6 +236,8 @@ pub struct Preferences {
/// The user-agent to use for Servo. This can also be set via [`UserAgentPlatform`] in
/// order to set the value to the default value for the given platform.
pub user_agent: String,
+
+ pub log_filter: String,
}
impl Preferences {
@@ -398,6 +400,7 @@ impl Preferences {
threadpools_webrender_workers_max: 4,
webgl_testing_context_creation_error: false,
user_agent: String::new(),
+ log_filter: String::new(),
}
}
}
diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs
index f3a15d7708d..5db37800c42 100644
--- a/components/constellation/constellation.rs
+++ b/components/constellation/constellation.rs
@@ -129,9 +129,10 @@ use embedder_traits::resources::{self, Resource};
use embedder_traits::user_content_manager::UserContentManager;
use embedder_traits::{
AnimationState, CompositorHitTestResult, Cursor, EmbedderMsg, EmbedderProxy,
- FocusSequenceNumber, ImeEvent, InputEvent, MediaSessionActionType, MediaSessionEvent,
- MediaSessionPlaybackState, MouseButton, MouseButtonAction, MouseButtonEvent, Theme,
- ViewportDetails, WebDriverCommandMsg, WebDriverLoadStatus,
+ FocusSequenceNumber, ImeEvent, InputEvent, JSValue, JavaScriptEvaluationError,
+ JavaScriptEvaluationId, MediaSessionActionType, MediaSessionEvent, MediaSessionPlaybackState,
+ MouseButton, MouseButtonAction, MouseButtonEvent, Theme, ViewportDetails, WebDriverCommandMsg,
+ WebDriverLoadStatus,
};
use euclid::Size2D;
use euclid::default::Size2D as UntypedSize2D;
@@ -1477,6 +1478,52 @@ where
EmbedderToConstellationMessage::PaintMetric(pipeline_id, paint_metric_event) => {
self.handle_paint_metric(pipeline_id, paint_metric_event);
},
+ EmbedderToConstellationMessage::EvaluateJavaScript(
+ webview_id,
+ evaluation_id,
+ script,
+ ) => {
+ self.handle_evaluate_javascript(webview_id, evaluation_id, script);
+ },
+ }
+ }
+
+ #[cfg_attr(
+ feature = "tracing",
+ tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
+ )]
+ fn handle_evaluate_javascript(
+ &mut self,
+ webview_id: WebViewId,
+ evaluation_id: JavaScriptEvaluationId,
+ script: String,
+ ) {
+ let browsing_context_id = BrowsingContextId::from(webview_id);
+ let Some(pipeline) = self
+ .browsing_contexts
+ .get(&browsing_context_id)
+ .and_then(|browsing_context| self.pipelines.get(&browsing_context.pipeline_id))
+ else {
+ self.handle_finish_javascript_evaluation(
+ evaluation_id,
+ Err(JavaScriptEvaluationError::InternalError),
+ );
+ return;
+ };
+
+ if pipeline
+ .event_loop
+ .send(ScriptThreadMessage::EvaluateJavaScript(
+ pipeline.id,
+ evaluation_id,
+ script,
+ ))
+ .is_err()
+ {
+ self.handle_finish_javascript_evaluation(
+ evaluation_id,
+ Err(JavaScriptEvaluationError::InternalError),
+ );
}
}
@@ -1817,6 +1864,9 @@ where
self.mem_profiler_chan
.send(mem::ProfilerMsg::Report(sender));
},
+ ScriptToConstellationMessage::FinishJavaScriptEvaluation(evaluation_id, result) => {
+ self.handle_finish_javascript_evaluation(evaluation_id, result)
+ },
}
}
@@ -3182,6 +3232,22 @@ where
feature = "tracing",
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
)]
+ fn handle_finish_javascript_evaluation(
+ &mut self,
+ evaluation_id: JavaScriptEvaluationId,
+ result: Result<JSValue, JavaScriptEvaluationError>,
+ ) {
+ self.embedder_proxy
+ .send(EmbedderMsg::FinishJavaScriptEvaluation(
+ evaluation_id,
+ result,
+ ));
+ }
+
+ #[cfg_attr(
+ feature = "tracing",
+ tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
+ )]
fn handle_subframe_loaded(&mut self, pipeline_id: PipelineId) {
let browsing_context_id = match self.pipelines.get(&pipeline_id) {
Some(pipeline) => pipeline.browsing_context_id,
@@ -4691,6 +4757,7 @@ where
NavigationHistoryBehavior::Replace,
);
},
+ // TODO: This should use the ScriptThreadMessage::EvaluateJavaScript command
WebDriverCommandMsg::ScriptCommand(browsing_context_id, cmd) => {
let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) {
Some(browsing_context) => browsing_context.pipeline_id,
@@ -4783,6 +4850,12 @@ where
self.compositor_proxy
.send(CompositorMsg::WebDriverMouseMoveEvent(webview_id, x, y));
},
+ WebDriverCommandMsg::WheelScrollAction(webview, x, y, delta_x, delta_y) => {
+ self.compositor_proxy
+ .send(CompositorMsg::WebDriverWheelScrollEvent(
+ webview, x, y, delta_x, delta_y,
+ ));
+ },
WebDriverCommandMsg::TakeScreenshot(webview_id, rect, response_sender) => {
self.compositor_proxy.send(CompositorMsg::CreatePng(
webview_id,
diff --git a/components/constellation/sandboxing.rs b/components/constellation/sandboxing.rs
index 3738b4f288b..b4c6e7a9a39 100644
--- a/components/constellation/sandboxing.rs
+++ b/components/constellation/sandboxing.rs
@@ -159,6 +159,7 @@ pub fn spawn_multiprocess(content: UnprivilegedContent) -> Result<Process, Error
let mut child_process = process::Command::new(path_to_self);
setup_common(&mut child_process, token);
+ #[allow(clippy::zombie_processes)]
let child = child_process
.spawn()
.expect("Failed to start unsandboxed child process!");
diff --git a/components/constellation/tracing.rs b/components/constellation/tracing.rs
index eff7f755c6b..fd7fe7dd251 100644
--- a/components/constellation/tracing.rs
+++ b/components/constellation/tracing.rs
@@ -77,6 +77,7 @@ mod from_compositor {
Self::SetWebViewThrottled(_, _) => target!("SetWebViewThrottled"),
Self::SetScrollStates(..) => target!("SetScrollStates"),
Self::PaintMetric(..) => target!("PaintMetric"),
+ Self::EvaluateJavaScript(..) => target!("EvaluateJavaScript"),
}
}
}
@@ -176,6 +177,7 @@ mod from_script {
Self::TitleChanged(..) => target!("TitleChanged"),
Self::IFrameSizes(..) => target!("IFrameSizes"),
Self::ReportMemory(..) => target!("ReportMemory"),
+ Self::FinishJavaScriptEvaluation(..) => target!("FinishJavaScriptEvaluation"),
}
}
}
@@ -238,6 +240,9 @@ mod from_script {
Self::ShutdownComplete => target_variant!("ShutdownComplete"),
Self::ShowNotification(..) => target_variant!("ShowNotification"),
Self::ShowSelectElementMenu(..) => target_variant!("ShowSelectElementMenu"),
+ Self::FinishJavaScriptEvaluation(..) => {
+ target_variant!("FinishJavaScriptEvaluation")
+ },
}
}
}
diff --git a/components/devtools/actors/browsing_context.rs b/components/devtools/actors/browsing_context.rs
index 5de0855df4a..81a00e82d47 100644
--- a/components/devtools/actors/browsing_context.rs
+++ b/components/devtools/actors/browsing_context.rs
@@ -149,10 +149,6 @@ impl ResourceAvailable for BrowsingContextActor {
fn actor_name(&self) -> String {
self.name.clone()
}
-
- fn get_streams(&self) -> &RefCell<HashMap<StreamId, TcpStream>> {
- &self.streams
- }
}
impl Actor for BrowsingContextActor {
diff --git a/components/devtools/actors/console.rs b/components/devtools/actors/console.rs
index 3897ffa0fce..fd0bd184bfa 100644
--- a/components/devtools/actors/console.rs
+++ b/components/devtools/actors/console.rs
@@ -252,6 +252,7 @@ impl ConsoleActor {
page_error: PageError,
id: UniqueId,
registry: &ActorRegistry,
+ stream: &mut TcpStream,
) {
self.cached_events
.borrow_mut()
@@ -262,7 +263,11 @@ impl ConsoleActor {
if let Root::BrowsingContext(bc) = &self.root {
registry
.find::<BrowsingContextActor>(bc)
- .resource_available(PageErrorWrapper { page_error }, "error-message".into())
+ .resource_available(
+ PageErrorWrapper { page_error },
+ "error-message".into(),
+ stream,
+ )
};
}
}
@@ -272,6 +277,7 @@ impl ConsoleActor {
console_message: ConsoleMessage,
id: UniqueId,
registry: &ActorRegistry,
+ stream: &mut TcpStream,
) {
let log_message: ConsoleLog = console_message.into();
self.cached_events
@@ -283,7 +289,7 @@ impl ConsoleActor {
if let Root::BrowsingContext(bc) = &self.root {
registry
.find::<BrowsingContextActor>(bc)
- .resource_available(log_message, "console-message".into())
+ .resource_available(log_message, "console-message".into(), stream)
};
}
}
diff --git a/components/devtools/actors/watcher.rs b/components/devtools/actors/watcher.rs
index b0b2c755fd8..061ffc92336 100644
--- a/components/devtools/actors/watcher.rs
+++ b/components/devtools/actors/watcher.rs
@@ -31,7 +31,7 @@ use crate::actors::watcher::thread_configuration::{
ThreadConfigurationActor, ThreadConfigurationActorMsg,
};
use crate::protocol::JsonPacketStream;
-use crate::resource::{ResourceAvailable, ResourceAvailableReply};
+use crate::resource::ResourceAvailable;
use crate::{EmptyReplyMsg, StreamId, WorkerActor};
pub mod network_parent;
@@ -291,28 +291,28 @@ impl Actor for WatcherActor {
title: Some(target.title.borrow().clone()),
url: Some(target.url.borrow().clone()),
};
- target.resource_available(event, "document-event".into());
+ target.resource_available(event, "document-event".into(), stream);
}
},
"source" => {
let thread_actor = registry.find::<ThreadActor>(&target.thread);
let sources = thread_actor.source_manager.sources();
- target.resources_available(sources.iter().collect(), "source".into());
+ target.resources_available(
+ sources.iter().collect(),
+ "source".into(),
+ stream,
+ );
for worker_name in &root.workers {
let worker = registry.find::<WorkerActor>(worker_name);
let thread = registry.find::<ThreadActor>(&worker.thread);
let worker_sources = thread.source_manager.sources();
- let msg = ResourceAvailableReply {
- from: worker.name(),
- type_: "resources-available-array".into(),
- array: vec![(
- "source".to_string(),
- worker_sources.iter().cloned().collect(),
- )],
- };
- let _ = stream.write_json_packet(&msg);
+ worker.resources_available(
+ worker_sources.iter().collect(),
+ "source".into(),
+ stream,
+ );
}
},
"console-message" | "error-message" => {},
diff --git a/components/devtools/actors/worker.rs b/components/devtools/actors/worker.rs
index 68ff56fb3b2..f3ca4f2aed7 100644
--- a/components/devtools/actors/worker.rs
+++ b/components/devtools/actors/worker.rs
@@ -17,7 +17,7 @@ use servo_url::ServoUrl;
use crate::StreamId;
use crate::actor::{Actor, ActorMessageStatus, ActorRegistry};
use crate::protocol::JsonPacketStream;
-use crate::resource::{ResourceAvailable, ResourceAvailableReply};
+use crate::resource::ResourceAvailable;
#[derive(Clone, Copy)]
#[allow(dead_code)]
@@ -60,10 +60,6 @@ impl ResourceAvailable for WorkerActor {
fn actor_name(&self) -> String {
self.name.clone()
}
-
- fn get_streams(&self) -> &RefCell<HashMap<StreamId, TcpStream>> {
- &self.streams
- }
}
impl Actor for WorkerActor {
@@ -133,28 +129,6 @@ impl Actor for WorkerActor {
}
}
-impl WorkerActor {
- pub(crate) fn resource_available<T: Serialize>(&self, resource: T, resource_type: String) {
- self.resources_available(vec![resource], resource_type);
- }
-
- pub(crate) fn resources_available<T: Serialize>(
- &self,
- resources: Vec<T>,
- resource_type: String,
- ) {
- let msg = ResourceAvailableReply::<T> {
- from: self.name(),
- type_: "resources-available-array".into(),
- array: vec![(resource_type, resources)],
- };
-
- for stream in self.streams.borrow_mut().values_mut() {
- let _ = stream.write_json_packet(&msg);
- }
- }
-}
-
#[derive(Serialize)]
struct DetachedReply {
from: String,
diff --git a/components/devtools/lib.rs b/components/devtools/lib.rs
index 5fb9485e9d3..d097cb25e9d 100644
--- a/components/devtools/lib.rs
+++ b/components/devtools/lib.rs
@@ -414,7 +414,7 @@ impl DevtoolsInstance {
}
fn handle_page_error(
- &self,
+ &mut self,
pipeline_id: PipelineId,
worker_id: Option<WorkerId>,
page_error: PageError,
@@ -426,11 +426,13 @@ impl DevtoolsInstance {
let actors = self.actors.lock().unwrap();
let console_actor = actors.find::<ConsoleActor>(&console_actor_name);
let id = worker_id.map_or(UniqueId::Pipeline(pipeline_id), UniqueId::Worker);
- console_actor.handle_page_error(page_error, id, &actors);
+ for stream in self.connections.values_mut() {
+ console_actor.handle_page_error(page_error.clone(), id.clone(), &actors, stream);
+ }
}
fn handle_console_message(
- &self,
+ &mut self,
pipeline_id: PipelineId,
worker_id: Option<WorkerId>,
console_message: ConsoleMessage,
@@ -442,7 +444,9 @@ impl DevtoolsInstance {
let actors = self.actors.lock().unwrap();
let console_actor = actors.find::<ConsoleActor>(&console_actor_name);
let id = worker_id.map_or(UniqueId::Pipeline(pipeline_id), UniqueId::Worker);
- console_actor.handle_console_api(console_message, id, &actors);
+ for stream in self.connections.values_mut() {
+ console_actor.handle_console_api(console_message.clone(), id.clone(), &actors, stream);
+ }
}
fn find_console_actor(
@@ -529,7 +533,10 @@ impl DevtoolsInstance {
};
let worker_actor = actors.find::<WorkerActor>(worker_actor_name);
- worker_actor.resource_available(source, "source".into());
+
+ for stream in self.connections.values_mut() {
+ worker_actor.resource_available(&source, "source".into(), stream);
+ }
} else {
let Some(browsing_context_id) = self.pipelines.get(&pipeline_id) else {
return;
@@ -556,7 +563,10 @@ impl DevtoolsInstance {
// Notify browsing context about the new source
let browsing_context = actors.find::<BrowsingContextActor>(actor_name);
- browsing_context.resource_available(source, "source".into());
+
+ for stream in self.connections.values_mut() {
+ browsing_context.resource_available(&source, "source".into(), stream);
+ }
}
}
}
diff --git a/components/devtools/resource.rs b/components/devtools/resource.rs
index 7cef8188cc8..4e6aa4042b8 100644
--- a/components/devtools/resource.rs
+++ b/components/devtools/resource.rs
@@ -2,13 +2,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-use std::cell::RefCell;
-use std::collections::HashMap;
use std::net::TcpStream;
use serde::Serialize;
-use crate::StreamId;
use crate::protocol::JsonPacketStream;
#[derive(Serialize)]
@@ -22,21 +19,27 @@ pub(crate) struct ResourceAvailableReply<T: Serialize> {
pub(crate) trait ResourceAvailable {
fn actor_name(&self) -> String;
- fn get_streams(&self) -> &RefCell<HashMap<StreamId, TcpStream>>;
-
- fn resource_available<T: Serialize>(&self, resource: T, resource_type: String) {
- self.resources_available(vec![resource], resource_type);
+ fn resource_available<T: Serialize>(
+ &self,
+ resource: T,
+ resource_type: String,
+ stream: &mut TcpStream,
+ ) {
+ self.resources_available(vec![resource], resource_type, stream);
}
- fn resources_available<T: Serialize>(&self, resources: Vec<T>, resource_type: String) {
+ fn resources_available<T: Serialize>(
+ &self,
+ resources: Vec<T>,
+ resource_type: String,
+ stream: &mut TcpStream,
+ ) {
let msg = ResourceAvailableReply::<T> {
from: self.actor_name(),
type_: "resources-available-array".into(),
array: vec![(resource_type, resources)],
};
- for stream in self.get_streams().borrow_mut().values_mut() {
- let _ = stream.write_json_packet(&msg);
- }
+ let _ = stream.write_json_packet(&msg);
}
}
diff --git a/components/fonts/Cargo.toml b/components/fonts/Cargo.toml
index 2323cb1b240..ce51a9f9112 100644
--- a/components/fonts/Cargo.toml
+++ b/components/fonts/Cargo.toml
@@ -38,6 +38,7 @@ memmap2 = { workspace = true }
net_traits = { workspace = true }
num-traits = { workspace = true }
parking_lot = { workspace = true }
+profile_traits = { workspace = true }
range = { path = "../range" }
serde = { workspace = true }
servo_arc = { workspace = true }
diff --git a/components/fonts/font_store.rs b/components/fonts/font_store.rs
index 826be947672..0099c56c266 100644
--- a/components/fonts/font_store.rs
+++ b/components/fonts/font_store.rs
@@ -5,8 +5,8 @@
use std::collections::HashMap;
use std::sync::Arc;
-use atomic_refcell::AtomicRefCell;
use log::warn;
+use malloc_size_of_derive::MallocSizeOf;
use parking_lot::RwLock;
use style::stylesheets::DocumentStyleSheet;
use style::values::computed::{FontStyle, FontWeight};
@@ -15,7 +15,7 @@ use crate::font::FontDescriptor;
use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods, IsOblique};
use crate::system_font_service::{FontIdentifier, LowercaseFontFamilyName};
-#[derive(Default)]
+#[derive(Default, MallocSizeOf)]
pub struct FontStore {
pub(crate) families: HashMap<LowercaseFontFamilyName, FontTemplates>,
web_fonts_loading_for_stylesheets: Vec<(DocumentStyleSheet, usize)>,
@@ -134,7 +134,7 @@ impl FontStore {
///
/// This optimization is taken from:
/// <https://searchfox.org/mozilla-central/source/gfx/thebes/gfxFontEntry.cpp>.
-#[derive(Clone, Debug, Default)]
+#[derive(Clone, Debug, Default, MallocSizeOf)]
struct SimpleFamily {
regular: Option<FontTemplateRef>,
bold: Option<FontTemplateRef>,
@@ -190,7 +190,7 @@ impl SimpleFamily {
}
}
/// A list of font templates that make up a given font family.
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, MallocSizeOf)]
pub struct FontTemplates {
pub(crate) templates: Vec<FontTemplateRef>,
simple_family: Option<SimpleFamily>,
@@ -263,7 +263,7 @@ impl FontTemplates {
}
}
- let new_template = Arc::new(AtomicRefCell::new(new_template));
+ let new_template = FontTemplateRef::new(new_template);
self.templates.push(new_template.clone());
self.update_simple_family(new_template);
}
diff --git a/components/fonts/font_template.rs b/components/fonts/font_template.rs
index eca1017d14e..b8173ee0317 100644
--- a/components/fonts/font_template.rs
+++ b/components/fonts/font_template.rs
@@ -3,7 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::fmt::{Debug, Error, Formatter};
-use std::ops::RangeInclusive;
+use std::ops::{Deref, RangeInclusive};
use std::sync::Arc;
use atomic_refcell::AtomicRefCell;
@@ -20,7 +20,21 @@ use crate::system_font_service::{
};
/// A reference to a [`FontTemplate`] with shared ownership and mutability.
-pub type FontTemplateRef = Arc<AtomicRefCell<FontTemplate>>;
+#[derive(Clone, Debug, MallocSizeOf)]
+pub struct FontTemplateRef(#[conditional_malloc_size_of] Arc<AtomicRefCell<FontTemplate>>);
+
+impl FontTemplateRef {
+ pub fn new(template: FontTemplate) -> Self {
+ Self(Arc::new(AtomicRefCell::new(template)))
+ }
+}
+
+impl Deref for FontTemplateRef {
+ type Target = Arc<AtomicRefCell<FontTemplate>>;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
/// Describes how to select a font from a given family. This is very basic at the moment and needs
/// to be expanded or refactored when we support more of the font styling parameters.
diff --git a/components/fonts/system_font_service.rs b/components/fonts/system_font_service.rs
index 91b2d810eff..f799affa7c8 100644
--- a/components/fonts/system_font_service.rs
+++ b/components/fonts/system_font_service.rs
@@ -6,16 +6,19 @@ use std::borrow::ToOwned;
use std::cell::OnceCell;
use std::collections::HashMap;
use std::ops::{Deref, RangeInclusive};
-use std::sync::Arc;
use std::{fmt, thread};
use app_units::Au;
-use atomic_refcell::AtomicRefCell;
use compositing_traits::CrossProcessCompositorApi;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
use log::debug;
+use malloc_size_of::MallocSizeOf as MallocSizeOfTrait;
use malloc_size_of_derive::MallocSizeOf;
use parking_lot::{Mutex, RwLock};
+use profile_traits::mem::{
+ ProcessReports, ProfilerChan, Report, ReportKind, ReportsChan, perform_memory_report,
+};
+use profile_traits::path;
use serde::{Deserialize, Serialize};
use servo_config::pref;
use servo_url::ServoUrl;
@@ -66,11 +69,12 @@ pub enum SystemFontServiceMessage {
),
GetFontKey(IpcSender<FontKey>),
GetFontInstanceKey(IpcSender<FontInstanceKey>),
+ CollectMemoryReport(ReportsChan),
Exit(IpcSender<()>),
Ping,
}
-#[derive(Default)]
+#[derive(Default, MallocSizeOf)]
struct ResolvedGenericFontFamilies {
default: OnceCell<LowercaseFontFamilyName>,
serif: OnceCell<LowercaseFontFamilyName>,
@@ -84,6 +88,7 @@ struct ResolvedGenericFontFamilies {
/// The system font service. There is one of these for every Servo instance. This is a thread,
/// responsible for reading the list of system fonts, handling requests to match against
/// them, and ensuring that only one copy of system font data is loaded at a time.
+#[derive(MallocSizeOf)]
pub struct SystemFontService {
port: IpcReceiver<SystemFontServiceMessage>,
local_families: FontStore,
@@ -118,8 +123,12 @@ impl SystemFontServiceProxySender {
}
impl SystemFontService {
- pub fn spawn(compositor_api: CrossProcessCompositorApi) -> SystemFontServiceProxySender {
+ pub fn spawn(
+ compositor_api: CrossProcessCompositorApi,
+ memory_profiler_sender: ProfilerChan,
+ ) -> SystemFontServiceProxySender {
let (sender, receiver) = ipc::channel().unwrap();
+ let memory_reporter_sender = sender.clone();
thread::Builder::new()
.name("SystemFontService".to_owned())
@@ -138,7 +147,13 @@ impl SystemFontService {
cache.fetch_new_keys();
cache.refresh_local_families();
- cache.run();
+
+ memory_profiler_sender.run_with_memory_reporting(
+ || cache.run(),
+ "system-fonts".to_owned(),
+ memory_reporter_sender,
+ SystemFontServiceMessage::CollectMemoryReport,
+ );
})
.expect("Thread spawning failed");
@@ -172,6 +187,9 @@ impl SystemFontService {
self.fetch_new_keys();
let _ = result_sender.send(self.free_font_instance_keys.pop().unwrap());
},
+ SystemFontServiceMessage::CollectMemoryReport(report_sender) => {
+ self.collect_memory_report(report_sender);
+ },
SystemFontServiceMessage::Ping => (),
SystemFontServiceMessage::Exit(result) => {
let _ = result.send(());
@@ -181,6 +199,17 @@ impl SystemFontService {
}
}
+ fn collect_memory_report(&self, report_sender: ReportsChan) {
+ perform_memory_report(|ops| {
+ let reports = vec![Report {
+ path: path!["system-fonts"],
+ kind: ReportKind::ExplicitSystemHeapSize,
+ size: self.size_of(ops),
+ }];
+ report_sender.send(ProcessReports::new(reports));
+ });
+ }
+
#[cfg_attr(
feature = "tracing",
tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace")
@@ -528,11 +557,7 @@ impl SystemFontServiceProxy {
panic!("SystemFontService has already exited.");
};
- let templates: Vec<_> = templates
- .into_iter()
- .map(AtomicRefCell::new)
- .map(Arc::new)
- .collect();
+ let templates: Vec<_> = templates.into_iter().map(FontTemplateRef::new).collect();
self.templates.write().insert(cache_key, templates.clone());
templates
diff --git a/components/fonts/tests/font.rs b/components/fonts/tests/font.rs
index 78c507e7b93..a473be9222b 100644
--- a/components/fonts/tests/font.rs
+++ b/components/fonts/tests/font.rs
@@ -5,14 +5,13 @@
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
-use std::sync::Arc;
use app_units::Au;
use euclid::num::Zero;
use fonts::platform::font::PlatformFont;
use fonts::{
- Font, FontData, FontDescriptor, FontIdentifier, FontTemplate, PlatformFontMethods,
- ShapingFlags, ShapingOptions,
+ Font, FontData, FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef,
+ PlatformFontMethods, ShapingFlags, ShapingOptions,
};
use servo_url::ServoUrl;
use style::properties::longhands::font_variant_caps::computed_value::T as FontVariantCaps;
@@ -42,13 +41,7 @@ fn make_font(path: PathBuf) -> Font {
variant: FontVariantCaps::Normal,
pt_size: Au::from_px(24),
};
- Font::new(
- Arc::new(atomic_refcell::AtomicRefCell::new(template)),
- descriptor,
- Some(data),
- None,
- )
- .unwrap()
+ Font::new(FontTemplateRef::new(template), descriptor, Some(data), None).unwrap()
}
#[test]
diff --git a/components/fonts/tests/font_context.rs b/components/fonts/tests/font_context.rs
index aeafa02bcc1..0793c1e4ce1 100644
--- a/components/fonts/tests/font_context.rs
+++ b/components/fonts/tests/font_context.rs
@@ -137,6 +137,7 @@ mod font_context {
break;
},
SystemFontServiceMessage::Ping => {},
+ SystemFontServiceMessage::CollectMemoryReport(..) => {},
}
}
}
diff --git a/components/layout/construct_modern.rs b/components/layout/construct_modern.rs
index 22f179d146c..8f1282ec9f6 100644
--- a/components/layout/construct_modern.rs
+++ b/components/layout/construct_modern.rs
@@ -12,7 +12,7 @@ use style::selector_parser::PseudoElement;
use crate::PropagatedBoxTreeData;
use crate::context::LayoutContext;
-use crate::dom::{BoxSlot, NodeExt};
+use crate::dom::BoxSlot;
use crate::dom_traversal::{Contents, NodeAndStyleInfo, TraversalHandler};
use crate::flow::inline::construct::InlineFormattingContextBuilder;
use crate::flow::{BlockContainer, BlockFormattingContext};
@@ -24,32 +24,32 @@ use crate::layout_box_base::LayoutBoxBase;
use crate::style_ext::DisplayGeneratingBox;
/// A builder used for both flex and grid containers.
-pub(crate) struct ModernContainerBuilder<'a, 'dom, Node> {
+pub(crate) struct ModernContainerBuilder<'a, 'dom> {
context: &'a LayoutContext<'a>,
- info: &'a NodeAndStyleInfo<Node>,
+ info: &'a NodeAndStyleInfo<'dom>,
propagated_data: PropagatedBoxTreeData,
- contiguous_text_runs: Vec<ModernContainerTextRun<'dom, Node>>,
+ contiguous_text_runs: Vec<ModernContainerTextRun<'dom>>,
/// To be run in parallel with rayon in `finish`
- jobs: Vec<ModernContainerJob<'dom, Node>>,
+ jobs: Vec<ModernContainerJob<'dom>>,
has_text_runs: bool,
}
-enum ModernContainerJob<'dom, Node> {
+enum ModernContainerJob<'dom> {
ElementOrPseudoElement {
- info: NodeAndStyleInfo<Node>,
+ info: NodeAndStyleInfo<'dom>,
display: DisplayGeneratingBox,
contents: Contents,
box_slot: BoxSlot<'dom>,
},
- TextRuns(Vec<ModernContainerTextRun<'dom, Node>>),
+ TextRuns(Vec<ModernContainerTextRun<'dom>>),
}
-struct ModernContainerTextRun<'dom, Node> {
- info: NodeAndStyleInfo<Node>,
+struct ModernContainerTextRun<'dom> {
+ info: NodeAndStyleInfo<'dom>,
text: Cow<'dom, str>,
}
-impl<Node> ModernContainerTextRun<'_, Node> {
+impl ModernContainerTextRun<'_> {
/// <https://drafts.csswg.org/css-text/#white-space>
fn is_only_document_white_space(&self) -> bool {
// FIXME: is this the right definition? See
@@ -73,11 +73,8 @@ pub(crate) struct ModernItem<'dom> {
pub formatting_context: IndependentFormattingContext,
}
-impl<'dom, Node: 'dom> TraversalHandler<'dom, Node> for ModernContainerBuilder<'_, 'dom, Node>
-where
- Node: NodeExt<'dom>,
-{
- fn handle_text(&mut self, info: &NodeAndStyleInfo<Node>, text: Cow<'dom, str>) {
+impl<'dom> TraversalHandler<'dom> for ModernContainerBuilder<'_, 'dom> {
+ fn handle_text(&mut self, info: &NodeAndStyleInfo<'dom>, text: Cow<'dom, str>) {
self.contiguous_text_runs.push(ModernContainerTextRun {
info: info.clone(),
text,
@@ -87,7 +84,7 @@ where
/// Or pseudo-element
fn handle_element(
&mut self,
- info: &NodeAndStyleInfo<Node>,
+ info: &NodeAndStyleInfo<'dom>,
display: DisplayGeneratingBox,
contents: Contents,
box_slot: BoxSlot<'dom>,
@@ -103,13 +100,10 @@ where
}
}
-impl<'a, 'dom, Node: 'dom> ModernContainerBuilder<'a, 'dom, Node>
-where
- Node: NodeExt<'dom>,
-{
+impl<'a, 'dom> ModernContainerBuilder<'a, 'dom> {
pub fn new(
context: &'a LayoutContext<'a>,
- info: &'a NodeAndStyleInfo<Node>,
+ info: &'a NodeAndStyleInfo<'dom>,
propagated_data: PropagatedBoxTreeData,
) -> Self {
ModernContainerBuilder {
@@ -148,7 +142,7 @@ where
.filter_map(|job| match job {
ModernContainerJob::TextRuns(runs) => {
let mut inline_formatting_context_builder =
- InlineFormattingContextBuilder::new();
+ InlineFormattingContextBuilder::new(self.info);
for flex_text_run in runs.into_iter() {
inline_formatting_context_builder
.push_text(flex_text_run.text, &flex_text_run.info);
@@ -165,7 +159,7 @@ where
let block_formatting_context = BlockFormattingContext::from_block_container(
BlockContainer::InlineFormattingContext(inline_formatting_context),
);
- let info: &NodeAndStyleInfo<_> = &*anonymous_info;
+ let info: &NodeAndStyleInfo = &anonymous_info;
let formatting_context = IndependentFormattingContext {
base: LayoutBoxBase::new(info.into(), info.style.clone()),
contents: IndependentFormattingContextContents::NonReplaced(
diff --git a/components/layout/display_list/mod.rs b/components/layout/display_list/mod.rs
index 8799dd2da0c..f017642908d 100644
--- a/components/layout/display_list/mod.rs
+++ b/components/layout/display_list/mod.rs
@@ -14,6 +14,7 @@ use euclid::{Point2D, SideOffsets2D, Size2D, UnknownUnit};
use fonts::GlyphStore;
use gradient::WebRenderGradient;
use range::Range as ServoRange;
+use servo_arc::Arc as ServoArc;
use servo_geometry::MaxRect;
use style::Zero;
use style::color::{AbsoluteColor, ColorSpace};
@@ -153,10 +154,6 @@ pub(crate) struct DisplayListBuilder<'a> {
/// list building functions.
current_clip_chain_id: ClipChainId,
- /// The [OpaqueNode] handle to the node used to paint the page background
- /// if the background was a canvas.
- element_for_canvas_background: OpaqueNode,
-
/// A [LayoutContext] used to get information about the device pixel ratio
/// and get handles to WebRender images.
pub context: &'a LayoutContext<'a>,
@@ -169,6 +166,11 @@ pub(crate) struct DisplayListBuilder<'a> {
/// This data is collected during the traversal of the fragment tree and used
/// to paint the highlight at the very end.
inspector_highlight: Option<InspectorHighlight>,
+
+ /// Whether or not the `<body>` element should be painted. This is false if the root `<html>`
+ /// element inherits the `<body>`'s background to paint the page canvas background.
+ /// See <https://drafts.csswg.org/css-backgrounds/#body-background>.
+ paint_body_background: bool,
}
struct InspectorHighlight {
@@ -218,12 +220,12 @@ impl DisplayList {
current_scroll_node_id: self.compositor_info.root_reference_frame_id,
current_reference_frame_scroll_node_id: self.compositor_info.root_reference_frame_id,
current_clip_chain_id: ClipChainId::INVALID,
- element_for_canvas_background: fragment_tree.canvas_background.from_element,
context,
display_list: self,
inspector_highlight: context
.highlighted_dom_node
.map(InspectorHighlight::for_node),
+ paint_body_background: true,
};
fragment_tree.build_display_list(&mut builder, root_stacking_context);
@@ -469,18 +471,16 @@ impl Fragment {
Fragment::AbsoluteOrFixedPositioned(_) => {},
Fragment::Positioning(positioning_fragment) => {
let positioning_fragment = positioning_fragment.borrow();
- if let Some(style) = positioning_fragment.style.as_ref() {
- let rect = positioning_fragment
- .rect
- .translate(containing_block.origin.to_vector());
- self.maybe_push_hit_test_for_style_and_tag(
- builder,
- style,
- positioning_fragment.base.tag,
- rect,
- Cursor::Default,
- );
- }
+ let rect = positioning_fragment
+ .rect
+ .translate(containing_block.origin.to_vector());
+ self.maybe_push_hit_test_for_style_and_tag(
+ builder,
+ &positioning_fragment.style,
+ positioning_fragment.base.tag,
+ rect,
+ Cursor::Default,
+ );
},
Fragment::Image(image) => {
let image = image.borrow();
@@ -543,7 +543,13 @@ impl Fragment {
},
Fragment::Text(text) => {
let text = &*text.borrow();
- match text.parent_style.get_inherited_box().visibility {
+ match text
+ .inline_styles
+ .style
+ .borrow()
+ .get_inherited_box()
+ .visibility
+ {
Visibility::Visible => {
self.build_display_list_for_text_fragment(text, builder, containing_block)
},
@@ -603,22 +609,23 @@ impl Fragment {
return;
}
+ let parent_style = fragment.inline_styles.style.borrow();
self.maybe_push_hit_test_for_style_and_tag(
builder,
- &fragment.parent_style,
+ &parent_style,
fragment.base.tag,
rect,
Cursor::Text,
);
- let color = fragment.parent_style.clone_color();
+ let color = parent_style.clone_color();
let font_metrics = &fragment.font_metrics;
let dppx = builder.context.style_context.device_pixel_ratio().get();
- let common = builder.common_properties(rect.to_webrender(), &fragment.parent_style);
+ let common = builder.common_properties(rect.to_webrender(), &parent_style);
// Shadows. According to CSS-BACKGROUNDS, text shadows render in *reverse* order (front to
// back).
- let shadows = &fragment.parent_style.get_inherited_text().text_shadow;
+ let shadows = &parent_style.get_inherited_text().text_shadow;
for shadow in shadows.0.iter().rev() {
builder.wr().push_shadow(
&wr::SpaceAndClipInfo {
@@ -641,7 +648,7 @@ impl Fragment {
let mut rect = rect;
rect.origin.y += font_metrics.ascent - font_metrics.underline_offset;
rect.size.height = Au::from_f32_px(font_metrics.underline_size.to_nearest_pixel(dppx));
- self.build_display_list_for_text_decoration(fragment, builder, &rect, &color);
+ self.build_display_list_for_text_decoration(&parent_style, builder, &rect, &color);
}
if fragment
@@ -650,7 +657,7 @@ impl Fragment {
{
let mut rect = rect;
rect.size.height = Au::from_f32_px(font_metrics.underline_size.to_nearest_pixel(dppx));
- self.build_display_list_for_text_decoration(fragment, builder, &rect, &color);
+ self.build_display_list_for_text_decoration(&parent_style, builder, &rect, &color);
}
// TODO: This caret/text selection implementation currently does not account for vertical text
@@ -677,12 +684,13 @@ impl Fragment {
Point2D::new(end.x.to_f32_px(), containing_block.max_y().to_f32_px()),
);
if let Some(selection_color) = fragment
- .selected_style
+ .inline_styles
+ .selected
+ .borrow()
.clone_background_color()
.as_absolute()
{
- let selection_common =
- builder.common_properties(selection_rect, &fragment.parent_style);
+ let selection_common = builder.common_properties(selection_rect, &parent_style);
builder.wr().push_rect(
&selection_common,
selection_rect,
@@ -708,7 +716,7 @@ impl Fragment {
),
);
let insertion_point_common =
- builder.common_properties(insertion_point_rect, &fragment.parent_style);
+ builder.common_properties(insertion_point_rect, &parent_style);
// TODO: The color of the caret is currently hardcoded to the text color.
// We should be retrieving the caret color from the style properly.
builder
@@ -733,7 +741,7 @@ impl Fragment {
let mut rect = rect;
rect.origin.y += font_metrics.ascent - font_metrics.strikeout_offset;
rect.size.height = Au::from_f32_px(font_metrics.strikeout_size.to_nearest_pixel(dppx));
- self.build_display_list_for_text_decoration(fragment, builder, &rect, &color);
+ self.build_display_list_for_text_decoration(&parent_style, builder, &rect, &color);
}
if !shadows.0.is_empty() {
@@ -743,23 +751,22 @@ impl Fragment {
fn build_display_list_for_text_decoration(
&self,
- fragment: &TextFragment,
+ parent_style: &ServoArc<ComputedValues>,
builder: &mut DisplayListBuilder,
rect: &PhysicalRect<Au>,
color: &AbsoluteColor,
) {
let rect = rect.to_webrender();
let wavy_line_thickness = (0.33 * rect.size().height).ceil();
- let text_decoration_color = fragment
- .parent_style
+ let text_decoration_color = parent_style
.clone_text_decoration_color()
.resolve_to_absolute(color);
- let text_decoration_style = fragment.parent_style.clone_text_decoration_style();
+ let text_decoration_style = parent_style.clone_text_decoration_style();
if text_decoration_style == ComputedTextDecorationStyle::MozNone {
return;
}
builder.display_list.wr.push_line(
- &builder.common_properties(rect, &fragment.parent_style),
+ &builder.common_properties(rect, parent_style),
&rect,
wavy_line_thickness,
wr::LineOrientation::Horizontal,
@@ -999,12 +1006,17 @@ impl<'a> BuilderForBoxFragment<'a> {
}
fn build_background(&mut self, builder: &mut DisplayListBuilder) {
- if self
- .fragment
- .base
- .is_for_node(builder.element_for_canvas_background)
+ let flags = self.fragment.base.flags;
+
+ // The root element's background is painted separately as it might inherit the `<body>`'s
+ // background.
+ if flags.intersects(FragmentFlags::IS_ROOT_ELEMENT) {
+ return;
+ }
+ // If the `<body>` background was inherited by the root element, don't paint it again here.
+ if !builder.paint_body_background &&
+ flags.intersects(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT)
{
- // This background is already painted for the canvas, don’t paint it again here.
return;
}
@@ -1020,7 +1032,7 @@ impl<'a> BuilderForBoxFragment<'a> {
for extra_background in extra_backgrounds {
let positioning_area = extra_background.rect;
let painter = BackgroundPainter {
- style: &extra_background.style,
+ style: &extra_background.style.borrow_mut(),
painting_area_override: None,
positioning_area_override: Some(
positioning_area
diff --git a/components/layout/display_list/stacking_context.rs b/components/layout/display_list/stacking_context.rs
index b044b713260..27fa73a680c 100644
--- a/components/layout/display_list/stacking_context.rs
+++ b/components/layout/display_list/stacking_context.rs
@@ -582,15 +582,42 @@ impl StackingContext {
&self,
builder: &mut DisplayListBuilder,
fragment_tree: &crate::FragmentTree,
- containing_block_rect: &PhysicalRect<Au>,
) {
- let style = if let Some(style) = &fragment_tree.canvas_background.style {
- style
- } else {
- // The root element has `display: none`,
- // or the canvas background is taken from `<body>` which has `display: none`
+ let Some(root_fragment) = fragment_tree.root_fragments.iter().find(|fragment| {
+ fragment
+ .base()
+ .is_some_and(|base| base.flags.intersects(FragmentFlags::IS_ROOT_ELEMENT))
+ }) else {
return;
};
+ let root_fragment = match root_fragment {
+ Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => box_fragment,
+ _ => return,
+ }
+ .borrow();
+
+ let source_style = {
+ // > For documents whose root element is an HTML HTML element or an XHTML html element
+ // > [HTML]: if the computed value of background-image on the root element is none and its
+ // > background-color is transparent, user agents must instead propagate the computed
+ // > values of the background properties from that element’s first HTML BODY or XHTML body
+ // > child element.
+ if root_fragment.style.background_is_transparent() {
+ let body_fragment = fragment_tree.body_fragment();
+ builder.paint_body_background = body_fragment.is_none();
+ body_fragment
+ .map(|body_fragment| body_fragment.borrow().style.clone())
+ .unwrap_or(root_fragment.style.clone())
+ } else {
+ root_fragment.style.clone()
+ }
+ };
+
+ // This can happen if the root fragment does not have a `<body>` child (either because it is
+ // `display: none` or `display: contents`) or if the `<body>`'s background is transparent.
+ if source_style.background_is_transparent() {
+ return;
+ }
// The painting area is theoretically the infinite 2D plane,
// but we need a rectangle with finite coordinates.
@@ -598,14 +625,15 @@ impl StackingContext {
// If the document is smaller than the viewport (and doesn’t scroll),
// we still want to paint the rest of the viewport.
// If it’s larger, we also want to paint areas reachable after scrolling.
- let mut painting_area = fragment_tree
+ let painting_area = fragment_tree
.initial_containing_block
.union(&fragment_tree.scrollable_overflow)
.to_webrender();
- let background_color = style.resolve_color(&style.get_background().background_color);
+ let background_color =
+ source_style.resolve_color(&source_style.get_background().background_color);
if background_color.alpha > 0.0 {
- let common = builder.common_properties(painting_area, style);
+ let common = builder.common_properties(painting_area, &source_style);
let color = super::rgba(background_color);
builder
.display_list
@@ -613,97 +641,14 @@ impl StackingContext {
.push_rect(&common, painting_area, color)
}
- // `background-color` was comparatively easy,
- // but `background-image` needs a positioning area based on the root element.
- // Let’s find the corresponding fragment.
-
- // The fragment generated by the root element is the first one here, unless…
- let first_if_any = self.contents.first().or_else(|| {
- // There wasn’t any `StackingContextFragment` in the root `StackingContext`,
- // because the root element generates a stacking context. Let’s find that one.
- self.real_stacking_contexts_and_positioned_stacking_containers
- .first()
- .and_then(|first_child_stacking_context| {
- first_child_stacking_context.contents.first()
- })
- });
-
- macro_rules! debug_panic {
- ($msg: expr) => {
- if cfg!(debug_assertions) {
- panic!($msg);
- } else {
- warn!($msg);
- return;
- }
- };
- }
-
- let first_stacking_context_fragment = if let Some(first) = first_if_any {
- first
- } else {
- // This should only happen if the root element has `display: none`
- // TODO(servo#30569) revert to debug_panic!() once underlying bug is fixed
- log::warn!(
- "debug assertion failed! `CanvasBackground::for_root_element` should have returned `style: None`",
- );
- return;
- };
-
- let StackingContextContent::Fragment {
- fragment,
- scroll_node_id,
- containing_block,
- ..
- } = first_stacking_context_fragment
- else {
- debug_panic!("Expected a fragment, not a stacking container");
- };
- let box_fragment = match fragment {
- Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => box_fragment,
- _ => debug_panic!("Expected a box-generated fragment"),
- };
- let box_fragment = &*box_fragment.borrow();
-
- // The `StackingContextFragment` we found is for the root DOM element:
- debug_assert_eq!(
- fragment.tag().map(|tag| tag.node),
- Some(fragment_tree.canvas_background.root_element),
- );
-
- // The root element may have a CSS transform, and we want the canvas’
- // background image to be transformed. To do so, take its `SpatialId`
- // (but not its `ClipId`)
- builder.current_scroll_node_id = *scroll_node_id;
-
- // Now we need express the painting area rectangle in the local coordinate system,
- // which differs from the top-level coordinate system based on…
-
- // Convert the painting area rectangle to the local coordinate system of this `SpatialId`
- if let Some(reference_frame_data) =
- box_fragment.reference_frame_data_if_necessary(containing_block_rect)
- {
- painting_area.min -= reference_frame_data.origin.to_webrender().to_vector();
- if let Some(transformed) = reference_frame_data
- .transform
- .inverse()
- .and_then(|inversed| inversed.outer_transformed_rect(&painting_area.to_rect()))
- {
- painting_area = transformed.to_box2d();
- } else {
- // The desired rect cannot be represented, so skip painting this background-image
- return;
- }
- }
-
let mut fragment_builder = BuilderForBoxFragment::new(
- box_fragment,
- containing_block,
+ &root_fragment,
+ &fragment_tree.initial_containing_block,
false, /* is_hit_test_for_scrollable_overflow */
false, /* is_collapsed_table_borders */
);
let painter = super::background::BackgroundPainter {
- style,
+ style: &source_style,
painting_area_override: Some(painting_area),
positioning_area_override: None,
};
diff --git a/components/layout/dom.rs b/components/layout/dom.rs
index 8f2697e670a..e3a22eb5197 100644
--- a/components/layout/dom.rs
+++ b/components/layout/dom.rs
@@ -11,6 +11,7 @@ use base::id::{BrowsingContextId, PipelineId};
use html5ever::{local_name, ns};
use malloc_size_of_derive::MallocSizeOf;
use pixels::Image;
+use script::layout_dom::ServoLayoutNode;
use script_layout_interface::wrapper_traits::{
LayoutDataTrait, LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
};
@@ -18,14 +19,14 @@ use script_layout_interface::{
GenericLayoutDataTrait, LayoutElementType, LayoutNodeType as ScriptLayoutNodeType,
};
use servo_arc::Arc as ServoArc;
+use style::context::SharedStyleContext;
use style::properties::ComputedValues;
use style::selector_parser::PseudoElement;
use crate::cell::ArcRefCell;
-use crate::context::LayoutContext;
use crate::flexbox::FlexLevelBox;
use crate::flow::BlockLevelBox;
-use crate::flow::inline::InlineItem;
+use crate::flow::inline::{InlineItem, SharedInlineStyles};
use crate::fragment_tree::Fragment;
use crate::geom::PhysicalSize;
use crate::replaced::CanvasInfo;
@@ -58,7 +59,7 @@ impl InnerDOMLayoutData {
/// A box that is stored in one of the `DOMLayoutData` slots.
#[derive(MallocSizeOf)]
pub(super) enum LayoutBox {
- DisplayContents,
+ DisplayContents(SharedInlineStyles),
BlockLevel(ArcRefCell<BlockLevelBox>),
InlineLevel(Vec<ArcRefCell<InlineItem>>),
FlexLevel(ArcRefCell<FlexLevelBox>),
@@ -69,7 +70,7 @@ pub(super) enum LayoutBox {
impl LayoutBox {
fn invalidate_cached_fragment(&self) {
match self {
- LayoutBox::DisplayContents => {},
+ LayoutBox::DisplayContents(..) => {},
LayoutBox::BlockLevel(block_level_box) => {
block_level_box.borrow().invalidate_cached_fragment()
},
@@ -90,7 +91,7 @@ impl LayoutBox {
pub(crate) fn fragments(&self) -> Vec<Fragment> {
match self {
- LayoutBox::DisplayContents => vec![],
+ LayoutBox::DisplayContents(..) => vec![],
LayoutBox::BlockLevel(block_level_box) => block_level_box.borrow().fragments(),
LayoutBox::InlineLevel(inline_items) => inline_items
.iter()
@@ -101,6 +102,41 @@ impl LayoutBox {
LayoutBox::TableLevelBox(table_box) => table_box.fragments(),
}
}
+
+ fn repair_style(
+ &self,
+ context: &SharedStyleContext,
+ node: &ServoLayoutNode,
+ new_style: &ServoArc<ComputedValues>,
+ ) {
+ match self {
+ LayoutBox::DisplayContents(inline_shared_styles) => {
+ *inline_shared_styles.style.borrow_mut() = new_style.clone();
+ *inline_shared_styles.selected.borrow_mut() = node.to_threadsafe().selected_style();
+ },
+ LayoutBox::BlockLevel(block_level_box) => {
+ block_level_box
+ .borrow_mut()
+ .repair_style(context, node, new_style);
+ },
+ LayoutBox::InlineLevel(inline_items) => {
+ for inline_item in inline_items {
+ inline_item
+ .borrow_mut()
+ .repair_style(context, node, new_style);
+ }
+ },
+ LayoutBox::FlexLevel(flex_level_box) => {
+ flex_level_box.borrow_mut().repair_style(context, new_style)
+ },
+ LayoutBox::TableLevelBox(table_level_box) => {
+ table_level_box.repair_style(context, new_style)
+ },
+ LayoutBox::TaffyItemBox(taffy_item_box) => {
+ taffy_item_box.borrow_mut().repair_style(context, new_style)
+ },
+ }
+ }
}
/// A wrapper for [`InnerDOMLayoutData`]. This is necessary to give the entire data
@@ -158,34 +194,33 @@ impl Drop for BoxSlot<'_> {
}
}
-pub(crate) trait NodeExt<'dom>: 'dom + LayoutNode<'dom> {
+pub(crate) trait NodeExt<'dom> {
/// Returns the image if it’s loaded, and its size in image pixels
/// adjusted for `image_density`.
- fn as_image(self) -> Option<(Option<Arc<Image>>, PhysicalSize<f64>)>;
- fn as_canvas(self) -> Option<(CanvasInfo, PhysicalSize<f64>)>;
- fn as_iframe(self) -> Option<(PipelineId, BrowsingContextId)>;
- fn as_video(self) -> Option<(Option<webrender_api::ImageKey>, Option<PhysicalSize<f64>>)>;
- fn as_typeless_object_with_data_attribute(self) -> Option<String>;
- fn style(self, context: &LayoutContext) -> ServoArc<ComputedValues>;
-
- fn layout_data_mut(self) -> AtomicRefMut<'dom, InnerDOMLayoutData>;
- fn layout_data(self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>>;
+ fn as_image(&self) -> Option<(Option<Arc<Image>>, PhysicalSize<f64>)>;
+ fn as_canvas(&self) -> Option<(CanvasInfo, PhysicalSize<f64>)>;
+ fn as_iframe(&self) -> Option<(PipelineId, BrowsingContextId)>;
+ fn as_video(&self) -> Option<(Option<webrender_api::ImageKey>, Option<PhysicalSize<f64>>)>;
+ fn as_typeless_object_with_data_attribute(&self) -> Option<String>;
+ fn style(&self, context: &SharedStyleContext) -> ServoArc<ComputedValues>;
+
+ fn layout_data_mut(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData>;
+ fn layout_data(&self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>>;
fn element_box_slot(&self) -> BoxSlot<'dom>;
fn pseudo_element_box_slot(&self, which: PseudoElement) -> BoxSlot<'dom>;
- fn unset_pseudo_element_box(self, which: PseudoElement);
+ fn unset_pseudo_element_box(&self, which: PseudoElement);
/// Remove boxes for the element itself, and its `:before` and `:after` if any.
- fn unset_all_boxes(self);
+ fn unset_all_boxes(&self);
fn fragments_for_pseudo(&self, pseudo_element: Option<PseudoElement>) -> Vec<Fragment>;
- fn invalidate_cached_fragment(self);
+ fn invalidate_cached_fragment(&self);
+
+ fn repair_style(&self, context: &SharedStyleContext);
}
-impl<'dom, LayoutNodeType> NodeExt<'dom> for LayoutNodeType
-where
- LayoutNodeType: 'dom + LayoutNode<'dom>,
-{
- fn as_image(self) -> Option<(Option<Arc<Image>>, PhysicalSize<f64>)> {
+impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> {
+ fn as_image(&self) -> Option<(Option<Arc<Image>>, PhysicalSize<f64>)> {
let node = self.to_threadsafe();
let (resource, metadata) = node.image_data()?;
let (width, height) = resource
@@ -201,7 +236,7 @@ where
Some((resource, PhysicalSize::new(width, height)))
}
- fn as_video(self) -> Option<(Option<webrender_api::ImageKey>, Option<PhysicalSize<f64>>)> {
+ fn as_video(&self) -> Option<(Option<webrender_api::ImageKey>, Option<PhysicalSize<f64>>)> {
let node = self.to_threadsafe();
let data = node.media_data()?;
let natural_size = if let Some(frame) = data.current_frame {
@@ -216,7 +251,7 @@ where
))
}
- fn as_canvas(self) -> Option<(CanvasInfo, PhysicalSize<f64>)> {
+ fn as_canvas(&self) -> Option<(CanvasInfo, PhysicalSize<f64>)> {
let node = self.to_threadsafe();
let canvas_data = node.canvas_data()?;
let source = canvas_data.source;
@@ -226,7 +261,7 @@ where
))
}
- fn as_iframe(self) -> Option<(PipelineId, BrowsingContextId)> {
+ fn as_iframe(&self) -> Option<(PipelineId, BrowsingContextId)> {
let node = self.to_threadsafe();
match (node.iframe_pipeline_id(), node.iframe_browsing_context_id()) {
(Some(pipeline_id), Some(browsing_context_id)) => {
@@ -236,8 +271,10 @@ where
}
}
- fn as_typeless_object_with_data_attribute(self) -> Option<String> {
- if self.type_id() != ScriptLayoutNodeType::Element(LayoutElementType::HTMLObjectElement) {
+ fn as_typeless_object_with_data_attribute(&self) -> Option<String> {
+ if LayoutNode::type_id(self) !=
+ ScriptLayoutNodeType::Element(LayoutElementType::HTMLObjectElement)
+ {
return None;
}
@@ -253,15 +290,15 @@ where
.map(|string| string.to_owned())
}
- fn style(self, context: &LayoutContext) -> ServoArc<ComputedValues> {
- self.to_threadsafe().style(context.shared_context())
+ fn style(&self, context: &SharedStyleContext) -> ServoArc<ComputedValues> {
+ self.to_threadsafe().style(context)
}
- fn layout_data_mut(self) -> AtomicRefMut<'dom, InnerDOMLayoutData> {
- if LayoutNode::layout_data(&self).is_none() {
+ fn layout_data_mut(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData> {
+ if LayoutNode::layout_data(self).is_none() {
self.initialize_layout_data::<DOMLayoutData>();
}
- LayoutNode::layout_data(&self)
+ LayoutNode::layout_data(self)
.unwrap()
.as_any()
.downcast_ref::<DOMLayoutData>()
@@ -270,8 +307,8 @@ where
.borrow_mut()
}
- fn layout_data(self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>> {
- LayoutNode::layout_data(&self).map(|data| {
+ fn layout_data(&self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>> {
+ LayoutNode::layout_data(self).map(|data| {
data.as_any()
.downcast_ref::<DOMLayoutData>()
.unwrap()
@@ -298,7 +335,7 @@ where
BoxSlot::new(cell.clone())
}
- fn unset_pseudo_element_box(self, pseudo_element_type: PseudoElement) {
+ fn unset_pseudo_element_box(&self, pseudo_element_type: PseudoElement) {
let data = self.layout_data_mut();
let cell = match pseudo_element_type {
PseudoElement::Before => &data.pseudo_before_box,
@@ -312,7 +349,7 @@ where
*cell.borrow_mut() = None;
}
- fn unset_all_boxes(self) {
+ fn unset_all_boxes(&self) {
let data = self.layout_data_mut();
*data.self_box.borrow_mut() = None;
*data.pseudo_before_box.borrow_mut() = None;
@@ -322,7 +359,7 @@ where
// for DOM descendants of elements with `display: none`.
}
- fn invalidate_cached_fragment(self) {
+ fn invalidate_cached_fragment(&self) {
let data = self.layout_data_mut();
if let Some(data) = data.self_box.borrow_mut().as_mut() {
data.invalidate_cached_fragment();
@@ -330,7 +367,7 @@ where
}
fn fragments_for_pseudo(&self, pseudo_element: Option<PseudoElement>) -> Vec<Fragment> {
- NodeExt::layout_data(*self)
+ NodeExt::layout_data(self)
.and_then(|layout_data| {
layout_data
.for_pseudo(pseudo_element)
@@ -339,4 +376,30 @@ where
})
.unwrap_or_default()
}
+
+ fn repair_style(&self, context: &SharedStyleContext) {
+ let data = self.layout_data_mut();
+ if let Some(layout_object) = &*data.self_box.borrow() {
+ let style = self.to_threadsafe().style(context);
+ layout_object.repair_style(context, self, &style);
+ }
+
+ if let Some(layout_object) = &*data.pseudo_before_box.borrow() {
+ if let Some(node) = self.to_threadsafe().with_pseudo(PseudoElement::Before) {
+ layout_object.repair_style(context, self, &node.style(context));
+ }
+ }
+
+ if let Some(layout_object) = &*data.pseudo_after_box.borrow() {
+ if let Some(node) = self.to_threadsafe().with_pseudo(PseudoElement::After) {
+ layout_object.repair_style(context, self, &node.style(context));
+ }
+ }
+
+ if let Some(layout_object) = &*data.pseudo_marker_box.borrow() {
+ if let Some(node) = self.to_threadsafe().with_pseudo(PseudoElement::Marker) {
+ layout_object.repair_style(context, self, &node.style(context));
+ }
+ }
+ }
}
diff --git a/components/layout/dom_traversal.rs b/components/layout/dom_traversal.rs
index 42101e3edbc..0201d72dbe2 100644
--- a/components/layout/dom_traversal.rs
+++ b/components/layout/dom_traversal.rs
@@ -8,11 +8,14 @@ use std::iter::FusedIterator;
use fonts::ByteIndex;
use html5ever::{LocalName, local_name};
use range::Range;
-use script_layout_interface::wrapper_traits::{ThreadSafeLayoutElement, ThreadSafeLayoutNode};
+use script::layout_dom::ServoLayoutNode;
+use script_layout_interface::wrapper_traits::{
+ LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
+};
use script_layout_interface::{LayoutElementType, LayoutNodeType};
use selectors::Element as SelectorsElement;
use servo_arc::Arc as ServoArc;
-use style::dom::{TElement, TShadowRoot};
+use style::dom::{NodeInfo, TElement, TNode, TShadowRoot};
use style::properties::ComputedValues;
use style::selector_parser::PseudoElement;
use style::values::generics::counters::{Content, ContentItem};
@@ -20,6 +23,7 @@ use style::values::specified::Quotes;
use crate::context::LayoutContext;
use crate::dom::{BoxSlot, LayoutBox, NodeExt};
+use crate::flow::inline::SharedInlineStyles;
use crate::fragment_tree::{BaseFragmentInfo, FragmentFlags, Tag};
use crate::quotes::quotes_for_lang;
use crate::replaced::ReplacedContents;
@@ -28,15 +32,15 @@ use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside, DisplayOuts
/// A data structure used to pass and store related layout information together to
/// avoid having to repeat the same arguments in argument lists.
#[derive(Clone)]
-pub(crate) struct NodeAndStyleInfo<Node> {
- pub node: Node,
+pub(crate) struct NodeAndStyleInfo<'dom> {
+ pub node: ServoLayoutNode<'dom>,
pub pseudo_element_type: Option<PseudoElement>,
pub style: ServoArc<ComputedValues>,
}
-impl<'dom, Node: NodeExt<'dom>> NodeAndStyleInfo<Node> {
+impl<'dom> NodeAndStyleInfo<'dom> {
fn new_with_pseudo(
- node: Node,
+ node: ServoLayoutNode<'dom>,
pseudo_element_type: PseudoElement,
style: ServoArc<ComputedValues>,
) -> Self {
@@ -47,7 +51,7 @@ impl<'dom, Node: NodeExt<'dom>> NodeAndStyleInfo<Node> {
}
}
- pub(crate) fn new(node: Node, style: ServoArc<ComputedValues>) -> Self {
+ pub(crate) fn new(node: ServoLayoutNode<'dom>, style: ServoArc<ComputedValues>) -> Self {
Self {
node,
pseudo_element_type: None,
@@ -86,11 +90,8 @@ impl<'dom, Node: NodeExt<'dom>> NodeAndStyleInfo<Node> {
}
}
-impl<'dom, Node> From<&NodeAndStyleInfo<Node>> for BaseFragmentInfo
-where
- Node: NodeExt<'dom>,
-{
- fn from(info: &NodeAndStyleInfo<Node>) -> Self {
+impl<'dom> From<&NodeAndStyleInfo<'dom>> for BaseFragmentInfo {
+ fn from(info: &NodeAndStyleInfo<'dom>) -> Self {
let node = info.node;
let pseudo = info.pseudo_element_type;
let threadsafe_node = node.to_threadsafe();
@@ -174,29 +175,30 @@ pub(super) enum PseudoElementContentItem {
Replaced(ReplacedContents),
}
-pub(super) trait TraversalHandler<'dom, Node>
-where
- Node: 'dom,
-{
- fn handle_text(&mut self, info: &NodeAndStyleInfo<Node>, text: Cow<'dom, str>);
+pub(super) trait TraversalHandler<'dom> {
+ fn handle_text(&mut self, info: &NodeAndStyleInfo<'dom>, text: Cow<'dom, str>);
/// Or pseudo-element
fn handle_element(
&mut self,
- info: &NodeAndStyleInfo<Node>,
+ info: &NodeAndStyleInfo<'dom>,
display: DisplayGeneratingBox,
contents: Contents,
box_slot: BoxSlot<'dom>,
);
+
+ /// Notify the handler that we are about to recurse into a `display: contents` element.
+ fn enter_display_contents(&mut self, _: SharedInlineStyles) {}
+
+ /// Notify the handler that we have finished a `display: contents` element.
+ fn leave_display_contents(&mut self) {}
}
-fn traverse_children_of<'dom, Node>(
- parent_element: Node,
+fn traverse_children_of<'dom>(
+ parent_element: ServoLayoutNode<'dom>,
context: &LayoutContext,
- handler: &mut impl TraversalHandler<'dom, Node>,
-) where
- Node: NodeExt<'dom>,
-{
+ handler: &mut impl TraversalHandler<'dom>,
+) {
traverse_eager_pseudo_element(PseudoElement::Before, parent_element, context, handler);
let is_text_input_element = matches!(
@@ -210,7 +212,10 @@ fn traverse_children_of<'dom, Node>(
);
if is_text_input_element || is_textarea_element {
- let info = NodeAndStyleInfo::new(parent_element, parent_element.style(context));
+ let info = NodeAndStyleInfo::new(
+ parent_element,
+ parent_element.style(context.shared_context()),
+ );
let node_text_content = parent_element.to_threadsafe().node_text_content();
if node_text_content.is_empty() {
// The addition of zero-width space here forces the text input to have an inline formatting
@@ -229,7 +234,7 @@ fn traverse_children_of<'dom, Node>(
if !is_text_input_element && !is_textarea_element {
for child in iter_child_nodes(parent_element) {
if child.is_text_node() {
- let info = NodeAndStyleInfo::new(child, child.style(context));
+ let info = NodeAndStyleInfo::new(child, child.style(context.shared_context()));
handler.handle_text(&info, child.to_threadsafe().node_text_content());
} else if child.is_element() {
traverse_element(child, context, handler);
@@ -240,19 +245,17 @@ fn traverse_children_of<'dom, Node>(
traverse_eager_pseudo_element(PseudoElement::After, parent_element, context, handler);
}
-fn traverse_element<'dom, Node>(
- element: Node,
+fn traverse_element<'dom>(
+ element: ServoLayoutNode<'dom>,
context: &LayoutContext,
- handler: &mut impl TraversalHandler<'dom, Node>,
-) where
- Node: NodeExt<'dom>,
-{
+ handler: &mut impl TraversalHandler<'dom>,
+) {
// Clear any existing pseudo-element box slot, because markers are not handled like
// `::before`` and `::after`. They are processed during box tree creation.
element.unset_pseudo_element_box(PseudoElement::Marker);
let replaced = ReplacedContents::for_element(element, context);
- let style = element.style(context);
+ let style = element.style(context.shared_context());
match Display::from(style.get_box().display) {
Display::None => element.unset_all_boxes(),
Display::Contents => {
@@ -261,8 +264,15 @@ fn traverse_element<'dom, Node>(
// <https://drafts.csswg.org/css-display-3/#valdef-display-contents>
element.unset_all_boxes()
} else {
- element.element_box_slot().set(LayoutBox::DisplayContents);
- traverse_children_of(element, context, handler)
+ let shared_inline_styles: SharedInlineStyles =
+ (&NodeAndStyleInfo::new(element, style)).into();
+ element
+ .element_box_slot()
+ .set(LayoutBox::DisplayContents(shared_inline_styles.clone()));
+
+ handler.enter_display_contents(shared_inline_styles);
+ traverse_children_of(element, context, handler);
+ handler.leave_display_contents();
}
},
Display::GeneratingBox(display) => {
@@ -286,14 +296,12 @@ fn traverse_element<'dom, Node>(
}
}
-fn traverse_eager_pseudo_element<'dom, Node>(
+fn traverse_eager_pseudo_element<'dom>(
pseudo_element_type: PseudoElement,
- node: Node,
+ node: ServoLayoutNode<'dom>,
context: &LayoutContext,
- handler: &mut impl TraversalHandler<'dom, Node>,
-) where
- Node: NodeExt<'dom>,
-{
+ handler: &mut impl TraversalHandler<'dom>,
+) {
assert!(pseudo_element_type.is_eager());
// First clear any old contents from the node.
@@ -317,8 +325,12 @@ fn traverse_eager_pseudo_element<'dom, Node>(
Display::Contents => {
let items = generate_pseudo_element_content(&info.style, node, context);
let box_slot = node.pseudo_element_box_slot(pseudo_element_type);
- box_slot.set(LayoutBox::DisplayContents);
+ let shared_inline_styles: SharedInlineStyles = (&info).into();
+ box_slot.set(LayoutBox::DisplayContents(shared_inline_styles.clone()));
+
+ handler.enter_display_contents(shared_inline_styles);
traverse_pseudo_element_contents(&info, context, handler, items);
+ handler.leave_display_contents();
},
Display::GeneratingBox(display) => {
let items = generate_pseudo_element_content(&info.style, node, context);
@@ -329,14 +341,12 @@ fn traverse_eager_pseudo_element<'dom, Node>(
}
}
-fn traverse_pseudo_element_contents<'dom, Node>(
- info: &NodeAndStyleInfo<Node>,
+fn traverse_pseudo_element_contents<'dom>(
+ info: &NodeAndStyleInfo<'dom>,
context: &LayoutContext,
- handler: &mut impl TraversalHandler<'dom, Node>,
+ handler: &mut impl TraversalHandler<'dom>,
items: Vec<PseudoElementContentItem>,
-) where
- Node: NodeExt<'dom>,
-{
+) {
let mut anonymous_info = None;
for item in items {
match item {
@@ -396,14 +406,12 @@ impl std::convert::TryFrom<Contents> for NonReplacedContents {
}
impl NonReplacedContents {
- pub(crate) fn traverse<'dom, Node>(
+ pub(crate) fn traverse<'dom>(
self,
context: &LayoutContext,
- info: &NodeAndStyleInfo<Node>,
- handler: &mut impl TraversalHandler<'dom, Node>,
- ) where
- Node: NodeExt<'dom>,
- {
+ info: &NodeAndStyleInfo<'dom>,
+ handler: &mut impl TraversalHandler<'dom>,
+ ) {
match self {
NonReplacedContents::OfElement | NonReplacedContents::OfTextControl => {
traverse_children_of(info.node, context, handler)
@@ -427,14 +435,11 @@ where
}
/// <https://www.w3.org/TR/CSS2/generate.html#propdef-content>
-fn generate_pseudo_element_content<'dom, Node>(
+fn generate_pseudo_element_content(
pseudo_element_style: &ComputedValues,
- element: Node,
+ element: ServoLayoutNode<'_>,
context: &LayoutContext,
-) -> Vec<PseudoElementContentItem>
-where
- Node: NodeExt<'dom>,
-{
+) -> Vec<PseudoElementContentItem> {
match &pseudo_element_style.get_counters().content {
Content::Items(items) => {
let mut vec = vec![];
@@ -517,18 +522,14 @@ where
}
}
-pub enum ChildNodeIterator<Node> {
+pub enum ChildNodeIterator<'dom> {
/// Iterating over the children of a node
- Node(Option<Node>),
+ Node(Option<ServoLayoutNode<'dom>>),
/// Iterating over the assigned nodes of a `HTMLSlotElement`
- Slottables(<Vec<Node> as IntoIterator>::IntoIter),
+ Slottables(<Vec<ServoLayoutNode<'dom>> as IntoIterator>::IntoIter),
}
-#[allow(clippy::unnecessary_to_owned)] // Clippy is wrong.
-pub(crate) fn iter_child_nodes<'dom, Node>(parent: Node) -> ChildNodeIterator<Node>
-where
- Node: NodeExt<'dom>,
-{
+pub(crate) fn iter_child_nodes(parent: ServoLayoutNode<'_>) -> ChildNodeIterator<'_> {
if let Some(element) = parent.as_element() {
if let Some(shadow) = element.shadow_root() {
return iter_child_nodes(shadow.as_node());
@@ -536,6 +537,7 @@ where
let slotted_nodes = element.slotted_nodes();
if !slotted_nodes.is_empty() {
+ #[allow(clippy::unnecessary_to_owned)] // Clippy is wrong.
return ChildNodeIterator::Slottables(slotted_nodes.to_owned().into_iter());
}
}
@@ -544,11 +546,8 @@ where
ChildNodeIterator::Node(first)
}
-impl<'dom, Node> Iterator for ChildNodeIterator<Node>
-where
- Node: NodeExt<'dom>,
-{
- type Item = Node;
+impl<'dom> Iterator for ChildNodeIterator<'dom> {
+ type Item = ServoLayoutNode<'dom>;
fn next(&mut self) -> Option<Self::Item> {
match self {
@@ -562,4 +561,4 @@ where
}
}
-impl<'dom, Node> FusedIterator for ChildNodeIterator<Node> where Node: NodeExt<'dom> {}
+impl FusedIterator for ChildNodeIterator<'_> {}
diff --git a/components/layout/flexbox/layout.rs b/components/layout/flexbox/layout.rs
index e69b792e272..80057af2b34 100644
--- a/components/layout/flexbox/layout.rs
+++ b/components/layout/flexbox/layout.rs
@@ -29,8 +29,10 @@ use super::{FlexContainer, FlexContainerConfig, FlexItemBox, FlexLevelBox};
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
use crate::formatting_contexts::{Baselines, IndependentFormattingContextContents};
-use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags};
-use crate::geom::{AuOrAuto, LogicalRect, LogicalSides, LogicalVec2, Size, Sizes};
+use crate::fragment_tree::{
+ BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags, SpecificLayoutInfo,
+};
+use crate::geom::{AuOrAuto, LazySize, LogicalRect, LogicalSides, LogicalVec2, Size, Sizes};
use crate::layout_box_base::CacheableLayoutResult;
use crate::positioned::{
AbsolutelyPositionedBox, PositioningContext, PositioningContextLength, relative_adjustement,
@@ -142,6 +144,9 @@ struct FlexItemLayoutResult {
// Whether or not this layout had a child that dependeded on block constraints.
has_child_which_depends_on_block_constraints: bool,
+
+ // The specific layout info that this flex item had.
+ specific_layout_info: Option<SpecificLayoutInfo>,
}
impl FlexItemLayoutResult {
@@ -295,7 +300,8 @@ impl FlexLineItem<'_> {
.sides_to_flow_relative(item_margin)
.to_physical(container_writing_mode),
None, /* clearance */
- );
+ )
+ .with_specific_layout_info(self.layout_result.specific_layout_info);
// If this flex item establishes a containing block for absolutely-positioned
// descendants, then lay out any relevant absolutely-positioned children. This
@@ -649,6 +655,7 @@ impl FlexContainer {
positioning_context: &mut PositioningContext,
containing_block: &ContainingBlock,
depends_on_block_constraints: bool,
+ lazy_block_size: &LazySize,
) -> CacheableLayoutResult {
let depends_on_block_constraints =
depends_on_block_constraints || self.config.flex_direction == FlexDirection::Column;
@@ -670,14 +677,13 @@ impl FlexContainer {
// https://drafts.csswg.org/css-flexbox/#algo-main-container
let container_main_size = match self.config.flex_axis {
FlexAxis::Row => containing_block.size.inline,
- FlexAxis::Column => match containing_block.size.block {
- SizeConstraint::Definite(size) => size,
- SizeConstraint::MinMax(min, max) => self
- .main_content_sizes(layout_context, &containing_block.into(), || &flex_context)
+ FlexAxis::Column => lazy_block_size.resolve(|| {
+ let mut containing_block = IndefiniteContainingBlock::from(containing_block);
+ containing_block.size.block = None;
+ self.main_content_sizes(layout_context, &containing_block, || &flex_context)
.sizes
.max_content
- .clamp_between_extremums(min, max),
- },
+ }),
};
// Actual length may be less, but we guess that usually not by a lot
@@ -760,30 +766,23 @@ impl FlexContainer {
.map(|layout| layout.line_size.cross)
.sum::<Au>() +
cross_gap * (line_count as i32 - 1);
+ let content_block_size = match self.config.flex_axis {
+ FlexAxis::Row => content_cross_size,
+ FlexAxis::Column => container_main_size,
+ };
// https://drafts.csswg.org/css-flexbox/#algo-cross-container
- let container_cross_size = match flex_context.container_inner_size_constraint.cross {
- SizeConstraint::Definite(cross_size) => cross_size,
- SizeConstraint::MinMax(min, max) => {
- content_cross_size.clamp_between_extremums(min, max)
- },
+ let container_cross_size = match self.config.flex_axis {
+ FlexAxis::Row => lazy_block_size.resolve(|| content_cross_size),
+ FlexAxis::Column => containing_block.size.inline,
};
let container_size = FlexRelativeVec2 {
main: container_main_size,
cross: container_cross_size,
};
- let content_block_size = flex_context
- .config
- .flex_axis
- .vec2_to_flow_relative(container_size)
- .block;
-
- let mut remaining_free_cross_space =
- match flex_context.container_inner_size_constraint.cross {
- SizeConstraint::Definite(cross_size) => cross_size - content_cross_size,
- _ => Au::zero(),
- };
+
+ let mut remaining_free_cross_space = container_cross_size - content_cross_size;
// Implement fallback alignment.
//
@@ -1910,6 +1909,7 @@ impl FlexItem<'_> {
// size can differ from the hypothetical cross size, we should defer
// synthesizing until needed.
baseline_relative_to_margin_box: None,
+ specific_layout_info: None,
})
},
IndependentFormattingContextContents::NonReplaced(non_replaced) => {
@@ -1929,6 +1929,27 @@ impl FlexItem<'_> {
}
}
+ let lazy_block_size = if cross_axis_is_item_block_axis {
+ // This means that an auto size with stretch alignment will behave different than
+ // a stretch size. That's not what the spec says, but matches other browsers.
+ // To be discussed in https://github.com/w3c/csswg-drafts/issues/11784.
+ let stretch_size = containing_block
+ .size
+ .block
+ .to_definite()
+ .map(|size| Au::zero().max(size - self.pbm_auto_is_zero.cross));
+ LazySize::new(
+ &self.content_cross_sizes,
+ Direction::Block,
+ Size::FitContent,
+ Au::zero,
+ stretch_size,
+ self.is_table(),
+ )
+ } else {
+ used_main_size.into()
+ };
+
let layout = non_replaced.layout(
flex_context.layout_context,
&mut positioning_context,
@@ -1938,12 +1959,14 @@ impl FlexItem<'_> {
flex_axis == FlexAxis::Column ||
self.cross_size_stretches_to_line ||
self.depends_on_block_constraints,
+ &lazy_block_size,
);
let CacheableLayoutResult {
fragments,
content_block_size,
baselines: content_box_baselines,
depends_on_block_constraints,
+ specific_layout_info,
..
} = layout;
@@ -1954,22 +1977,7 @@ impl FlexItem<'_> {
});
let hypothetical_cross_size = if cross_axis_is_item_block_axis {
- // This means that an auto size with stretch alignment will behave different than
- // a stretch size. That's not what the spec says, but matches other browsers.
- // To be discussed in https://github.com/w3c/csswg-drafts/issues/11784.
- let stretch_size = containing_block
- .size
- .block
- .to_definite()
- .map(|size| Au::zero().max(size - self.pbm_auto_is_zero.cross));
- self.content_cross_sizes.resolve(
- Direction::Block,
- Size::FitContent,
- Au::zero,
- stretch_size,
- || content_block_size.into(),
- self.is_table(),
- )
+ lazy_block_size.resolve(|| content_block_size)
} else {
inline_size
};
@@ -2012,6 +2020,7 @@ impl FlexItem<'_> {
containing_block_block_size: item_as_containing_block.size.block,
depends_on_block_constraints,
has_child_which_depends_on_block_constraints,
+ specific_layout_info,
})
},
}
@@ -2678,6 +2687,7 @@ impl FlexItemBox {
flex_context.containing_block,
&self.independent_formatting_context.base,
false, /* depends_on_block_constraints */
+ &LazySize::intrinsic(),
)
.content_block_size
};
diff --git a/components/layout/flexbox/mod.rs b/components/layout/flexbox/mod.rs
index e1f8213f1e9..91a12b31812 100644
--- a/components/layout/flexbox/mod.rs
+++ b/components/layout/flexbox/mod.rs
@@ -5,6 +5,7 @@
use geom::{FlexAxis, MainStartCrossStart};
use malloc_size_of_derive::MallocSizeOf;
use servo_arc::Arc as ServoArc;
+use style::context::SharedStyleContext;
use style::logical_geometry::WritingMode;
use style::properties::ComputedValues;
use style::properties::longhands::align_items::computed_value::T as AlignItems;
@@ -17,7 +18,7 @@ use crate::PropagatedBoxTreeData;
use crate::cell::ArcRefCell;
use crate::construct_modern::{ModernContainerBuilder, ModernItemKind};
use crate::context::LayoutContext;
-use crate::dom::{LayoutBox, NodeExt};
+use crate::dom::LayoutBox;
use crate::dom_traversal::{NodeAndStyleInfo, NonReplacedContents};
use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragment_tree::{BaseFragmentInfo, Fragment};
@@ -90,7 +91,6 @@ impl FlexContainerConfig {
pub(crate) struct FlexContainer {
children: Vec<ArcRefCell<FlexLevelBox>>,
- #[conditional_malloc_size_of]
style: ServoArc<ComputedValues>,
/// The configuration of this [`FlexContainer`].
@@ -98,9 +98,9 @@ pub(crate) struct FlexContainer {
}
impl FlexContainer {
- pub fn construct<'dom>(
+ pub fn construct(
context: &LayoutContext,
- info: &NodeAndStyleInfo<impl NodeExt<'dom>>,
+ info: &NodeAndStyleInfo<'_>,
contents: NonReplacedContents,
propagated_data: PropagatedBoxTreeData,
) -> Self {
@@ -137,6 +137,11 @@ impl FlexContainer {
config: FlexContainerConfig::new(&info.style),
}
}
+
+ pub(crate) fn repair_style(&mut self, new_style: &ServoArc<ComputedValues>) {
+ self.config = FlexContainerConfig::new(new_style);
+ self.style = new_style.clone();
+ }
}
#[derive(Debug, MallocSizeOf)]
@@ -146,6 +151,22 @@ pub(crate) enum FlexLevelBox {
}
impl FlexLevelBox {
+ pub(crate) fn repair_style(
+ &mut self,
+ context: &SharedStyleContext,
+ new_style: &ServoArc<ComputedValues>,
+ ) {
+ match self {
+ FlexLevelBox::FlexItem(flex_item_box) => flex_item_box
+ .independent_formatting_context
+ .repair_style(context, new_style),
+ FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
+ .borrow_mut()
+ .context
+ .repair_style(context, new_style),
+ }
+ }
+
pub(crate) fn invalidate_cached_fragment(&self) {
match self {
FlexLevelBox::FlexItem(flex_item_box) => flex_item_box
diff --git a/components/layout/flow/construct.rs b/components/layout/flow/construct.rs
index 5ed567f513b..334da8ae2b0 100644
--- a/components/layout/flow/construct.rs
+++ b/components/layout/flow/construct.rs
@@ -13,9 +13,9 @@ use style::selector_parser::PseudoElement;
use style::str::char_is_whitespace;
use super::OutsideMarker;
-use super::inline::InlineFormattingContext;
use super::inline::construct::InlineFormattingContextBuilder;
use super::inline::inline_box::InlineBox;
+use super::inline::{InlineFormattingContext, SharedInlineStyles};
use crate::PropagatedBoxTreeData;
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
@@ -33,16 +33,13 @@ use crate::style_ext::{ComputedValuesExt, DisplayGeneratingBox, DisplayInside, D
use crate::table::{AnonymousTableContent, Table};
impl BlockFormattingContext {
- pub(crate) fn construct<'dom, Node>(
+ pub(crate) fn construct(
context: &LayoutContext,
- info: &NodeAndStyleInfo<Node>,
+ info: &NodeAndStyleInfo<'_>,
contents: NonReplacedContents,
propagated_data: PropagatedBoxTreeData,
is_list_item: bool,
- ) -> Self
- where
- Node: NodeExt<'dom>,
- {
+ ) -> Self {
Self::from_block_container(BlockContainer::construct(
context,
info,
@@ -61,8 +58,8 @@ impl BlockFormattingContext {
}
}
-struct BlockLevelJob<'dom, Node> {
- info: NodeAndStyleInfo<Node>,
+struct BlockLevelJob<'dom> {
+ info: NodeAndStyleInfo<'dom>,
box_slot: BoxSlot<'dom>,
propagated_data: PropagatedBoxTreeData,
kind: BlockLevelCreator,
@@ -111,12 +108,12 @@ enum IntermediateBlockContainer {
///
/// This builder starts from the first child of a given DOM node
/// and does a preorder traversal of all of its inclusive siblings.
-pub(crate) struct BlockContainerBuilder<'dom, 'style, Node> {
+pub(crate) struct BlockContainerBuilder<'dom, 'style> {
context: &'style LayoutContext<'style>,
/// This NodeAndStyleInfo contains the root node, the corresponding pseudo
/// content designator, and the block container style.
- info: &'style NodeAndStyleInfo<Node>,
+ info: &'style NodeAndStyleInfo<'dom>,
/// The list of block-level boxes to be built for the final block container.
///
@@ -131,7 +128,7 @@ pub(crate) struct BlockContainerBuilder<'dom, 'style, Node> {
/// doesn't have a next sibling, we either reached the end of the container
/// root or there are ongoing inline-level boxes
/// (see `handle_block_level_element`).
- block_level_boxes: Vec<BlockLevelJob<'dom, Node>>,
+ block_level_boxes: Vec<BlockLevelJob<'dom>>,
/// Whether or not this builder has yet produced a block which would be
/// be considered the first line for the purposes of `text-indent`.
@@ -140,29 +137,35 @@ pub(crate) struct BlockContainerBuilder<'dom, 'style, Node> {
/// The propagated data to use for BoxTree construction.
propagated_data: PropagatedBoxTreeData,
- inline_formatting_context_builder: InlineFormattingContextBuilder,
+ /// The [`InlineFormattingContextBuilder`] if we have encountered any inline items,
+ /// otherwise None.
+ ///
+ /// TODO: This can be `OnceCell` once `OnceCell::get_mut_or_init` is stabilized.
+ inline_formatting_context_builder: Option<InlineFormattingContextBuilder>,
/// The [`NodeAndStyleInfo`] to use for anonymous block boxes pushed to the list of
- /// block-level boxes, lazily initialized (see `end_ongoing_inline_formatting_context`).
- anonymous_box_info: Option<NodeAndStyleInfo<Node>>,
+ /// block-level boxes, lazily initialized.
+ anonymous_box_info: Option<NodeAndStyleInfo<'dom>>,
/// A collection of content that is being added to an anonymous table. This is
/// composed of any sequence of internal table elements or table captions that
/// are found outside of a table.
- anonymous_table_content: Vec<AnonymousTableContent<'dom, Node>>,
+ anonymous_table_content: Vec<AnonymousTableContent<'dom>>,
+
+ /// Any [`InlineFormattingContexts`] created need to know about the ongoing `display: contents`
+ /// ancestors that have been processed. This `Vec` allows passing those into new
+ /// [`InlineFormattingContext`]s that we create.
+ display_contents_shared_styles: Vec<SharedInlineStyles>,
}
impl BlockContainer {
- pub fn construct<'dom, Node>(
+ pub fn construct(
context: &LayoutContext,
- info: &NodeAndStyleInfo<Node>,
+ info: &NodeAndStyleInfo<'_>,
contents: NonReplacedContents,
propagated_data: PropagatedBoxTreeData,
is_list_item: bool,
- ) -> BlockContainer
- where
- Node: NodeExt<'dom>,
- {
+ ) -> BlockContainer {
let mut builder = BlockContainerBuilder::new(context, info, propagated_data);
if is_list_item {
@@ -186,13 +189,10 @@ impl BlockContainer {
}
}
-impl<'dom, 'style, Node> BlockContainerBuilder<'dom, 'style, Node>
-where
- Node: NodeExt<'dom>,
-{
+impl<'dom, 'style> BlockContainerBuilder<'dom, 'style> {
pub(crate) fn new(
context: &'style LayoutContext,
- info: &'style NodeAndStyleInfo<Node>,
+ info: &'style NodeAndStyleInfo<'dom>,
propagated_data: PropagatedBoxTreeData,
) -> Self {
BlockContainerBuilder {
@@ -203,26 +203,44 @@ where
have_already_seen_first_line_for_text_indent: false,
anonymous_box_info: None,
anonymous_table_content: Vec::new(),
- inline_formatting_context_builder: InlineFormattingContextBuilder::new(),
+ inline_formatting_context_builder: None,
+ display_contents_shared_styles: Vec::new(),
}
}
- pub(crate) fn finish(mut self) -> BlockContainer {
- debug_assert!(
- !self
- .inline_formatting_context_builder
- .currently_processing_inline_box()
- );
+ fn currently_processing_inline_box(&self) -> bool {
+ self.inline_formatting_context_builder
+ .as_ref()
+ .is_some_and(InlineFormattingContextBuilder::currently_processing_inline_box)
+ }
- self.finish_anonymous_table_if_needed();
+ fn ensure_inline_formatting_context_builder(&mut self) -> &mut InlineFormattingContextBuilder {
+ self.inline_formatting_context_builder
+ .get_or_insert_with(|| {
+ let mut builder = InlineFormattingContextBuilder::new(self.info);
+ for shared_inline_styles in self.display_contents_shared_styles.iter() {
+ builder.enter_display_contents(shared_inline_styles.clone());
+ }
+ builder
+ })
+ }
- if let Some(inline_formatting_context) = self.inline_formatting_context_builder.finish(
+ fn finish_ongoing_inline_formatting_context(&mut self) -> Option<InlineFormattingContext> {
+ self.inline_formatting_context_builder.take()?.finish(
self.context,
self.propagated_data,
!self.have_already_seen_first_line_for_text_indent,
self.info.is_single_line_text_input(),
self.info.style.writing_mode.to_bidi_level(),
- ) {
+ )
+ }
+
+ pub(crate) fn finish(mut self) -> BlockContainer {
+ debug_assert!(!self.currently_processing_inline_box());
+
+ self.finish_anonymous_table_if_needed();
+
+ if let Some(inline_formatting_context) = self.finish_ongoing_inline_formatting_context() {
// There are two options here. This block was composed of both one or more inline formatting contexts
// and child blocks OR this block was a single inline formatting context. In the latter case, we
// just return the inline formatting context as the block itself.
@@ -260,9 +278,7 @@ where
//
// Note that text content in the inline formatting context isn't enough to force the
// creation of an inline table. It requires the parent to be an inline box.
- let inline_table = self
- .inline_formatting_context_builder
- .currently_processing_inline_box();
+ let inline_table = self.currently_processing_inline_box();
// Text decorations are not propagated to atomic inline-level descendants.
// From https://drafts.csswg.org/css2/#lining-striking-props:
@@ -274,7 +290,7 @@ where
false => self.propagated_data,
};
- let contents: Vec<AnonymousTableContent<'dom, Node>> =
+ let contents: Vec<AnonymousTableContent<'dom>> =
self.anonymous_table_content.drain(..).collect();
let last_text = match contents.last() {
Some(AnonymousTableContent::Text(info, text)) => Some((info.clone(), text.clone())),
@@ -285,10 +301,16 @@ where
Table::construct_anonymous(self.context, self.info, contents, propagated_data);
if inline_table {
- self.inline_formatting_context_builder.push_atomic(ifc);
+ self.ensure_inline_formatting_context_builder()
+ .push_atomic(ifc);
} else {
let table_block = ArcRefCell::new(BlockLevelBox::Independent(ifc));
- self.end_ongoing_inline_formatting_context();
+
+ if let Some(inline_formatting_context) = self.finish_ongoing_inline_formatting_context()
+ {
+ self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
+ }
+
self.block_level_boxes.push(BlockLevelJob {
info: table_info,
box_slot: BoxSlot::dummy(),
@@ -312,13 +334,10 @@ where
}
}
-impl<'dom, Node> TraversalHandler<'dom, Node> for BlockContainerBuilder<'dom, '_, Node>
-where
- Node: NodeExt<'dom>,
-{
+impl<'dom> TraversalHandler<'dom> for BlockContainerBuilder<'dom, '_> {
fn handle_element(
&mut self,
- info: &NodeAndStyleInfo<Node>,
+ info: &NodeAndStyleInfo<'dom>,
display: DisplayGeneratingBox,
contents: Contents,
box_slot: BoxSlot<'dom>,
@@ -359,7 +378,7 @@ where
}
}
- fn handle_text(&mut self, info: &NodeAndStyleInfo<Node>, text: Cow<'dom, str>) {
+ fn handle_text(&mut self, info: &NodeAndStyleInfo<'dom>, text: Cow<'dom, str>) {
if text.is_empty() {
return;
}
@@ -375,18 +394,30 @@ where
self.finish_anonymous_table_if_needed();
}
- self.inline_formatting_context_builder.push_text(text, info);
+ self.ensure_inline_formatting_context_builder()
+ .push_text(text, info);
+ }
+
+ fn enter_display_contents(&mut self, styles: SharedInlineStyles) {
+ self.display_contents_shared_styles.push(styles.clone());
+ if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
+ builder.enter_display_contents(styles);
+ }
+ }
+
+ fn leave_display_contents(&mut self) {
+ self.display_contents_shared_styles.pop();
+ if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
+ builder.leave_display_contents();
+ }
}
}
-impl<'dom, Node> BlockContainerBuilder<'dom, '_, Node>
-where
- Node: NodeExt<'dom>,
-{
+impl<'dom> BlockContainerBuilder<'dom, '_> {
fn handle_list_item_marker_inside(
&mut self,
- marker_info: &NodeAndStyleInfo<Node>,
- container_info: &NodeAndStyleInfo<Node>,
+ marker_info: &NodeAndStyleInfo<'dom>,
+ container_info: &NodeAndStyleInfo<'dom>,
contents: Vec<crate::dom_traversal::PseudoElementContentItem>,
) {
// TODO: We do not currently support saving box slots for ::marker pseudo-elements
@@ -411,8 +442,8 @@ where
fn handle_list_item_marker_outside(
&mut self,
- marker_info: &NodeAndStyleInfo<Node>,
- container_info: &NodeAndStyleInfo<Node>,
+ marker_info: &NodeAndStyleInfo<'dom>,
+ container_info: &NodeAndStyleInfo<'dom>,
contents: Vec<crate::dom_traversal::PseudoElementContentItem>,
list_item_style: Arc<ComputedValues>,
) {
@@ -439,7 +470,7 @@ where
fn handle_inline_level_element(
&mut self,
- info: &NodeAndStyleInfo<Node>,
+ info: &NodeAndStyleInfo<'dom>,
display_inside: DisplayInside,
contents: Contents,
box_slot: BoxSlot<'dom>,
@@ -448,14 +479,16 @@ where
(display_inside, contents.is_replaced())
else {
// If this inline element is an atomic, handle it and return.
- let atomic = self.inline_formatting_context_builder.push_atomic(
+ let context = self.context;
+ let propagaged_data = self.propagated_data.without_text_decorations();
+ let atomic = self.ensure_inline_formatting_context_builder().push_atomic(
IndependentFormattingContext::construct(
- self.context,
+ context,
info,
display_inside,
contents,
// Text decorations are not propagated to atomic inline-level descendants.
- self.propagated_data.without_text_decorations(),
+ propagaged_data,
),
);
box_slot.set(LayoutBox::InlineLevel(vec![atomic]));
@@ -464,7 +497,7 @@ where
// Otherwise, this is just a normal inline box. Whatever happened before, all we need to do
// before recurring is to remember this ongoing inline level box.
- self.inline_formatting_context_builder
+ self.ensure_inline_formatting_context_builder()
.start_inline_box(InlineBox::new(info), None);
if is_list_item {
@@ -491,13 +524,16 @@ where
// `InlineFormattingContextBuilder::end_inline_box()` is returning all of those box tree
// items.
box_slot.set(LayoutBox::InlineLevel(
- self.inline_formatting_context_builder.end_inline_box(),
+ self.inline_formatting_context_builder
+ .as_mut()
+ .expect("Should be building an InlineFormattingContext")
+ .end_inline_box(),
));
}
fn handle_block_level_element(
&mut self,
- info: &NodeAndStyleInfo<Node>,
+ info: &NodeAndStyleInfo<'dom>,
display_inside: DisplayInside,
contents: Contents,
box_slot: BoxSlot<'dom>,
@@ -510,12 +546,15 @@ where
// that we want to have after we push the block below.
if let Some(inline_formatting_context) = self
.inline_formatting_context_builder
- .split_around_block_and_finish(
- self.context,
- self.propagated_data,
- !self.have_already_seen_first_line_for_text_indent,
- self.info.style.writing_mode.to_bidi_level(),
- )
+ .as_mut()
+ .and_then(|builder| {
+ builder.split_around_block_and_finish(
+ self.context,
+ self.propagated_data,
+ !self.have_already_seen_first_line_for_text_indent,
+ self.info.style.writing_mode.to_bidi_level(),
+ )
+ })
{
self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
}
@@ -565,22 +604,23 @@ where
fn handle_absolutely_positioned_element(
&mut self,
- info: &NodeAndStyleInfo<Node>,
+ info: &NodeAndStyleInfo<'dom>,
display_inside: DisplayInside,
contents: Contents,
box_slot: BoxSlot<'dom>,
) {
- if !self.inline_formatting_context_builder.is_empty() {
- let inline_level_box = self
- .inline_formatting_context_builder
- .push_absolutely_positioned_box(AbsolutelyPositionedBox::construct(
- self.context,
- info,
- display_inside,
- contents,
- ));
- box_slot.set(LayoutBox::InlineLevel(vec![inline_level_box]));
- return;
+ if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
+ if !builder.is_empty() {
+ let inline_level_box =
+ builder.push_absolutely_positioned_box(AbsolutelyPositionedBox::construct(
+ self.context,
+ info,
+ display_inside,
+ contents,
+ ));
+ box_slot.set(LayoutBox::InlineLevel(vec![inline_level_box]));
+ return;
+ }
}
let kind = BlockLevelCreator::OutOfFlowAbsolutelyPositionedBox {
@@ -597,23 +637,23 @@ where
fn handle_float_element(
&mut self,
- info: &NodeAndStyleInfo<Node>,
+ info: &NodeAndStyleInfo<'dom>,
display_inside: DisplayInside,
contents: Contents,
box_slot: BoxSlot<'dom>,
) {
- if !self.inline_formatting_context_builder.is_empty() {
- let inline_level_box =
- self.inline_formatting_context_builder
- .push_float_box(FloatBox::construct(
- self.context,
- info,
- display_inside,
- contents,
- self.propagated_data,
- ));
- box_slot.set(LayoutBox::InlineLevel(vec![inline_level_box]));
- return;
+ if let Some(builder) = self.inline_formatting_context_builder.as_mut() {
+ if !builder.is_empty() {
+ let inline_level_box = builder.push_float_box(FloatBox::construct(
+ self.context,
+ info,
+ display_inside,
+ contents,
+ self.propagated_data,
+ ));
+ box_slot.set(LayoutBox::InlineLevel(vec![inline_level_box]));
+ return;
+ }
}
let kind = BlockLevelCreator::OutOfFlowFloatBox {
@@ -628,18 +668,6 @@ where
});
}
- fn end_ongoing_inline_formatting_context(&mut self) {
- if let Some(inline_formatting_context) = self.inline_formatting_context_builder.finish(
- self.context,
- self.propagated_data,
- !self.have_already_seen_first_line_for_text_indent,
- self.info.is_single_line_text_input(),
- self.info.style.writing_mode.to_bidi_level(),
- ) {
- self.push_block_level_job_for_inline_formatting_context(inline_formatting_context);
- }
- }
-
fn push_block_level_job_for_inline_formatting_context(
&mut self,
inline_formatting_context: InlineFormattingContext,
@@ -670,10 +698,7 @@ where
}
}
-impl<'dom, Node> BlockLevelJob<'dom, Node>
-where
- Node: NodeExt<'dom>,
-{
+impl BlockLevelJob<'_> {
fn finish(self, context: &LayoutContext) -> ArcRefCell<BlockLevelBox> {
let info = &self.info;
let block_level_box = match self.kind {
@@ -747,14 +772,7 @@ where
}
impl IntermediateBlockContainer {
- fn finish<'dom, Node>(
- self,
- context: &LayoutContext,
- info: &NodeAndStyleInfo<Node>,
- ) -> BlockContainer
- where
- Node: NodeExt<'dom>,
- {
+ fn finish(self, context: &LayoutContext, info: &NodeAndStyleInfo<'_>) -> BlockContainer {
match self {
IntermediateBlockContainer::Deferred {
contents,
diff --git a/components/layout/flow/float.rs b/components/layout/flow/float.rs
index dbc50c07603..f1ae2b4459a 100644
--- a/components/layout/flow/float.rs
+++ b/components/layout/flow/float.rs
@@ -22,7 +22,6 @@ use style::properties::ComputedValues;
use style::values::computed::Clear as StyleClear;
use crate::context::LayoutContext;
-use crate::dom::NodeExt;
use crate::dom_traversal::{Contents, NodeAndStyleInfo};
use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragment_tree::{BoxFragment, CollapsedMargin};
@@ -885,9 +884,9 @@ impl FloatBandLink {
impl FloatBox {
/// Creates a new float box.
- pub fn construct<'dom>(
+ pub fn construct(
context: &LayoutContext,
- info: &NodeAndStyleInfo<impl NodeExt<'dom>>,
+ info: &NodeAndStyleInfo<'_>,
display_inside: DisplayInside,
contents: Contents,
propagated_data: PropagatedBoxTreeData,
diff --git a/components/layout/flow/inline/construct.rs b/components/layout/flow/inline/construct.rs
index 61292701a9f..a99de1679a4 100644
--- a/components/layout/flow/inline/construct.rs
+++ b/components/layout/flow/inline/construct.rs
@@ -7,17 +7,18 @@ use std::char::{ToLowercase, ToUppercase};
use icu_segmenter::WordSegmenter;
use itertools::izip;
-use servo_arc::Arc;
use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
use style::values::specified::text::TextTransformCase;
use unicode_bidi::Level;
use super::text_run::TextRun;
-use super::{InlineBox, InlineBoxIdentifier, InlineBoxes, InlineFormattingContext, InlineItem};
+use super::{
+ InlineBox, InlineBoxIdentifier, InlineBoxes, InlineFormattingContext, InlineItem,
+ SharedInlineStyles,
+};
use crate::PropagatedBoxTreeData;
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
-use crate::dom::NodeExt;
use crate::dom_traversal::NodeAndStyleInfo;
use crate::flow::float::FloatBox;
use crate::formatting_contexts::IndependentFormattingContext;
@@ -26,6 +27,12 @@ use crate::style_ext::ComputedValuesExt;
#[derive(Default)]
pub(crate) struct InlineFormattingContextBuilder {
+ /// A stack of [`SharedInlineStyles`] including one for the root, one for each inline box on the
+ /// inline box stack, and importantly, one for every `display: contents` element that we are
+ /// currently processing. Normally `display: contents` elements don't affect the structure of
+ /// the [`InlineFormattingContext`], but the styles they provide do style their children.
+ shared_inline_styles_stack: Vec<SharedInlineStyles>,
+
/// The collection of text strings that make up this [`InlineFormattingContext`] under
/// construction.
pub text_segments: Vec<String>,
@@ -64,7 +71,7 @@ pub(crate) struct InlineFormattingContextBuilder {
/// The traversal is at all times as deep in the tree as this stack is,
/// which is why the code doesn't need to keep track of the actual
/// container root (see `handle_inline_level_element`).
- ///
+ //_
/// When an inline box ends, it's removed from this stack.
inline_box_stack: Vec<InlineBoxIdentifier>,
@@ -84,10 +91,17 @@ pub(crate) struct InlineFormattingContextBuilder {
}
impl InlineFormattingContextBuilder {
- pub(crate) fn new() -> Self {
- // For the purposes of `text-transform: capitalize` the start of the IFC is a word boundary.
+ pub(crate) fn new(info: &NodeAndStyleInfo) -> Self {
+ Self::new_for_shared_styles(vec![info.into()])
+ }
+
+ pub(crate) fn new_for_shared_styles(
+ shared_inline_styles_stack: Vec<SharedInlineStyles>,
+ ) -> Self {
Self {
+ // For the purposes of `text-transform: capitalize` the start of the IFC is a word boundary.
on_word_boundary: true,
+ shared_inline_styles_stack,
..Default::default()
}
}
@@ -101,6 +115,13 @@ impl InlineFormattingContextBuilder {
self.current_text_offset += string_to_push.len();
}
+ fn shared_inline_styles(&self) -> SharedInlineStyles {
+ self.shared_inline_styles_stack
+ .last()
+ .expect("Should always have at least one SharedInlineStyles")
+ .clone()
+ }
+
/// Return true if this [`InlineFormattingContextBuilder`] is empty for the purposes of ignoring
/// during box tree construction. An IFC is empty if it only contains TextRuns with
/// completely collapsible whitespace. When that happens it can be ignored completely.
@@ -136,7 +157,7 @@ impl InlineFormattingContextBuilder {
independent_formatting_context: IndependentFormattingContext,
) -> ArcRefCell<InlineItem> {
let inline_level_box = ArcRefCell::new(InlineItem::Atomic(
- Arc::new(independent_formatting_context),
+ ArcRefCell::new(independent_formatting_context),
self.current_text_offset,
Level::ltr(), /* This will be assigned later if necessary. */
));
@@ -167,7 +188,8 @@ impl InlineFormattingContextBuilder {
}
pub(crate) fn push_float_box(&mut self, float_box: FloatBox) -> ArcRefCell<InlineItem> {
- let inline_level_box = ArcRefCell::new(InlineItem::OutOfFlowFloatBox(Arc::new(float_box)));
+ let inline_level_box =
+ ArcRefCell::new(InlineItem::OutOfFlowFloatBox(ArcRefCell::new(float_box)));
self.inline_items.push(inline_level_box.clone());
self.contains_floats = true;
inline_level_box
@@ -180,6 +202,14 @@ impl InlineFormattingContextBuilder {
) {
self.push_control_character_string(inline_box.base.style.bidi_control_chars().0);
+ // Don't push a `SharedInlineStyles` if we are pushing this box when splitting
+ // an IFC for a block-in-inline split. Shared styles are pushed as part of setting
+ // up the second split of the IFC.
+ if inline_box.is_first_split {
+ self.shared_inline_styles_stack
+ .push(inline_box.shared_inline_styles.clone());
+ }
+
let (identifier, inline_box) = self.inline_boxes.start_inline_box(inline_box);
let inline_level_box = ArcRefCell::new(InlineItem::StartInlineBox(inline_box));
self.inline_items.push(inline_level_box.clone());
@@ -195,6 +225,8 @@ impl InlineFormattingContextBuilder {
/// a single box tree items may be produced for a single inline box when that inline
/// box is split around a block-level element.
pub(crate) fn end_inline_box(&mut self) -> Vec<ArcRefCell<InlineItem>> {
+ self.shared_inline_styles_stack.pop();
+
let (identifier, block_in_inline_splits) = self.end_inline_box_internal();
let inline_level_box = self.inline_boxes.get(&identifier);
{
@@ -225,11 +257,7 @@ impl InlineFormattingContextBuilder {
(identifier, block_in_inline_splits)
}
- pub(crate) fn push_text<'dom, Node: NodeExt<'dom>>(
- &mut self,
- text: Cow<'dom, str>,
- info: &NodeAndStyleInfo<Node>,
- ) {
+ pub(crate) fn push_text<'dom>(&mut self, text: Cow<'dom, str>, info: &NodeAndStyleInfo<'dom>) {
let white_space_collapse = info.style.clone_white_space_collapse();
let collapsed = WhitespaceCollapse::new(
text.chars(),
@@ -277,8 +305,6 @@ impl InlineFormattingContextBuilder {
}
let selection_range = info.get_selection_range();
- let selected_style = info.get_selected_style();
-
if let Some(last_character) = new_text.chars().next_back() {
self.on_word_boundary = last_character.is_whitespace();
self.last_inline_box_ended_with_collapsible_white_space =
@@ -300,14 +326,21 @@ impl InlineFormattingContextBuilder {
.push(ArcRefCell::new(InlineItem::TextRun(ArcRefCell::new(
TextRun::new(
info.into(),
- info.style.clone(),
+ self.shared_inline_styles(),
new_range,
selection_range,
- selected_style,
),
))));
}
+ pub(crate) fn enter_display_contents(&mut self, shared_inline_styles: SharedInlineStyles) {
+ self.shared_inline_styles_stack.push(shared_inline_styles);
+ }
+
+ pub(crate) fn leave_display_contents(&mut self) {
+ self.shared_inline_styles_stack.pop();
+ }
+
pub(crate) fn split_around_block_and_finish(
&mut self,
layout_context: &LayoutContext,
@@ -323,7 +356,8 @@ impl InlineFormattingContextBuilder {
// context. It has the same inline box structure as this builder, except the boxes are
// marked as not being the first fragment. No inline content is carried over to this new
// builder.
- let mut new_builder = InlineFormattingContextBuilder::new();
+ let mut new_builder = Self::new_for_shared_styles(self.shared_inline_styles_stack.clone());
+
let block_in_inline_splits = std::mem::take(&mut self.block_in_inline_splits);
for (identifier, historical_inline_boxes) in
izip!(self.inline_box_stack.iter(), block_in_inline_splits)
@@ -361,7 +395,7 @@ impl InlineFormattingContextBuilder {
/// Finish the current inline formatting context, returning [`None`] if the context was empty.
pub(crate) fn finish(
- &mut self,
+ self,
layout_context: &LayoutContext,
propagated_data: PropagatedBoxTreeData,
has_first_formatted_line: bool,
@@ -372,11 +406,9 @@ impl InlineFormattingContextBuilder {
return None;
}
- let old_builder = std::mem::replace(self, InlineFormattingContextBuilder::new());
- assert!(old_builder.inline_box_stack.is_empty());
-
+ assert!(self.inline_box_stack.is_empty());
Some(InlineFormattingContext::new_with_builder(
- old_builder,
+ self,
layout_context,
propagated_data,
has_first_formatted_line,
diff --git a/components/layout/flow/inline/inline_box.rs b/components/layout/flow/inline/inline_box.rs
index de79f876340..b547f3b5935 100644
--- a/components/layout/flow/inline/inline_box.rs
+++ b/components/layout/flow/inline/inline_box.rs
@@ -7,12 +7,18 @@ use std::vec::IntoIter;
use app_units::Au;
use fonts::FontMetrics;
use malloc_size_of_derive::MallocSizeOf;
-
-use super::{InlineContainerState, InlineContainerStateFlags, inline_container_needs_strut};
+use script::layout_dom::ServoLayoutNode;
+use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
+use servo_arc::Arc as ServoArc;
+use style::properties::ComputedValues;
+
+use super::{
+ InlineContainerState, InlineContainerStateFlags, SharedInlineStyles,
+ inline_container_needs_strut,
+};
use crate::ContainingBlock;
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
-use crate::dom::NodeExt;
use crate::dom_traversal::NodeAndStyleInfo;
use crate::fragment_tree::BaseFragmentInfo;
use crate::layout_box_base::LayoutBoxBase;
@@ -21,6 +27,9 @@ use crate::style_ext::{LayoutStyle, PaddingBorderMargin};
#[derive(Debug, MallocSizeOf)]
pub(crate) struct InlineBox {
pub base: LayoutBoxBase,
+ /// The [`SharedInlineStyles`] for this [`InlineBox`] that are used to share styles
+ /// with all [`super::TextRun`] children.
+ pub(super) shared_inline_styles: SharedInlineStyles,
/// The identifier of this inline box in the containing [`super::InlineFormattingContext`].
pub(super) identifier: InlineBoxIdentifier,
/// Whether or not this is the first instance of an [`InlineBox`] before a possible
@@ -35,9 +44,10 @@ pub(crate) struct InlineBox {
}
impl InlineBox {
- pub(crate) fn new<'dom, Node: NodeExt<'dom>>(info: &NodeAndStyleInfo<Node>) -> Self {
+ pub(crate) fn new(info: &NodeAndStyleInfo) -> Self {
Self {
base: LayoutBoxBase::new(info.into(), info.style.clone()),
+ shared_inline_styles: info.into(),
// This will be assigned later, when the box is actually added to the IFC.
identifier: InlineBoxIdentifier::default(),
is_first_split: true,
@@ -49,6 +59,7 @@ impl InlineBox {
pub(crate) fn split_around_block(&self) -> Self {
Self {
base: LayoutBoxBase::new(self.base.base_fragment_info, self.base.style.clone()),
+ shared_inline_styles: self.shared_inline_styles.clone(),
is_first_split: false,
is_last_split: false,
..*self
@@ -59,6 +70,16 @@ impl InlineBox {
pub(crate) fn layout_style(&self) -> LayoutStyle {
LayoutStyle::Default(&self.base.style)
}
+
+ pub(crate) fn repair_style(
+ &mut self,
+ node: &ServoLayoutNode,
+ new_style: &ServoArc<ComputedValues>,
+ ) {
+ self.base.repair_style(new_style);
+ *self.shared_inline_styles.style.borrow_mut() = new_style.clone();
+ *self.shared_inline_styles.selected.borrow_mut() = node.to_threadsafe().selected_style();
+ }
}
#[derive(Debug, Default, MallocSizeOf)]
diff --git a/components/layout/flow/inline/line.rs b/components/layout/flow/inline/line.rs
index 80bab1080ed..3b92078d67d 100644
--- a/components/layout/flow/inline/line.rs
+++ b/components/layout/flow/inline/line.rs
@@ -7,7 +7,6 @@ use bitflags::bitflags;
use fonts::{ByteIndex, FontMetrics, GlyphStore};
use itertools::Either;
use range::Range;
-use servo_arc::Arc;
use style::Zero;
use style::computed_values::position::T as Position;
use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
@@ -21,7 +20,7 @@ use unicode_bidi::{BidiInfo, Level};
use webrender_api::FontInstanceKey;
use super::inline_box::{InlineBoxContainerState, InlineBoxIdentifier, InlineBoxTreePathToken};
-use super::{InlineFormattingContextLayout, LineBlockSizes};
+use super::{InlineFormattingContextLayout, LineBlockSizes, SharedInlineStyles};
use crate::cell::ArcRefCell;
use crate::fragment_tree::{BaseFragmentInfo, BoxFragment, Fragment, TextFragment};
use crate::geom::{LogicalRect, LogicalVec2, PhysicalRect, ToLogical};
@@ -568,7 +567,7 @@ impl LineItemLayout<'_, '_> {
self.current_state.fragments.push((
Fragment::Text(ArcRefCell::new(TextFragment {
base: text_item.base_fragment_info.into(),
- parent_style: text_item.parent_style,
+ inline_styles: text_item.inline_styles.clone(),
rect: PhysicalRect::zero(),
font_metrics: text_item.font_metrics,
font_key: text_item.font_key,
@@ -576,7 +575,6 @@ impl LineItemLayout<'_, '_> {
text_decoration_line: text_item.text_decoration_line,
justification_adjustment: self.justification_adjustment,
selection_range: text_item.selection_range,
- selected_style: text_item.selected_style,
})),
content_rect,
));
@@ -763,7 +761,7 @@ impl LineItem {
pub(super) struct TextRunLineItem {
pub base_fragment_info: BaseFragmentInfo,
- pub parent_style: Arc<ComputedValues>,
+ pub inline_styles: SharedInlineStyles,
pub text: Vec<std::sync::Arc<GlyphStore>>,
pub font_metrics: FontMetrics,
pub font_key: FontInstanceKey,
@@ -771,13 +769,16 @@ pub(super) struct TextRunLineItem {
/// The BiDi level of this [`TextRunLineItem`] to enable reordering.
pub bidi_level: Level,
pub selection_range: Option<Range<ByteIndex>>,
- pub selected_style: Arc<ComputedValues>,
}
impl TextRunLineItem {
fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool {
if matches!(
- self.parent_style.get_inherited_text().white_space_collapse,
+ self.inline_styles
+ .style
+ .borrow()
+ .get_inherited_text()
+ .white_space_collapse,
WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
) {
return false;
@@ -803,7 +804,11 @@ impl TextRunLineItem {
fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool {
if matches!(
- self.parent_style.get_inherited_text().white_space_collapse,
+ self.inline_styles
+ .style
+ .borrow()
+ .get_inherited_text()
+ .white_space_collapse,
WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces
) {
return false;
diff --git a/components/layout/flow/inline/mod.rs b/components/layout/flow/inline/mod.rs
index 2023f4e7174..7e69aa1aaae 100644
--- a/components/layout/flow/inline/mod.rs
+++ b/components/layout/flow/inline/mod.rs
@@ -90,12 +90,13 @@ use line::{
use line_breaker::LineBreaker;
use malloc_size_of_derive::MallocSizeOf;
use range::Range;
+use script::layout_dom::ServoLayoutNode;
use servo_arc::Arc;
use style::Zero;
use style::computed_values::text_wrap_mode::T as TextWrapMode;
use style::computed_values::vertical_align::T as VerticalAlign;
use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse;
-use style::context::QuirksMode;
+use style::context::{QuirksMode, SharedStyleContext};
use style::properties::ComputedValues;
use style::properties::style_structs::InheritedText;
use style::values::generics::box_::VerticalAlignKeyword;
@@ -118,6 +119,7 @@ use super::{
};
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
+use crate::dom_traversal::NodeAndStyleInfo;
use crate::flow::CollapsibleWithParentStartMargin;
use crate::flow::float::{FloatBox, SequentialLayoutState};
use crate::formatting_contexts::{
@@ -131,7 +133,7 @@ use crate::geom::{LogicalRect, LogicalVec2, ToLogical};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult};
use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin};
-use crate::{ConstraintSpace, ContainingBlock, PropagatedBoxTreeData};
+use crate::{ConstraintSpace, ContainingBlock, PropagatedBoxTreeData, SharedStyle};
// From gfxFontConstants.h in Firefox.
static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20;
@@ -173,6 +175,25 @@ pub(crate) struct InlineFormattingContext {
pub(super) has_right_to_left_content: bool,
}
+/// [`TextRun`] and `TextFragment`s need a handle on their parent inline box (or inline
+/// formatting context root)'s style. In order to implement incremental layout, these are
+/// wrapped in [`SharedStyle`]. This allows updating the parent box tree element without
+/// updating every single descendant box tree node and fragment.
+#[derive(Clone, Debug, MallocSizeOf)]
+pub(crate) struct SharedInlineStyles {
+ pub style: SharedStyle,
+ pub selected: SharedStyle,
+}
+
+impl From<&NodeAndStyleInfo<'_>> for SharedInlineStyles {
+ fn from(info: &NodeAndStyleInfo) -> Self {
+ Self {
+ style: SharedStyle::new(info.style.clone()),
+ selected: SharedStyle::new(info.get_selected_style()),
+ }
+ }
+}
+
/// A collection of data used to cache [`FontMetrics`] in the [`InlineFormattingContext`]
#[derive(Debug, MallocSizeOf)]
pub(crate) struct FontKeyAndMetrics {
@@ -190,15 +211,41 @@ pub(crate) enum InlineItem {
ArcRefCell<AbsolutelyPositionedBox>,
usize, /* offset_in_text */
),
- OutOfFlowFloatBox(#[conditional_malloc_size_of] Arc<FloatBox>),
+ OutOfFlowFloatBox(ArcRefCell<FloatBox>),
Atomic(
- #[conditional_malloc_size_of] Arc<IndependentFormattingContext>,
+ ArcRefCell<IndependentFormattingContext>,
usize, /* offset_in_text */
Level, /* bidi_level */
),
}
impl InlineItem {
+ pub(crate) fn repair_style(
+ &self,
+ context: &SharedStyleContext,
+ node: &ServoLayoutNode,
+ new_style: &Arc<ComputedValues>,
+ ) {
+ match self {
+ InlineItem::StartInlineBox(inline_box) => {
+ inline_box.borrow_mut().repair_style(node, new_style);
+ },
+ InlineItem::EndInlineBox => {},
+ // TextRun holds a handle the `InlineSharedStyles` which is updated when repairing inline box
+ // and `display: contents` styles.
+ InlineItem::TextRun(..) => {},
+ InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box
+ .borrow_mut()
+ .context
+ .repair_style(context, new_style),
+ InlineItem::OutOfFlowFloatBox(float_box) => float_box
+ .borrow_mut()
+ .contents
+ .repair_style(context, new_style),
+ InlineItem::Atomic(atomic, ..) => atomic.borrow_mut().repair_style(context, new_style),
+ }
+ }
+
pub(crate) fn invalidate_cached_fragment(&self) {
match self {
InlineItem::StartInlineBox(inline_box) => {
@@ -212,11 +259,14 @@ impl InlineItem {
.base
.invalidate_cached_fragment();
},
- InlineItem::OutOfFlowFloatBox(float_box) => {
- float_box.contents.base.invalidate_cached_fragment()
- },
+ InlineItem::OutOfFlowFloatBox(float_box) => float_box
+ .borrow()
+ .contents
+ .base
+ .invalidate_cached_fragment(),
InlineItem::Atomic(independent_formatting_context, ..) => {
independent_formatting_context
+ .borrow()
.base
.invalidate_cached_fragment();
},
@@ -232,9 +282,11 @@ impl InlineItem {
InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => {
positioned_box.borrow().context.base.fragments()
},
- InlineItem::OutOfFlowFloatBox(float_box) => float_box.contents.base.fragments(),
+ InlineItem::OutOfFlowFloatBox(float_box) => {
+ float_box.borrow().contents.base.fragments()
+ },
InlineItem::Atomic(independent_formatting_context, ..) => {
- independent_formatting_context.base.fragments()
+ independent_formatting_context.borrow().base.fragments()
},
}
}
@@ -958,6 +1010,7 @@ impl InlineFormattingContextLayout<'_> {
.as_physical(Some(self.containing_block));
self.fragments
.push(Fragment::Positioning(PositioningFragment::new_anonymous(
+ self.root_nesting_level.style.clone(),
physical_line_rect,
fragments,
)));
@@ -1313,7 +1366,7 @@ impl InlineFormattingContextLayout<'_> {
) {
let inline_advance = glyph_store.total_advance();
let flags = if glyph_store.is_whitespace() {
- SegmentContentFlags::from(text_run.parent_style.get_inherited_text())
+ SegmentContentFlags::from(text_run.inline_styles.style.borrow().get_inherited_text())
} else {
SegmentContentFlags::empty()
};
@@ -1398,13 +1451,12 @@ impl InlineFormattingContextLayout<'_> {
TextRunLineItem {
text: vec![glyph_store],
base_fragment_info: text_run.base_fragment_info,
- parent_style: text_run.parent_style.clone(),
+ inline_styles: text_run.inline_styles.clone(),
font_metrics,
font_key: ifc_font_info.key,
text_decoration_line: self.current_inline_container_state().text_decoration_line,
bidi_level,
selection_range,
- selected_style: text_run.selected_style.clone(),
},
));
}
@@ -1751,7 +1803,7 @@ impl InlineFormattingContext {
InlineItem::EndInlineBox => layout.finish_inline_box(),
InlineItem::TextRun(run) => run.borrow().layout_into_line_items(&mut layout),
InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => {
- atomic_formatting_context.layout_into_line_items(
+ atomic_formatting_context.borrow().layout_into_line_items(
&mut layout,
*offset_in_text,
*bidi_level,
@@ -1766,7 +1818,7 @@ impl InlineFormattingContext {
));
},
InlineItem::OutOfFlowFloatBox(float_box) => {
- float_box.layout_into_line_items(&mut layout);
+ float_box.borrow().layout_into_line_items(&mut layout);
},
}
}
@@ -2363,8 +2415,9 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
},
InlineItem::TextRun(text_run) => {
let text_run = &*text_run.borrow();
+ let parent_style = text_run.inline_styles.style.borrow();
for segment in text_run.shaped_text.iter() {
- let style_text = text_run.parent_style.get_inherited_text();
+ let style_text = parent_style.get_inherited_text();
let can_wrap = style_text.text_wrap_mode == TextWrapMode::Wrap;
// TODO: This should take account whether or not the first and last character prevent
@@ -2428,7 +2481,7 @@ impl<'layout_data> ContentSizesComputation<'layout_data> {
let InlineContentSizesResult {
sizes: outer,
depends_on_block_constraints,
- } = atomic.outer_inline_content_sizes(
+ } = atomic.borrow().outer_inline_content_sizes(
self.layout_context,
&self.constraint_space.into(),
&LogicalVec2::zero(),
diff --git a/components/layout/flow/inline/text_run.rs b/components/layout/flow/inline/text_run.rs
index 0d0c6398017..591c7b9b5e2 100644
--- a/components/layout/flow/inline/text_run.rs
+++ b/components/layout/flow/inline/text_run.rs
@@ -26,7 +26,7 @@ use unicode_script::Script;
use xi_unicode::linebreak_property;
use super::line_breaker::LineBreaker;
-use super::{FontKeyAndMetrics, InlineFormattingContextLayout};
+use super::{FontKeyAndMetrics, InlineFormattingContextLayout, SharedInlineStyles};
use crate::fragment_tree::BaseFragmentInfo;
// These constants are the xi-unicode line breaking classes that are defined in
@@ -37,22 +37,6 @@ pub(crate) const XI_LINE_BREAKING_CLASS_ZW: u8 = 28;
pub(crate) const XI_LINE_BREAKING_CLASS_WJ: u8 = 30;
pub(crate) const XI_LINE_BREAKING_CLASS_ZWJ: u8 = 42;
-/// <https://www.w3.org/TR/css-display-3/#css-text-run>
-#[derive(Debug, MallocSizeOf)]
-pub(crate) struct TextRun {
- pub base_fragment_info: BaseFragmentInfo,
- #[conditional_malloc_size_of]
- pub parent_style: Arc<ComputedValues>,
- pub text_range: Range<usize>,
-
- /// The text of this [`TextRun`] with a font selected, broken into unbreakable
- /// segments, and shaped.
- pub shaped_text: Vec<TextRunSegment>,
- pub selection_range: Option<ServoRange<ByteIndex>>,
- #[conditional_malloc_size_of]
- pub selected_style: Arc<ComputedValues>,
-}
-
// There are two reasons why we might want to break at the start:
//
// 1. The line breaker told us that a break was necessary between two separate
@@ -334,21 +318,49 @@ impl TextRunSegment {
}
}
+/// A single [`TextRun`] for the box tree. These are all descendants of
+/// [`super::InlineBox`] or the root of the [`super::InlineFormattingContext`]. During
+/// box tree construction, text is split into [`TextRun`]s based on their font, script,
+/// etc. When these are created text is already shaped.
+///
+/// <https://www.w3.org/TR/css-display-3/#css-text-run>
+#[derive(Debug, MallocSizeOf)]
+pub(crate) struct TextRun {
+ /// The [`BaseFragmentInfo`] for this [`TextRun`]. Usually this comes from the
+ /// original text node in the DOM for the text.
+ pub base_fragment_info: BaseFragmentInfo,
+
+ /// The [`crate::SharedStyle`] from this [`TextRun`]s parent element. This is
+ /// shared so that incremental layout can simply update the parent element and
+ /// this [`TextRun`] will be updated automatically.
+ pub inline_styles: SharedInlineStyles,
+
+ /// The range of text in [`super::InlineFormattingContext::text_content`] of the
+ /// [`super::InlineFormattingContext`] that owns this [`TextRun`]. These are UTF-8 offsets.
+ pub text_range: Range<usize>,
+
+ /// The text of this [`TextRun`] with a font selected, broken into unbreakable
+ /// segments, and shaped.
+ pub shaped_text: Vec<TextRunSegment>,
+
+ /// The selection range for the DOM text node that originated this [`TextRun`]. This
+ /// comes directly from the DOM.
+ pub selection_range: Option<ServoRange<ByteIndex>>,
+}
+
impl TextRun {
pub(crate) fn new(
base_fragment_info: BaseFragmentInfo,
- parent_style: Arc<ComputedValues>,
+ inline_styles: SharedInlineStyles,
text_range: Range<usize>,
selection_range: Option<ServoRange<ByteIndex>>,
- selected_style: Arc<ComputedValues>,
) -> Self {
Self {
base_fragment_info,
- parent_style,
+ inline_styles,
text_range,
shaped_text: Vec::new(),
selection_range,
- selected_style,
}
}
@@ -360,11 +372,12 @@ impl TextRun {
font_cache: &mut Vec<FontKeyAndMetrics>,
bidi_info: &BidiInfo,
) {
- let inherited_text_style = self.parent_style.get_inherited_text().clone();
+ let parent_style = self.inline_styles.style.borrow().clone();
+ let inherited_text_style = parent_style.get_inherited_text().clone();
let letter_spacing = inherited_text_style
.letter_spacing
.0
- .resolve(self.parent_style.clone_font().font_size.computed_size());
+ .resolve(parent_style.clone_font().font_size.computed_size());
let letter_spacing = if letter_spacing.px() != 0. {
Some(app_units::Au::from(letter_spacing))
} else {
@@ -384,7 +397,13 @@ impl TextRun {
let style_word_spacing: Option<Au> = specified_word_spacing.to_length().map(|l| l.into());
let segments = self
- .segment_text_by_font(formatting_context_text, font_context, font_cache, bidi_info)
+ .segment_text_by_font(
+ formatting_context_text,
+ font_context,
+ font_cache,
+ bidi_info,
+ &parent_style,
+ )
.into_iter()
.map(|(mut segment, font)| {
let word_spacing = style_word_spacing.unwrap_or_else(|| {
@@ -407,7 +426,7 @@ impl TextRun {
};
segment.shape_text(
- &self.parent_style,
+ &parent_style,
formatting_context_text,
linebreaker,
&shaping_options,
@@ -430,8 +449,9 @@ impl TextRun {
font_context: &FontContext,
font_cache: &mut Vec<FontKeyAndMetrics>,
bidi_info: &BidiInfo,
+ parent_style: &Arc<ComputedValues>,
) -> Vec<(TextRunSegment, FontRef)> {
- let font_group = font_context.font_group(self.parent_style.clone_font());
+ let font_group = font_context.font_group(parent_style.clone_font());
let mut current: Option<(TextRunSegment, FontRef)> = None;
let mut results = Vec::new();
diff --git a/components/layout/flow/mod.rs b/components/layout/flow/mod.rs
index 0c326c4cc6d..99b84d088e5 100644
--- a/components/layout/flow/mod.rs
+++ b/components/layout/flow/mod.rs
@@ -9,9 +9,11 @@ use app_units::{Au, MAX_AU};
use inline::InlineFormattingContext;
use malloc_size_of_derive::MallocSizeOf;
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
+use script::layout_dom::ServoLayoutNode;
use servo_arc::Arc;
use style::Zero;
use style::computed_values::clear::T as StyleClear;
+use style::context::SharedStyleContext;
use style::logical_geometry::Direction;
use style::properties::ComputedValues;
use style::servo::selector_parser::PseudoElement;
@@ -21,6 +23,7 @@ use style::values::specified::{Display, TextAlignKeyword};
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
+use crate::dom::NodeExt;
use crate::flow::float::{
Clear, ContainingBlockPositionInfo, FloatBox, FloatSide, PlacementAmongFloats,
SequentialLayoutState,
@@ -33,8 +36,8 @@ use crate::fragment_tree::{
BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, FragmentFlags,
};
use crate::geom::{
- AuOrAuto, LogicalRect, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalPoint, PhysicalRect,
- PhysicalSides, Size, Sizes, ToLogical, ToLogicalWithContainingBlock,
+ AuOrAuto, LazySize, LogicalRect, LogicalSides, LogicalSides1D, LogicalVec2, PhysicalPoint,
+ PhysicalRect, PhysicalSides, Size, Sizes, ToLogical, ToLogicalWithContainingBlock,
};
use crate::layout_box_base::{CacheableLayoutResult, LayoutBoxBase};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
@@ -52,7 +55,7 @@ pub mod inline;
mod root;
pub(crate) use construct::BlockContainerBuilder;
-pub use root::{BoxTree, CanvasBackground};
+pub use root::BoxTree;
#[derive(Debug, MallocSizeOf)]
pub(crate) struct BlockFormattingContext {
@@ -91,6 +94,36 @@ pub(crate) enum BlockLevelBox {
}
impl BlockLevelBox {
+ pub(crate) fn repair_style(
+ &mut self,
+ context: &SharedStyleContext,
+ node: &ServoLayoutNode,
+ new_style: &Arc<ComputedValues>,
+ ) {
+ self.with_base_mut(|base| {
+ base.repair_style(new_style);
+ });
+
+ match self {
+ BlockLevelBox::Independent(independent_formatting_context) => {
+ independent_formatting_context.repair_style(context, new_style)
+ },
+ BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
+ .borrow_mut()
+ .context
+ .repair_style(context, new_style),
+ BlockLevelBox::OutOfFlowFloatBox(float_box) => {
+ float_box.contents.repair_style(context, new_style)
+ },
+ BlockLevelBox::OutsideMarker(outside_marker) => {
+ outside_marker.repair_style(context, node, new_style)
+ },
+ BlockLevelBox::SameFormattingContextBlock { base, .. } => {
+ base.repair_style(new_style);
+ },
+ }
+ }
+
pub(crate) fn invalidate_cached_fragment(&self) {
self.with_base(LayoutBoxBase::invalidate_cached_fragment);
}
@@ -113,6 +146,20 @@ impl BlockLevelBox {
}
}
+ pub(crate) fn with_base_mut<T>(&mut self, callback: impl Fn(&mut LayoutBoxBase) -> T) -> T {
+ match self {
+ BlockLevelBox::Independent(independent_formatting_context) => {
+ callback(&mut independent_formatting_context.base)
+ },
+ BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
+ callback(&mut positioned_box.borrow_mut().context.base)
+ },
+ BlockLevelBox::OutOfFlowFloatBox(float_box) => callback(&mut float_box.contents.base),
+ BlockLevelBox::OutsideMarker(outside_marker) => callback(&mut outside_marker.base),
+ BlockLevelBox::SameFormattingContextBlock { base, .. } => callback(base),
+ }
+ }
+
fn contains_floats(&self) -> bool {
match self {
BlockLevelBox::SameFormattingContextBlock {
@@ -249,7 +296,6 @@ pub(crate) struct CollapsibleWithParentStartMargin(bool);
/// for a list that has `list-style-position: outside`.
#[derive(Debug, MallocSizeOf)]
pub(crate) struct OutsideMarker {
- #[conditional_malloc_size_of]
pub list_item_style: Arc<ComputedValues>,
pub base: LayoutBoxBase,
pub block_container: BlockContainer,
@@ -361,6 +407,16 @@ impl OutsideMarker {
None,
)))
}
+
+ fn repair_style(
+ &mut self,
+ context: &SharedStyleContext,
+ node: &ServoLayoutNode,
+ new_style: &Arc<ComputedValues>,
+ ) {
+ self.list_item_style = node.style(context);
+ self.base.repair_style(new_style);
+ }
}
impl BlockFormattingContext {
@@ -1161,6 +1217,15 @@ impl IndependentNonReplacedContents {
ignore_block_margins_for_stretch,
);
+ let lazy_block_size = LazySize::new(
+ &block_sizes,
+ Direction::Block,
+ Size::FitContent,
+ Au::zero,
+ available_block_size,
+ layout_style.is_table(),
+ );
+
let layout = self.layout(
layout_context,
positioning_context,
@@ -1168,19 +1233,13 @@ impl IndependentNonReplacedContents {
containing_block,
base,
false, /* depends_on_block_constraints */
+ &lazy_block_size,
);
let inline_size = layout
.content_inline_size_for_table
.unwrap_or(containing_block_for_children.size.inline);
- let block_size = block_sizes.resolve(
- Direction::Block,
- Size::FitContent,
- Au::zero,
- available_block_size,
- || layout.content_block_size.into(),
- layout_style.is_table(),
- );
+ let block_size = lazy_block_size.resolve(|| layout.content_block_size);
let ResolvedMargins {
margin,
@@ -1313,16 +1372,14 @@ impl IndependentNonReplacedContents {
)
};
- let compute_block_size = |layout: &CacheableLayoutResult| {
- content_box_sizes.block.resolve(
- Direction::Block,
- Size::FitContent,
- Au::zero,
- available_block_size,
- || layout.content_block_size.into(),
- is_table,
- )
- };
+ let lazy_block_size = LazySize::new(
+ &content_box_sizes.block,
+ Direction::Block,
+ Size::FitContent,
+ Au::zero,
+ available_block_size,
+ is_table,
+ );
// The final inline size can depend on the available space, which depends on where
// we are placing the box, since floats reduce the available space.
@@ -1351,10 +1408,11 @@ impl IndependentNonReplacedContents {
containing_block,
base,
false, /* depends_on_block_constraints */
+ &lazy_block_size,
);
content_size = LogicalVec2 {
- block: compute_block_size(&layout),
+ block: lazy_block_size.resolve(|| layout.content_block_size),
inline: layout.content_inline_size_for_table.unwrap_or(inline_size),
};
@@ -1416,6 +1474,7 @@ impl IndependentNonReplacedContents {
containing_block,
base,
false, /* depends_on_block_constraints */
+ &lazy_block_size,
);
let inline_size = if let Some(inline_size) = layout.content_inline_size_for_table {
@@ -1429,7 +1488,7 @@ impl IndependentNonReplacedContents {
proposed_inline_size
};
content_size = LogicalVec2 {
- block: compute_block_size(&layout),
+ block: lazy_block_size.resolve(|| layout.content_block_size),
inline: inline_size,
};
@@ -2363,6 +2422,15 @@ impl IndependentFormattingContext {
"Mixed horizontal and vertical writing modes are not supported yet"
);
+ let lazy_block_size = LazySize::new(
+ &content_box_sizes_and_pbm.content_box_sizes.block,
+ Direction::Block,
+ Size::FitContent,
+ Au::zero,
+ available_block_size,
+ is_table,
+ );
+
let independent_layout = non_replaced.layout(
layout_context,
child_positioning_context,
@@ -2370,18 +2438,12 @@ impl IndependentFormattingContext {
containing_block,
&self.base,
false, /* depends_on_block_constraints */
+ &lazy_block_size,
);
let inline_size = independent_layout
.content_inline_size_for_table
.unwrap_or(inline_size);
- let block_size = content_box_sizes_and_pbm.content_box_sizes.block.resolve(
- Direction::Block,
- Size::FitContent,
- Au::zero,
- available_block_size,
- || independent_layout.content_block_size.into(),
- is_table,
- );
+ let block_size = lazy_block_size.resolve(|| independent_layout.content_block_size);
let content_size = LogicalVec2 {
block: block_size,
diff --git a/components/layout/flow/root.rs b/components/layout/flow/root.rs
index bb9ff1d337a..a37db54065d 100644
--- a/components/layout/flow/root.rs
+++ b/components/layout/flow/root.rs
@@ -6,12 +6,13 @@ use app_units::Au;
use atomic_refcell::AtomicRef;
use compositing_traits::display_list::AxesScrollSensitivity;
use malloc_size_of_derive::MallocSizeOf;
+use script::layout_dom::ServoLayoutNode;
use script_layout_interface::wrapper_traits::{
LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
};
use script_layout_interface::{LayoutElementType, LayoutNodeType};
use servo_arc::Arc;
-use style::dom::OpaqueNode;
+use style::dom::{NodeInfo, TNode};
use style::properties::ComputedValues;
use style::values::computed::Overflow;
use style_traits::CSSPixel;
@@ -29,7 +30,7 @@ use crate::fragment_tree::FragmentTree;
use crate::geom::{LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSize};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
use crate::replaced::ReplacedContents;
-use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayInside};
+use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside};
use crate::taffy::{TaffyItemBox, TaffyItemBoxInner};
use crate::{DefiniteContainingBlock, PropagatedBoxTreeData};
@@ -39,18 +40,12 @@ pub struct BoxTree {
/// There may be zero if that element has `display: none`.
root: BlockFormattingContext,
- /// <https://drafts.csswg.org/css-backgrounds/#special-backgrounds>
- canvas_background: CanvasBackground,
-
/// Whether or not the viewport should be sensitive to scrolling input events in two axes
viewport_scroll_sensitivity: AxesScrollSensitivity,
}
impl BoxTree {
- pub fn construct<'dom, Node>(context: &LayoutContext, root_element: Node) -> Self
- where
- Node: 'dom + Copy + LayoutNode<'dom> + Send + Sync,
- {
+ pub fn construct(context: &LayoutContext, root_element: ServoLayoutNode<'_>) -> Self {
let boxes = construct_for_root_element(context, root_element);
// Zero box for `:root { display: none }`, one for the root element otherwise.
@@ -64,7 +59,7 @@ impl BoxTree {
// > none, user agents must instead apply the overflow-* values of the first such child
// > element to the viewport. The element from which the value is propagated must then have a
// > used overflow value of visible.
- let root_style = root_element.style(context);
+ let root_style = root_element.style(context.shared_context());
let mut viewport_overflow_x = root_style.clone_overflow_x();
let mut viewport_overflow_y = root_style.clone_overflow_y();
@@ -81,7 +76,7 @@ impl BoxTree {
continue;
}
- let style = child.style(context);
+ let style = child.style(context.shared_context());
if !style.get_box().display.is_none() {
viewport_overflow_x = style.clone_overflow_x();
viewport_overflow_y = style.clone_overflow_y();
@@ -98,7 +93,6 @@ impl BoxTree {
contents,
contains_floats,
},
- canvas_background: CanvasBackground::for_root_element(context, root_element),
// From https://www.w3.org/TR/css-overflow-3/#overflow-propagation:
// > If visible is applied to the viewport, it must be interpreted as auto.
// > If clip is applied to the viewport, it must be interpreted as hidden.
@@ -129,10 +123,7 @@ impl BoxTree {
/// * how intrinsic content sizes are computed eagerly makes it hard
/// to update those sizes for ancestors of the node from which we
/// made an incremental update.
- pub fn update<'dom, Node>(context: &LayoutContext, mut dirty_node: Node) -> bool
- where
- Node: 'dom + Copy + LayoutNode<'dom> + Send + Sync,
- {
+ pub fn update(context: &LayoutContext, mut dirty_node: ServoLayoutNode<'_>) -> bool {
#[allow(clippy::enum_variant_names)]
enum UpdatePoint {
AbsolutelyPositionedBlockLevelBox(ArcRefCell<BlockLevelBox>),
@@ -141,12 +132,9 @@ impl BoxTree {
AbsolutelyPositionedTaffyLevelBox(ArcRefCell<TaffyItemBox>),
}
- fn update_point<'dom, Node>(
- node: Node,
- ) -> Option<(Arc<ComputedValues>, DisplayInside, UpdatePoint)>
- where
- Node: NodeExt<'dom>,
- {
+ fn update_point(
+ node: ServoLayoutNode<'_>,
+ ) -> Option<(Arc<ComputedValues>, DisplayInside, UpdatePoint)> {
if !node.is_element() {
return None;
}
@@ -162,7 +150,7 @@ impl BoxTree {
return None;
}
- let layout_data = node.layout_data()?;
+ let layout_data = NodeExt::layout_data(&node)?;
if layout_data.pseudo_before_box.borrow().is_some() {
return None;
}
@@ -186,7 +174,7 @@ impl BoxTree {
let update_point =
match &*AtomicRef::filter_map(layout_data.self_box.borrow(), Option::as_ref)? {
- LayoutBox::DisplayContents => return None,
+ LayoutBox::DisplayContents(..) => return None,
LayoutBox::BlockLevel(block_level_box) => match &*block_level_box.borrow() {
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_)
if box_style.position.is_absolutely_positioned() =>
@@ -301,11 +289,11 @@ impl BoxTree {
}
}
-fn construct_for_root_element<'dom>(
+fn construct_for_root_element(
context: &LayoutContext,
- root_element: impl NodeExt<'dom>,
+ root_element: ServoLayoutNode<'_>,
) -> Vec<ArcRefCell<BlockLevelBox>> {
- let info = NodeAndStyleInfo::new(root_element, root_element.style(context));
+ let info = NodeAndStyleInfo::new(root_element, root_element.style(context.shared_context()));
let box_style = info.style.get_box();
let display_inside = match Display::from(box_style.display) {
@@ -433,69 +421,7 @@ impl BoxTree {
root_fragments,
scrollable_overflow,
physical_containing_block,
- self.canvas_background.clone(),
self.viewport_scroll_sensitivity,
)
}
}
-
-/// <https://drafts.csswg.org/css-backgrounds/#root-background>
-#[derive(Clone, MallocSizeOf)]
-pub struct CanvasBackground {
- /// DOM node for the root element
- pub root_element: OpaqueNode,
-
- /// The element whose style the canvas takes background properties from (see next field).
- /// This can be the root element (same as the previous field), or the HTML `<body>` element.
- /// See <https://drafts.csswg.org/css-backgrounds/#body-background>
- pub from_element: OpaqueNode,
-
- /// The computed styles to take background properties from.
- #[conditional_malloc_size_of]
- pub style: Option<Arc<ComputedValues>>,
-}
-
-impl CanvasBackground {
- fn for_root_element<'dom>(context: &LayoutContext, root_element: impl NodeExt<'dom>) -> Self {
- let root_style = root_element.style(context);
-
- let mut style = root_style;
- let mut from_element = root_element;
-
- // https://drafts.csswg.org/css-backgrounds/#body-background
- // “if the computed value of background-image on the root element is none
- // and its background-color is transparent”
- if style.background_is_transparent() &&
- // “For documents whose root element is an HTML `HTML` element
- // or an XHTML `html` element”
- root_element.type_id() == LayoutNodeType::Element(LayoutElementType::HTMLHtmlElement) &&
- // Don’t try to access styles for an unstyled subtree
- !matches!(style.clone_display().into(), Display::None)
- {
- // “that element’s first HTML `BODY` or XHTML `body` child element”
- if let Some(body) = iter_child_nodes(root_element).find(|child| {
- child.is_element() &&
- child.type_id() ==
- LayoutNodeType::Element(LayoutElementType::HTMLBodyElement)
- }) {
- style = body.style(context);
- from_element = body;
- }
- }
-
- Self {
- root_element: root_element.opaque(),
- from_element: from_element.opaque(),
-
- // “However, if no boxes are generated for the element
- // whose background would be used for the canvas
- // (for example, if the root element has display: none),
- // then the canvas background is transparent.”
- style: if let Display::GeneratingBox(_) = style.clone_display().into() {
- Some(style)
- } else {
- None
- },
- }
- }
-}
diff --git a/components/layout/formatting_contexts.rs b/components/layout/formatting_contexts.rs
index 4982d0dae1a..a489df2b663 100644
--- a/components/layout/formatting_contexts.rs
+++ b/components/layout/formatting_contexts.rs
@@ -4,16 +4,18 @@
use app_units::Au;
use malloc_size_of_derive::MallocSizeOf;
+use script::layout_dom::ServoLayoutElement;
use servo_arc::Arc;
+use style::context::SharedStyleContext;
use style::properties::ComputedValues;
use style::selector_parser::PseudoElement;
use crate::context::LayoutContext;
-use crate::dom::NodeExt;
use crate::dom_traversal::{Contents, NodeAndStyleInfo};
use crate::flexbox::FlexContainer;
use crate::flow::BlockFormattingContext;
use crate::fragment_tree::{BaseFragmentInfo, FragmentFlags};
+use crate::geom::LazySize;
use crate::layout_box_base::{
CacheableLayoutResult, CacheableLayoutResultAndInputs, LayoutBoxBase,
};
@@ -69,9 +71,9 @@ impl Baselines {
}
impl IndependentFormattingContext {
- pub fn construct<'dom, Node: NodeExt<'dom>>(
+ pub fn construct(
context: &LayoutContext,
- node_and_style_info: &NodeAndStyleInfo<Node>,
+ node_and_style_info: &NodeAndStyleInfo,
display_inside: DisplayInside,
contents: Contents,
propagated_data: PropagatedBoxTreeData,
@@ -111,11 +113,11 @@ impl IndependentFormattingContext {
let table_grid_style = context
.shared_context()
.stylist
- .style_for_anonymous::<Node::ConcreteElement>(
- &context.shared_context().guards,
- &PseudoElement::ServoTableGrid,
- &node_and_style_info.style,
- );
+ .style_for_anonymous::<ServoLayoutElement>(
+ &context.shared_context().guards,
+ &PseudoElement::ServoTableGrid,
+ &node_and_style_info.style,
+ );
base_fragment_info.flags.insert(FragmentFlags::DO_NOT_PAINT);
IndependentNonReplacedContents::Table(Table::construct(
context,
@@ -217,6 +219,20 @@ impl IndependentFormattingContext {
},
}
}
+
+ pub(crate) fn repair_style(
+ &mut self,
+ context: &SharedStyleContext,
+ new_style: &Arc<ComputedValues>,
+ ) {
+ self.base.repair_style(new_style);
+ match &mut self.contents {
+ IndependentFormattingContextContents::NonReplaced(content) => {
+ content.repair_style(context, new_style);
+ },
+ IndependentFormattingContextContents::Replaced(..) => {},
+ }
+ }
}
impl IndependentNonReplacedContents {
@@ -227,6 +243,7 @@ impl IndependentNonReplacedContents {
containing_block_for_children: &ContainingBlock,
containing_block: &ContainingBlock,
depends_on_block_constraints: bool,
+ lazy_block_size: &LazySize,
) -> CacheableLayoutResult {
match self {
IndependentNonReplacedContents::Flow(bfc) => bfc.layout(
@@ -240,6 +257,7 @@ impl IndependentNonReplacedContents {
positioning_context,
containing_block_for_children,
depends_on_block_constraints,
+ lazy_block_size,
),
IndependentNonReplacedContents::Grid(fc) => fc.layout(
layout_context,
@@ -266,6 +284,7 @@ impl IndependentNonReplacedContents {
level = "trace",
)
)]
+ #[allow(clippy::too_many_arguments)]
pub fn layout(
&self,
layout_context: &LayoutContext,
@@ -274,6 +293,7 @@ impl IndependentNonReplacedContents {
containing_block: &ContainingBlock,
base: &LayoutBoxBase,
depends_on_block_constraints: bool,
+ lazy_block_size: &LazySize,
) -> CacheableLayoutResult {
if let Some(cache) = base.cached_layout_result.borrow().as_ref() {
let cache = &**cache;
@@ -302,6 +322,7 @@ impl IndependentNonReplacedContents {
containing_block_for_children,
containing_block,
depends_on_block_constraints,
+ lazy_block_size,
);
*base.cached_layout_result.borrow_mut() = Some(Box::new(CacheableLayoutResultAndInputs {
@@ -334,6 +355,19 @@ impl IndependentNonReplacedContents {
pub(crate) fn is_table(&self) -> bool {
matches!(self, Self::Table(_))
}
+
+ fn repair_style(&mut self, context: &SharedStyleContext, new_style: &Arc<ComputedValues>) {
+ match self {
+ IndependentNonReplacedContents::Flow(..) => {},
+ IndependentNonReplacedContents::Flex(flex_container) => {
+ flex_container.repair_style(new_style)
+ },
+ IndependentNonReplacedContents::Grid(taffy_container) => {
+ taffy_container.repair_style(new_style)
+ },
+ IndependentNonReplacedContents::Table(table) => table.repair_style(context, new_style),
+ }
+ }
}
impl ComputeInlineContentSizes for IndependentNonReplacedContents {
diff --git a/components/layout/fragment_tree/base_fragment.rs b/components/layout/fragment_tree/base_fragment.rs
index 0cf6ee511cb..ff5df44c225 100644
--- a/components/layout/fragment_tree/base_fragment.rs
+++ b/components/layout/fragment_tree/base_fragment.rs
@@ -32,10 +32,8 @@ impl BaseFragment {
}
}
- /// Returns true if this fragment is non-anonymous and it is for the given
- /// OpaqueNode, regardless of the pseudo element.
- pub(crate) fn is_for_node(&self, node: OpaqueNode) -> bool {
- self.tag.map(|tag| tag.node == node).unwrap_or(false)
+ pub(crate) fn is_anonymous(&self) -> bool {
+ self.tag.is_none()
}
}
diff --git a/components/layout/fragment_tree/box_fragment.rs b/components/layout/fragment_tree/box_fragment.rs
index 65ad1c4aa93..9b96b1c4fb4 100644
--- a/components/layout/fragment_tree/box_fragment.rs
+++ b/components/layout/fragment_tree/box_fragment.rs
@@ -16,7 +16,8 @@ use style::logical_geometry::WritingMode;
use style::properties::ComputedValues;
use style::values::specified::box_::DisplayOutside;
-use super::{BaseFragment, BaseFragmentInfo, CollapsedBlockMargins, Fragment};
+use super::{BaseFragment, BaseFragmentInfo, CollapsedBlockMargins, Fragment, FragmentFlags};
+use crate::SharedStyle;
use crate::display_list::ToWebRender;
use crate::formatting_contexts::Baselines;
use crate::geom::{
@@ -39,11 +40,9 @@ pub(crate) enum BackgroundMode {
/// Draw the background normally, getting information from the Fragment style.
Normal,
}
-
#[derive(MallocSizeOf)]
pub(crate) struct ExtraBackground {
- #[conditional_malloc_size_of]
- pub style: ServoArc<ComputedValues>,
+ pub style: SharedStyle,
pub rect: PhysicalRect<Au>,
}
@@ -59,7 +58,6 @@ pub(crate) enum SpecificLayoutInfo {
pub(crate) struct BoxFragment {
pub base: BaseFragment,
- #[conditional_malloc_size_of]
pub style: ServoArc<ComputedValues>,
pub children: Vec<Fragment>,
@@ -238,6 +236,16 @@ impl BoxFragment {
self.margin + self.border + self.padding
}
+ pub(crate) fn is_root_element(&self) -> bool {
+ self.base.flags.intersects(FragmentFlags::IS_ROOT_ELEMENT)
+ }
+
+ pub(crate) fn is_body_element_of_html_element_root(&self) -> bool {
+ self.base
+ .flags
+ .intersects(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT)
+ }
+
pub fn print(&self, tree: &mut PrintTree) {
tree.new_level(format!(
"Box\
diff --git a/components/layout/fragment_tree/fragment.rs b/components/layout/fragment_tree/fragment.rs
index 1c5324fa1c4..1ebc7b3c989 100644
--- a/components/layout/fragment_tree/fragment.rs
+++ b/components/layout/fragment_tree/fragment.rs
@@ -22,6 +22,7 @@ use super::{
Tag,
};
use crate::cell::ArcRefCell;
+use crate::flow::inline::SharedInlineStyles;
use crate::geom::{LogicalSides, PhysicalPoint, PhysicalRect};
use crate::style_ext::ComputedValuesExt;
@@ -64,8 +65,7 @@ pub(crate) struct CollapsedMargin {
#[derive(MallocSizeOf)]
pub(crate) struct TextFragment {
pub base: BaseFragment,
- #[conditional_malloc_size_of]
- pub parent_style: ServoArc<ComputedValues>,
+ pub inline_styles: SharedInlineStyles,
pub rect: PhysicalRect<Au>,
pub font_metrics: FontMetrics,
pub font_key: FontInstanceKey,
@@ -78,14 +78,11 @@ pub(crate) struct TextFragment {
/// Extra space to add for each justification opportunity.
pub justification_adjustment: Au,
pub selection_range: Option<ServoRange<ByteIndex>>,
- #[conditional_malloc_size_of]
- pub selected_style: ServoArc<ComputedValues>,
}
#[derive(MallocSizeOf)]
pub(crate) struct ImageFragment {
pub base: BaseFragment,
- #[conditional_malloc_size_of]
pub style: ServoArc<ComputedValues>,
pub rect: PhysicalRect<Au>,
pub clip: PhysicalRect<Au>,
@@ -97,7 +94,6 @@ pub(crate) struct IFrameFragment {
pub base: BaseFragment,
pub pipeline_id: PipelineId,
pub rect: PhysicalRect<Au>,
- #[conditional_malloc_size_of]
pub style: ServoArc<ComputedValues>,
}
@@ -308,6 +304,25 @@ impl Fragment {
_ => None,
}
}
+
+ pub(crate) fn repair_style(&self, style: &ServoArc<ComputedValues>) {
+ match self {
+ Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
+ box_fragment.borrow_mut().style = style.clone()
+ },
+ Fragment::Positioning(positioning_fragment) => {
+ positioning_fragment.borrow_mut().style = style.clone();
+ },
+ Fragment::AbsoluteOrFixedPositioned(positioned_fragment) => {
+ if let Some(ref fragment) = positioned_fragment.borrow().fragment {
+ fragment.repair_style(style);
+ }
+ },
+ Fragment::Text(..) => unreachable!("Should never try to repair style of TextFragment"),
+ Fragment::Image(image_fragment) => image_fragment.borrow_mut().style = style.clone(),
+ Fragment::IFrame(iframe_fragment) => iframe_fragment.borrow_mut().style = style.clone(),
+ }
+ }
}
impl TextFragment {
diff --git a/components/layout/fragment_tree/fragment_tree.rs b/components/layout/fragment_tree/fragment_tree.rs
index 1499a50dacf..979bd0090fc 100644
--- a/components/layout/fragment_tree/fragment_tree.rs
+++ b/components/layout/fragment_tree/fragment_tree.rs
@@ -11,10 +11,10 @@ use malloc_size_of_derive::MallocSizeOf;
use style::animation::AnimationSetKey;
use webrender_api::units;
-use super::{ContainingBlockManager, Fragment};
+use super::{BoxFragment, ContainingBlockManager, Fragment};
+use crate::ArcRefCell;
use crate::context::LayoutContext;
use crate::display_list::StackingContext;
-use crate::flow::CanvasBackground;
use crate::geom::PhysicalRect;
#[derive(MallocSizeOf)]
@@ -36,9 +36,6 @@ pub struct FragmentTree {
/// The containing block used in the layout of this fragment tree.
pub(crate) initial_containing_block: PhysicalRect<Au>,
- /// <https://drafts.csswg.org/css-backgrounds/#special-backgrounds>
- pub(crate) canvas_background: CanvasBackground,
-
/// Whether or not the viewport is sensitive to scroll input events.
pub viewport_scroll_sensitivity: AxesScrollSensitivity,
}
@@ -49,14 +46,12 @@ impl FragmentTree {
root_fragments: Vec<Fragment>,
scrollable_overflow: PhysicalRect<Au>,
initial_containing_block: PhysicalRect<Au>,
- canvas_background: CanvasBackground,
viewport_scroll_sensitivity: AxesScrollSensitivity,
) -> Self {
let fragment_tree = Self {
root_fragments,
scrollable_overflow,
initial_containing_block,
- canvas_background,
viewport_scroll_sensitivity,
};
@@ -102,11 +97,7 @@ impl FragmentTree {
root_stacking_context: &StackingContext,
) {
// Paint the canvas’ background (if any) before/under everything else
- root_stacking_context.build_canvas_background_display_list(
- builder,
- self,
- &self.initial_containing_block,
- );
+ root_stacking_context.build_canvas_background_display_list(builder, self);
root_stacking_context.build_display_list(builder);
}
@@ -160,4 +151,45 @@ impl FragmentTree {
scroll_area
}
}
+
+ /// Find the `<body>` element's [`Fragment`], if it exists in this [`FragmentTree`].
+ pub(crate) fn body_fragment(&self) -> Option<ArcRefCell<BoxFragment>> {
+ fn find_body(children: &[Fragment]) -> Option<ArcRefCell<BoxFragment>> {
+ children.iter().find_map(|fragment| {
+ match fragment {
+ Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => {
+ let borrowed_box_fragment = box_fragment.borrow();
+ if borrowed_box_fragment.is_body_element_of_html_element_root() {
+ return Some(box_fragment.clone());
+ }
+
+ // The fragment for the `<body>` element is typically a child of the root (though,
+ // not if it's absolutely positioned), so we need to recurse into the children of
+ // the root to find it.
+ //
+ // Additionally, recurse into any anonymous fragments, as the `<body>` fragment may
+ // have created anonymous parents (for instance by creating an inline formatting context).
+ if borrowed_box_fragment.is_root_element() ||
+ borrowed_box_fragment.base.is_anonymous()
+ {
+ find_body(&borrowed_box_fragment.children)
+ } else {
+ None
+ }
+ },
+ Fragment::Positioning(positioning_context)
+ if positioning_context.borrow().base.is_anonymous() =>
+ {
+ // If the `<body>` element is a `display: inline` then it might be nested inside of a
+ // `PositioningFragment` for the purposes of putting it on the first line of the implied
+ // inline formatting context.
+ find_body(&positioning_context.borrow().children)
+ },
+ _ => None,
+ }
+ })
+ }
+
+ find_body(&self.root_fragments)
+ }
}
diff --git a/components/layout/fragment_tree/positioning_fragment.rs b/components/layout/fragment_tree/positioning_fragment.rs
index 0cf525a3479..e45a6137bff 100644
--- a/components/layout/fragment_tree/positioning_fragment.rs
+++ b/components/layout/fragment_tree/positioning_fragment.rs
@@ -24,9 +24,8 @@ pub(crate) struct PositioningFragment {
/// The scrollable overflow of this anonymous fragment's children.
pub scrollable_overflow: PhysicalRect<Au>,
- /// If this fragment was created with a style, the style of the fragment.
- #[conditional_malloc_size_of]
- pub style: Option<ServoArc<ComputedValues>>,
+ /// The style of the fragment.
+ pub style: ServoArc<ComputedValues>,
/// This [`PositioningFragment`]'s containing block rectangle in coordinates relative to
/// the initial containing block, but not taking into account any transforms.
@@ -34,8 +33,12 @@ pub(crate) struct PositioningFragment {
}
impl PositioningFragment {
- pub fn new_anonymous(rect: PhysicalRect<Au>, children: Vec<Fragment>) -> ArcRefCell<Self> {
- Self::new_with_base_fragment(BaseFragment::anonymous(), None, rect, children)
+ pub fn new_anonymous(
+ style: ServoArc<ComputedValues>,
+ rect: PhysicalRect<Au>,
+ children: Vec<Fragment>,
+ ) -> ArcRefCell<Self> {
+ Self::new_with_base_fragment(BaseFragment::anonymous(), style, rect, children)
}
pub fn new_empty(
@@ -43,12 +46,12 @@ impl PositioningFragment {
rect: PhysicalRect<Au>,
style: ServoArc<ComputedValues>,
) -> ArcRefCell<Self> {
- Self::new_with_base_fragment(base_fragment_info.into(), Some(style), rect, Vec::new())
+ Self::new_with_base_fragment(base_fragment_info.into(), style, rect, Vec::new())
}
fn new_with_base_fragment(
base: BaseFragment,
- style: Option<ServoArc<ComputedValues>>,
+ style: ServoArc<ComputedValues>,
rect: PhysicalRect<Au>,
children: Vec<Fragment>,
) -> ArcRefCell<Self> {
diff --git a/components/layout/geom.rs b/components/layout/geom.rs
index 6a09519b7ed..4065b785832 100644
--- a/components/layout/geom.rs
+++ b/components/layout/geom.rs
@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-use std::cell::LazyCell;
+use std::cell::{LazyCell, OnceCell};
use std::convert::From;
use std::fmt;
use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
@@ -1102,3 +1102,87 @@ impl Sizes {
)
}
}
+
+struct LazySizeData<'a> {
+ sizes: &'a Sizes,
+ axis: Direction,
+ automatic_size: Size<Au>,
+ get_automatic_minimum_size: fn() -> Au,
+ stretch_size: Option<Au>,
+ is_table: bool,
+}
+
+/// Represents a size that can't be fully resolved until the intrinsic size
+/// is known. This is useful in the block axis, since the intrinsic size
+/// depends on layout, but the other inputs are known beforehand.
+pub(crate) struct LazySize<'a> {
+ result: OnceCell<Au>,
+ data: Option<LazySizeData<'a>>,
+}
+
+impl<'a> LazySize<'a> {
+ pub(crate) fn new(
+ sizes: &'a Sizes,
+ axis: Direction,
+ automatic_size: Size<Au>,
+ get_automatic_minimum_size: fn() -> Au,
+ stretch_size: Option<Au>,
+ is_table: bool,
+ ) -> Self {
+ Self {
+ result: OnceCell::new(),
+ data: Some(LazySizeData {
+ sizes,
+ axis,
+ automatic_size,
+ get_automatic_minimum_size,
+ stretch_size,
+ is_table,
+ }),
+ }
+ }
+
+ /// Creates a [`LazySize`] that will resolve to the intrinsic size.
+ /// Should be equivalent to [`LazySize::new()`] with default parameters,
+ /// but avoiding the trouble of getting a reference to a [`Sizes::default()`]
+ /// which lives long enough.
+ ///
+ /// TODO: It's not clear what this should do if/when [`LazySize::resolve()`]
+ /// is changed to accept a [`ContentSizes`] as the intrinsic size.
+ pub(crate) fn intrinsic() -> Self {
+ Self {
+ result: OnceCell::new(),
+ data: None,
+ }
+ }
+
+ /// Resolves the [`LazySize`] into [`Au`], caching the result.
+ /// The argument is a callback that computes the intrinsic size lazily.
+ ///
+ /// TODO: The intrinsic size should probably be a [`ContentSizes`] instead of [`Au`].
+ pub(crate) fn resolve(&self, get_content_size: impl FnOnce() -> Au) -> Au {
+ *self.result.get_or_init(|| {
+ let Some(ref data) = self.data else {
+ return get_content_size();
+ };
+ data.sizes.resolve(
+ data.axis,
+ data.automatic_size,
+ data.get_automatic_minimum_size,
+ data.stretch_size,
+ || get_content_size().into(),
+ data.is_table,
+ )
+ })
+ }
+}
+
+impl From<Au> for LazySize<'_> {
+ /// Creates a [`LazySize`] that will resolve to the given [`Au`],
+ /// ignoring the intrinsic size.
+ fn from(value: Au) -> Self {
+ let result = OnceCell::new();
+ result.set(value).unwrap();
+ LazySize { result, data: None }
+ }
+}
diff --git a/components/layout/layout_box_base.rs b/components/layout/layout_box_base.rs
index 71fbfdeced1..161aee0f9bb 100644
--- a/components/layout/layout_box_base.rs
+++ b/components/layout/layout_box_base.rs
@@ -27,7 +27,6 @@ use crate::{ConstraintSpace, ContainingBlockSize};
#[derive(MallocSizeOf)]
pub(crate) struct LayoutBoxBase {
pub base_fragment_info: BaseFragmentInfo,
- #[conditional_malloc_size_of]
pub style: Arc<ComputedValues>,
pub cached_inline_content_size:
AtomicRefCell<Option<Box<(SizeConstraint, InlineContentSizesResult)>>>,
@@ -90,6 +89,13 @@ impl LayoutBoxBase {
pub(crate) fn clear_fragments(&self) {
self.fragments.borrow_mut().clear();
}
+
+ pub(crate) fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
+ self.style = new_style.clone();
+ for fragment in self.fragments.borrow_mut().iter_mut() {
+ fragment.repair_style(new_style);
+ }
+ }
}
impl Debug for LayoutBoxBase {
diff --git a/components/layout/layout_impl.rs b/components/layout/layout_impl.rs
index b8d91c38027..fcf658036b2 100644
--- a/components/layout/layout_impl.rs
+++ b/components/layout/layout_impl.rs
@@ -4,9 +4,8 @@
#![allow(unsafe_code)]
-use std::cell::{Cell, LazyCell, RefCell};
-use std::collections::{HashMap, HashSet};
-use std::ffi::c_void;
+use std::cell::{Cell, RefCell};
+use std::collections::HashMap;
use std::fmt::Debug;
use std::process;
use std::sync::{Arc, LazyLock};
@@ -57,7 +56,7 @@ use style::media_queries::{Device, MediaList, MediaType};
use style::properties::style_structs::Font;
use style::properties::{ComputedValues, PropertyId};
use style::queries::values::PrefersColorScheme;
-use style::selector_parser::{PseudoElement, SnapshotMap};
+use style::selector_parser::{PseudoElement, RestyleDamage, SnapshotMap};
use style::servo::media_queries::FontMetricsProvider;
use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards};
use style::stylesheets::{
@@ -84,7 +83,7 @@ use crate::query::{
process_content_boxes_request, process_node_scroll_area_request, process_offset_parent_query,
process_resolved_font_style_query, process_resolved_style_request, process_text_index_request,
};
-use crate::traversal::RecalcStyle;
+use crate::traversal::{RecalcStyle, compute_damage_and_repair_style};
use crate::{BoxTree, FragmentTree};
// This mutex is necessary due to syncronisation issues between two different types of thread-local storage
@@ -95,10 +94,6 @@ use crate::{BoxTree, FragmentTree};
static STYLE_THREAD_POOL: Mutex<&style::global_style_data::STYLE_THREAD_POOL> =
Mutex::new(&style::global_style_data::STYLE_THREAD_POOL);
-thread_local!(static SEEN_POINTERS: LazyCell<RefCell<HashSet<*const c_void>>> = const {
- LazyCell::new(|| RefCell::new(HashSet::new()))
-});
-
/// A CSS file to style the user agent stylesheet.
static USER_AGENT_CSS: &[u8] = include_bytes!("./stylesheets/user-agent.css");
@@ -770,6 +765,12 @@ impl LayoutThread {
driver::traverse_dom(&recalc_style_traversal, token, rayon_pool).as_node();
let root_node = root_element.as_node();
+ let damage = compute_damage_and_repair_style(layout_context.shared_context(), root_node);
+ if damage == RestyleDamage::REPAINT {
+ layout_context.style_context.stylist.rule_tree().maybe_gc();
+ return;
+ }
+
let mut box_tree = self.box_tree.borrow_mut();
let box_tree = &mut *box_tree;
let mut build_box_tree = || {
diff --git a/components/layout/lib.rs b/components/layout/lib.rs
index af7d432c4d8..cd992387277 100644
--- a/components/layout/lib.rs
+++ b/components/layout/lib.rs
@@ -38,6 +38,7 @@ pub use flow::BoxTree;
pub use fragment_tree::FragmentTree;
pub use layout_impl::LayoutFactoryImpl;
use malloc_size_of_derive::MallocSizeOf;
+use servo_arc::Arc as ServoArc;
use style::logical_geometry::WritingMode;
use style::properties::ComputedValues;
use style::values::computed::TextDecorationLine;
@@ -45,6 +46,16 @@ use style::values::computed::TextDecorationLine;
use crate::geom::{LogicalVec2, SizeConstraint};
use crate::style_ext::AspectRatio;
+/// At times, a style is "owned" by more than one layout object. For example, text
+/// fragments need a handle on their parent inline box's style. In order to make
+/// incremental layout easier to implement, another layer of shared ownership is added via
+/// [`SharedStyle`]. This allows updating the style in originating layout object and
+/// having all "depdendent" objects update automatically.
+///
+/// Note that this is not a cost-free data structure, so should only be
+/// used when necessary.
+pub(crate) type SharedStyle = ArcRefCell<ServoArc<ComputedValues>>;
+
/// Represents the set of constraints that we use when computing the min-content
/// and max-content inline sizes of an element.
pub(crate) struct ConstraintSpace {
diff --git a/components/layout/lists.rs b/components/layout/lists.rs
index d5a1f863865..8c653cb6858 100644
--- a/components/layout/lists.rs
+++ b/components/layout/lists.rs
@@ -7,18 +7,14 @@ use style::properties::style_structs;
use style::values::computed::Image;
use crate::context::LayoutContext;
-use crate::dom::NodeExt;
use crate::dom_traversal::{NodeAndStyleInfo, PseudoElementContentItem};
use crate::replaced::ReplacedContents;
/// <https://drafts.csswg.org/css-lists/#content-property>
-pub(crate) fn make_marker<'dom, Node>(
+pub(crate) fn make_marker<'dom>(
context: &LayoutContext,
- info: &NodeAndStyleInfo<Node>,
-) -> Option<(NodeAndStyleInfo<Node>, Vec<PseudoElementContentItem>)>
-where
- Node: NodeExt<'dom>,
-{
+ info: &NodeAndStyleInfo<'dom>,
+) -> Option<(NodeAndStyleInfo<'dom>, Vec<PseudoElementContentItem>)> {
let marker_info = info.pseudo(context, style::selector_parser::PseudoElement::Marker)?;
let style = &marker_info.style;
let list_style = style.get_list();
diff --git a/components/layout/positioned.rs b/components/layout/positioned.rs
index ff361396fa3..6280864d533 100644
--- a/components/layout/positioned.rs
+++ b/components/layout/positioned.rs
@@ -16,7 +16,6 @@ use style::values::specified::align::AlignFlags;
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
-use crate::dom::NodeExt;
use crate::dom_traversal::{Contents, NodeAndStyleInfo};
use crate::formatting_contexts::{
IndependentFormattingContext, IndependentFormattingContextContents,
@@ -25,12 +24,11 @@ use crate::fragment_tree::{
BoxFragment, Fragment, FragmentFlags, HoistedSharedFragment, SpecificLayoutInfo,
};
use crate::geom::{
- AuOrAuto, LengthPercentageOrAuto, LogicalRect, LogicalSides, LogicalSides1D, LogicalVec2,
- PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, PhysicalVec, Size, Sizes, ToLogical,
- ToLogicalWithContainingBlock,
+ AuOrAuto, LazySize, LengthPercentageOrAuto, LogicalRect, LogicalSides, LogicalSides1D,
+ LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, PhysicalVec, Size,
+ Sizes, ToLogical, ToLogicalWithContainingBlock,
};
use crate::layout_box_base::LayoutBoxBase;
-use crate::sizing::ContentSizes;
use crate::style_ext::{Clamp, ComputedValuesExt, ContentBoxSizesAndPBM, DisplayInside};
use crate::{
ConstraintSpace, ContainingBlock, ContainingBlockSize, DefiniteContainingBlock,
@@ -57,9 +55,9 @@ impl AbsolutelyPositionedBox {
Self { context }
}
- pub fn construct<'dom>(
+ pub fn construct(
context: &LayoutContext,
- node_info: &NodeAndStyleInfo<impl NodeExt<'dom>>,
+ node_info: &NodeAndStyleInfo,
display_inside: DisplayInside,
contents: Contents,
) -> Self {
@@ -474,7 +472,7 @@ impl HoistedAbsolutelyPositionedBox {
false => shared_fragment.resolved_alignment.inline,
};
- let mut inline_axis_solver = AbsoluteAxisSolver {
+ let inline_axis_solver = AbsoluteAxisSolver {
axis: Direction::Inline,
containing_size: cbis,
padding_border_sum: pbm.padding_border_sums.inline,
@@ -497,7 +495,7 @@ impl HoistedAbsolutelyPositionedBox {
true => style.clone_align_self().0.0,
false => shared_fragment.resolved_alignment.block,
};
- let mut block_axis_solver = AbsoluteAxisSolver {
+ let block_axis_solver = AbsoluteAxisSolver {
axis: Direction::Block,
containing_size: cbbs,
padding_border_sum: pbm.padding_border_sums.block,
@@ -512,52 +510,6 @@ impl HoistedAbsolutelyPositionedBox {
is_table,
};
- if let IndependentFormattingContextContents::Replaced(replaced) = &context.contents {
- // https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
- // https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
- let inset_sums = LogicalVec2 {
- inline: inline_axis_solver.inset_sum(),
- block: block_axis_solver.inset_sum(),
- };
- let automatic_size = |alignment: AlignFlags, offsets: &LogicalSides1D<_>| {
- if alignment.value() == AlignFlags::STRETCH && !offsets.either_auto() {
- Size::Stretch
- } else {
- Size::FitContent
- }
- };
- let used_size = replaced.used_size_as_if_inline_element_from_content_box_sizes(
- containing_block,
- &style,
- context.preferred_aspect_ratio(&pbm.padding_border_sums),
- LogicalVec2 {
- inline: &inline_axis_solver.computed_sizes,
- block: &block_axis_solver.computed_sizes,
- },
- LogicalVec2 {
- inline: automatic_size(inline_alignment, &inline_axis_solver.box_offsets),
- block: automatic_size(block_alignment, &block_axis_solver.box_offsets),
- },
- pbm.padding_border_sums + pbm.margin.auto_is(Au::zero).sum() + inset_sums,
- );
- inline_axis_solver.override_size(used_size.inline);
- block_axis_solver.override_size(used_size.block);
- }
-
- // The block axis can depend on layout results, so we only solve it tentatively,
- // we may have to resolve it properly later on.
- let mut block_axis = block_axis_solver.solve_tentatively();
-
- // The inline axis can be fully resolved, computing intrinsic sizes using the
- // tentative block size.
- let mut inline_axis = inline_axis_solver.solve(Some(|| {
- let ratio = context.preferred_aspect_ratio(&pbm.padding_border_sums);
- let constraint_space = ConstraintSpace::new(block_axis.size, style.writing_mode, ratio);
- context
- .inline_content_sizes(layout_context, &constraint_space)
- .sizes
- }));
-
let mut positioning_context = PositioningContext::default();
let mut new_fragment = {
let content_size: LogicalVec2<Au>;
@@ -567,10 +519,34 @@ impl HoistedAbsolutelyPositionedBox {
IndependentFormattingContextContents::Replaced(replaced) => {
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
// https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
- content_size = LogicalVec2 {
- inline: inline_axis.size.to_definite().unwrap(),
- block: block_axis.size.to_definite().unwrap(),
+ let inset_sums = LogicalVec2 {
+ inline: inline_axis_solver.inset_sum(),
+ block: block_axis_solver.inset_sum(),
+ };
+ let automatic_size = |alignment: AlignFlags, offsets: &LogicalSides1D<_>| {
+ if alignment.value() == AlignFlags::STRETCH && !offsets.either_auto() {
+ Size::Stretch
+ } else {
+ Size::FitContent
+ }
};
+ content_size = replaced.used_size_as_if_inline_element_from_content_box_sizes(
+ containing_block,
+ &style,
+ context.preferred_aspect_ratio(&pbm.padding_border_sums),
+ LogicalVec2 {
+ inline: &inline_axis_solver.computed_sizes,
+ block: &block_axis_solver.computed_sizes,
+ },
+ LogicalVec2 {
+ inline: automatic_size(
+ inline_alignment,
+ &inline_axis_solver.box_offsets,
+ ),
+ block: automatic_size(block_alignment, &block_axis_solver.box_offsets),
+ },
+ pbm.padding_border_sums + pbm.margin.auto_is(Au::zero).sum() + inset_sums,
+ );
fragments = replaced.make_fragments(
layout_context,
&style,
@@ -580,11 +556,40 @@ impl HoistedAbsolutelyPositionedBox {
IndependentFormattingContextContents::NonReplaced(non_replaced) => {
// https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width
// https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height
- let inline_size = inline_axis.size.to_definite().unwrap();
+
+ // The block size can depend on layout results, so we only solve it extrinsically,
+ // we may have to resolve it properly later on.
+ let block_automatic_size = block_axis_solver.automatic_size();
+ let block_stretch_size = Some(block_axis_solver.stretch_size());
+ let extrinsic_block_size = block_axis_solver.computed_sizes.resolve_extrinsic(
+ block_automatic_size,
+ Au::zero(),
+ block_stretch_size,
+ );
+
+ // The inline axis can be fully resolved, computing intrinsic sizes using the
+ // extrinsic block size.
+ let get_inline_content_size = || {
+ let ratio = context.preferred_aspect_ratio(&pbm.padding_border_sums);
+ let constraint_space =
+ ConstraintSpace::new(extrinsic_block_size, style.writing_mode, ratio);
+ context
+ .inline_content_sizes(layout_context, &constraint_space)
+ .sizes
+ };
+ let inline_size = inline_axis_solver.computed_sizes.resolve(
+ Direction::Inline,
+ inline_axis_solver.automatic_size(),
+ Au::zero,
+ Some(inline_axis_solver.stretch_size()),
+ get_inline_content_size,
+ is_table,
+ );
+
let containing_block_for_children = ContainingBlock {
size: ContainingBlockSize {
inline: inline_size,
- block: block_axis.size,
+ block: extrinsic_block_size,
},
style: &style,
};
@@ -595,6 +600,14 @@ impl HoistedAbsolutelyPositionedBox {
"Mixed horizontal and vertical writing modes are not supported yet"
);
+ let lazy_block_size = LazySize::new(
+ &block_axis_solver.computed_sizes,
+ Direction::Block,
+ block_automatic_size,
+ Au::zero,
+ block_stretch_size,
+ is_table,
+ );
let independent_layout = non_replaced.layout(
layout_context,
&mut positioning_context,
@@ -602,24 +615,17 @@ impl HoistedAbsolutelyPositionedBox {
containing_block,
&context.base,
false, /* depends_on_block_constraints */
+ &lazy_block_size,
);
- let inline_size = if let Some(inline_size) =
- independent_layout.content_inline_size_for_table
- {
- // Tables can become narrower than predicted due to collapsed columns,
- // so we need to solve again to update margins.
- inline_axis_solver.override_size(inline_size);
- inline_axis = inline_axis_solver.solve_tentatively();
- inline_size
- } else {
- inline_size
- };
+ // Tables can become narrower than predicted due to collapsed columns
+ let inline_size = independent_layout
+ .content_inline_size_for_table
+ .unwrap_or(inline_size);
// Now we can properly solve the block size.
- block_axis = block_axis_solver
- .solve(Some(|| independent_layout.content_block_size.into()));
- let block_size = block_axis.size.to_definite().unwrap();
+ let block_size =
+ lazy_block_size.resolve(|| independent_layout.content_block_size);
content_size = LogicalVec2 {
inline: inline_size,
@@ -630,11 +636,13 @@ impl HoistedAbsolutelyPositionedBox {
},
};
+ let inline_margins = inline_axis_solver.solve_margins(content_size.inline);
+ let block_margins = block_axis_solver.solve_margins(content_size.block);
let margin = LogicalSides {
- inline_start: inline_axis.margin_start,
- inline_end: inline_axis.margin_end,
- block_start: block_axis.margin_start,
- block_end: block_axis.margin_end,
+ inline_start: inline_margins.start,
+ inline_end: inline_margins.end,
+ block_start: block_margins.start,
+ block_end: block_margins.end,
};
let pb = pbm.padding + pbm.border;
@@ -716,12 +724,6 @@ impl LogicalRect<Au> {
}
}
-struct AxisResult {
- size: SizeConstraint,
- margin_start: Au,
- margin_end: Au,
-}
-
struct AbsoluteAxisSolver<'a> {
axis: Direction,
containing_size: Au,
@@ -764,101 +766,56 @@ impl AbsoluteAxisSolver<'_> {
}
}
- /// This unifies some of the parts in common in:
- ///
- /// * <https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width>
- /// * <https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height>
- ///
- /// … and:
- ///
- /// * <https://drafts.csswg.org/css2/visudet.html#abs-replaced-width>
- /// * <https://drafts.csswg.org/css2/visudet.html#abs-replaced-height>
- ///
- /// In the replaced case, `size` is never `Auto`.
- fn solve(&self, get_content_size: Option<impl FnOnce() -> ContentSizes>) -> AxisResult {
- let solve_size = |initial_behavior, stretch_size: Au| -> SizeConstraint {
- let stretch_size = stretch_size.max(Au::zero());
- if let Some(get_content_size) = get_content_size {
- SizeConstraint::Definite(self.computed_sizes.resolve(
- self.axis,
- initial_behavior,
- Au::zero,
- Some(stretch_size),
- get_content_size,
- self.is_table,
- ))
- } else {
- self.computed_sizes.resolve_extrinsic(
- initial_behavior,
- Au::zero(),
- Some(stretch_size),
- )
- }
- };
- if self.box_offsets.either_auto() {
- let margin_start = self.computed_margin_start.auto_is(Au::zero);
- let margin_end = self.computed_margin_end.auto_is(Au::zero);
- let stretch_size = self.containing_size -
- self.inset_sum() -
- self.padding_border_sum -
- margin_start -
- margin_end;
- let size = solve_size(Size::FitContent, stretch_size);
- AxisResult {
- size,
- margin_start,
- margin_end,
- }
- } else {
- let mut free_space = self.containing_size - self.inset_sum() - self.padding_border_sum;
- let stretch_size = free_space -
- self.computed_margin_start.auto_is(Au::zero) -
- self.computed_margin_end.auto_is(Au::zero);
- let initial_behavior = match self.alignment.value() {
- AlignFlags::NORMAL | AlignFlags::AUTO if !self.is_table => Size::Stretch,
- AlignFlags::STRETCH => Size::Stretch,
- _ => Size::FitContent,
- };
- let size = solve_size(initial_behavior, stretch_size);
- if let Some(used_size) = size.to_definite() {
- free_space -= used_size;
- } else {
- free_space = Au::zero();
- }
- let (margin_start, margin_end) =
- match (self.computed_margin_start, self.computed_margin_end) {
- (AuOrAuto::Auto, AuOrAuto::Auto) => {
- if self.avoid_negative_margin_start && free_space < Au::zero() {
- (Au::zero(), free_space)
- } else {
- let margin_start = free_space / 2;
- (margin_start, free_space - margin_start)
- }
- },
- (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => (free_space - end, end),
- (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => {
- (start, free_space - start)
- },
- (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
- (start, end)
- },
- };
- AxisResult {
- size,
- margin_start,
- margin_end,
- }
+ #[inline]
+ fn automatic_size(&self) -> Size<Au> {
+ match self.alignment.value() {
+ _ if self.box_offsets.either_auto() => Size::FitContent,
+ AlignFlags::NORMAL | AlignFlags::AUTO if !self.is_table => Size::Stretch,
+ AlignFlags::STRETCH => Size::Stretch,
+ _ => Size::FitContent,
}
}
- fn solve_tentatively(&mut self) -> AxisResult {
- self.solve(None::<fn() -> ContentSizes>)
+ #[inline]
+ fn stretch_size(&self) -> Au {
+ Au::zero().max(
+ self.containing_size -
+ self.inset_sum() -
+ self.padding_border_sum -
+ self.computed_margin_start.auto_is(Au::zero) -
+ self.computed_margin_end.auto_is(Au::zero),
+ )
}
- fn override_size(&mut self, size: Au) {
- self.computed_sizes.preferred = Size::Numeric(size);
- self.computed_sizes.min = Size::default();
- self.computed_sizes.max = Size::default();
+ fn solve_margins(&self, size: Au) -> LogicalSides1D<Au> {
+ if self.box_offsets.either_auto() {
+ LogicalSides1D::new(
+ self.computed_margin_start.auto_is(Au::zero),
+ self.computed_margin_end.auto_is(Au::zero),
+ )
+ } else {
+ let free_space =
+ self.containing_size - self.inset_sum() - self.padding_border_sum - size;
+ match (self.computed_margin_start, self.computed_margin_end) {
+ (AuOrAuto::Auto, AuOrAuto::Auto) => {
+ if self.avoid_negative_margin_start && free_space < Au::zero() {
+ LogicalSides1D::new(Au::zero(), free_space)
+ } else {
+ let margin_start = free_space / 2;
+ LogicalSides1D::new(margin_start, free_space - margin_start)
+ }
+ },
+ (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => {
+ LogicalSides1D::new(free_space - end, end)
+ },
+ (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => {
+ LogicalSides1D::new(start, free_space - start)
+ },
+ (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => {
+ LogicalSides1D::new(start, end)
+ },
+ }
+ }
}
fn origin_for_margin_box(
diff --git a/components/layout/query.rs b/components/layout/query.rs
index e78acdd0ca8..ca9db9ceaf1 100644
--- a/components/layout/query.rs
+++ b/components/layout/query.rs
@@ -9,6 +9,7 @@ use app_units::Au;
use euclid::default::{Point2D, Rect};
use euclid::{SideOffsets2D, Size2D};
use itertools::Itertools;
+use script::layout_dom::ServoLayoutNode;
use script_layout_interface::wrapper_traits::{
LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
};
@@ -20,7 +21,7 @@ use style::computed_values::position::T as Position;
use style::computed_values::visibility::T as Visibility;
use style::computed_values::white_space_collapse::T as WhiteSpaceCollapseValue;
use style::context::{QuirksMode, SharedStyleContext, StyleContext, ThreadLocalStyleContext};
-use style::dom::{OpaqueNode, TElement};
+use style::dom::{NodeInfo, OpaqueNode, TElement, TNode};
use style::properties::style_structs::Font;
use style::properties::{
ComputedValues, Importance, LonghandId, PropertyDeclarationBlock, PropertyDeclarationId,
@@ -46,7 +47,7 @@ use crate::fragment_tree::{
};
use crate::taffy::SpecificTaffyGridInfo;
-pub fn process_content_box_request<'dom>(node: impl LayoutNode<'dom> + 'dom) -> Option<Rect<Au>> {
+pub fn process_content_box_request(node: ServoLayoutNode<'_>) -> Option<Rect<Au>> {
let rects: Vec<_> = node
.fragments_for_pseudo(None)
.iter()
@@ -61,7 +62,7 @@ pub fn process_content_box_request<'dom>(node: impl LayoutNode<'dom> + 'dom) ->
}))
}
-pub fn process_content_boxes_request<'dom>(node: impl LayoutNode<'dom> + 'dom) -> Vec<Rect<Au>> {
+pub fn process_content_boxes_request(node: ServoLayoutNode<'_>) -> Vec<Rect<Au>> {
node.fragments_for_pseudo(None)
.iter()
.filter_map(Fragment::cumulative_border_box_rect)
@@ -69,7 +70,7 @@ pub fn process_content_boxes_request<'dom>(node: impl LayoutNode<'dom> + 'dom) -
.collect()
}
-pub fn process_client_rect_request<'dom>(node: impl LayoutNode<'dom> + 'dom) -> Rect<i32> {
+pub fn process_client_rect_request(node: ServoLayoutNode<'_>) -> Rect<i32> {
node.fragments_for_pseudo(None)
.first()
.map(Fragment::client_rect)
@@ -77,8 +78,8 @@ pub fn process_client_rect_request<'dom>(node: impl LayoutNode<'dom> + 'dom) ->
}
/// <https://drafts.csswg.org/cssom-view/#scrolling-area>
-pub fn process_node_scroll_area_request<'dom>(
- requested_node: Option<impl LayoutNode<'dom> + 'dom>,
+pub fn process_node_scroll_area_request(
+ requested_node: Option<ServoLayoutNode<'_>>,
fragment_tree: Option<Arc<FragmentTree>>,
) -> Rect<i32> {
let Some(tree) = fragment_tree else {
@@ -105,9 +106,9 @@ pub fn process_node_scroll_area_request<'dom>(
/// Return the resolved value of property for a given (pseudo)element.
/// <https://drafts.csswg.org/cssom/#resolved-value>
-pub fn process_resolved_style_request<'dom>(
+pub fn process_resolved_style_request(
context: &SharedStyleContext,
- node: impl LayoutNode<'dom> + 'dom,
+ node: ServoLayoutNode<'_>,
pseudo: &Option<PseudoElement>,
property: &PropertyId,
) -> String {
@@ -361,9 +362,9 @@ fn resolve_grid_template(
}
}
-pub fn process_resolved_style_request_for_unstyled_node<'dom>(
+pub fn process_resolved_style_request_for_unstyled_node(
context: &SharedStyleContext,
- node: impl LayoutNode<'dom>,
+ node: ServoLayoutNode<'_>,
pseudo: &Option<PseudoElement>,
property: &PropertyId,
) -> String {
@@ -434,9 +435,7 @@ struct OffsetParentFragments {
}
/// <https://www.w3.org/TR/2016/WD-cssom-view-1-20160317/#dom-htmlelement-offsetparent>
-fn offset_parent_fragments<'dom>(
- node: impl LayoutNode<'dom> + 'dom,
-) -> Option<OffsetParentFragments> {
+fn offset_parent_fragments(node: ServoLayoutNode<'_>) -> Option<OffsetParentFragments> {
// 1. If any of the following holds true return null and terminate this algorithm:
// * The element does not have an associated CSS layout box.
// * The element is the root element.
@@ -498,9 +497,7 @@ fn offset_parent_fragments<'dom>(
}
#[inline]
-pub fn process_offset_parent_query<'dom>(
- node: impl LayoutNode<'dom> + 'dom,
-) -> Option<OffsetParentResponse> {
+pub fn process_offset_parent_query(node: ServoLayoutNode<'_>) -> Option<OffsetParentResponse> {
// Only consider the first fragment of the node found as per a
// possible interpretation of the specification: "[...] return the
// y-coordinate of the top border edge of the first CSS layout box
@@ -580,7 +577,7 @@ pub fn process_offset_parent_query<'dom>(
}
/// <https://html.spec.whatwg.org/multipage/#get-the-text-steps>
-pub fn get_the_text_steps<'dom>(node: impl LayoutNode<'dom>) -> String {
+pub fn get_the_text_steps(node: ServoLayoutNode<'_>) -> String {
// Step 1: If element is not being rendered or if the user agent is a non-CSS user agent, then
// return element's descendant text content.
// This is taken care of in HTMLElemnent code
@@ -668,8 +665,8 @@ impl Default for RenderedTextCollectionState {
}
/// <https://html.spec.whatwg.org/multipage/#rendered-text-collection-steps>
-fn rendered_text_collection_steps<'dom>(
- node: impl LayoutNode<'dom>,
+fn rendered_text_collection_steps(
+ node: ServoLayoutNode<'_>,
state: &mut RenderedTextCollectionState,
) -> Vec<InnerOrOuterTextItem> {
// Step 1. Let items be the result of running the rendered text collection
diff --git a/components/layout/replaced.rs b/components/layout/replaced.rs
index bbebc57aa97..0cc95c1a87d 100644
--- a/components/layout/replaced.rs
+++ b/components/layout/replaced.rs
@@ -13,10 +13,12 @@ use euclid::{Scale, Size2D};
use malloc_size_of_derive::MallocSizeOf;
use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder};
use pixels::Image;
+use script::layout_dom::ServoLayoutNode;
use script_layout_interface::IFrameSize;
use servo_arc::Arc as ServoArc;
use style::Zero;
use style::computed_values::object_fit::T as ObjectFit;
+use style::dom::TNode;
use style::logical_geometry::{Direction, WritingMode};
use style::properties::ComputedValues;
use style::servo::url::ComputedUrl;
@@ -120,7 +122,7 @@ pub(crate) enum ReplacedContentKind {
}
impl ReplacedContents {
- pub fn for_element<'dom>(element: impl NodeExt<'dom>, context: &LayoutContext) -> Option<Self> {
+ pub fn for_element(element: ServoLayoutNode<'_>, context: &LayoutContext) -> Option<Self> {
if let Some(ref data_attribute_string) = element.as_typeless_object_with_data_attribute() {
if let Some(url) = try_to_parse_image_data_url(data_attribute_string) {
return Self::from_image_url(
@@ -184,8 +186,8 @@ impl ReplacedContents {
})
}
- pub fn from_image_url<'dom>(
- element: impl NodeExt<'dom>,
+ pub fn from_image_url(
+ element: ServoLayoutNode<'_>,
context: &LayoutContext,
image_url: &ComputedUrl,
) -> Option<Self> {
@@ -213,8 +215,8 @@ impl ReplacedContents {
None
}
- pub fn from_image<'dom>(
- element: impl NodeExt<'dom>,
+ pub fn from_image(
+ element: ServoLayoutNode<'_>,
context: &LayoutContext,
image: &ComputedImage,
) -> Option<Self> {
diff --git a/components/layout/table/construct.rs b/components/layout/table/construct.rs
index 56e11320be4..0c238073df2 100644
--- a/components/layout/table/construct.rs
+++ b/components/layout/table/construct.rs
@@ -8,7 +8,7 @@ use std::iter::repeat;
use atomic_refcell::AtomicRef;
use log::warn;
-use script_layout_interface::wrapper_traits::ThreadSafeLayoutNode;
+use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
use servo_arc::Arc;
use style::properties::ComputedValues;
use style::properties::style_structs::Font;
@@ -19,10 +19,9 @@ use super::{
Table, TableCaption, TableLevelBox, TableSlot, TableSlotCell, TableSlotCoordinates,
TableSlotOffset, TableTrack, TableTrackGroup, TableTrackGroupType,
};
-use crate::PropagatedBoxTreeData;
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
-use crate::dom::{BoxSlot, LayoutBox, NodeExt};
+use crate::dom::{BoxSlot, LayoutBox};
use crate::dom_traversal::{Contents, NodeAndStyleInfo, NonReplacedContents, TraversalHandler};
use crate::flow::{BlockContainerBuilder, BlockFormattingContext};
use crate::formatting_contexts::{
@@ -32,6 +31,7 @@ use crate::formatting_contexts::{
use crate::fragment_tree::BaseFragmentInfo;
use crate::layout_box_base::LayoutBoxBase;
use crate::style_ext::{DisplayGeneratingBox, DisplayLayoutInternal};
+use crate::{PropagatedBoxTreeData, SharedStyle};
/// A reference to a slot and its coordinates in the table
#[derive(Debug)]
@@ -50,17 +50,17 @@ impl ResolvedSlotAndLocation<'_> {
}
}
-pub(crate) enum AnonymousTableContent<'dom, Node> {
- Text(NodeAndStyleInfo<Node>, Cow<'dom, str>),
+pub(crate) enum AnonymousTableContent<'dom> {
+ Text(NodeAndStyleInfo<'dom>, Cow<'dom, str>),
Element {
- info: NodeAndStyleInfo<Node>,
+ info: NodeAndStyleInfo<'dom>,
display: DisplayGeneratingBox,
contents: Contents,
box_slot: BoxSlot<'dom>,
},
}
-impl<Node> AnonymousTableContent<'_, Node> {
+impl AnonymousTableContent<'_> {
fn is_whitespace_only(&self) -> bool {
match self {
Self::Element { .. } => false,
@@ -74,9 +74,9 @@ impl<Node> AnonymousTableContent<'_, Node> {
}
impl Table {
- pub(crate) fn construct<'dom>(
+ pub(crate) fn construct(
context: &LayoutContext,
- info: &NodeAndStyleInfo<impl NodeExt<'dom>>,
+ info: &NodeAndStyleInfo,
grid_style: Arc<ComputedValues>,
contents: NonReplacedContents,
propagated_data: PropagatedBoxTreeData,
@@ -91,15 +91,12 @@ impl Table {
traversal.finish()
}
- pub(crate) fn construct_anonymous<'dom, Node>(
+ pub(crate) fn construct_anonymous<'dom>(
context: &LayoutContext,
- parent_info: &NodeAndStyleInfo<Node>,
- contents: Vec<AnonymousTableContent<'dom, Node>>,
+ parent_info: &NodeAndStyleInfo<'dom>,
+ contents: Vec<AnonymousTableContent<'dom>>,
propagated_data: PropagatedBoxTreeData,
- ) -> (NodeAndStyleInfo<Node>, IndependentFormattingContext)
- where
- Node: crate::dom::NodeExt<'dom>,
- {
+ ) -> (NodeAndStyleInfo<'dom>, IndependentFormattingContext) {
let table_info = parent_info
.pseudo(context, PseudoElement::ServoAnonymousTable)
.expect("Should never fail to create anonymous table info.");
@@ -645,9 +642,9 @@ impl TableBuilder {
}
}
-pub(crate) struct TableBuilderTraversal<'style, 'dom, Node> {
+pub(crate) struct TableBuilderTraversal<'style, 'dom> {
context: &'style LayoutContext<'style>,
- info: &'style NodeAndStyleInfo<Node>,
+ info: &'style NodeAndStyleInfo<'dom>,
/// The value of the [`PropagatedBoxTreeData`] to use, either for the row group
/// if processing one or for the table itself if outside a row group.
@@ -657,19 +654,16 @@ pub(crate) struct TableBuilderTraversal<'style, 'dom, Node> {
/// into another struct so that we can write unit tests against the builder.
builder: TableBuilder,
- current_anonymous_row_content: Vec<AnonymousTableContent<'dom, Node>>,
+ current_anonymous_row_content: Vec<AnonymousTableContent<'dom>>,
/// The index of the current row group, if there is one.
current_row_group_index: Option<usize>,
}
-impl<'style, 'dom, Node> TableBuilderTraversal<'style, 'dom, Node>
-where
- Node: NodeExt<'dom>,
-{
+impl<'style, 'dom> TableBuilderTraversal<'style, 'dom> {
pub(crate) fn new(
context: &'style LayoutContext<'style>,
- info: &'style NodeAndStyleInfo<Node>,
+ info: &'style NodeAndStyleInfo<'dom>,
grid_style: Arc<ComputedValues>,
propagated_data: PropagatedBoxTreeData,
) -> Self {
@@ -728,9 +722,10 @@ where
let style = anonymous_info.style.clone();
self.push_table_row(ArcRefCell::new(TableTrack {
- base: LayoutBoxBase::new((&anonymous_info).into(), style),
+ base: LayoutBoxBase::new((&anonymous_info).into(), style.clone()),
group_index: self.current_row_group_index,
is_anonymous: true,
+ shared_background_style: SharedStyle::new(style),
}));
}
@@ -745,11 +740,8 @@ where
}
}
-impl<'dom, Node: 'dom> TraversalHandler<'dom, Node> for TableBuilderTraversal<'_, 'dom, Node>
-where
- Node: NodeExt<'dom>,
-{
- fn handle_text(&mut self, info: &NodeAndStyleInfo<Node>, text: Cow<'dom, str>) {
+impl<'dom> TraversalHandler<'dom> for TableBuilderTraversal<'_, 'dom> {
+ fn handle_text(&mut self, info: &NodeAndStyleInfo<'dom>, text: Cow<'dom, str>) {
self.current_anonymous_row_content
.push(AnonymousTableContent::Text(info.clone(), text));
}
@@ -757,7 +749,7 @@ where
/// <https://html.spec.whatwg.org/multipage/#forming-a-table>
fn handle_element(
&mut self,
- info: &NodeAndStyleInfo<Node>,
+ info: &NodeAndStyleInfo<'dom>,
display: DisplayGeneratingBox,
contents: Contents,
box_slot: BoxSlot<'dom>,
@@ -775,6 +767,7 @@ where
base: LayoutBoxBase::new(info.into(), info.style.clone()),
group_type: internal.into(),
track_range: next_row_index..next_row_index,
+ shared_background_style: SharedStyle::new(info.style.clone()),
});
self.builder.table.row_groups.push(row_group.clone());
@@ -817,6 +810,7 @@ where
base: LayoutBoxBase::new(info.into(), info.style.clone()),
group_index: self.current_row_group_index,
is_anonymous: false,
+ shared_background_style: SharedStyle::new(info.style.clone()),
});
self.push_table_row(row.clone());
box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::Track(row)));
@@ -862,6 +856,7 @@ where
base: LayoutBoxBase::new(info.into(), info.style.clone()),
group_type: internal.into(),
track_range: first_column..self.builder.table.columns.len(),
+ shared_background_style: SharedStyle::new(info.style.clone()),
});
self.builder.table.column_groups.push(column_group.clone());
box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::TrackGroup(
@@ -916,26 +911,23 @@ where
}
}
-struct TableRowBuilder<'style, 'builder, 'dom, 'a, Node> {
- table_traversal: &'builder mut TableBuilderTraversal<'style, 'dom, Node>,
+struct TableRowBuilder<'style, 'builder, 'dom, 'a> {
+ table_traversal: &'builder mut TableBuilderTraversal<'style, 'dom>,
/// The [`NodeAndStyleInfo`] of this table row, which we use to
/// construct anonymous table cells.
- info: &'a NodeAndStyleInfo<Node>,
+ info: &'a NodeAndStyleInfo<'dom>,
- current_anonymous_cell_content: Vec<AnonymousTableContent<'dom, Node>>,
+ current_anonymous_cell_content: Vec<AnonymousTableContent<'dom>>,
/// The [`PropagatedBoxTreeData`] to use for all children of this row.
propagated_data: PropagatedBoxTreeData,
}
-impl<'style, 'builder, 'dom, 'a, Node: 'dom> TableRowBuilder<'style, 'builder, 'dom, 'a, Node>
-where
- Node: NodeExt<'dom>,
-{
+impl<'style, 'builder, 'dom, 'a> TableRowBuilder<'style, 'builder, 'dom, 'a> {
fn new(
- table_traversal: &'builder mut TableBuilderTraversal<'style, 'dom, Node>,
- info: &'a NodeAndStyleInfo<Node>,
+ table_traversal: &'builder mut TableBuilderTraversal<'style, 'dom>,
+ info: &'a NodeAndStyleInfo<'dom>,
propagated_data: PropagatedBoxTreeData,
) -> Self {
table_traversal.builder.start_row();
@@ -996,11 +988,8 @@ where
}
}
-impl<'dom, Node: 'dom> TraversalHandler<'dom, Node> for TableRowBuilder<'_, '_, 'dom, '_, Node>
-where
- Node: NodeExt<'dom>,
-{
- fn handle_text(&mut self, info: &NodeAndStyleInfo<Node>, text: Cow<'dom, str>) {
+impl<'dom> TraversalHandler<'dom> for TableRowBuilder<'_, '_, 'dom, '_> {
+ fn handle_text(&mut self, info: &NodeAndStyleInfo<'dom>, text: Cow<'dom, str>) {
self.current_anonymous_cell_content
.push(AnonymousTableContent::Text(info.clone(), text));
}
@@ -1008,7 +997,7 @@ where
/// <https://html.spec.whatwg.org/multipage/#algorithm-for-processing-rows>
fn handle_element(
&mut self,
- info: &NodeAndStyleInfo<Node>,
+ info: &NodeAndStyleInfo<'dom>,
display: DisplayGeneratingBox,
contents: Contents,
box_slot: BoxSlot<'dom>,
@@ -1091,14 +1080,11 @@ struct TableColumnGroupBuilder {
columns: Vec<ArcRefCell<TableTrack>>,
}
-impl<'dom, Node: 'dom> TraversalHandler<'dom, Node> for TableColumnGroupBuilder
-where
- Node: NodeExt<'dom>,
-{
- fn handle_text(&mut self, _info: &NodeAndStyleInfo<Node>, _text: Cow<'dom, str>) {}
+impl<'dom> TraversalHandler<'dom> for TableColumnGroupBuilder {
+ fn handle_text(&mut self, _info: &NodeAndStyleInfo<'dom>, _text: Cow<'dom, str>) {}
fn handle_element(
&mut self,
- info: &NodeAndStyleInfo<Node>,
+ info: &NodeAndStyleInfo<'dom>,
display: DisplayGeneratingBox,
_contents: Contents,
box_slot: BoxSlot<'dom>,
@@ -1134,9 +1120,9 @@ impl From<DisplayLayoutInternal> for TableTrackGroupType {
}
}
-fn add_column<'dom, Node: NodeExt<'dom>>(
+fn add_column(
collection: &mut Vec<ArcRefCell<TableTrack>>,
- column_info: &NodeAndStyleInfo<Node>,
+ column_info: &NodeAndStyleInfo,
group_index: Option<usize>,
is_anonymous: bool,
) -> ArcRefCell<TableTrack> {
@@ -1153,6 +1139,7 @@ fn add_column<'dom, Node: NodeExt<'dom>>(
base: LayoutBoxBase::new(column_info.into(), column_info.style.clone()),
group_index,
is_anonymous,
+ shared_background_style: SharedStyle::new(column_info.style.clone()),
});
collection.extend(repeat(column.clone()).take(span as usize));
column
diff --git a/components/layout/table/layout.rs b/components/layout/table/layout.rs
index 2efe339837e..5b7e79d7fb0 100644
--- a/components/layout/table/layout.rs
+++ b/components/layout/table/layout.rs
@@ -2063,7 +2063,7 @@ impl<'a> TableLayout<'a> {
let column_group = column_group.borrow();
let rect = make_relative_to_row_start(dimensions.get_column_group_rect(&column_group));
fragment.add_extra_background(ExtraBackground {
- style: column_group.base.style.clone(),
+ style: column_group.shared_background_style.clone(),
rect,
})
}
@@ -2072,7 +2072,7 @@ impl<'a> TableLayout<'a> {
if !column.is_anonymous {
let rect = make_relative_to_row_start(dimensions.get_column_rect(column_index));
fragment.add_extra_background(ExtraBackground {
- style: column.base.style.clone(),
+ style: column.shared_background_style.clone(),
rect,
})
}
@@ -2085,7 +2085,7 @@ impl<'a> TableLayout<'a> {
let rect =
make_relative_to_row_start(dimensions.get_row_group_rect(&row_group.borrow()));
fragment.add_extra_background(ExtraBackground {
- style: row_group.borrow().base.style.clone(),
+ style: row_group.borrow().shared_background_style.clone(),
rect,
})
}
@@ -2093,7 +2093,7 @@ impl<'a> TableLayout<'a> {
let row = row.borrow();
let rect = make_relative_to_row_start(row_fragment_layout.rect);
fragment.add_extra_background(ExtraBackground {
- style: row.base.style.clone(),
+ style: row.shared_background_style.clone(),
rect,
})
}
@@ -2867,6 +2867,7 @@ impl TableSlotCell {
block: vertical_align_offset,
};
let vertical_align_fragment = PositioningFragment::new_anonymous(
+ self.base.style.clone(),
vertical_align_fragment_rect.as_physical(None),
layout.layout.fragments,
);
diff --git a/components/layout/table/mod.rs b/components/layout/table/mod.rs
index fe7f90437b8..72b67863e7d 100644
--- a/components/layout/table/mod.rs
+++ b/components/layout/table/mod.rs
@@ -76,12 +76,16 @@ pub(crate) use construct::AnonymousTableContent;
pub use construct::TableBuilder;
use euclid::{Point2D, Size2D, UnknownUnit, Vector2D};
use malloc_size_of_derive::MallocSizeOf;
+use script::layout_dom::ServoLayoutElement;
use servo_arc::Arc;
+use style::context::SharedStyleContext;
use style::properties::ComputedValues;
use style::properties::style_structs::Font;
+use style::selector_parser::PseudoElement;
use style_traits::dom::OpaqueNode;
use super::flow::BlockFormattingContext;
+use crate::SharedStyle;
use crate::cell::ArcRefCell;
use crate::flow::BlockContainer;
use crate::formatting_contexts::IndependentFormattingContext;
@@ -98,12 +102,10 @@ pub struct Table {
/// The style of this table. These are the properties that apply to the "wrapper" ie the element
/// that contains both the grid and the captions. Not all properties are actually used on the
/// wrapper though, such as background and borders, which apply to the grid.
- #[conditional_malloc_size_of]
style: Arc<ComputedValues>,
/// The style of this table's grid. This is an anonymous style based on the table's style, but
/// eliminating all the properties handled by the "wrapper."
- #[conditional_malloc_size_of]
grid_style: Arc<ComputedValues>,
/// The [`BaseFragmentInfo`] for this table's grid. This is necessary so that when the
@@ -192,6 +194,19 @@ impl Table {
),
}
}
+
+ pub(crate) fn repair_style(
+ &mut self,
+ context: &SharedStyleContext,
+ new_style: &Arc<ComputedValues>,
+ ) {
+ self.style = new_style.clone();
+ self.grid_style = context.stylist.style_for_anonymous::<ServoLayoutElement>(
+ &context.guards,
+ &PseudoElement::ServoTableGrid,
+ new_style,
+ );
+ }
}
type TableSlotCoordinates = Point2D<usize, UnknownUnit>;
@@ -233,6 +248,10 @@ impl TableSlotCell {
pub fn node_id(&self) -> usize {
self.base.base_fragment_info.tag.map_or(0, |tag| tag.node.0)
}
+
+ fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
+ self.base.repair_style(new_style);
+ }
}
/// A single table slot. It may be an actual cell, or a reference
@@ -288,6 +307,18 @@ pub struct TableTrack {
/// Whether or not this [`TableTrack`] was anonymous, for instance created due to
/// a `span` attribute set on a parent `<colgroup>`.
is_anonymous: bool,
+
+ /// A shared container for this track's style, used to share the style for the purposes
+ /// of drawing backgrounds in individual cells. This allows updating the style in a
+ /// single place and having it affect all cell `Fragment`s.
+ shared_background_style: SharedStyle,
+}
+
+impl TableTrack {
+ fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
+ self.base.repair_style(new_style);
+ self.shared_background_style = SharedStyle::new(new_style.clone());
+ }
}
#[derive(Debug, MallocSizeOf, PartialEq)]
@@ -308,12 +339,22 @@ pub struct TableTrackGroup {
/// The range of tracks in this [`TableTrackGroup`].
track_range: Range<usize>,
+
+ /// A shared container for this track's style, used to share the style for the purposes
+ /// of drawing backgrounds in individual cells. This allows updating the style in a
+ /// single place and having it affect all cell `Fragment`s.
+ shared_background_style: SharedStyle,
}
impl TableTrackGroup {
pub(super) fn is_empty(&self) -> bool {
self.track_range.is_empty()
}
+
+ fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
+ self.base.repair_style(new_style);
+ self.shared_background_style = SharedStyle::new(new_style.clone());
+ }
}
#[derive(Debug, MallocSizeOf)]
@@ -380,4 +421,22 @@ impl TableLevelBox {
TableLevelBox::Track(track) => track.borrow().base.fragments(),
}
}
+
+ pub(crate) fn repair_style(
+ &self,
+ context: &SharedStyleContext<'_>,
+ new_style: &Arc<ComputedValues>,
+ ) {
+ match self {
+ TableLevelBox::Caption(caption) => caption
+ .borrow_mut()
+ .context
+ .repair_style(context, new_style),
+ TableLevelBox::Cell(cell) => cell.borrow_mut().repair_style(new_style),
+ TableLevelBox::TrackGroup(track_group) => {
+ track_group.borrow_mut().repair_style(new_style);
+ },
+ TableLevelBox::Track(track) => track.borrow_mut().repair_style(new_style),
+ }
+ }
}
diff --git a/components/layout/taffy/layout.rs b/components/layout/taffy/layout.rs
index a5838c1bd65..61c4a0508e9 100644
--- a/components/layout/taffy/layout.rs
+++ b/components/layout/taffy/layout.rs
@@ -23,8 +23,8 @@ use crate::fragment_tree::{
BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags, SpecificLayoutInfo,
};
use crate::geom::{
- LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, Size, SizeConstraint,
- Sizes,
+ LazySize, LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, Size,
+ SizeConstraint, Sizes,
};
use crate::layout_box_base::CacheableLayoutResult;
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
@@ -251,6 +251,12 @@ impl taffy::LayoutPartialTree for TaffyContainerContext<'_> {
style,
};
+ let lazy_block_size = match content_box_known_dimensions.height {
+ // FIXME: use the correct min/max sizes.
+ None => LazySize::intrinsic(),
+ Some(height) => Au::from_f32_px(height).into(),
+ };
+
child.positioning_context = PositioningContext::default();
let layout = non_replaced.layout_without_caching(
self.layout_context,
@@ -258,13 +264,16 @@ impl taffy::LayoutPartialTree for TaffyContainerContext<'_> {
&content_box_size_override,
containing_block,
false, /* depends_on_block_constraints */
+ &lazy_block_size,
);
child.child_fragments = layout.fragments;
self.child_specific_layout_infos[usize::from(node_id)] =
layout.specific_layout_info;
- let block_size = layout.content_block_size.to_f32_px();
+ let block_size = lazy_block_size
+ .resolve(|| layout.content_block_size)
+ .to_f32_px();
let computed_size = taffy::Size {
width: inline_size + pb_sum.inline,
diff --git a/components/layout/taffy/mod.rs b/components/layout/taffy/mod.rs
index b1ff753ea78..ba80824fa99 100644
--- a/components/layout/taffy/mod.rs
+++ b/components/layout/taffy/mod.rs
@@ -8,6 +8,7 @@ use std::fmt;
use app_units::Au;
use malloc_size_of_derive::MallocSizeOf;
use servo_arc::Arc;
+use style::context::SharedStyleContext;
use style::properties::ComputedValues;
use stylo_taffy::TaffyStyloStyle;
@@ -15,7 +16,7 @@ use crate::PropagatedBoxTreeData;
use crate::cell::ArcRefCell;
use crate::construct_modern::{ModernContainerBuilder, ModernItemKind};
use crate::context::LayoutContext;
-use crate::dom::{LayoutBox, NodeExt};
+use crate::dom::LayoutBox;
use crate::dom_traversal::{NodeAndStyleInfo, NonReplacedContents};
use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragment_tree::Fragment;
@@ -24,14 +25,13 @@ use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
#[derive(Debug, MallocSizeOf)]
pub(crate) struct TaffyContainer {
children: Vec<ArcRefCell<TaffyItemBox>>,
- #[conditional_malloc_size_of]
style: Arc<ComputedValues>,
}
impl TaffyContainer {
- pub fn construct<'dom>(
+ pub fn construct(
context: &LayoutContext,
- info: &NodeAndStyleInfo<impl NodeExt<'dom>>,
+ info: &NodeAndStyleInfo,
contents: NonReplacedContents,
propagated_data: PropagatedBoxTreeData,
) -> Self {
@@ -69,6 +69,10 @@ impl TaffyContainer {
style: info.style.clone(),
}
}
+
+ pub(crate) fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
+ self.style = new_style.clone();
+ }
}
#[derive(MallocSizeOf)]
@@ -76,7 +80,6 @@ pub(crate) struct TaffyItemBox {
pub(crate) taffy_layout: taffy::Layout,
pub(crate) child_fragments: Vec<Fragment>,
pub(crate) positioning_context: PositioningContext,
- #[conditional_malloc_size_of]
pub(crate) style: Arc<ComputedValues>,
pub(crate) taffy_level_box: TaffyItemBoxInner,
}
@@ -145,6 +148,23 @@ impl TaffyItemBox {
},
}
}
+
+ pub(crate) fn repair_style(
+ &mut self,
+ context: &SharedStyleContext,
+ new_style: &Arc<ComputedValues>,
+ ) {
+ self.style = new_style.clone();
+ match &mut self.taffy_level_box {
+ TaffyItemBoxInner::InFlowBox(independent_formatting_context) => {
+ independent_formatting_context.repair_style(context, new_style)
+ },
+ TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box
+ .borrow_mut()
+ .context
+ .repair_style(context, new_style),
+ }
+ }
}
/// Details from Taffy grid layout that will be stored
diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs
index bf60c41d6ba..17c3d0b1c20 100644
--- a/components/layout/traversal.rs
+++ b/components/layout/traversal.rs
@@ -2,14 +2,18 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+use script::layout_dom::ServoLayoutNode;
use script_layout_interface::wrapper_traits::LayoutNode;
use style::context::{SharedStyleContext, StyleContext};
use style::data::ElementData;
use style::dom::{NodeInfo, TElement, TNode};
+use style::selector_parser::RestyleDamage;
use style::traversal::{DomTraversal, PerLevelTraversalData, recalc_style_at};
+use style::values::computed::Display;
use crate::context::LayoutContext;
-use crate::dom::DOMLayoutData;
+use crate::dom::{DOMLayoutData, NodeExt};
+use crate::dom_traversal::iter_child_nodes;
pub struct RecalcStyle<'a> {
context: &'a LayoutContext<'a>,
@@ -40,14 +44,33 @@ where
) where
F: FnMut(E::ConcreteNode),
{
+ if node.is_text_node() {
+ return;
+ }
+
+ let had_style_data = node.style_data().is_some();
unsafe {
node.initialize_style_and_layout_data::<DOMLayoutData>();
- if !node.is_text_node() {
- let el = node.as_element().unwrap();
- let mut data = el.mutate_data().unwrap();
- recalc_style_at(self, traversal_data, context, el, &mut data, note_child);
- el.unset_dirty_descendants();
- }
+ }
+
+ let element = node.as_element().unwrap();
+ let mut element_data = element.mutate_data().unwrap();
+
+ if !had_style_data {
+ element_data.damage = RestyleDamage::reconstruct();
+ }
+
+ recalc_style_at(
+ self,
+ traversal_data,
+ context,
+ element,
+ &mut element_data,
+ note_child,
+ );
+
+ unsafe {
+ element.unset_dirty_descendants();
}
}
@@ -68,3 +91,48 @@ where
&self.context.style_context
}
}
+
+pub(crate) fn compute_damage_and_repair_style(
+ context: &SharedStyleContext,
+ node: ServoLayoutNode<'_>,
+) -> RestyleDamage {
+ compute_damage_and_repair_style_inner(context, node, RestyleDamage::empty())
+}
+
+pub(crate) fn compute_damage_and_repair_style_inner(
+ context: &SharedStyleContext,
+ node: ServoLayoutNode<'_>,
+ parent_restyle_damage: RestyleDamage,
+) -> RestyleDamage {
+ let original_damage;
+ let damage = {
+ let mut element_data = node
+ .style_data()
+ .expect("Should not run `compute_damage` before styling.")
+ .element_data
+ .borrow_mut();
+
+ if let Some(ref style) = element_data.styles.primary {
+ if style.get_box().display == Display::None {
+ return parent_restyle_damage;
+ }
+ }
+
+ original_damage = std::mem::take(&mut element_data.damage);
+ element_data.damage |= parent_restyle_damage;
+ element_data.damage
+ };
+
+ let mut propagated_damage = damage;
+ for child in iter_child_nodes(node) {
+ if child.is_element() {
+ propagated_damage |= compute_damage_and_repair_style_inner(context, child, damage);
+ }
+ }
+
+ if propagated_damage == RestyleDamage::REPAINT && original_damage == RestyleDamage::REPAINT {
+ node.repair_style(context);
+ }
+
+ propagated_damage
+}
diff --git a/components/malloc_size_of/Cargo.toml b/components/malloc_size_of/Cargo.toml
index f6f25075ed6..a24bc70a211 100644
--- a/components/malloc_size_of/Cargo.toml
+++ b/components/malloc_size_of/Cargo.toml
@@ -35,6 +35,7 @@ tokio = { workspace = true, features = ["sync"] }
unicode-bidi = { workspace = true }
unicode-script = { workspace = true }
url = { workspace = true }
+urlpattern = { workspace = true }
uuid = { workspace = true }
webrender_api = { workspace = true }
wr_malloc_size_of = { workspace = true }
diff --git a/components/malloc_size_of/lib.rs b/components/malloc_size_of/lib.rs
index 52523af7cb1..ae951da97e5 100644
--- a/components/malloc_size_of/lib.rs
+++ b/components/malloc_size_of/lib.rs
@@ -50,8 +50,10 @@ use std::cell::OnceCell;
use std::collections::BinaryHeap;
use std::hash::{BuildHasher, Hash};
use std::ops::Range;
+use std::rc::Rc;
use std::sync::Arc;
+use style::properties::ComputedValues;
use style::values::generics::length::GenericLengthPercentageOrAuto;
pub use stylo_malloc_size_of::MallocSizeOfOps;
use uuid::Uuid;
@@ -577,6 +579,28 @@ impl<T: MallocSizeOf> MallocConditionalSizeOf for Arc<T> {
}
}
+impl<T> MallocUnconditionalShallowSizeOf for Rc<T> {
+ fn unconditional_shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
+ unsafe { ops.malloc_size_of(Rc::as_ptr(self)) }
+ }
+}
+
+impl<T: MallocSizeOf> MallocUnconditionalSizeOf for Rc<T> {
+ fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
+ self.unconditional_shallow_size_of(ops) + (**self).size_of(ops)
+ }
+}
+
+impl<T: MallocSizeOf> MallocConditionalSizeOf for Rc<T> {
+ fn conditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
+ if ops.have_seen_ptr(Rc::as_ptr(self)) {
+ 0
+ } else {
+ self.unconditional_size_of(ops)
+ }
+ }
+}
+
/// If a mutex is stored directly as a member of a data type that is being measured,
/// it is the unique owner of its contents and deserves to be measured.
///
@@ -709,6 +733,12 @@ impl<T> MallocSizeOf for ipc_channel::ipc::IpcSender<T> {
}
}
+impl<T> MallocSizeOf for ipc_channel::ipc::IpcReceiver<T> {
+ fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
+ 0
+ }
+}
+
impl MallocSizeOf for ipc_channel::ipc::IpcSharedMemory {
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
self.len()
@@ -721,6 +751,12 @@ impl<T: MallocSizeOf> MallocSizeOf for accountable_refcell::RefCell<T> {
}
}
+impl MallocSizeOf for servo_arc::Arc<ComputedValues> {
+ fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
+ self.conditional_size_of(ops)
+ }
+}
+
malloc_size_of_hash_map!(indexmap::IndexMap<K, V, S>);
malloc_size_of_hash_set!(indexmap::IndexSet<T, S>);
@@ -749,10 +785,14 @@ malloc_size_of_is_0!(std::time::SystemTime);
malloc_size_of_is_0!(style::data::ElementData);
malloc_size_of_is_0!(style::font_face::SourceList);
malloc_size_of_is_0!(style::properties::ComputedValues);
+malloc_size_of_is_0!(style::properties::declaration_block::PropertyDeclarationBlock);
malloc_size_of_is_0!(style::queries::values::PrefersColorScheme);
+malloc_size_of_is_0!(style::stylesheets::Stylesheet);
+malloc_size_of_is_0!(style::values::specified::source_size_list::SourceSizeList);
malloc_size_of_is_0!(taffy::Layout);
malloc_size_of_is_0!(unicode_bidi::Level);
malloc_size_of_is_0!(unicode_script::Script);
+malloc_size_of_is_0!(urlpattern::UrlPattern);
macro_rules! malloc_size_of_is_webrender_malloc_size_of(
($($ty:ty),+) => (
@@ -772,6 +812,7 @@ malloc_size_of_is_webrender_malloc_size_of!(webrender_api::BorderStyle);
malloc_size_of_is_webrender_malloc_size_of!(webrender_api::BoxShadowClipMode);
malloc_size_of_is_webrender_malloc_size_of!(webrender_api::ColorF);
malloc_size_of_is_webrender_malloc_size_of!(webrender_api::ExtendMode);
+malloc_size_of_is_webrender_malloc_size_of!(webrender_api::FontKey);
malloc_size_of_is_webrender_malloc_size_of!(webrender_api::FontInstanceKey);
malloc_size_of_is_webrender_malloc_size_of!(webrender_api::GlyphInstance);
malloc_size_of_is_webrender_malloc_size_of!(webrender_api::GradientStop);
@@ -816,6 +857,14 @@ where
}
}
+impl<T> MallocSizeOf for style::shared_lock::Locked<T> {
+ fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
+ // TODO: fix this implementation when Locked derives MallocSizeOf.
+ 0
+ //<style::shared_lock::Locked<T> as stylo_malloc_size_of::MallocSizeOf>::size_of(self, ops)
+ }
+}
+
impl<T: MallocSizeOf> MallocSizeOf for atomic_refcell::AtomicRefCell<T> {
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
self.borrow().size_of(ops)
diff --git a/components/net/hsts.rs b/components/net/hsts.rs
index d74794ce60a..be955980d2b 100644
--- a/components/net/hsts.rs
+++ b/components/net/hsts.rs
@@ -4,26 +4,37 @@
use std::collections::HashMap;
use std::net::{Ipv4Addr, Ipv6Addr};
+use std::num::NonZeroU64;
+use std::sync::LazyLock;
use std::time::Duration;
-use base::cross_process_instant::CrossProcessInstant;
use embedder_traits::resources::{self, Resource};
use headers::{HeaderMapExt, StrictTransportSecurity};
use http::HeaderMap;
-use log::{error, info};
+use log::{debug, error, info};
use malloc_size_of_derive::MallocSizeOf;
use net_traits::IncludeSubdomains;
use net_traits::pub_domains::reg_suffix;
use serde::{Deserialize, Serialize};
use servo_config::pref;
use servo_url::{Host, ServoUrl};
+use time::UtcDateTime;
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
pub struct HstsEntry {
pub host: String,
pub include_subdomains: bool,
- pub max_age: Option<Duration>,
- pub timestamp: Option<CrossProcessInstant>,
+ // Nonzero to allow for memory optimization
+ pub expires_at: Option<NonZeroU64>,
+}
+
+// Zero and negative times are all expired
+fn unix_timestamp_to_nonzerou64(timestamp: i64) -> NonZeroU64 {
+ if timestamp <= 0 {
+ NonZeroU64::new(1).unwrap()
+ } else {
+ NonZeroU64::new(timestamp.try_into().unwrap()).unwrap()
+ }
}
impl HstsEntry {
@@ -32,43 +43,59 @@ impl HstsEntry {
subdomains: IncludeSubdomains,
max_age: Option<Duration>,
) -> Option<HstsEntry> {
+ let expires_at = max_age.map(|duration| {
+ unix_timestamp_to_nonzerou64((UtcDateTime::now() + duration).unix_timestamp())
+ });
if host.parse::<Ipv4Addr>().is_ok() || host.parse::<Ipv6Addr>().is_ok() {
None
} else {
Some(HstsEntry {
host,
include_subdomains: (subdomains == IncludeSubdomains::Included),
- max_age,
- timestamp: Some(CrossProcessInstant::now()),
+ expires_at,
})
}
}
pub fn is_expired(&self) -> bool {
- match (self.max_age, self.timestamp) {
- (Some(max_age), Some(timestamp)) => CrossProcessInstant::now() - timestamp >= max_age,
-
+ match self.expires_at {
+ Some(timestamp) => {
+ unix_timestamp_to_nonzerou64(UtcDateTime::now().unix_timestamp()) >= timestamp
+ },
_ => false,
}
}
fn matches_domain(&self, host: &str) -> bool {
- !self.is_expired() && self.host == host
+ self.host == host
}
fn matches_subdomain(&self, host: &str) -> bool {
- !self.is_expired() && host.ends_with(&format!(".{}", self.host))
+ host.ends_with(&format!(".{}", self.host))
}
}
#[derive(Clone, Debug, Default, Deserialize, MallocSizeOf, Serialize)]
pub struct HstsList {
+ // Map from base domains to a list of entries that are subdomains of base domain
pub entries_map: HashMap<String, Vec<HstsEntry>>,
}
-impl HstsList {
+/// Represents the portion of the HSTS list that comes from the preload list
+/// it is split out to allow sharing between the private and public http state
+/// as well as potentially swpaping out the underlying type to something immutable
+/// and more efficient like FSTs or DAFSA/DAWGs.
+#[derive(Clone, Debug, Default, Deserialize, MallocSizeOf, Serialize)]
+pub struct HstsPreloadList {
+ pub entries_map: HashMap<String, Vec<HstsEntry>>,
+}
+
+pub static PRELOAD_LIST_ENTRIES: LazyLock<HstsPreloadList> =
+ LazyLock::new(HstsPreloadList::from_servo_preload);
+
+impl HstsPreloadList {
/// Create an `HstsList` from the bytes of a JSON preload file.
- pub fn from_preload(preload_content: &str) -> Option<HstsList> {
+ pub fn from_preload(preload_content: &str) -> Option<HstsPreloadList> {
#[derive(Deserialize)]
struct HstsEntries {
entries: Vec<HstsEntry>,
@@ -77,7 +104,7 @@ impl HstsList {
let hsts_entries: Option<HstsEntries> = serde_json::from_str(preload_content).ok();
hsts_entries.map(|hsts_entries| {
- let mut hsts_list: HstsList = HstsList::default();
+ let mut hsts_list: HstsPreloadList = HstsPreloadList::default();
for hsts_entry in hsts_entries.entries {
hsts_list.push(hsts_entry);
@@ -87,17 +114,21 @@ impl HstsList {
})
}
- pub fn from_servo_preload() -> HstsList {
+ pub fn from_servo_preload() -> HstsPreloadList {
+ debug!("Intializing HSTS Preload list");
let list = resources::read_string(Resource::HstsPreloadList);
- HstsList::from_preload(&list).unwrap_or_else(|| {
+ HstsPreloadList::from_preload(&list).unwrap_or_else(|| {
error!("HSTS preload file is invalid. Setting HSTS list to default values");
- HstsList::default()
+ HstsPreloadList {
+ entries_map: Default::default(),
+ }
})
}
pub fn is_host_secure(&self, host: &str) -> bool {
let base_domain = reg_suffix(host);
self.entries_map.get(base_domain).is_some_and(|entries| {
+ // No need to check for expiration in the preload list
entries.iter().any(|e| {
if e.include_subdomains {
e.matches_subdomain(host) || e.matches_domain(host)
@@ -108,16 +139,74 @@ impl HstsList {
})
}
- fn has_domain(&self, host: &str, base_domain: &str) -> bool {
+ pub fn has_domain(&self, host: &str, base_domain: &str) -> bool {
self.entries_map
.get(base_domain)
.is_some_and(|entries| entries.iter().any(|e| e.matches_domain(host)))
}
- fn has_subdomain(&self, host: &str, base_domain: &str) -> bool {
+ pub fn has_subdomain(&self, host: &str, base_domain: &str) -> bool {
+ self.entries_map.get(base_domain).is_some_and(|entries| {
+ entries
+ .iter()
+ .any(|e| e.include_subdomains && e.matches_subdomain(host))
+ })
+ }
+
+ pub fn push(&mut self, entry: HstsEntry) {
+ let host = entry.host.clone();
+ let base_domain = reg_suffix(&host);
+ let have_domain = self.has_domain(&entry.host, base_domain);
+ let have_subdomain = self.has_subdomain(&entry.host, base_domain);
+
+ let entries = self.entries_map.entry(base_domain.to_owned()).or_default();
+ if !have_domain && !have_subdomain {
+ entries.push(entry);
+ } else if !have_subdomain {
+ for e in entries {
+ if e.matches_domain(&entry.host) {
+ e.include_subdomains = entry.include_subdomains;
+ // TODO(sebsebmc): We could shrink the the HSTS preload memory use further by using a type
+ // that doesn't store an expiry since all preload entries should be "forever"
+ e.expires_at = entry.expires_at;
+ }
+ }
+ }
+ }
+}
+
+impl HstsList {
+ pub fn is_host_secure(&self, host: &str) -> bool {
+ debug!("HSTS: is {host} secure?");
+ if PRELOAD_LIST_ENTRIES.is_host_secure(host) {
+ info!("{host} is in the preload list");
+ return true;
+ }
+
+ let base_domain = reg_suffix(host);
+ self.entries_map.get(base_domain).is_some_and(|entries| {
+ entries.iter().filter(|e| !e.is_expired()).any(|e| {
+ if e.include_subdomains {
+ e.matches_subdomain(host) || e.matches_domain(host)
+ } else {
+ e.matches_domain(host)
+ }
+ })
+ })
+ }
+
+ fn has_domain(&self, host: &str, base_domain: &str) -> bool {
self.entries_map
.get(base_domain)
- .is_some_and(|entries| entries.iter().any(|e| e.matches_subdomain(host)))
+ .is_some_and(|entries| entries.iter().any(|e| e.matches_domain(host)))
+ }
+
+ fn has_subdomain(&self, host: &str, base_domain: &str) -> bool {
+ self.entries_map.get(base_domain).is_some_and(|entries| {
+ entries
+ .iter()
+ .any(|e| e.include_subdomains && e.matches_subdomain(host))
+ })
}
pub fn push(&mut self, entry: HstsEntry) {
@@ -130,13 +219,14 @@ impl HstsList {
if !have_domain && !have_subdomain {
entries.push(entry);
} else if !have_subdomain {
- for e in entries {
+ for e in entries.iter_mut() {
if e.matches_domain(&entry.host) {
e.include_subdomains = entry.include_subdomains;
- e.max_age = entry.max_age;
+ e.expires_at = entry.expires_at;
}
}
}
+ entries.retain(|e| !e.is_expired());
}
/// Step 2.9 of <https://fetch.spec.whatwg.org/#concept-main-fetch>.
diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs
index e0867b8d07f..704901f6940 100644
--- a/components/net/http_loader.rs
+++ b/components/net/http_loader.rs
@@ -37,7 +37,7 @@ use hyper::ext::ReasonPhrase;
use hyper::header::{HeaderName, TRANSFER_ENCODING};
use hyper_serde::Serde;
use hyper_util::client::legacy::Client;
-use ipc_channel::ipc::{self, IpcSender};
+use ipc_channel::ipc::{self, IpcSender, IpcSharedMemory};
use ipc_channel::router::ROUTER;
use log::{debug, error, info, log_enabled, warn};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
@@ -462,7 +462,7 @@ fn auth_from_cache(
/// used to fill the body with bytes coming-in over IPC.
enum BodyChunk {
/// A chunk of bytes.
- Chunk(Vec<u8>),
+ Chunk(IpcSharedMemory),
/// Body is done.
Done,
}
@@ -489,12 +489,14 @@ enum BodySink {
}
impl BodySink {
- fn transmit_bytes(&self, bytes: Vec<u8>) {
+ fn transmit_bytes(&self, bytes: IpcSharedMemory) {
match self {
BodySink::Chunked(sender) => {
let sender = sender.clone();
HANDLE.spawn(async move {
- let _ = sender.send(Ok(Frame::data(bytes.into()))).await;
+ let _ = sender
+ .send(Ok(Frame::data(Bytes::copy_from_slice(&bytes))))
+ .await;
});
},
BodySink::Buffered(sender) => {
@@ -577,7 +579,7 @@ async fn obtain_response(
body_port,
Box::new(move |message| {
info!("Received message");
- let bytes: Vec<u8> = match message.unwrap() {
+ let bytes = match message.unwrap() {
BodyChunkResponse::Chunk(bytes) => bytes,
BodyChunkResponse::Done => {
// Step 3, abort these parallel steps.
@@ -622,8 +624,8 @@ async fn obtain_response(
let mut body = vec![];
loop {
match receiver.recv().await {
- Some(BodyChunk::Chunk(mut bytes)) => {
- body.append(&mut bytes);
+ Some(BodyChunk::Chunk(bytes)) => {
+ body.extend_from_slice(&bytes);
},
Some(BodyChunk::Done) => break,
None => warn!("Failed to read all chunks from request body."),
diff --git a/components/net/image_cache.rs b/components/net/image_cache.rs
index e3d31b11736..46a2a4ea111 100644
--- a/components/net/image_cache.rs
+++ b/components/net/image_cache.rs
@@ -7,7 +7,7 @@ use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::sync::{Arc, Mutex};
use std::{mem, thread};
-use compositing_traits::{CrossProcessCompositorApi, SerializableImageData};
+use compositing_traits::{CrossProcessCompositorApi, ImageUpdate, SerializableImageData};
use imsz::imsz_from_reader;
use ipc_channel::ipc::IpcSharedMemory;
use log::{debug, warn};
@@ -675,6 +675,20 @@ impl ImageCache for ImageCacheImpl {
}
}
+impl Drop for ImageCacheStore {
+ fn drop(&mut self) {
+ let image_updates = self
+ .completed_loads
+ .values()
+ .filter_map(|load| match &load.image_response {
+ ImageResponse::Loaded(image, _) => image.id.map(ImageUpdate::DeleteImage),
+ _ => None,
+ })
+ .collect();
+ self.compositor_api.update_images(image_updates);
+ }
+}
+
impl ImageCacheImpl {
/// Require self.store.lock() before calling.
fn add_listener_with_store(&self, store: &mut ImageCacheStore, listener: ImageResponder) {
diff --git a/components/net/resource_thread.rs b/components/net/resource_thread.rs
index 5d1ede28c32..d361d63f44a 100644
--- a/components/net/resource_thread.rs
+++ b/components/net/resource_thread.rs
@@ -21,6 +21,7 @@ use embedder_traits::EmbedderProxy;
use hyper_serde::Serde;
use ipc_channel::ipc::{self, IpcReceiver, IpcReceiverSet, IpcSender};
use log::{debug, trace, warn};
+use malloc_size_of::MallocSizeOf;
use net_traits::blob_url_store::parse_blob_url;
use net_traits::filemanager_thread::FileTokenCheck;
use net_traits::request::{Destination, RequestBuilder, RequestId};
@@ -32,8 +33,10 @@ use net_traits::{
WebSocketDomAction, WebSocketNetworkEvent,
};
use profile_traits::mem::{
- ProcessReports, ProfilerChan as MemProfilerChan, ReportsChan, perform_memory_report,
+ ProcessReports, ProfilerChan as MemProfilerChan, Report, ReportKind, ReportsChan,
+ perform_memory_report,
};
+use profile_traits::path;
use profile_traits::time::ProfilerChan;
use rustls::RootCertStore;
use serde::{Deserialize, Serialize};
@@ -50,7 +53,7 @@ use crate::fetch::cors_cache::CorsCache;
use crate::fetch::fetch_params::FetchParams;
use crate::fetch::methods::{CancellationListener, FetchContext, fetch};
use crate::filemanager_thread::FileManager;
-use crate::hsts::HstsList;
+use crate::hsts::{self, HstsList};
use crate::http_cache::HttpCache;
use crate::http_loader::{HttpState, http_redirect_fetch};
use crate::protocols::ProtocolRegistry;
@@ -176,7 +179,7 @@ fn create_http_states(
ignore_certificate_errors: bool,
embedder_proxy: EmbedderProxy,
) -> (Arc<HttpState>, Arc<HttpState>) {
- let mut hsts_list = HstsList::from_servo_preload();
+ let mut hsts_list = HstsList::default();
let mut auth_cache = AuthCache::default();
let http_cache = HttpCache::default();
let mut cookie_jar = CookieStorage::new(150);
@@ -205,7 +208,7 @@ fn create_http_states(
let override_manager = CertificateErrorOverrideManager::new();
let private_http_state = HttpState {
- hsts_list: RwLock::new(HstsList::from_servo_preload()),
+ hsts_list: RwLock::new(HstsList::default()),
cookie_jar: RwLock::new(CookieStorage::new(150)),
auth_cache: RwLock::new(AuthCache::default()),
history_states: RwLock::new(HashMap::new()),
@@ -284,6 +287,11 @@ impl ResourceChannelManager {
perform_memory_report(|ops| {
let mut reports = public_http_state.memory_reports("public", ops);
reports.extend(private_http_state.memory_reports("private", ops));
+ reports.push(Report {
+ path: path!["hsts-preload-list"],
+ kind: ReportKind::ExplicitJemallocHeapSize,
+ size: hsts::PRELOAD_LIST_ENTRIES.size_of(ops),
+ });
msg.send(ProcessReports::new(reports));
})
}
diff --git a/components/net/tests/hsts.rs b/components/net/tests/hsts.rs
index 863cbc56fe1..e1e754beb3c 100644
--- a/components/net/tests/hsts.rs
+++ b/components/net/tests/hsts.rs
@@ -3,32 +3,18 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::collections::HashMap;
+use std::num::NonZeroU64;
use std::time::Duration as StdDuration;
-use base::cross_process_instant::CrossProcessInstant;
-use net::hsts::{HstsEntry, HstsList};
+use net::hsts::{HstsEntry, HstsList, HstsPreloadList};
use net_traits::IncludeSubdomains;
-use time::Duration;
#[test]
-fn test_hsts_entry_is_not_expired_when_it_has_no_timestamp() {
+fn test_hsts_entry_is_not_expired_when_it_has_no_expires_at() {
let entry = HstsEntry {
host: "mozilla.org".to_owned(),
include_subdomains: false,
- max_age: Some(StdDuration::from_secs(20)),
- timestamp: None,
- };
-
- assert!(!entry.is_expired());
-}
-
-#[test]
-fn test_hsts_entry_is_not_expired_when_it_has_no_max_age() {
- let entry = HstsEntry {
- host: "mozilla.org".to_owned(),
- include_subdomains: false,
- max_age: None,
- timestamp: Some(CrossProcessInstant::now()),
+ expires_at: None,
};
assert!(!entry.is_expired());
@@ -39,8 +25,7 @@ fn test_hsts_entry_is_expired_when_it_has_reached_its_max_age() {
let entry = HstsEntry {
host: "mozilla.org".to_owned(),
include_subdomains: false,
- max_age: Some(StdDuration::from_secs(10)),
- timestamp: Some(CrossProcessInstant::now() - Duration::seconds(20)),
+ expires_at: Some(NonZeroU64::new(1).unwrap()),
};
assert!(entry.is_expired());
@@ -102,7 +87,7 @@ fn test_base_domain_in_entries_map() {
}
#[test]
-fn test_push_entry_with_0_max_age_evicts_entry_from_list() {
+fn test_push_entry_with_0_max_age_is_not_secure() {
let mut entries_map = HashMap::new();
entries_map.insert(
"mozilla.org".to_owned(),
@@ -131,6 +116,36 @@ fn test_push_entry_with_0_max_age_evicts_entry_from_list() {
assert_eq!(list.is_host_secure("mozilla.org"), false)
}
+fn test_push_entry_with_0_max_age_evicts_entry_from_list() {
+ let mut entries_map = HashMap::new();
+ entries_map.insert(
+ "mozilla.org".to_owned(),
+ vec![
+ HstsEntry::new(
+ "mozilla.org".to_owned(),
+ IncludeSubdomains::NotIncluded,
+ Some(StdDuration::from_secs(500000)),
+ )
+ .unwrap(),
+ ],
+ );
+ let mut list = HstsList {
+ entries_map: entries_map,
+ };
+
+ assert_eq!(list.entries_map.get("mozilla.org").unwrap().len(), 1);
+
+ list.push(
+ HstsEntry::new(
+ "mozilla.org".to_owned(),
+ IncludeSubdomains::NotIncluded,
+ Some(StdDuration::ZERO),
+ )
+ .unwrap(),
+ );
+ assert_eq!(list.entries_map.get("mozilla.org").unwrap().len(), 0);
+}
+
#[test]
fn test_push_entry_to_hsts_list_should_not_add_subdomains_whose_superdomain_is_already_matched() {
let mut entries_map = HashMap::new();
@@ -155,6 +170,36 @@ fn test_push_entry_to_hsts_list_should_not_add_subdomains_whose_superdomain_is_a
}
#[test]
+fn test_push_entry_to_hsts_list_should_add_subdomains_whose_superdomain_doesnt_include() {
+ let mut entries_map = HashMap::new();
+ entries_map.insert(
+ "mozilla.org".to_owned(),
+ vec![
+ HstsEntry::new(
+ "mozilla.org".to_owned(),
+ IncludeSubdomains::NotIncluded,
+ None,
+ )
+ .unwrap(),
+ ],
+ );
+ let mut list = HstsList {
+ entries_map: entries_map,
+ };
+
+ list.push(
+ HstsEntry::new(
+ "servo.mozilla.org".to_owned(),
+ IncludeSubdomains::NotIncluded,
+ None,
+ )
+ .unwrap(),
+ );
+
+ assert_eq!(list.entries_map.get("mozilla.org").unwrap().len(), 2)
+}
+
+#[test]
fn test_push_entry_to_hsts_list_should_update_existing_domain_entrys_include_subdomains() {
let mut entries_map = HashMap::new();
entries_map.insert(
@@ -244,7 +289,7 @@ fn test_push_entry_to_hsts_list_should_add_an_entry() {
fn test_parse_hsts_preload_should_return_none_when_json_invalid() {
let mock_preload_content = "derp";
assert!(
- HstsList::from_preload(mock_preload_content).is_none(),
+ HstsPreloadList::from_preload(mock_preload_content).is_none(),
"invalid preload list should not have parsed"
)
}
@@ -253,7 +298,7 @@ fn test_parse_hsts_preload_should_return_none_when_json_invalid() {
fn test_parse_hsts_preload_should_return_none_when_json_contains_no_entries_map_key() {
let mock_preload_content = "{\"nothing\": \"to see here\"}";
assert!(
- HstsList::from_preload(mock_preload_content).is_none(),
+ HstsPreloadList::from_preload(mock_preload_content).is_none(),
"invalid preload list should not have parsed"
)
}
@@ -266,7 +311,7 @@ fn test_parse_hsts_preload_should_decode_host_and_includes_subdomains() {
\"include_subdomains\": false}\
]\
}";
- let hsts_list = HstsList::from_preload(mock_preload_content);
+ let hsts_list = HstsPreloadList::from_preload(mock_preload_content);
let entries_map = hsts_list.unwrap().entries_map;
assert_eq!(
@@ -378,8 +423,7 @@ fn test_hsts_list_with_expired_entry_is_not_is_host_secure() {
vec![HstsEntry {
host: "mozilla.org".to_owned(),
include_subdomains: false,
- max_age: Some(StdDuration::from_secs(20)),
- timestamp: Some(CrossProcessInstant::now() - Duration::seconds(100)),
+ expires_at: Some(NonZeroU64::new(1).unwrap()),
}],
);
let hsts_list = HstsList {
@@ -391,6 +435,6 @@ fn test_hsts_list_with_expired_entry_is_not_is_host_secure() {
#[test]
fn test_preload_hsts_domains_well_formed() {
- let hsts_list = HstsList::from_servo_preload();
+ let hsts_list = HstsPreloadList::from_servo_preload();
assert!(!hsts_list.entries_map.is_empty());
}
diff --git a/components/net/tests/http_loader.rs b/components/net/tests/http_loader.rs
index 1fc2d1b662d..b1e90276472 100644
--- a/components/net/tests/http_loader.rs
+++ b/components/net/tests/http_loader.rs
@@ -31,7 +31,7 @@ use http::{HeaderName, Method, StatusCode};
use http_body_util::combinators::BoxBody;
use hyper::body::{Body, Bytes, Incoming};
use hyper::{Request as HyperRequest, Response as HyperResponse};
-use ipc_channel::ipc;
+use ipc_channel::ipc::{self, IpcSharedMemory};
use ipc_channel::router::ROUTER;
use net::cookie::ServoCookie;
use net::cookie_storage::CookieStorage;
@@ -100,7 +100,7 @@ pub fn expect_devtools_http_response(
}
}
-fn create_request_body_with_content(content: Vec<u8>) -> RequestBody {
+fn create_request_body_with_content(content: IpcSharedMemory) -> RequestBody {
let content_len = content.len();
let (chunk_request_sender, chunk_request_receiver) = ipc::channel().unwrap();
@@ -592,7 +592,7 @@ fn test_load_doesnt_send_request_body_on_any_redirect() {
let (pre_server, pre_url) = make_server(pre_handler);
let content = b"Body on POST!";
- let request_body = create_request_body_with_content(content.to_vec());
+ let request_body = create_request_body_with_content(IpcSharedMemory::from_bytes(content));
let request = RequestBuilder::new(None, pre_url.clone(), Referrer::NoReferrer)
.body(Some(request_body))
@@ -904,7 +904,7 @@ fn test_load_sets_content_length_to_length_of_request_body() {
};
let (server, url) = make_server(handler);
- let request_body = create_request_body_with_content(content.to_vec());
+ let request_body = create_request_body_with_content(IpcSharedMemory::from_bytes(content));
let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer)
.method(Method::POST)
diff --git a/components/pixels/lib.rs b/components/pixels/lib.rs
index 24386ff830a..c1f57875c6d 100644
--- a/components/pixels/lib.rs
+++ b/components/pixels/lib.rs
@@ -294,9 +294,10 @@ pub fn generic_transform_inplace<
match MULTIPLY {
1 => {
let a = rgba[3];
- multiply_u8_color(rgba[0], a);
- multiply_u8_color(rgba[1], a);
- multiply_u8_color(rgba[2], a);
+
+ rgba[0] = multiply_u8_color(rgba[0], a);
+ rgba[1] = multiply_u8_color(rgba[1], a);
+ rgba[2] = multiply_u8_color(rgba[2], a);
},
2 => {
let a = rgba[3] as u32;
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml
index 1aa821cdbd3..ea1ec52f911 100644
--- a/components/script/Cargo.toml
+++ b/components/script/Cargo.toml
@@ -132,6 +132,7 @@ tracing = { workspace = true, optional = true }
unicode-bidi = { workspace = true }
unicode-segmentation = { workspace = true }
url = { workspace = true }
+urlpattern = { workspace = true }
utf-8 = "0.7"
uuid = { workspace = true, features = ["serde"] }
webdriver = { workspace = true }
diff --git a/components/script/body.rs b/components/script/body.rs
index 113f3ac7adb..cc7870a0845 100644
--- a/components/script/body.rs
+++ b/components/script/body.rs
@@ -7,7 +7,7 @@ use std::{ptr, slice, str};
use constellation_traits::BlobImpl;
use encoding_rs::{Encoding, UTF_8};
-use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
+use ipc_channel::ipc::{self, IpcReceiver, IpcSender, IpcSharedMemory};
use ipc_channel::router::ROUTER;
use js::jsapi::{Heap, JS_ClearPendingException, JSObject, Value as JSValue};
use js::jsval::{JSVal, UndefinedValue};
@@ -73,7 +73,7 @@ struct TransmitBodyConnectHandler {
task_source: SendableTaskSource,
bytes_sender: Option<IpcSender<BodyChunkResponse>>,
control_sender: IpcSender<BodyChunkRequest>,
- in_memory: Option<Vec<u8>>,
+ in_memory: Option<IpcSharedMemory>,
in_memory_done: bool,
source: BodySource,
}
@@ -83,7 +83,7 @@ impl TransmitBodyConnectHandler {
stream: Trusted<ReadableStream>,
task_source: SendableTaskSource,
control_sender: IpcSender<BodyChunkRequest>,
- in_memory: Option<Vec<u8>>,
+ in_memory: Option<IpcSharedMemory>,
source: BodySource,
) -> TransmitBodyConnectHandler {
TransmitBodyConnectHandler {
@@ -160,7 +160,7 @@ impl TransmitBodyConnectHandler {
.bytes_sender
.as_ref()
.expect("No bytes sender to transmit source.")
- .send(BodyChunkResponse::Chunk(bytes.clone()));
+ .send(BodyChunkResponse::Chunk(bytes));
return;
}
warn!("Re-directs for file-based Blobs not supported yet.");
@@ -310,7 +310,11 @@ impl Callback for TransmitBodyPromiseHandler {
// Step 5.1 and 5.2, transmit chunk.
// Send the chunk to the body transmitter in net::http_loader::obtain_response.
// TODO: queue a fetch task on request to process request body for request.
- let _ = self.bytes_sender.send(BodyChunkResponse::Chunk(chunk));
+ let _ = self
+ .bytes_sender
+ .send(BodyChunkResponse::Chunk(IpcSharedMemory::from_bytes(
+ &chunk,
+ )));
}
}
diff --git a/components/script/canvas_context.rs b/components/script/canvas_context.rs
index 7dfdf48e3f5..ec388e039f1 100644
--- a/components/script/canvas_context.rs
+++ b/components/script/canvas_context.rs
@@ -125,7 +125,11 @@ impl CanvasContext for RenderingContext {
fn resize(&self) {
match self {
- RenderingContext::Placeholder(context) => (*context.context().unwrap()).resize(),
+ RenderingContext::Placeholder(offscreen_canvas) => {
+ if let Some(context) = offscreen_canvas.context() {
+ context.resize()
+ }
+ },
RenderingContext::Context2d(context) => context.resize(),
RenderingContext::WebGL(context) => context.resize(),
RenderingContext::WebGL2(context) => context.resize(),
diff --git a/components/script/dom/attr.rs b/components/script/dom/attr.rs
index 52d0ca7e20c..9f1520bd085 100644
--- a/components/script/dom/attr.rs
+++ b/components/script/dom/attr.rs
@@ -8,7 +8,7 @@ use std::mem;
use devtools_traits::AttrInfo;
use dom_struct::dom_struct;
-use html5ever::{LocalName, Namespace, Prefix, ns};
+use html5ever::{LocalName, Namespace, Prefix, local_name, ns};
use style::attr::{AttrIdentifier, AttrValue};
use style::values::GenericAtomIdent;
use stylo_atoms::Atom;
@@ -179,7 +179,7 @@ impl Attr {
assert_eq!(Some(owner), self.owner().as_deref());
owner.will_mutate_attr(self);
self.swap_value(&mut value);
- if *self.namespace() == ns!() {
+ if is_relevant_attribute(self.namespace(), self.local_name()) {
vtable_for(owner.upcast()).attribute_mutated(
self,
AttributeMutation::Set(Some(&value)),
@@ -283,3 +283,9 @@ impl<'dom> AttrHelpersForLayout<'dom> for LayoutDom<'dom, Attr> {
&self.unsafe_get().identifier.namespace.0
}
}
+
+/// A helper function to check if attribute is relevant.
+pub(crate) fn is_relevant_attribute(namespace: &Namespace, local_name: &LocalName) -> bool {
+ // <https://svgwg.org/svg2-draft/linking.html#XLinkHrefAttribute>
+ namespace == &ns!() || (namespace == &ns!(xlink) && local_name == &local_name!("href"))
+}
diff --git a/components/script/dom/bindings/buffer_source.rs b/components/script/dom/bindings/buffer_source.rs
index dd6984e1eab..14a71532e9d 100644
--- a/components/script/dom/bindings/buffer_source.rs
+++ b/components/script/dom/bindings/buffer_source.rs
@@ -37,7 +37,7 @@ use js::rust::{
#[cfg(feature = "webgpu")]
use js::typedarray::{ArrayBuffer, HeapArrayBuffer};
use js::typedarray::{
- ArrayBufferU8, ArrayBufferView, ArrayBufferViewU8, CreateWith, TypedArray, TypedArrayElement,
+ ArrayBufferU8, ArrayBufferViewU8, CreateWith, TypedArray, TypedArrayElement,
TypedArrayElementCreator,
};
@@ -63,36 +63,25 @@ pub(crate) enum BufferSource {
ArrayBuffer(Box<Heap<*mut JSObject>>),
}
-pub(crate) fn new_initialized_heap_buffer_source<T>(
- init: HeapTypedArrayInit,
+pub(crate) fn create_heap_buffer_source_with_length<T>(
+ cx: JSContext,
+ len: u32,
can_gc: CanGc,
-) -> Result<HeapBufferSource<T>, ()>
+) -> Fallible<HeapBufferSource<T>>
where
T: TypedArrayElement + TypedArrayElementCreator,
T::Element: Clone + Copy,
{
- let heap_buffer_source = match init {
- HeapTypedArrayInit::Buffer(buffer_source) => HeapBufferSource {
- buffer_source,
- phantom: PhantomData,
- },
- HeapTypedArrayInit::Info { len, cx } => {
- rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
- let typed_array_result =
- create_buffer_source_with_length::<T>(cx, len as usize, array.handle_mut(), can_gc);
- if typed_array_result.is_err() {
- return Err(());
- }
-
- HeapBufferSource::<T>::new(BufferSource::ArrayBufferView(Heap::boxed(*array.handle())))
- },
- };
- Ok(heap_buffer_source)
-}
+ rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
+ let typed_array_result =
+ create_buffer_source_with_length::<T>(cx, len as usize, array.handle_mut(), can_gc);
+ if typed_array_result.is_err() {
+ return Err(Error::JSFailed);
+ }
-pub(crate) enum HeapTypedArrayInit {
- Buffer(BufferSource),
- Info { len: u32, cx: JSContext },
+ Ok(HeapBufferSource::<T>::new(BufferSource::ArrayBufferView(
+ Heap::boxed(*array.handle()),
+ )))
}
pub(crate) struct HeapBufferSource<T> {
@@ -131,11 +120,11 @@ where
}
pub(crate) fn from_view(
- chunk: CustomAutoRooterGuard<ArrayBufferView>,
- ) -> HeapBufferSource<ArrayBufferViewU8> {
- HeapBufferSource::<ArrayBufferViewU8>::new(BufferSource::ArrayBufferView(Heap::boxed(
- unsafe { *chunk.underlying_object() },
- )))
+ chunk: CustomAutoRooterGuard<TypedArray<T, *mut JSObject>>,
+ ) -> HeapBufferSource<T> {
+ HeapBufferSource::<T>::new(BufferSource::ArrayBufferView(Heap::boxed(unsafe {
+ *chunk.underlying_object()
+ })))
}
pub(crate) fn default() -> Self {
diff --git a/components/script/dom/bindings/cell.rs b/components/script/dom/bindings/cell.rs
index 6c987270911..7e7e752bc0c 100644
--- a/components/script/dom/bindings/cell.rs
+++ b/components/script/dom/bindings/cell.rs
@@ -10,6 +10,7 @@ pub(crate) use std::cell::{Ref, RefCell, RefMut};
#[cfg(feature = "refcell_backtrace")]
pub(crate) use accountable_refcell::{Ref, RefCell, RefMut, ref_filter_map};
+use malloc_size_of::{MallocConditionalSizeOf, MallocSizeOfOps};
#[cfg(not(feature = "refcell_backtrace"))]
pub(crate) use ref_filter_map::ref_filter_map;
@@ -24,6 +25,12 @@ pub(crate) struct DomRefCell<T> {
value: RefCell<T>,
}
+impl<T: MallocConditionalSizeOf> MallocConditionalSizeOf for DomRefCell<T> {
+ fn conditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
+ self.value.borrow().conditional_size_of(ops)
+ }
+}
+
// Functionality specific to Servo's `DomRefCell` type
// ===================================================
diff --git a/components/script/dom/bindings/structuredclone.rs b/components/script/dom/bindings/structuredclone.rs
index c9a49ba00c9..70638238123 100644
--- a/components/script/dom/bindings/structuredclone.rs
+++ b/components/script/dom/bindings/structuredclone.rs
@@ -44,7 +44,7 @@ use crate::dom::dompointreadonly::DOMPointReadOnly;
use crate::dom::globalscope::GlobalScope;
use crate::dom::messageport::MessagePort;
use crate::dom::readablestream::ReadableStream;
-use crate::dom::types::DOMException;
+use crate::dom::types::{DOMException, TransformStream};
use crate::dom::writablestream::WritableStream;
use crate::realms::{AlreadyInRealm, InRealm, enter_realm};
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
@@ -65,6 +65,7 @@ pub(super) enum StructuredCloneTags {
ReadableStream = 0xFFFF8006,
DomException = 0xFFFF8007,
WritableStream = 0xFFFF8008,
+ TransformStream = 0xFFFF8009,
Max = 0xFFFFFFFF,
}
@@ -85,6 +86,7 @@ impl From<TransferrableInterface> for StructuredCloneTags {
TransferrableInterface::MessagePort => StructuredCloneTags::MessagePort,
TransferrableInterface::ReadableStream => StructuredCloneTags::ReadableStream,
TransferrableInterface::WritableStream => StructuredCloneTags::WritableStream,
+ TransferrableInterface::TransformStream => StructuredCloneTags::TransformStream,
}
}
}
@@ -265,6 +267,7 @@ fn receiver_for_type(
TransferrableInterface::MessagePort => receive_object::<MessagePort>,
TransferrableInterface::ReadableStream => receive_object::<ReadableStream>,
TransferrableInterface::WritableStream => receive_object::<WritableStream>,
+ TransferrableInterface::TransformStream => receive_object::<TransformStream>,
}
}
@@ -390,6 +393,7 @@ fn transfer_for_type(val: TransferrableInterface) -> TransferOperation {
TransferrableInterface::MessagePort => try_transfer::<MessagePort>,
TransferrableInterface::ReadableStream => try_transfer::<ReadableStream>,
TransferrableInterface::WritableStream => try_transfer::<WritableStream>,
+ TransferrableInterface::TransformStream => try_transfer::<TransformStream>,
}
}
@@ -438,6 +442,7 @@ unsafe fn can_transfer_for_type(
TransferrableInterface::MessagePort => can_transfer::<MessagePort>(obj, cx),
TransferrableInterface::ReadableStream => can_transfer::<ReadableStream>(obj, cx),
TransferrableInterface::WritableStream => can_transfer::<WritableStream>(obj, cx),
+ TransferrableInterface::TransformStream => can_transfer::<TransformStream>(obj, cx),
}
}
diff --git a/components/script/dom/clipboard.rs b/components/script/dom/clipboard.rs
index 94c8b3c0f19..25878a1b29b 100644
--- a/components/script/dom/clipboard.rs
+++ b/components/script/dom/clipboard.rs
@@ -3,24 +3,75 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::rc::Rc;
+use std::str::FromStr;
use constellation_traits::BlobImpl;
+use data_url::mime::Mime;
use dom_struct::dom_struct;
use embedder_traits::EmbedderMsg;
+use js::rust::HandleValue as SafeHandleValue;
use crate::dom::bindings::codegen::Bindings::ClipboardBinding::{
ClipboardMethods, PresentationStyle,
};
+use crate::dom::bindings::error::Error;
use crate::dom::bindings::refcounted::TrustedPromise;
use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::blob::Blob;
+use crate::dom::clipboarditem::Representation;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
+use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
use crate::dom::window::Window;
-use crate::script_runtime::CanGc;
+use crate::realms::{InRealm, enter_realm};
+use crate::routed_promise::{RoutedPromiseListener, route_promise};
+use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
+
+/// The fulfillment handler for the reacting to representationDataPromise part of
+/// <https://w3c.github.io/clipboard-apis/#dom-clipboard-readtext>.
+#[derive(Clone, JSTraceable, MallocSizeOf)]
+struct RepresentationDataPromiseFulfillmentHandler {
+ #[ignore_malloc_size_of = "Rc are hard"]
+ promise: Rc<Promise>,
+}
+
+impl Callback for RepresentationDataPromiseFulfillmentHandler {
+ /// The fulfillment case of Step 3.4.1.1.4.3 of
+ /// <https://w3c.github.io/clipboard-apis/#dom-clipboard-readtext>.
+ fn callback(&self, cx: SafeJSContext, v: SafeHandleValue, _realm: InRealm, can_gc: CanGc) {
+ // If v is a DOMString, then follow the below steps:
+ // Resolve p with v.
+ // Return p.
+ self.promise.resolve(cx, v, can_gc);
+
+ // NOTE: Since we ask text from arboard, v can't be a Blob
+ // If v is a Blob, then follow the below steps:
+ // Let string be the result of UTF-8 decoding v’s underlying byte sequence.
+ // Resolve p with string.
+ // Return p.
+ }
+}
+
+/// The rejection handler for the reacting to representationDataPromise part of
+/// <https://w3c.github.io/clipboard-apis/#dom-clipboard-readtext>.
+#[derive(Clone, JSTraceable, MallocSizeOf)]
+struct RepresentationDataPromiseRejectionHandler {
+ #[ignore_malloc_size_of = "Rc are hard"]
+ promise: Rc<Promise>,
+}
+
+impl Callback for RepresentationDataPromiseRejectionHandler {
+ /// The rejection case of Step 3.4.1.1.4.3 of
+ /// <https://w3c.github.io/clipboard-apis/#dom-clipboard-readtext>.
+ fn callback(&self, _cx: SafeJSContext, _v: SafeHandleValue, _realm: InRealm, can_gc: CanGc) {
+ // Reject p with "NotFoundError" DOMException in realm.
+ // Return p.
+ self.promise.reject_error(Error::NotFound, can_gc);
+ }
+}
#[dom_struct]
pub(crate) struct Clipboard {
@@ -40,6 +91,34 @@ impl Clipboard {
}
impl ClipboardMethods<crate::DomTypeHolder> for Clipboard {
+ /// <https://w3c.github.io/clipboard-apis/#dom-clipboard-readtext>
+ fn ReadText(&self, can_gc: CanGc) -> Rc<Promise> {
+ // Step 1 Let realm be this's relevant realm.
+ let global = self.global();
+
+ // Step 2 Let p be a new promise in realm.
+ let p = Promise::new(&global, can_gc);
+
+ // Step 3 Run the following steps in parallel:
+
+ // TODO Step 3.1 Let r be the result of running check clipboard read permission.
+ // Step 3.2 If r is false, then:
+ // Step 3.2.1 Queue a global task on the permission task source, given realm’s global object,
+ // to reject p with "NotAllowedError" DOMException in realm.
+ // Step 3.2.2 Abort these steps.
+
+ // Step 3.3 Let data be a copy of the system clipboard data.
+ let window = global.as_window();
+ let sender = route_promise(&p, self, global.task_manager().clipboard_task_source());
+ window.send_to_embedder(EmbedderMsg::GetClipboardText(window.webview_id(), sender));
+
+ // Step 3.4 Queue a global task on the clipboard task source,
+ // given realm’s global object, to perform the below steps:
+ // NOTE: We queue the task inside route_promise and perform the steps inside handle_response
+
+ p
+ }
+
/// <https://w3c.github.io/clipboard-apis/#dom-clipboard-writetext>
fn WriteText(&self, data: DOMString, can_gc: CanGc) -> Rc<Promise> {
// Step 1 Let realm be this's relevant realm.
@@ -95,6 +174,71 @@ impl ClipboardMethods<crate::DomTypeHolder> for Clipboard {
}
}
+impl RoutedPromiseListener<Result<String, String>> for Clipboard {
+ fn handle_response(
+ &self,
+ response: Result<String, String>,
+ promise: &Rc<Promise>,
+ can_gc: CanGc,
+ ) {
+ let global = self.global();
+ let text = response.unwrap_or_default();
+
+ // Step 3.4.1 For each systemClipboardItem in data:
+ // Step 3.4.1.1 For each systemClipboardRepresentation in systemClipboardItem:
+ // TODO: Arboard provide the first item that has a String representation
+
+ // Step 3.4.1.1.1 Let mimeType be the result of running the
+ // well-known mime type from os specific format algorithm given systemClipboardRepresentation’s name.
+ // Note: This is done by arboard, so we just convert the format to a MIME
+ let mime_type = Mime::from_str("text/plain").unwrap();
+
+ // Step 3.4.1.1.2 If mimeType is null, continue this loop.
+ // Note: Since the previous step is infallible, we don't need to handle this case
+
+ // Step 3.4.1.1.3 Let representation be a new representation.
+ let representation = Representation {
+ mime_type,
+ is_custom: false,
+ data: Promise::new_resolved(
+ &global,
+ GlobalScope::get_cx(),
+ DOMString::from(text),
+ can_gc,
+ ),
+ };
+
+ // Step 3.4.1.1.4 If representation’s MIME type essence is "text/plain", then:
+
+ // Step 3.4.1.1.4.1 Set representation’s MIME type to mimeType.
+ // Note: Done when creating a new representation
+
+ // Step 3.4.1.1.4.2 Let representationDataPromise be the representation’s data.
+ // Step 3.4.1.1.4.3 React to representationDataPromise:
+ let fulfillment_handler = Box::new(RepresentationDataPromiseFulfillmentHandler {
+ promise: promise.clone(),
+ });
+ let rejection_handler = Box::new(RepresentationDataPromiseRejectionHandler {
+ promise: promise.clone(),
+ });
+ let handler = PromiseNativeHandler::new(
+ &global,
+ Some(fulfillment_handler),
+ Some(rejection_handler),
+ can_gc,
+ );
+ let realm = enter_realm(&*global);
+ let comp = InRealm::Entered(&realm);
+ representation
+ .data
+ .append_native_handler(&handler, comp, can_gc);
+
+ // Step 3.4.2 Reject p with "NotFoundError" DOMException in realm.
+ // Step 3.4.3 Return p.
+ // NOTE: We follow the same behaviour of Gecko by doing nothing if no text is available instead of rejecting p
+ }
+}
+
/// <https://w3c.github.io/clipboard-apis/#write-blobs-and-option-to-the-clipboard>
fn write_blobs_and_option_to_the_clipboard(
window: &Window,
diff --git a/components/script/dom/clipboarditem.rs b/components/script/dom/clipboarditem.rs
index c1c66a403b3..129beb686c1 100644
--- a/components/script/dom/clipboarditem.rs
+++ b/components/script/dom/clipboarditem.rs
@@ -29,13 +29,13 @@ const CUSTOM_FORMAT_PREFIX: &str = "web ";
/// <https://w3c.github.io/clipboard-apis/#representation>
#[derive(JSTraceable, MallocSizeOf)]
-struct Representation {
+pub(super) struct Representation {
#[no_trace]
#[ignore_malloc_size_of = "Extern type"]
- mime_type: Mime,
- is_custom: bool,
+ pub mime_type: Mime,
+ pub is_custom: bool,
#[ignore_malloc_size_of = "Rc is hard"]
- data: Rc<Promise>,
+ pub data: Rc<Promise>,
}
#[dom_struct]
diff --git a/components/script/dom/create.rs b/components/script/dom/create.rs
index 5722dc4f6ac..2e7c4cf8def 100644
--- a/components/script/dom/create.rs
+++ b/components/script/dom/create.rs
@@ -85,6 +85,7 @@ use crate::dom::htmlulistelement::HTMLUListElement;
use crate::dom::htmlunknownelement::HTMLUnknownElement;
use crate::dom::htmlvideoelement::HTMLVideoElement;
use crate::dom::svgelement::SVGElement;
+use crate::dom::svgimageelement::SVGImageElement;
use crate::dom::svgsvgelement::SVGSVGElement;
use crate::realms::{InRealm, enter_realm};
use crate::script_runtime::CanGc;
@@ -114,6 +115,7 @@ fn create_svg_element(
}
match name.local {
+ local_name!("image") => make!(SVGImageElement),
local_name!("svg") => make!(SVGSVGElement),
_ => make!(SVGElement),
}
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index ae48fa1fb2f..78cb2c33075 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -4307,21 +4307,19 @@ impl Document {
type_: csp::InlineCheckType,
source: &str,
) -> csp::CheckResult {
- let element = csp::Element {
- nonce: el
- .get_attribute(&ns!(), &local_name!("nonce"))
- .map(|attr| Cow::Owned(attr.value().to_string())),
- };
let (result, violations) = match self.get_csp_list() {
None => {
return csp::CheckResult::Allowed;
},
Some(csp_list) => {
+ let element = csp::Element {
+ nonce: el.nonce_value_if_nonceable().map(Cow::Owned),
+ };
csp_list.should_elements_inline_type_behavior_be_blocked(&element, type_, source)
},
};
- self.global().report_csp_violations(violations);
+ self.global().report_csp_violations(violations, Some(el));
result
}
@@ -6569,9 +6567,6 @@ impl DocumentMethods<crate::DomTypeHolder> for Document {
Ok(())
}
- // https://html.spec.whatwg.org/multipage/#documentandelementeventhandlers
- document_and_element_event_handlers!();
-
// https://fullscreen.spec.whatwg.org/#handler-document-onfullscreenerror
event_handler!(fullscreenerror, GetOnfullscreenerror, SetOnfullscreenerror);
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index b2168846fad..ed58548a3e5 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -12,6 +12,7 @@ use std::rc::Rc;
use std::str::FromStr;
use std::{fmt, mem};
+use content_security_policy as csp;
use cssparser::match_ignore_ascii_case;
use devtools_traits::AttrInfo;
use dom_struct::dom_struct;
@@ -62,8 +63,9 @@ use xml5ever::serialize::TraversalScope::{
ChildrenOnly as XmlChildrenOnly, IncludeNode as XmlIncludeNode,
};
+use crate::conversions::Convert;
use crate::dom::activation::Activatable;
-use crate::dom::attr::{Attr, AttrHelpersForLayout};
+use crate::dom::attr::{Attr, AttrHelpersForLayout, is_relevant_attribute};
use crate::dom::bindings::cell::{DomRefCell, Ref, RefMut, ref_filter_map};
use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
@@ -79,7 +81,9 @@ use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{
use crate::dom::bindings::codegen::Bindings::WindowBinding::{
ScrollBehavior, ScrollToOptions, WindowMethods,
};
-use crate::dom::bindings::codegen::UnionTypes::{NodeOrString, TrustedScriptURLOrUSVString};
+use crate::dom::bindings::codegen::UnionTypes::{
+ NodeOrString, TrustedHTMLOrNullIsEmptyString, TrustedHTMLOrString, TrustedScriptURLOrUSVString,
+};
use crate::dom::bindings::conversions::DerivedFrom;
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
@@ -124,6 +128,7 @@ use crate::dom::htmllinkelement::HTMLLinkElement;
use crate::dom::htmlobjectelement::HTMLObjectElement;
use crate::dom::htmloptgroupelement::HTMLOptGroupElement;
use crate::dom::htmloutputelement::HTMLOutputElement;
+use crate::dom::htmlscriptelement::HTMLScriptElement;
use crate::dom::htmlselectelement::HTMLSelectElement;
use crate::dom::htmlslotelement::{HTMLSlotElement, Slottable};
use crate::dom::htmlstyleelement::HTMLStyleElement;
@@ -141,8 +146,8 @@ use crate::dom::intersectionobserver::{IntersectionObserver, IntersectionObserve
use crate::dom::mutationobserver::{Mutation, MutationObserver};
use crate::dom::namednodemap::NamedNodeMap;
use crate::dom::node::{
- BindContext, ChildrenMutation, LayoutNodeHelpers, Node, NodeDamage, NodeFlags, NodeTraits,
- ShadowIncluding, UnbindContext,
+ BindContext, ChildrenMutation, CloneChildrenFlag, LayoutNodeHelpers, Node, NodeDamage,
+ NodeFlags, NodeTraits, ShadowIncluding, UnbindContext,
};
use crate::dom::nodelist::NodeList;
use crate::dom::promise::Promise;
@@ -150,6 +155,7 @@ use crate::dom::raredata::ElementRareData;
use crate::dom::servoparser::ServoParser;
use crate::dom::shadowroot::{IsUserAgentWidget, ShadowRoot};
use crate::dom::text::Text;
+use crate::dom::trustedhtml::TrustedHTML;
use crate::dom::validation::Validatable;
use crate::dom::validitystate::ValidationFlags;
use crate::dom::virtualmethods::{VirtualMethods, vtable_for};
@@ -179,7 +185,7 @@ pub struct Element {
/// <https://dom.spec.whatwg.org/#concept-element-is-value>
#[no_trace]
is: DomRefCell<Option<LocalName>>,
- #[ignore_malloc_size_of = "Arc"]
+ #[conditional_malloc_size_of]
#[no_trace]
style_attribute: DomRefCell<Option<Arc<Locked<PropertyDeclarationBlock>>>>,
attr_list: MutNullableDom<NamedNodeMap>,
@@ -353,7 +359,7 @@ impl Element {
if damage == NodeDamage::OtherNodeDamage {
doc.note_node_with_dirty_descendants(self.upcast());
- restyle.damage = RestyleDamage::rebuild_and_reflow();
+ restyle.damage = RestyleDamage::reconstruct();
}
}
@@ -1699,7 +1705,7 @@ impl Element {
assert!(attr.GetOwnerElement().as_deref() == Some(self));
self.will_mutate_attr(attr);
self.attrs.borrow_mut().push(Dom::from_ref(attr));
- if attr.namespace() == &ns!() {
+ if is_relevant_attribute(attr.namespace(), attr.local_name()) {
vtable_for(self.upcast()).attribute_mutated(attr, AttributeMutation::Set(None), can_gc);
}
}
@@ -1841,7 +1847,7 @@ impl Element {
local_name: &LocalName,
value: DOMString,
) -> AttrValue {
- if *namespace == ns!() {
+ if is_relevant_attribute(namespace, local_name) {
vtable_for(self.upcast()).parse_plain_attribute(local_name, value)
} else {
AttrValue::String(value.into())
@@ -1896,7 +1902,7 @@ impl Element {
self.attrs.borrow_mut().remove(idx);
attr.set_owner(None);
- if attr.namespace() == &ns!() {
+ if is_relevant_attribute(attr.namespace(), attr.local_name()) {
vtable_for(self.upcast()).attribute_mutated(
&attr,
AttributeMutation::Removed,
@@ -1990,6 +1996,15 @@ impl Element {
.unwrap_or_else(|_| TrustedScriptURLOrUSVString::USVString(USVString(value.to_owned())))
}
+ pub(crate) fn get_trusted_html_attribute(&self, local_name: &LocalName) -> TrustedHTMLOrString {
+ assert_eq!(*local_name, local_name.to_ascii_lowercase());
+ let value = match self.get_attribute(&ns!(), local_name) {
+ Some(attr) => (&**attr.value()).into(),
+ None => "".into(),
+ };
+ TrustedHTMLOrString::String(value)
+ }
+
pub(crate) fn get_string_attribute(&self, local_name: &LocalName) -> DOMString {
match self.get_attribute(&ns!(), local_name) {
Some(x) => x.Value(),
@@ -2120,6 +2135,130 @@ impl Element {
node.owner_doc().element_attr_will_change(self, attr);
}
+ /// <https://html.spec.whatwg.org/multipage/#the-style-attribute>
+ fn update_style_attribute(&self, attr: &Attr, mutation: AttributeMutation) {
+ let doc = self.upcast::<Node>().owner_doc();
+ // Modifying the `style` attribute might change style.
+ *self.style_attribute.borrow_mut() = match mutation {
+ AttributeMutation::Set(..) => {
+ // This is the fast path we use from
+ // CSSStyleDeclaration.
+ //
+ // Juggle a bit to keep the borrow checker happy
+ // while avoiding the extra clone.
+ let is_declaration = matches!(*attr.value(), AttrValue::Declaration(..));
+
+ let block = if is_declaration {
+ let mut value = AttrValue::String(String::new());
+ attr.swap_value(&mut value);
+ let (serialization, block) = match value {
+ AttrValue::Declaration(s, b) => (s, b),
+ _ => unreachable!(),
+ };
+ let mut value = AttrValue::String(serialization);
+ attr.swap_value(&mut value);
+ block
+ } else {
+ let win = self.owner_window();
+ let source = &**attr.value();
+ // However, if the Should element's inline behavior be blocked by
+ // Content Security Policy? algorithm returns "Blocked" when executed
+ // upon the attribute's element, "style attribute", and the attribute's value,
+ // then the style rules defined in the attribute's value must not be applied to the element. [CSP]
+ if doc.should_elements_inline_type_behavior_be_blocked(
+ self,
+ csp::InlineCheckType::StyleAttribute,
+ source,
+ ) == csp::CheckResult::Blocked
+ {
+ return;
+ }
+ Arc::new(doc.style_shared_lock().wrap(parse_style_attribute(
+ source,
+ &UrlExtraData(doc.base_url().get_arc()),
+ win.css_error_reporter(),
+ doc.quirks_mode(),
+ CssRuleType::Style,
+ )))
+ };
+
+ Some(block)
+ },
+ AttributeMutation::Removed => None,
+ };
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#nonce-attributes>
+ pub(crate) fn update_nonce_internal_slot(&self, nonce: String) {
+ self.ensure_rare_data().cryptographic_nonce = nonce;
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#nonce-attributes>
+ pub(crate) fn nonce_value(&self) -> String {
+ match self.rare_data().as_ref() {
+ None => String::new(),
+ Some(rare_data) => rare_data.cryptographic_nonce.clone(),
+ }
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#nonce-attributes>
+ pub(crate) fn update_nonce_post_connection(&self) {
+ // Whenever an element including HTMLOrSVGElement becomes browsing-context connected,
+ // the user agent must execute the following steps on the element:
+ if !self.upcast::<Node>().is_connected_with_browsing_context() {
+ return;
+ }
+ let global = self.owner_global();
+ // Step 1: Let CSP list be element's shadow-including root's policy container's CSP list.
+ let csp_list = match global.get_csp_list() {
+ None => return,
+ Some(csp_list) => csp_list,
+ };
+ // Step 2: If CSP list contains a header-delivered Content Security Policy,
+ // and element has a nonce content attribute whose value is not the empty string, then:
+ if !csp_list.contains_a_header_delivered_content_security_policy() ||
+ self.get_string_attribute(&local_name!("nonce")).is_empty()
+ {
+ return;
+ }
+ // Step 2.1: Let nonce be element's [[CryptographicNonce]].
+ let nonce = self.nonce_value();
+ // Step 2.2: Set an attribute value for element using "nonce" and the empty string.
+ self.set_string_attribute(&local_name!("nonce"), "".into(), CanGc::note());
+ // Step 2.3: Set element's [[CryptographicNonce]] to nonce.
+ self.update_nonce_internal_slot(nonce);
+ }
+
+ /// <https://www.w3.org/TR/CSP/#is-element-nonceable>
+ pub(crate) fn nonce_value_if_nonceable(&self) -> Option<String> {
+ // Step 1: If element does not have an attribute named "nonce", return "Not Nonceable".
+ if !self.has_attribute(&local_name!("nonce")) {
+ return None;
+ }
+ // Step 2: If element is a script element, then for each attribute of element’s attribute list:
+ if self.downcast::<HTMLScriptElement>().is_some() {
+ for attr in self.attrs().iter() {
+ // Step 2.1: If attribute’s name contains an ASCII case-insensitive match
+ // for "<script" or "<style", return "Not Nonceable".
+ let attr_name = attr.name().to_ascii_lowercase();
+ if attr_name.contains("<script") || attr_name.contains("<style") {
+ return None;
+ }
+ // Step 2.2: If attribute’s value contains an ASCII case-insensitive match
+ // for "<script" or "<style", return "Not Nonceable".
+ let attr_value = attr.value().to_ascii_lowercase();
+ if attr_value.contains("<script") || attr_value.contains("<style") {
+ return None;
+ }
+ }
+ }
+ // Step 3: If element had a duplicate-attribute parse error during tokenization, return "Not Nonceable".
+ // TODO(https://github.com/servo/servo/issues/4577 and https://github.com/whatwg/html/issues/3257):
+ // Figure out how to retrieve this information from the parser
+ // Step 4: Return "Nonceable".
+ Some(self.nonce_value().trim().to_owned())
+ }
+
// https://dom.spec.whatwg.org/#insert-adjacent
pub(crate) fn insert_adjacent(
&self,
@@ -2239,18 +2378,25 @@ impl Element {
Ok(fragment)
}
+ /// Step 4 of <https://html.spec.whatwg.org/multipage/#dom-element-insertadjacenthtml>
pub(crate) fn fragment_parsing_context(
owner_doc: &Document,
element: Option<&Self>,
can_gc: CanGc,
) -> DomRoot<Self> {
+ // If context is not an Element or all of the following are true:
match element {
Some(elem)
+ // context's node document is an HTML document;
+ // context's local name is "html"; and
+ // context's namespace is the HTML namespace,
if elem.local_name() != &local_name!("html") ||
!elem.html_element_in_html_document() =>
{
DomRoot::from_ref(elem)
},
+ // set context to the result of creating an element
+ // given this's node document, "body", and the HTML namespace.
_ => DomRoot::upcast(HTMLBodyElement::new(
local_name!("body"),
None,
@@ -2363,6 +2509,13 @@ impl Element {
Dom::from_ref(&*ElementInternals::new(elem, can_gc))
}))
}
+
+ pub(crate) fn outer_html(&self, can_gc: CanGc) -> Fallible<DOMString> {
+ match self.GetOuterHTML(can_gc)? {
+ TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(str) => Ok(str),
+ TrustedHTMLOrNullIsEmptyString::TrustedHTML(_) => unreachable!(),
+ }
+ }
}
impl ElementMethods<crate::DomTypeHolder> for Element {
@@ -2621,7 +2774,7 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
attr.set_owner(Some(self));
self.attrs.borrow_mut()[position] = Dom::from_ref(attr);
old_attr.set_owner(None);
- if attr.namespace() == &ns!() {
+ if is_relevant_attribute(attr.namespace(), attr.local_name()) {
vtable.attribute_mutated(
attr,
AttributeMutation::Set(Some(&old_attr.value())),
@@ -3017,7 +3170,17 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
}
/// <https://html.spec.whatwg.org/multipage/#dom-element-sethtmlunsafe>
- fn SetHTMLUnsafe(&self, html: DOMString, can_gc: CanGc) {
+ fn SetHTMLUnsafe(&self, html: TrustedHTMLOrString, can_gc: CanGc) -> ErrorResult {
+ // Step 1. Let compliantHTML be the result of invoking the
+ // Get Trusted Type compliant string algorithm with TrustedHTML,
+ // this's relevant global object, html, "Element setHTMLUnsafe", and "script".
+ let html = DOMString::from(TrustedHTML::get_trusted_script_compliant_string(
+ &self.owner_global(),
+ html,
+ "Element",
+ "setHTMLUnsafe",
+ can_gc,
+ )?);
// Step 2. Let target be this's template contents if this is a template element; otherwise this.
let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
DomRoot::upcast(template.Content(can_gc))
@@ -3027,6 +3190,7 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
// Step 3. Unsafely set HTML given target, this, and compliantHTML
Node::unsafely_set_html(&target, self, html, can_gc);
+ Ok(())
}
/// <https://html.spec.whatwg.org/multipage/#dom-element-gethtml>
@@ -3042,7 +3206,7 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
}
/// <https://html.spec.whatwg.org/multipage/#dom-element-innerhtml>
- fn GetInnerHTML(&self, can_gc: CanGc) -> Fallible<DOMString> {
+ fn GetInnerHTML(&self, can_gc: CanGc) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
let qname = QualName::new(
self.prefix().clone(),
self.namespace().clone(),
@@ -3059,16 +3223,28 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
.xml_serialize(XmlChildrenOnly(Some(qname)))
};
- Ok(result)
+ Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result))
}
/// <https://html.spec.whatwg.org/multipage/#dom-element-innerhtml>
- fn SetInnerHTML(&self, value: DOMString, can_gc: CanGc) -> ErrorResult {
- // Step 2.
+ fn SetInnerHTML(&self, value: TrustedHTMLOrNullIsEmptyString, can_gc: CanGc) -> ErrorResult {
+ // Step 1: Let compliantString be the result of invoking the
+ // Get Trusted Type compliant string algorithm with TrustedHTML,
+ // this's relevant global object, the given value, "Element innerHTML", and "script".
+ let value = DOMString::from(TrustedHTML::get_trusted_script_compliant_string(
+ &self.owner_global(),
+ value.convert(),
+ "Element",
+ "innerHTML",
+ can_gc,
+ )?);
// https://github.com/w3c/DOM-Parsing/issues/1
let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
+ // Step 4: If context is a template element, then set context to
+ // the template element's template contents (a DocumentFragment).
DomRoot::upcast(template.Content(can_gc))
} else {
+ // Step 2: Let context be this.
DomRoot::from_ref(self.upcast())
};
@@ -3085,15 +3261,17 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
return Ok(());
}
- // Step 1.
+ // Step 3: Let fragment be the result of invoking the fragment parsing algorithm steps
+ // with context and compliantString.
let frag = self.parse_fragment(value, can_gc)?;
+ // Step 5: Replace all with fragment within context.
Node::replace_all(Some(frag.upcast()), &target, can_gc);
Ok(())
}
/// <https://html.spec.whatwg.org/multipage/#dom-element-outerhtml>
- fn GetOuterHTML(&self, can_gc: CanGc) -> Fallible<DOMString> {
+ fn GetOuterHTML(&self, can_gc: CanGc) -> Fallible<TrustedHTMLOrNullIsEmptyString> {
// FIXME: This should use the fragment serialization algorithm, which takes
// care of distinguishing between html/xml documents
let result = if self.owner_document().is_html_document() {
@@ -3103,27 +3281,39 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
self.upcast::<Node>().xml_serialize(XmlIncludeNode)
};
- Ok(result)
+ Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result))
}
/// <https://html.spec.whatwg.org/multipage/#dom-element-outerhtml>
- fn SetOuterHTML(&self, value: DOMString, can_gc: CanGc) -> ErrorResult {
+ fn SetOuterHTML(&self, value: TrustedHTMLOrNullIsEmptyString, can_gc: CanGc) -> ErrorResult {
+ // Step 1: Let compliantString be the result of invoking the
+ // Get Trusted Type compliant string algorithm with TrustedHTML,
+ // this's relevant global object, the given value, "Element outerHTML", and "script".
+ let value = DOMString::from(TrustedHTML::get_trusted_script_compliant_string(
+ &self.owner_global(),
+ value.convert(),
+ "Element",
+ "outerHTML",
+ can_gc,
+ )?);
let context_document = self.owner_document();
let context_node = self.upcast::<Node>();
- // Step 1.
+ // Step 2: Let parent be this's parent.
let context_parent = match context_node.GetParentNode() {
None => {
- // Step 2.
+ // Step 3: If parent is null, return. There would be no way to
+ // obtain a reference to the nodes created even if the remaining steps were run.
return Ok(());
},
Some(parent) => parent,
};
let parent = match context_parent.type_id() {
- // Step 3.
+ // Step 4: If parent is a Document, throw a "NoModificationAllowedError" DOMException.
NodeTypeId::Document(_) => return Err(Error::NoModificationAllowed),
- // Step 4.
+ // Step 5: If parent is a DocumentFragment, set parent to the result of
+ // creating an element given this's node document, "body", and the HTML namespace.
NodeTypeId::DocumentFragment(_) => {
let body_elem = Element::create(
QualName::new(None, ns!(html), local_name!("body")),
@@ -3139,9 +3329,10 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
_ => context_node.GetParentElement().unwrap(),
};
- // Step 5.
+ // Step 6: Let fragment be the result of invoking the
+ // fragment parsing algorithm steps given parent and compliantString.
let frag = parent.parse_fragment(value, can_gc)?;
- // Step 6.
+ // Step 7: Replace this with fragment within this's parent.
context_parent.ReplaceChild(frag.upcast(), context_node, can_gc)?;
Ok(())
}
@@ -3308,38 +3499,57 @@ impl ElementMethods<crate::DomTypeHolder> for Element {
fn InsertAdjacentHTML(
&self,
position: DOMString,
- text: DOMString,
+ text: TrustedHTMLOrString,
can_gc: CanGc,
) -> ErrorResult {
- // Step 1.
+ // Step 1: Let compliantString be the result of invoking the
+ // Get Trusted Type compliant string algorithm with TrustedHTML,
+ // this's relevant global object, string, "Element insertAdjacentHTML", and "script".
+ let text = DOMString::from(TrustedHTML::get_trusted_script_compliant_string(
+ &self.owner_global(),
+ text,
+ "Element",
+ "insertAdjacentHTML",
+ can_gc,
+ )?);
let position = position.parse::<AdjacentPosition>()?;
+ // Step 2: Let context be null.
+ // Step 3: Use the first matching item from this list:
let context = match position {
+ // If position is an ASCII case-insensitive match for the string "beforebegin"
+ // If position is an ASCII case-insensitive match for the string "afterend"
AdjacentPosition::BeforeBegin | AdjacentPosition::AfterEnd => {
match self.upcast::<Node>().GetParentNode() {
+ // Step 3.2: If context is null or a Document, throw a "NoModificationAllowedError" DOMException.
Some(ref node) if node.is::<Document>() => {
return Err(Error::NoModificationAllowed);
},
None => return Err(Error::NoModificationAllowed),
+ // Step 3.1: Set context to this's parent.
Some(node) => node,
}
},
+ // If position is an ASCII case-insensitive match for the string "afterbegin"
+ // If position is an ASCII case-insensitive match for the string "beforeend"
AdjacentPosition::AfterBegin | AdjacentPosition::BeforeEnd => {
+ // Set context to this.
DomRoot::from_ref(self.upcast::<Node>())
},
};
- // Step 2.
+ // Step 4.
let context = Element::fragment_parsing_context(
&context.owner_doc(),
context.downcast::<Element>(),
can_gc,
);
- // Step 3.
+ // Step 5: Let fragment be the result of invoking the
+ // fragment parsing algorithm steps with context and compliantString.
let fragment = context.parse_fragment(text, can_gc)?;
- // Step 4.
+ // Step 6.
self.insert_adjacent(position, fragment.upcast(), can_gc)
.map(|_| ())
}
@@ -3800,43 +4010,7 @@ impl VirtualMethods for Element {
&local_name!("tabindex") | &local_name!("draggable") | &local_name!("hidden") => {
self.update_sequentially_focusable_status(can_gc)
},
- &local_name!("style") => {
- // Modifying the `style` attribute might change style.
- *self.style_attribute.borrow_mut() = match mutation {
- AttributeMutation::Set(..) => {
- // This is the fast path we use from
- // CSSStyleDeclaration.
- //
- // Juggle a bit to keep the borrow checker happy
- // while avoiding the extra clone.
- let is_declaration = matches!(*attr.value(), AttrValue::Declaration(..));
-
- let block = if is_declaration {
- let mut value = AttrValue::String(String::new());
- attr.swap_value(&mut value);
- let (serialization, block) = match value {
- AttrValue::Declaration(s, b) => (s, b),
- _ => unreachable!(),
- };
- let mut value = AttrValue::String(serialization);
- attr.swap_value(&mut value);
- block
- } else {
- let win = self.owner_window();
- Arc::new(doc.style_shared_lock().wrap(parse_style_attribute(
- &attr.value(),
- &UrlExtraData(doc.base_url().get_arc()),
- win.css_error_reporter(),
- doc.quirks_mode(),
- CssRuleType::Style,
- )))
- };
-
- Some(block)
- },
- AttributeMutation::Removed => None,
- };
- },
+ &local_name!("style") => self.update_style_attribute(attr, mutation),
&local_name!("id") => {
*self.id_attribute.borrow_mut() = mutation.new_value(attr).and_then(|value| {
let value = value.as_atom();
@@ -4066,6 +4240,31 @@ impl VirtualMethods for Element {
self.tag_name.clear();
}
}
+
+ fn post_connection_steps(&self) {
+ if let Some(s) = self.super_type() {
+ s.post_connection_steps();
+ }
+
+ self.update_nonce_post_connection();
+ }
+
+ /// <https://html.spec.whatwg.org/multipage/#nonce-attributes%3Aconcept-node-clone-ext>
+ fn cloning_steps(
+ &self,
+ copy: &Node,
+ maybe_doc: Option<&Document>,
+ clone_children: CloneChildrenFlag,
+ can_gc: CanGc,
+ ) {
+ if let Some(s) = self.super_type() {
+ s.cloning_steps(copy, maybe_doc, clone_children, can_gc);
+ }
+ let elem = copy.downcast::<Element>().unwrap();
+ if let Some(rare_data) = self.rare_data().as_ref() {
+ elem.update_nonce_internal_slot(rare_data.cryptographic_nonce.clone());
+ }
+ }
}
#[derive(Clone, PartialEq)]
diff --git a/components/script/dom/event.rs b/components/script/dom/event.rs
index e199e12f655..743ced42a4b 100644
--- a/components/script/dom/event.rs
+++ b/components/script/dom/event.rs
@@ -1040,14 +1040,39 @@ impl From<bool> for EventCancelable {
}
impl From<EventCancelable> for bool {
- fn from(bubbles: EventCancelable) -> Self {
- match bubbles {
+ fn from(cancelable: EventCancelable) -> Self {
+ match cancelable {
EventCancelable::Cancelable => true,
EventCancelable::NotCancelable => false,
}
}
}
+#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
+pub(crate) enum EventComposed {
+ Composed,
+ NotComposed,
+}
+
+impl From<bool> for EventComposed {
+ fn from(boolean: bool) -> Self {
+ if boolean {
+ EventComposed::Composed
+ } else {
+ EventComposed::NotComposed
+ }
+ }
+}
+
+impl From<EventComposed> for bool {
+ fn from(composed: EventComposed) -> Self {
+ match composed {
+ EventComposed::Composed => true,
+ EventComposed::NotComposed => false,
+ }
+ }
+}
+
#[derive(Clone, Copy, Debug, Eq, JSTraceable, PartialEq)]
#[repr(u16)]
#[derive(MallocSizeOf)]
diff --git a/components/script/dom/eventsource.rs b/components/script/dom/eventsource.rs
index 273abda0a02..7cf7bd6106f 100644
--- a/components/script/dom/eventsource.rs
+++ b/components/script/dom/eventsource.rs
@@ -448,7 +448,7 @@ impl FetchResponseListener for EventSourceContext {
fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<csp::Violation>) {
let global = &self.resource_timing_global();
- global.report_csp_violations(violations);
+ global.report_csp_violations(violations, None);
}
}
diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs
index 98c4c3ed53d..55db2e4d248 100644
--- a/components/script/dom/globalscope.rs
+++ b/components/script/dom/globalscope.rs
@@ -106,6 +106,7 @@ use crate::dom::crypto::Crypto;
use crate::dom::dedicatedworkerglobalscope::{
DedicatedWorkerControlMsg, DedicatedWorkerGlobalScope,
};
+use crate::dom::element::Element;
use crate::dom::errorevent::ErrorEvent;
use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
use crate::dom::eventsource::EventSource;
@@ -115,6 +116,7 @@ use crate::dom::htmlscriptelement::{ScriptId, SourceCode};
use crate::dom::imagebitmap::ImageBitmap;
use crate::dom::messageevent::MessageEvent;
use crate::dom::messageport::MessagePort;
+use crate::dom::node::Node;
use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
use crate::dom::performance::Performance;
use crate::dom::performanceobserver::VALID_ENTRY_TYPES;
@@ -2930,7 +2932,7 @@ impl GlobalScope {
let (is_js_evaluation_allowed, violations) = csp_list.is_js_evaluation_allowed(source);
- self.report_csp_violations(violations);
+ self.report_csp_violations(violations, None);
is_js_evaluation_allowed == CheckResult::Allowed
}
@@ -2957,7 +2959,7 @@ impl GlobalScope {
let (result, violations) =
csp_list.should_navigation_request_be_blocked(&request, NavigationCheckType::Other);
- self.report_csp_violations(violations);
+ self.report_csp_violations(violations, None);
result == CheckResult::Blocked
}
@@ -3444,8 +3446,13 @@ impl GlobalScope {
unreachable!();
}
+ /// <https://www.w3.org/TR/CSP/#report-violation>
#[allow(unsafe_code)]
- pub(crate) fn report_csp_violations(&self, violations: Vec<Violation>) {
+ pub(crate) fn report_csp_violations(
+ &self,
+ violations: Vec<Violation>,
+ element: Option<&Element>,
+ ) {
let scripted_caller =
unsafe { describe_scripted_caller(*GlobalScope::get_cx()) }.unwrap_or_default();
for violation in violations {
@@ -3471,7 +3478,38 @@ impl GlobalScope {
.line_number(scripted_caller.line)
.column_number(scripted_caller.col + 1)
.build(self);
- let task = CSPViolationReportTask::new(self, report);
+ // Step 1: Let global be violation’s global object.
+ // We use `self` as `global`;
+ // Step 2: Let target be violation’s element.
+ let target = element.and_then(|event_target| {
+ // Step 3.1: If target is not null, and global is a Window,
+ // and target’s shadow-including root is not global’s associated Document, set target to null.
+ if let Some(window) = self.downcast::<Window>() {
+ if !window
+ .Document()
+ .upcast::<Node>()
+ .is_shadow_including_inclusive_ancestor_of(event_target.upcast())
+ {
+ return None;
+ }
+ }
+ Some(event_target)
+ });
+ let target = match target {
+ // Step 3.2: If target is null:
+ None => {
+ // Step 3.2.2: If target is a Window, set target to target’s associated Document.
+ if let Some(window) = self.downcast::<Window>() {
+ Trusted::new(window.Document().upcast())
+ } else {
+ // Step 3.2.1: Set target to violation’s global object.
+ Trusted::new(self.upcast())
+ }
+ },
+ Some(event_target) => Trusted::new(event_target.upcast()),
+ };
+ // Step 3: Queue a task to run the following steps:
+ let task = CSPViolationReportTask::new(Trusted::new(self), target, report);
self.task_manager()
.dom_manipulation_task_source()
.queue(task);
@@ -3524,10 +3562,6 @@ impl GlobalScopeHelpers<crate::DomTypeHolder> for GlobalScope {
GlobalScope::from_reflector(reflector, realm)
}
- unsafe fn from_object_maybe_wrapped(obj: *mut JSObject, cx: *mut JSContext) -> DomRoot<Self> {
- GlobalScope::from_object_maybe_wrapped(obj, cx)
- }
-
fn origin(&self) -> &MutableOrigin {
GlobalScope::origin(self)
}
diff --git a/components/script/dom/htmlbodyelement.rs b/components/script/dom/htmlbodyelement.rs
index b4efba9bed9..19b0ab4efce 100644
--- a/components/script/dom/htmlbodyelement.rs
+++ b/components/script/dom/htmlbodyelement.rs
@@ -181,26 +181,31 @@ impl VirtualMethods for HTMLBodyElement {
(name, AttributeMutation::Set(_)) if name.starts_with("on") => {
let window = self.owner_window();
// https://html.spec.whatwg.org/multipage/
- // #event-handlers-on-elements,-document-objects,-and-window-objects:event-handlers-3
+ // #event-handlers-on-elements,-document-objects,-and-window-objects:event-handlers-6
match name {
- &local_name!("onfocus") |
- &local_name!("onload") |
- &local_name!("onscroll") |
&local_name!("onafterprint") |
&local_name!("onbeforeprint") |
&local_name!("onbeforeunload") |
+ &local_name!("onerror") |
+ &local_name!("onfocus") |
&local_name!("onhashchange") |
+ &local_name!("onload") |
&local_name!("onlanguagechange") |
&local_name!("onmessage") |
+ &local_name!("onmessageerror") |
&local_name!("onoffline") |
&local_name!("ononline") |
&local_name!("onpagehide") |
+ &local_name!("onpagereveal") |
&local_name!("onpageshow") |
+ &local_name!("onpageswap") |
&local_name!("onpopstate") |
- &local_name!("onstorage") |
+ &local_name!("onrejectionhandled") |
&local_name!("onresize") |
- &local_name!("onunload") |
- &local_name!("onerror") => {
+ &local_name!("onscroll") |
+ &local_name!("onstorage") |
+ &local_name!("onunhandledrejection") |
+ &local_name!("onunload") => {
let source = &**attr.value();
let evtarget = window.upcast::<EventTarget>(); // forwarded event
let source_line = 1; //TODO(#9604) obtain current JS execution line
diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs
index 56e008839ba..c2bfc9c2d7f 100644
--- a/components/script/dom/htmlcanvaselement.rs
+++ b/components/script/dom/htmlcanvaselement.rs
@@ -103,10 +103,14 @@ impl EncodedImageType {
}
}
+/// <https://html.spec.whatwg.org/multipage/#htmlcanvaselement>
#[dom_struct]
pub(crate) struct HTMLCanvasElement {
htmlelement: HTMLElement,
- context: DomRefCell<Option<RenderingContext>>,
+
+ /// <https://html.spec.whatwg.org/multipage/#concept-canvas-context-mode>
+ context_mode: DomRefCell<Option<RenderingContext>>,
+
// This id and hashmap are used to keep track of ongoing toBlob() calls.
callback_id: Cell<u32>,
#[ignore_malloc_size_of = "not implemented for webidl callbacks"]
@@ -121,7 +125,7 @@ impl HTMLCanvasElement {
) -> HTMLCanvasElement {
HTMLCanvasElement {
htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
- context: DomRefCell::new(None),
+ context_mode: DomRefCell::new(None),
callback_id: Cell::new(0),
blob_callbacks: RefCell::new(HashMap::new()),
}
@@ -146,7 +150,7 @@ impl HTMLCanvasElement {
}
fn recreate_contexts_after_resize(&self) {
- if let Some(ref context) = *self.context.borrow() {
+ if let Some(ref context) = *self.context_mode.borrow() {
context.resize()
}
}
@@ -156,14 +160,14 @@ impl HTMLCanvasElement {
}
pub(crate) fn origin_is_clean(&self) -> bool {
- match *self.context.borrow() {
+ match *self.context_mode.borrow() {
Some(ref context) => context.origin_is_clean(),
_ => true,
}
}
pub(crate) fn mark_as_dirty(&self) {
- if let Some(ref context) = *self.context.borrow() {
+ if let Some(ref context) = *self.context_mode.borrow() {
context.mark_as_dirty()
}
}
@@ -193,7 +197,7 @@ impl LayoutHTMLCanvasElementHelpers for LayoutDom<'_, HTMLCanvasElement> {
#[allow(unsafe_code)]
fn data(self) -> HTMLCanvasData {
let source = unsafe {
- match self.unsafe_get().context.borrow_for_layout().as_ref() {
+ match self.unsafe_get().context_mode.borrow_for_layout().as_ref() {
Some(RenderingContext::Context2d(context)) => {
context.to_layout().canvas_data_source()
},
@@ -221,7 +225,7 @@ impl LayoutHTMLCanvasElementHelpers for LayoutDom<'_, HTMLCanvasElement> {
impl HTMLCanvasElement {
pub(crate) fn context(&self) -> Option<Ref<RenderingContext>> {
- ref_filter_map(self.context.borrow(), |ctx| ctx.as_ref())
+ ref_filter_map(self.context_mode.borrow(), |ctx| ctx.as_ref())
}
fn get_or_init_2d_context(&self, can_gc: CanGc) -> Option<DomRoot<CanvasRenderingContext2D>> {
@@ -235,7 +239,8 @@ impl HTMLCanvasElement {
let window = self.owner_window();
let size = self.get_size();
let context = CanvasRenderingContext2D::new(window.as_global_scope(), self, size, can_gc);
- *self.context.borrow_mut() = Some(RenderingContext::Context2d(Dom::from_ref(&*context)));
+ *self.context_mode.borrow_mut() =
+ Some(RenderingContext::Context2d(Dom::from_ref(&*context)));
Some(context)
}
@@ -263,7 +268,7 @@ impl HTMLCanvasElement {
attrs,
can_gc,
)?;
- *self.context.borrow_mut() = Some(RenderingContext::WebGL(Dom::from_ref(&*context)));
+ *self.context_mode.borrow_mut() = Some(RenderingContext::WebGL(Dom::from_ref(&*context)));
Some(context)
}
@@ -288,7 +293,7 @@ impl HTMLCanvasElement {
let attrs = Self::get_gl_attributes(cx, options)?;
let canvas = HTMLCanvasElementOrOffscreenCanvas::HTMLCanvasElement(DomRoot::from_ref(self));
let context = WebGL2RenderingContext::new(&window, &canvas, size, attrs, can_gc)?;
- *self.context.borrow_mut() = Some(RenderingContext::WebGL2(Dom::from_ref(&*context)));
+ *self.context_mode.borrow_mut() = Some(RenderingContext::WebGL2(Dom::from_ref(&*context)));
Some(context)
}
@@ -315,7 +320,7 @@ impl HTMLCanvasElement {
.expect("Failed to get WebGPU channel")
.map(|channel| {
let context = GPUCanvasContext::new(&global_scope, self, channel, can_gc);
- *self.context.borrow_mut() =
+ *self.context_mode.borrow_mut() =
Some(RenderingContext::WebGPU(Dom::from_ref(&*context)));
context
})
@@ -323,7 +328,7 @@ impl HTMLCanvasElement {
/// Gets the base WebGLRenderingContext for WebGL or WebGL 2, if exists.
pub(crate) fn get_base_webgl_context(&self) -> Option<DomRoot<WebGLRenderingContext>> {
- match *self.context.borrow() {
+ match *self.context_mode.borrow() {
Some(RenderingContext::WebGL(ref context)) => Some(DomRoot::from_ref(context)),
Some(RenderingContext::WebGL2(ref context)) => Some(context.base_context()),
_ => None,
@@ -352,7 +357,7 @@ impl HTMLCanvasElement {
}
pub(crate) fn get_image_data(&self) -> Option<Snapshot> {
- match self.context.borrow().as_ref() {
+ match self.context_mode.borrow().as_ref() {
Some(context) => context.get_image_data(),
None => {
let size = self.get_size();
@@ -431,12 +436,12 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
// https://html.spec.whatwg.org/multipage/#dom-canvas-width
make_uint_getter!(Width, "width", DEFAULT_WIDTH);
- // https://html.spec.whatwg.org/multipage/#dom-canvas-width
- // When setting the value of the width or height attribute, if the context mode of the canvas element
- // is set to placeholder, the user agent must throw an "InvalidStateError" DOMException and leave the
- // attribute's value unchanged.
+ /// <https://html.spec.whatwg.org/multipage/#dom-canvas-width>
fn SetWidth(&self, value: u32, can_gc: CanGc) -> Fallible<()> {
- if let Some(RenderingContext::Placeholder(_)) = *self.context.borrow() {
+ // > When setting the value of the width or height attribute, if the context mode of the canvas element
+ // > is set to placeholder, the user agent must throw an "InvalidStateError" DOMException and leave the
+ // > attribute's value unchanged.
+ if let Some(RenderingContext::Placeholder(_)) = *self.context_mode.borrow() {
return Err(Error::InvalidState);
}
@@ -453,9 +458,12 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
// https://html.spec.whatwg.org/multipage/#dom-canvas-height
make_uint_getter!(Height, "height", DEFAULT_HEIGHT);
- // https://html.spec.whatwg.org/multipage/#dom-canvas-height
+ /// <https://html.spec.whatwg.org/multipage/#dom-canvas-height>
fn SetHeight(&self, value: u32, can_gc: CanGc) -> Fallible<()> {
- if let Some(RenderingContext::Placeholder(_)) = *self.context.borrow() {
+ // > When setting the value of the width or height attribute, if the context mode of the canvas element
+ // > is set to placeholder, the user agent must throw an "InvalidStateError" DOMException and leave the
+ // > attribute's value unchanged.
+ if let Some(RenderingContext::Placeholder(_)) = *self.context_mode.borrow() {
return Err(Error::InvalidState);
}
@@ -478,7 +486,7 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
can_gc: CanGc,
) -> Fallible<Option<RootedRenderingContext>> {
// Always throw an InvalidState exception when the canvas is in Placeholder mode (See table in the spec).
- if let Some(RenderingContext::Placeholder(_)) = *self.context.borrow() {
+ if let Some(RenderingContext::Placeholder(_)) = *self.context_mode.borrow() {
return Err(Error::InvalidState);
}
@@ -622,7 +630,7 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
/// <https://html.spec.whatwg.org/multipage/#dom-canvas-transfercontroltooffscreen>
fn TransferControlToOffscreen(&self, can_gc: CanGc) -> Fallible<DomRoot<OffscreenCanvas>> {
- if self.context.borrow().is_some() {
+ if self.context_mode.borrow().is_some() {
// Step 1.
// If this canvas element's context mode is not set to none, throw an "InvalidStateError" DOMException.
return Err(Error::InvalidState);
@@ -641,8 +649,9 @@ impl HTMLCanvasElementMethods<crate::DomTypeHolder> for HTMLCanvasElement {
Some(&Dom::from_ref(self)),
can_gc,
);
+
// Step 4. Set this canvas element's context mode to placeholder.
- *self.context.borrow_mut() =
+ *self.context_mode.borrow_mut() =
Some(RenderingContext::Placeholder(offscreen_canvas.as_traced()));
// Step 5. Return offscreenCanvas.
diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs
index 59b71543d6d..f41370386e9 100644
--- a/components/script/dom/htmlelement.rs
+++ b/components/script/dom/htmlelement.rs
@@ -191,9 +191,6 @@ impl HTMLElementMethods<crate::DomTypeHolder> for HTMLElement {
// https://html.spec.whatwg.org/multipage/#globaleventhandlers
global_event_handlers!(NoOnload);
- // https://html.spec.whatwg.org/multipage/#documentandelementeventhandlers
- document_and_element_event_handlers!();
-
// https://html.spec.whatwg.org/multipage/#dom-dataset
fn Dataset(&self, can_gc: CanGc) -> DomRoot<DOMStringMap> {
self.dataset.or_init(|| DOMStringMap::new(self, can_gc))
@@ -648,13 +645,16 @@ impl HTMLElementMethods<crate::DomTypeHolder> for HTMLElement {
Ok(internals)
}
- // FIXME: The nonce should be stored in an internal slot instead of an
- // attribute (https://html.spec.whatwg.org/multipage/#cryptographicnonce)
// https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce
- make_getter!(Nonce, "nonce");
+ fn Nonce(&self) -> DOMString {
+ self.as_element().nonce_value().into()
+ }
// https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce
- make_setter!(SetNonce, "nonce");
+ fn SetNonce(&self, value: DOMString) {
+ self.as_element()
+ .update_nonce_internal_slot(value.to_string())
+ }
// https://html.spec.whatwg.org/multipage/#dom-fe-autofocus
fn Autofocus(&self) -> bool {
@@ -1141,6 +1141,15 @@ impl VirtualMethods for HTMLElement {
},
}
},
+ (&local_name!("nonce"), mutation) => match mutation {
+ AttributeMutation::Set(_) => {
+ let nonce = &**attr.value();
+ element.update_nonce_internal_slot(nonce.to_owned());
+ },
+ AttributeMutation::Removed => {
+ element.update_nonce_internal_slot("".to_owned());
+ },
+ },
_ => {},
}
}
diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs
index 0fbff86e44a..18116eee8ae 100644
--- a/components/script/dom/htmliframeelement.rs
+++ b/components/script/dom/htmliframeelement.rs
@@ -27,6 +27,8 @@ use crate::dom::attr::Attr;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods;
+use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrString;
+use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::DomGlobal;
use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom};
@@ -40,6 +42,7 @@ use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::node::{Node, NodeDamage, NodeTraits, UnbindContext};
+use crate::dom::trustedhtml::TrustedHTML;
use crate::dom::virtualmethods::VirtualMethods;
use crate::dom::windowproxy::WindowProxy;
use crate::script_runtime::CanGc;
@@ -595,10 +598,29 @@ impl HTMLIFrameElementMethods<crate::DomTypeHolder> for HTMLIFrameElement {
make_url_setter!(SetSrc, "src");
// https://html.spec.whatwg.org/multipage/#dom-iframe-srcdoc
- make_getter!(Srcdoc, "srcdoc");
+ fn Srcdoc(&self) -> TrustedHTMLOrString {
+ let element = self.upcast::<Element>();
+ element.get_trusted_html_attribute(&local_name!("srcdoc"))
+ }
// https://html.spec.whatwg.org/multipage/#dom-iframe-srcdoc
- make_setter!(SetSrcdoc, "srcdoc");
+ fn SetSrcdoc(&self, value: TrustedHTMLOrString, can_gc: CanGc) -> Fallible<()> {
+ // Step 1: Let compliantString be the result of invoking the
+ // Get Trusted Type compliant string algorithm with TrustedHTML,
+ // this's relevant global object, the given value, "HTMLIFrameElement srcdoc", and "script".
+ let element = self.upcast::<Element>();
+ let local_name = &local_name!("srcdoc");
+ let value = TrustedHTML::get_trusted_script_compliant_string(
+ &element.owner_global(),
+ value,
+ "HTMLIFrameElement",
+ local_name,
+ can_gc,
+ )?;
+ // Step 2: Set an attribute value given this, srcdoc's local name, and compliantString.
+ element.set_attribute(local_name, AttrValue::String(value), can_gc);
+ Ok(())
+ }
// https://html.spec.whatwg.org/multipage/#dom-iframe-sandbox
fn Sandbox(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> {
diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs
index adff445ae1c..a79c7f6e463 100644
--- a/components/script/dom/htmlimageelement.rs
+++ b/components/script/dom/htmlimageelement.rs
@@ -97,6 +97,7 @@ enum ParseState {
AfterDescriptor,
}
+#[derive(MallocSizeOf)]
pub(crate) struct SourceSet {
image_sources: Vec<ImageSource>,
source_size: SourceSizeList,
@@ -111,13 +112,13 @@ impl SourceSet {
}
}
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
pub struct ImageSource {
pub url: String,
pub descriptor: Descriptor,
}
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, MallocSizeOf, PartialEq)]
pub struct Descriptor {
pub width: Option<u32>,
pub density: Option<f64>,
@@ -145,7 +146,7 @@ struct ImageRequest {
parsed_url: Option<ServoUrl>,
source_url: Option<USVString>,
blocker: DomRefCell<Option<LoadBlocker>>,
- #[ignore_malloc_size_of = "Arc"]
+ #[conditional_malloc_size_of]
#[no_trace]
image: Option<Arc<Image>>,
#[no_trace]
@@ -162,7 +163,6 @@ pub(crate) struct HTMLImageElement {
pending_request: DomRefCell<ImageRequest>,
form_owner: MutNullableDom<HTMLFormElement>,
generation: Cell<u32>,
- #[ignore_malloc_size_of = "SourceSet"]
source_set: DomRefCell<SourceSet>,
last_selected_source: DomRefCell<Option<USVString>>,
#[ignore_malloc_size_of = "promises are hard"]
@@ -298,7 +298,7 @@ impl FetchResponseListener for ImageContext {
fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<csp::Violation>) {
let global = &self.resource_timing_global();
- global.report_csp_violations(violations);
+ global.report_csp_violations(violations, None);
}
}
diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs
index db5c14af450..f4e7683cf2a 100644
--- a/components/script/dom/htmllinkelement.rs
+++ b/components/script/dom/htmllinkelement.rs
@@ -31,7 +31,6 @@ use crate::dom::attr::Attr;
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::DOMTokenListBinding::DOMTokenList_Binding::DOMTokenListMethods;
use crate::dom::bindings::codegen::Bindings::HTMLLinkElementBinding::HTMLLinkElementMethods;
-use crate::dom::bindings::codegen::GenericBindings::HTMLElementBinding::HTMLElement_Binding::HTMLElementMethods;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::DomGlobal;
@@ -98,7 +97,7 @@ pub(crate) struct HTMLLinkElement {
#[no_trace]
relations: Cell<LinkRelations>,
- #[ignore_malloc_size_of = "Arc"]
+ #[conditional_malloc_size_of]
#[no_trace]
stylesheet: DomRefCell<Option<Arc<Stylesheet>>>,
cssom_stylesheet: MutNullableDom<CSSStyleSheet>,
@@ -344,7 +343,7 @@ impl HTMLLinkElement {
destination: Some(destination),
integrity: String::new(),
link_type: String::new(),
- cryptographic_nonce_metadata: self.upcast::<HTMLElement>().Nonce().into(),
+ cryptographic_nonce_metadata: self.upcast::<Element>().nonce_value(),
cross_origin: cors_setting_for_element(element),
referrer_policy: referrer_policy_for_element(element),
policy_container: document.policy_container().to_owned(),
@@ -773,7 +772,7 @@ impl FetchResponseListener for PrefetchContext {
fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<csp::Violation>) {
let global = &self.resource_timing_global();
- global.report_csp_violations(violations);
+ global.report_csp_violations(violations, None);
}
}
diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs
index 361c22c1250..391da272ef3 100644
--- a/components/script/dom/htmlmediaelement.rs
+++ b/components/script/dom/htmlmediaelement.rs
@@ -2951,7 +2951,7 @@ impl FetchResponseListener for HTMLMediaElementFetchListener {
fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<csp::Violation>) {
let global = &self.resource_timing_global();
- global.report_csp_violations(violations);
+ global.report_csp_violations(violations, None);
}
}
diff --git a/components/script/dom/htmloptgroupelement.rs b/components/script/dom/htmloptgroupelement.rs
index 55ffa92257b..f5256e71b70 100644
--- a/components/script/dom/htmloptgroupelement.rs
+++ b/components/script/dom/htmloptgroupelement.rs
@@ -135,8 +135,8 @@ impl VirtualMethods for HTMLOptGroupElement {
}
fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
- if let Some(s) = self.super_type() {
- s.bind_to_tree(context, can_gc);
+ if let Some(super_type) = self.super_type() {
+ super_type.bind_to_tree(context, can_gc);
}
self.update_select_validity(can_gc);
diff --git a/components/script/dom/htmloptionelement.rs b/components/script/dom/htmloptionelement.rs
index b573388c73a..800e88f0758 100644
--- a/components/script/dom/htmloptionelement.rs
+++ b/components/script/dom/htmloptionelement.rs
@@ -29,7 +29,7 @@ use crate::dom::htmlformelement::HTMLFormElement;
use crate::dom::htmloptgroupelement::HTMLOptGroupElement;
use crate::dom::htmlscriptelement::HTMLScriptElement;
use crate::dom::htmlselectelement::HTMLSelectElement;
-use crate::dom::node::{BindContext, Node, ShadowIncluding, UnbindContext};
+use crate::dom::node::{BindContext, ChildrenMutation, Node, ShadowIncluding, UnbindContext};
use crate::dom::text::Text;
use crate::dom::validation::Validatable;
use crate::dom::validitystate::ValidationFlags;
@@ -380,4 +380,26 @@ impl VirtualMethods for HTMLOptionElement {
el.check_disabled_attribute();
}
}
+
+ fn children_changed(&self, mutation: &ChildrenMutation) {
+ if let Some(super_type) = self.super_type() {
+ super_type.children_changed(mutation);
+ }
+
+ // Changing the descendants of a selected option can change it's displayed label
+ // if it does not have a label attribute
+ if !self
+ .upcast::<Element>()
+ .has_attribute(&local_name!("label"))
+ {
+ if let Some(owner_select) = self.owner_select_element() {
+ if owner_select
+ .selected_option()
+ .is_some_and(|selected_option| self == &*selected_option)
+ {
+ owner_select.update_shadow_tree(CanGc::note());
+ }
+ }
+ }
+ }
}
diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs
index 9d0ca807748..d1b3cfd3467 100644
--- a/components/script/dom/htmlscriptelement.rs
+++ b/components/script/dom/htmlscriptelement.rs
@@ -281,19 +281,18 @@ pub(crate) enum ScriptType {
pub(crate) struct CompiledSourceCode {
#[ignore_malloc_size_of = "SM handles JS values"]
pub(crate) source_code: Stencil,
- #[ignore_malloc_size_of = "Rc is hard"]
+ #[conditional_malloc_size_of = "Rc is hard"]
pub(crate) original_text: Rc<DOMString>,
}
-#[derive(JSTraceable)]
+#[derive(JSTraceable, MallocSizeOf)]
pub(crate) enum SourceCode {
- Text(Rc<DOMString>),
+ Text(#[conditional_malloc_size_of] Rc<DOMString>),
Compiled(CompiledSourceCode),
}
#[derive(JSTraceable, MallocSizeOf)]
pub(crate) struct ScriptOrigin {
- #[ignore_malloc_size_of = "Rc is hard"]
code: SourceCode,
#[no_trace]
url: ServoUrl,
@@ -547,7 +546,8 @@ impl FetchResponseListener for ClassicContext {
fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<csp::Violation>) {
let global = &self.resource_timing_global();
- global.report_csp_violations(violations);
+ let elem = self.elem.root();
+ global.report_csp_violations(violations, Some(elem.upcast::<Element>()));
}
}
@@ -781,7 +781,7 @@ impl HTMLScriptElement {
};
// Step 24. Let cryptographic nonce be el's [[CryptographicNonce]] internal slot's value.
- let cryptographic_nonce = self.upcast::<HTMLElement>().Nonce().into();
+ let cryptographic_nonce = self.upcast::<Element>().nonce_value();
// Step 25. If el has an integrity attribute, then let integrity metadata be that attribute's value.
// Otherwise, let integrity metadata be the empty string.
diff --git a/components/script/dom/htmlselectelement.rs b/components/script/dom/htmlselectelement.rs
index f4a62abe8b4..56fac20e841 100644
--- a/components/script/dom/htmlselectelement.rs
+++ b/components/script/dom/htmlselectelement.rs
@@ -153,7 +153,7 @@ impl HTMLSelectElement {
n
}
- // https://html.spec.whatwg.org/multipage/#concept-select-option-list
+ /// <https://html.spec.whatwg.org/multipage/#concept-select-option-list>
pub(crate) fn list_of_options(
&self,
) -> impl Iterator<Item = DomRoot<HTMLOptionElement>> + use<'_> {
@@ -353,8 +353,10 @@ impl HTMLSelectElement {
.fire_bubbling_event(atom!("change"), can_gc);
}
- fn selected_option(&self) -> Option<DomRoot<HTMLOptionElement>> {
- self.list_of_options().find(|opt_elem| opt_elem.Selected())
+ pub(crate) fn selected_option(&self) -> Option<DomRoot<HTMLOptionElement>> {
+ self.list_of_options()
+ .find(|opt_elem| opt_elem.Selected())
+ .or_else(|| self.list_of_options().next())
}
pub(crate) fn show_menu(&self, can_gc: CanGc) -> Option<usize> {
@@ -539,7 +541,8 @@ impl HTMLSelectElementMethods<crate::DomTypeHolder> for HTMLSelectElement {
/// <https://html.spec.whatwg.org/multipage/#dom-select-value>
fn Value(&self) -> DOMString {
- self.selected_option()
+ self.list_of_options()
+ .find(|opt_elem| opt_elem.Selected())
.map(|opt_elem| opt_elem.Value())
.unwrap_or_default()
}
diff --git a/components/script/dom/htmlstyleelement.rs b/components/script/dom/htmlstyleelement.rs
index 0deb507f283..aed08b7bcf6 100644
--- a/components/script/dom/htmlstyleelement.rs
+++ b/components/script/dom/htmlstyleelement.rs
@@ -4,6 +4,7 @@
use std::cell::Cell;
+use content_security_policy as csp;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
use js::rust::HandleObject;
@@ -33,7 +34,7 @@ use crate::stylesheet_loader::{StylesheetLoader, StylesheetOwner};
#[dom_struct]
pub(crate) struct HTMLStyleElement {
htmlelement: HTMLElement,
- #[ignore_malloc_size_of = "Arc"]
+ #[conditional_malloc_size_of]
#[no_trace]
stylesheet: DomRefCell<Option<Arc<Stylesheet>>>,
cssom_stylesheet: MutNullableDom<CSSStyleSheet>,
@@ -97,8 +98,21 @@ impl HTMLStyleElement {
return;
}
- let window = node.owner_window();
let doc = self.owner_document();
+
+ // Step 5: If the Should element's inline behavior be blocked by Content Security Policy? algorithm
+ // returns "Blocked" when executed upon the style element, "style",
+ // and the style element's child text content, then return. [CSP]
+ if doc.should_elements_inline_type_behavior_be_blocked(
+ self.upcast(),
+ csp::InlineCheckType::Style,
+ &node.child_text_content(),
+ ) == csp::CheckResult::Blocked
+ {
+ return;
+ }
+
+ let window = node.owner_window();
let data = node
.GetTextContent()
.expect("Element.textContent must be a string");
diff --git a/components/script/dom/htmlvideoelement.rs b/components/script/dom/htmlvideoelement.rs
index 6f27c164d02..c5d21c19d9b 100644
--- a/components/script/dom/htmlvideoelement.rs
+++ b/components/script/dom/htmlvideoelement.rs
@@ -422,7 +422,7 @@ impl FetchResponseListener for PosterFrameFetchContext {
fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<csp::Violation>) {
let global = &self.resource_timing_global();
- global.report_csp_violations(violations);
+ global.report_csp_violations(violations, None);
}
}
diff --git a/components/script/dom/imagedata.rs b/components/script/dom/imagedata.rs
index bd45a80fce2..a891064952a 100644
--- a/components/script/dom/imagedata.rs
+++ b/components/script/dom/imagedata.rs
@@ -9,13 +9,13 @@ use std::vec::Vec;
use dom_struct::dom_struct;
use euclid::default::{Rect, Size2D};
use ipc_channel::ipc::IpcSharedMemory;
-use js::jsapi::{Heap, JSObject};
+use js::gc::CustomAutoRooterGuard;
+use js::jsapi::JSObject;
use js::rust::HandleObject;
use js::typedarray::{ClampedU8, CreateWith, Uint8ClampedArray};
-use super::bindings::buffer_source::{
- BufferSource, HeapBufferSource, HeapTypedArrayInit, new_initialized_heap_buffer_source,
-};
+use super::bindings::buffer_source::{HeapBufferSource, create_heap_buffer_source_with_length};
+use crate::dom::bindings::buffer_source::create_buffer_source;
use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::ImageDataMethods;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
@@ -55,31 +55,31 @@ impl ImageData {
rooted!(in (*cx) let mut js_object = ptr::null_mut::<JSObject>());
if let Some(ref mut d) = data {
d.resize(len as usize, 0);
+
+ let typed_array =
+ create_buffer_source::<ClampedU8>(cx, &d[..], js_object.handle_mut(), can_gc)
+ .map_err(|_| Error::JSFailed)?;
+
let data = CreateWith::Slice(&d[..]);
Uint8ClampedArray::create(*cx, data, js_object.handle_mut()).unwrap();
- Self::new_with_jsobject(global, None, width, Some(height), js_object.get(), can_gc)
+ auto_root!(in(*cx) let data = typed_array);
+ Self::new_with_data(global, None, width, Some(height), data, can_gc)
} else {
- Self::new_without_jsobject(global, None, width, height, can_gc)
+ Self::new_without_data(global, None, width, height, can_gc)
}
}
}
#[allow(unsafe_code)]
- fn new_with_jsobject(
+ fn new_with_data(
global: &GlobalScope,
proto: Option<HandleObject>,
width: u32,
opt_height: Option<u32>,
- jsobject: *mut JSObject,
+ data: CustomAutoRooterGuard<Uint8ClampedArray>,
can_gc: CanGc,
) -> Fallible<DomRoot<ImageData>> {
- let heap_typed_array = match new_initialized_heap_buffer_source::<ClampedU8>(
- HeapTypedArrayInit::Buffer(BufferSource::ArrayBufferView(Heap::boxed(jsobject))),
- can_gc,
- ) {
- Ok(heap_typed_array) => heap_typed_array,
- Err(_) => return Err(Error::JSFailed),
- };
+ let heap_typed_array = HeapBufferSource::<ClampedU8>::from_view(data);
let typed_array = match heap_typed_array.get_typed_array() {
Ok(array) => array,
@@ -117,13 +117,14 @@ impl ImageData {
))
}
- fn new_without_jsobject(
+ fn new_without_data(
global: &GlobalScope,
proto: Option<HandleObject>,
width: u32,
height: u32,
can_gc: CanGc,
) -> Fallible<DomRoot<ImageData>> {
+ // If one or both of sw and sh are zero, then throw an "IndexSizeError" DOMException.
if width == 0 || height == 0 {
return Err(Error::IndexSize);
}
@@ -139,13 +140,8 @@ impl ImageData {
let cx = GlobalScope::get_cx();
- let heap_typed_array = match new_initialized_heap_buffer_source::<ClampedU8>(
- HeapTypedArrayInit::Info { len, cx },
- can_gc,
- ) {
- Ok(heap_typed_array) => heap_typed_array,
- Err(_) => return Err(Error::JSFailed),
- };
+ let heap_typed_array = create_heap_buffer_source_with_length::<ClampedU8>(cx, len, can_gc)?;
+
let imagedata = Box::new(ImageData {
reflector_: Reflector::new(),
width,
@@ -198,20 +194,19 @@ impl ImageDataMethods<crate::DomTypeHolder> for ImageData {
width: u32,
height: u32,
) -> Fallible<DomRoot<Self>> {
- Self::new_without_jsobject(global, proto, width, height, can_gc)
+ Self::new_without_data(global, proto, width, height, can_gc)
}
/// <https://html.spec.whatwg.org/multipage/#dom-imagedata-with-data>
fn Constructor_(
- _cx: JSContext,
global: &GlobalScope,
proto: Option<HandleObject>,
can_gc: CanGc,
- jsobject: *mut JSObject,
+ data: CustomAutoRooterGuard<Uint8ClampedArray>,
width: u32,
opt_height: Option<u32>,
) -> Fallible<DomRoot<Self>> {
- Self::new_with_jsobject(global, proto, width, opt_height, jsobject, can_gc)
+ Self::new_with_data(global, proto, width, opt_height, data, can_gc)
}
/// <https://html.spec.whatwg.org/multipage/#dom-imagedata-width>
diff --git a/components/script/dom/intersectionobserver.rs b/components/script/dom/intersectionobserver.rs
index ec98116d3a4..6a6f9ce45eb 100644
--- a/components/script/dom/intersectionobserver.rs
+++ b/components/script/dom/intersectionobserver.rs
@@ -524,11 +524,10 @@ impl IntersectionObserver {
// Step 9
// > Let targetArea be targetRect’s area.
- let target_area = target_rect.size.width.0 * target_rect.size.height.0;
-
// Step 10
// > Let intersectionArea be intersectionRect’s area.
- let intersection_area = intersection_rect.size.width.0 * intersection_rect.size.height.0;
+ // These steps are folded in Step 12, rewriting (w1 * h1) / (w2 * h2) as (w1 / w2) * (h1 / h2)
+ // to avoid multiplication overflows.
// Step 11
// > Let isIntersecting be true if targetRect and rootBounds intersect or are edge-adjacent,
@@ -545,9 +544,12 @@ impl IntersectionObserver {
// Step 12
// > If targetArea is non-zero, let intersectionRatio be intersectionArea divided by targetArea.
// > Otherwise, let intersectionRatio be 1 if isIntersecting is true, or 0 if isIntersecting is false.
- let intersection_ratio = match target_area {
- 0 => is_intersecting.into(),
- _ => (intersection_area as f64) / (target_area as f64),
+ let intersection_ratio = if target_rect.size.width.0 == 0 || target_rect.size.height.0 == 0
+ {
+ is_intersecting.into()
+ } else {
+ (intersection_rect.size.width.0 as f64 / target_rect.size.width.0 as f64) *
+ (intersection_rect.size.height.0 as f64 / target_rect.size.height.0 as f64)
};
// Step 13
diff --git a/components/script/dom/macros.rs b/components/script/dom/macros.rs
index cc44497d0b9..564fe810db0 100644
--- a/components/script/dom/macros.rs
+++ b/components/script/dom/macros.rs
@@ -523,21 +523,29 @@ macro_rules! global_event_handlers(
);
(NoOnload) => (
event_handler!(abort, GetOnabort, SetOnabort);
+ event_handler!(auxclick, GetOnauxclick, SetOnauxclick);
event_handler!(animationend, GetOnanimationend, SetOnanimationend);
event_handler!(animationiteration, GetOnanimationiteration, SetOnanimationiteration);
+ event_handler!(beforeinput, GetOnbeforeinput, SetOnbeforeinput);
+ event_handler!(beforematch, GetOnbeforematch, SetOnbeforematch);
+ event_handler!(beforetoggle, GetOnbeforetoggle, SetOnbeforetoggle);
event_handler!(cancel, GetOncancel, SetOncancel);
event_handler!(canplay, GetOncanplay, SetOncanplay);
event_handler!(canplaythrough, GetOncanplaythrough, SetOncanplaythrough);
event_handler!(change, GetOnchange, SetOnchange);
event_handler!(click, GetOnclick, SetOnclick);
event_handler!(close, GetOnclose, SetOnclose);
+ event_handler!(command, GetOncommand, SetOncommand);
+ event_handler!(contextlost, GetOncontextlost, SetOncontextlost);
event_handler!(contextmenu, GetOncontextmenu, SetOncontextmenu);
+ event_handler!(contextrestored, GetOncontextrestored, SetOncontextrestored);
+ event_handler!(copy, GetOncopy, SetOncopy);
event_handler!(cuechange, GetOncuechange, SetOncuechange);
+ event_handler!(cut, GetOncut, SetOncut);
event_handler!(dblclick, GetOndblclick, SetOndblclick);
event_handler!(drag, GetOndrag, SetOndrag);
event_handler!(dragend, GetOndragend, SetOndragend);
event_handler!(dragenter, GetOndragenter, SetOndragenter);
- event_handler!(dragexit, GetOndragexit, SetOndragexit);
event_handler!(dragleave, GetOndragleave, SetOndragleave);
event_handler!(dragover, GetOndragover, SetOndragover);
event_handler!(dragstart, GetOndragstart, SetOndragstart);
@@ -561,20 +569,21 @@ macro_rules! global_event_handlers(
event_handler!(mouseout, GetOnmouseout, SetOnmouseout);
event_handler!(mouseover, GetOnmouseover, SetOnmouseover);
event_handler!(mouseup, GetOnmouseup, SetOnmouseup);
- event_handler!(wheel, GetOnwheel, SetOnwheel);
+ event_handler!(paste, GetOnpaste, SetOnpaste);
event_handler!(pause, GetOnpause, SetOnpause);
event_handler!(play, GetOnplay, SetOnplay);
event_handler!(playing, GetOnplaying, SetOnplaying);
event_handler!(progress, GetOnprogress, SetOnprogress);
event_handler!(ratechange, GetOnratechange, SetOnratechange);
event_handler!(reset, GetOnreset, SetOnreset);
+ event_handler!(scrollend, GetOnscrollend, SetOnscrollend);
event_handler!(securitypolicyviolation, GetOnsecuritypolicyviolation, SetOnsecuritypolicyviolation);
event_handler!(seeked, GetOnseeked, SetOnseeked);
event_handler!(seeking, GetOnseeking, SetOnseeking);
event_handler!(select, GetOnselect, SetOnselect);
event_handler!(selectionchange, GetOnselectionchange, SetOnselectionchange);
event_handler!(selectstart, GetOnselectstart, SetOnselectstart);
- event_handler!(show, GetOnshow, SetOnshow);
+ event_handler!(slotchange, GetOnslotchange, SetOnslotchange);
event_handler!(stalled, GetOnstalled, SetOnstalled);
event_handler!(submit, GetOnsubmit, SetOnsubmit);
event_handler!(suspend, GetOnsuspend, SetOnsuspend);
@@ -585,6 +594,11 @@ macro_rules! global_event_handlers(
event_handler!(transitionrun, GetOntransitionrun, SetOntransitionrun);
event_handler!(volumechange, GetOnvolumechange, SetOnvolumechange);
event_handler!(waiting, GetOnwaiting, SetOnwaiting);
+ event_handler!(webkitanimationend, GetOnwebkitanimationend, SetOnwebkitanimationend);
+ event_handler!(webkitanimationiteration, GetOnwebkitanimationiteration, SetOnwebkitanimationiteration);
+ event_handler!(webkitanimationstart, GetOnwebkitanimationstart, SetOnwebkitanimationstart);
+ event_handler!(webkittransitionend, GetOnwebkittransitionend, SetOnwebkittransitionend);
+ event_handler!(wheel, GetOnwheel, SetOnwheel);
)
);
@@ -605,7 +619,9 @@ macro_rules! window_event_handlers(
event_handler!(offline, GetOnoffline, SetOnoffline);
event_handler!(online, GetOnonline, SetOnonline);
event_handler!(pagehide, GetOnpagehide, SetOnpagehide);
+ event_handler!(pagereveal, GetOnpagereveal, SetOnpagereveal);
event_handler!(pageshow, GetOnpageshow, SetOnpageshow);
+ event_handler!(pageswap, GetOnpageswap, SetOnpageswap);
event_handler!(popstate, GetOnpopstate, SetOnpopstate);
event_handler!(rejectionhandled, GetOnrejectionhandled,
SetOnrejectionhandled);
@@ -633,7 +649,9 @@ macro_rules! window_event_handlers(
window_owned_event_handler!(offline, GetOnoffline, SetOnoffline);
window_owned_event_handler!(online, GetOnonline, SetOnonline);
window_owned_event_handler!(pagehide, GetOnpagehide, SetOnpagehide);
+ window_owned_event_handler!(pagereveal, GetOnpagereveal, SetOnpagereveal);
window_owned_event_handler!(pageshow, GetOnpageshow, SetOnpageshow);
+ window_owned_event_handler!(pageswap, GetOnpageswap, SetOnpageswap);
window_owned_event_handler!(popstate, GetOnpopstate, SetOnpopstate);
window_owned_event_handler!(rejectionhandled, GetOnrejectionhandled,
SetOnrejectionhandled);
@@ -646,17 +664,6 @@ macro_rules! window_event_handlers(
);
);
-// https://html.spec.whatwg.org/multipage/#documentandelementeventhandlers
-// see webidls/EventHandler.webidl
-// As more methods get added, just update them here.
-macro_rules! document_and_element_event_handlers(
- () => (
- event_handler!(cut, GetOncut, SetOncut);
- event_handler!(copy, GetOncopy, SetOncopy);
- event_handler!(paste, GetOnpaste, SetOnpaste);
- )
-);
-
/// DOM struct implementation for simple interfaces inheriting from PerformanceEntry.
macro_rules! impl_performance_entry_struct(
($binding:ident, $struct:ident, $type:expr) => (
diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs
index 4bc272db8dd..91a4e1b1359 100644
--- a/components/script/dom/mod.rs
+++ b/components/script/dom/mod.rs
@@ -547,6 +547,7 @@ pub(crate) mod submitevent;
pub(crate) mod subtlecrypto;
pub(crate) mod svgelement;
pub(crate) mod svggraphicselement;
+pub(crate) mod svgimageelement;
pub(crate) mod svgsvgelement;
pub(crate) mod testbinding;
pub(crate) mod testbindingiterable;
@@ -631,6 +632,8 @@ pub(crate) mod webgpu;
pub(crate) use self::webgpu::*;
#[cfg(not(feature = "webgpu"))]
pub(crate) mod gpucanvascontext;
+pub(crate) mod transformstream;
+pub(crate) mod transformstreamdefaultcontroller;
pub(crate) mod wheelevent;
#[allow(dead_code)]
pub(crate) mod window;
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs
index e9d36a01426..5f08abce354 100644
--- a/components/script/dom/node.rs
+++ b/components/script/dom/node.rs
@@ -3110,11 +3110,15 @@ impl NodeMethods<crate::DomTypeHolder> for Node {
/// <https://dom.spec.whatwg.org/#dom-node-childnodes>
fn ChildNodes(&self, can_gc: CanGc) -> DomRoot<NodeList> {
- self.ensure_rare_data().child_list.or_init(|| {
- let doc = self.owner_doc();
- let window = doc.window();
- NodeList::new_child_list(window, self, can_gc)
- })
+ if let Some(list) = self.ensure_rare_data().child_list.get() {
+ return list;
+ }
+
+ let doc = self.owner_doc();
+ let window = doc.window();
+ let list = NodeList::new_child_list(window, self, can_gc);
+ self.ensure_rare_data().child_list.set(Some(&list));
+ list
}
/// <https://dom.spec.whatwg.org/#dom-node-firstchild>
@@ -4203,6 +4207,9 @@ impl From<ElementTypeIdWrapper> for LayoutElementType {
LayoutElementType::HTMLTextAreaElement
},
ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement(
+ SVGGraphicsElementTypeId::SVGImageElement,
+ )) => LayoutElementType::SVGImageElement,
+ ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement(
SVGGraphicsElementTypeId::SVGSVGElement,
)) => LayoutElementType::SVGSVGElement,
_ => LayoutElementType::Element,
diff --git a/components/script/dom/notification.rs b/components/script/dom/notification.rs
index 4dbb97430e5..1aecb785475 100644
--- a/components/script/dom/notification.rs
+++ b/components/script/dom/notification.rs
@@ -795,7 +795,7 @@ impl FetchResponseListener for ResourceFetchListener {
fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<csp::Violation>) {
let global = &self.resource_timing_global();
- global.report_csp_violations(violations);
+ global.report_csp_violations(violations, None);
}
}
diff --git a/components/script/dom/offscreencanvas.rs b/components/script/dom/offscreencanvas.rs
index 9947d35f4e0..bceed49ac7d 100644
--- a/components/script/dom/offscreencanvas.rs
+++ b/components/script/dom/offscreencanvas.rs
@@ -24,12 +24,20 @@ use crate::dom::htmlcanvaselement::HTMLCanvasElement;
use crate::dom::offscreencanvasrenderingcontext2d::OffscreenCanvasRenderingContext2D;
use crate::script_runtime::{CanGc, JSContext};
+/// <https://html.spec.whatwg.org/multipage/#offscreencanvas>
#[dom_struct]
pub(crate) struct OffscreenCanvas {
eventtarget: EventTarget,
width: Cell<u64>,
height: Cell<u64>,
+
+ /// Represents both the [bitmap] and the [context mode] of the canvas.
+ ///
+ /// [bitmap]: https://html.spec.whatwg.org/multipage/#offscreencanvas-bitmap
+ /// [context mode]: https://html.spec.whatwg.org/multipage/#offscreencanvas-context-mode
context: DomRefCell<Option<OffscreenRenderingContext>>,
+
+ /// <https://html.spec.whatwg.org/multipage/#offscreencanvas-placeholder>
placeholder: Option<Dom<HTMLCanvasElement>>,
}
@@ -119,7 +127,7 @@ impl OffscreenCanvas {
}
impl OffscreenCanvasMethods<crate::DomTypeHolder> for OffscreenCanvas {
- // https://html.spec.whatwg.org/multipage/#dom-offscreencanvas
+ /// <https://html.spec.whatwg.org/multipage/#dom-offscreencanvas>
fn Constructor(
global: &GlobalScope,
proto: Option<HandleObject>,
@@ -131,7 +139,7 @@ impl OffscreenCanvasMethods<crate::DomTypeHolder> for OffscreenCanvas {
Ok(offscreencanvas)
}
- // https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-getcontext
+ /// <https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-getcontext>
fn GetContext(
&self,
_cx: JSContext,
@@ -155,12 +163,12 @@ impl OffscreenCanvasMethods<crate::DomTypeHolder> for OffscreenCanvas {
}
}
- // https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-width
+ /// <https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-width>
fn Width(&self) -> u64 {
self.width.get()
}
- // https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-width
+ /// <https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-width>
fn SetWidth(&self, value: u64, can_gc: CanGc) {
self.width.set(value);
@@ -173,12 +181,12 @@ impl OffscreenCanvasMethods<crate::DomTypeHolder> for OffscreenCanvas {
}
}
- // https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-height
+ /// <https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-height>
fn Height(&self) -> u64 {
self.height.get()
}
- // https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-height
+ /// <https://html.spec.whatwg.org/multipage/#dom-offscreencanvas-height>
fn SetHeight(&self, value: u64, can_gc: CanGc) {
self.height.set(value);
diff --git a/components/script/dom/promise.rs b/components/script/dom/promise.rs
index 80b62f161bc..0efffbe6fe2 100644
--- a/components/script/dom/promise.rs
+++ b/components/script/dom/promise.rs
@@ -29,7 +29,6 @@ use js::rust::wrappers::{
ResolvePromise, SetAnyPromiseIsHandled, SetPromiseUserInputEventHandlingState,
};
use js::rust::{HandleObject, HandleValue, MutableHandleObject, Runtime};
-use script_bindings::interfaces::PromiseHelpers;
use crate::dom::bindings::conversions::root_from_object;
use crate::dom::bindings::error::{Error, ErrorToJsval};
@@ -388,16 +387,6 @@ fn create_native_handler_function(
}
}
-impl PromiseHelpers<crate::DomTypeHolder> for Promise {
- fn new_resolved(
- global: &GlobalScope,
- cx: SafeJSContext,
- value: impl ToJSValConvertible,
- ) -> Rc<Promise> {
- Promise::new_resolved(global, cx, value, CanGc::note())
- }
-}
-
impl FromJSValConvertibleRc for Promise {
#[allow(unsafe_code)]
unsafe fn from_jsval(
@@ -407,16 +396,12 @@ impl FromJSValConvertibleRc for Promise {
if value.get().is_null() {
return Ok(ConversionResult::Failure("null not allowed".into()));
}
- if !value.get().is_object() {
- return Ok(ConversionResult::Failure("not an object".into()));
- }
- rooted!(in(cx) let obj = value.get().to_object());
let cx = SafeJSContext::from_ptr(cx);
let in_realm_proof = AlreadyInRealm::assert_for_cx(cx);
let global_scope = GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof));
- let promise = Promise::new_resolved(&global_scope, cx, *obj, CanGc::note());
+ let promise = Promise::new_resolved(&global_scope, cx, value, CanGc::note());
Ok(ConversionResult::Success(promise))
}
}
diff --git a/components/script/dom/raredata.rs b/components/script/dom/raredata.rs
index 3afa000511e..0c048956217 100644
--- a/components/script/dom/raredata.rs
+++ b/components/script/dom/raredata.rs
@@ -75,4 +75,5 @@ pub(crate) struct ElementRareData {
/// > Element objects have an internal [[RegisteredIntersectionObservers]] slot,
/// > which is initialized to an empty list. This list holds IntersectionObserverRegistration records, which have:
pub(crate) registered_intersection_observers: Vec<IntersectionObserverRegistration>,
+ pub(crate) cryptographic_nonce: String,
}
diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs
index 4982bfa32e3..d631a01e1e7 100644
--- a/components/script/dom/readablestream.rs
+++ b/components/script/dom/readablestream.rs
@@ -11,6 +11,7 @@ use std::rc::Rc;
use base::id::{MessagePortId, MessagePortIndex};
use constellation_traits::MessagePortImpl;
use dom_struct::dom_struct;
+use ipc_channel::ipc::IpcSharedMemory;
use js::conversions::ToJSValConvertible;
use js::jsapi::{Heap, JSObject};
use js::jsval::{JSVal, NullValue, ObjectValue, UndefinedValue};
@@ -1131,12 +1132,14 @@ impl ReadableStream {
/// Return bytes for synchronous use, if the stream has all data in memory.
/// Useful for native source integration only.
- pub(crate) fn get_in_memory_bytes(&self) -> Option<Vec<u8>> {
+ pub(crate) fn get_in_memory_bytes(&self) -> Option<IpcSharedMemory> {
match self.controller.borrow().as_ref() {
Some(ControllerType::Default(controller)) => controller
.get()
.expect("Stream should have controller.")
- .get_in_memory_bytes(),
+ .get_in_memory_bytes()
+ .as_deref()
+ .map(IpcSharedMemory::from_bytes),
_ => {
unreachable!("Getting in-memory bytes for a stream with a non-default controller")
},
diff --git a/components/script/dom/readablestreamdefaultcontroller.rs b/components/script/dom/readablestreamdefaultcontroller.rs
index c52fb712a03..80c800d1bbe 100644
--- a/components/script/dom/readablestreamdefaultcontroller.rs
+++ b/components/script/dom/readablestreamdefaultcontroller.rs
@@ -383,7 +383,6 @@ impl ReadableStreamDefaultController {
}
/// <https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller>
- #[allow(unsafe_code)]
pub(crate) fn setup(
&self,
stream: DomRoot<ReadableStream>,
@@ -866,7 +865,6 @@ impl ReadableStreamDefaultController {
}
/// <https://streams.spec.whatwg.org/#rs-default-controller-has-backpressure>
- #[allow(unused)]
pub(crate) fn has_backpressure(&self) -> bool {
// If ! ReadableStreamDefaultControllerShouldCallPull(controller) is true, return false.
// Otherwise, return true.
diff --git a/components/script/dom/readablestreamgenericreader.rs b/components/script/dom/readablestreamgenericreader.rs
index 8ba1149bcb5..d2a109ee692 100644
--- a/components/script/dom/readablestreamgenericreader.rs
+++ b/components/script/dom/readablestreamgenericreader.rs
@@ -80,7 +80,6 @@ pub(crate) trait ReadableStreamGenericReader {
}
/// <https://streams.spec.whatwg.org/#readable-stream-reader-generic-release>
- #[allow(unsafe_code)]
fn generic_release(&self, can_gc: CanGc) -> Fallible<()> {
// Let stream be reader.[[stream]].
diff --git a/components/script/dom/securitypolicyviolationevent.rs b/components/script/dom/securitypolicyviolationevent.rs
index 3580e525e55..3c528cc5814 100644
--- a/components/script/dom/securitypolicyviolationevent.rs
+++ b/components/script/dom/securitypolicyviolationevent.rs
@@ -15,7 +15,7 @@ use crate::dom::bindings::codegen::Bindings::SecurityPolicyViolationEventBinding
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::{DOMString, USVString};
-use crate::dom::event::{Event, EventBubbles, EventCancelable};
+use crate::dom::event::{Event, EventBubbles, EventCancelable, EventComposed};
use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::CanGc;
@@ -70,12 +70,14 @@ impl SecurityPolicyViolationEvent {
)
}
+ #[allow(clippy::too_many_arguments)]
fn new_with_proto(
global: &GlobalScope,
proto: Option<HandleObject>,
type_: Atom,
bubbles: EventBubbles,
cancelable: EventCancelable,
+ composed: EventComposed,
init: &SecurityPolicyViolationEventInit,
can_gc: CanGc,
) -> DomRoot<Self> {
@@ -83,6 +85,7 @@ impl SecurityPolicyViolationEvent {
{
let event = ev.upcast::<Event>();
event.init_event(type_, bool::from(bubbles), bool::from(cancelable));
+ event.set_composed(bool::from(composed));
}
ev
}
@@ -92,10 +95,13 @@ impl SecurityPolicyViolationEvent {
type_: Atom,
bubbles: EventBubbles,
cancelable: EventCancelable,
+ composed: EventComposed,
init: &SecurityPolicyViolationEventInit,
can_gc: CanGc,
) -> DomRoot<Self> {
- Self::new_with_proto(global, None, type_, bubbles, cancelable, init, can_gc)
+ Self::new_with_proto(
+ global, None, type_, bubbles, cancelable, composed, init, can_gc,
+ )
}
}
@@ -115,6 +121,7 @@ impl SecurityPolicyViolationEventMethods<crate::DomTypeHolder> for SecurityPolic
Atom::from(type_),
EventBubbles::from(init.parent.bubbles),
EventCancelable::from(init.parent.cancelable),
+ EventComposed::from(init.parent.composed),
init,
can_gc,
)
diff --git a/components/script/dom/servoparser/html.rs b/components/script/dom/servoparser/html.rs
index 07848c87678..9dfbeda4030 100644
--- a/components/script/dom/servoparser/html.rs
+++ b/components/script/dom/servoparser/html.rs
@@ -16,6 +16,7 @@ use html5ever::{QualName, local_name, ns};
use markup5ever::TokenizerResult;
use script_bindings::trace::CustomTraceable;
use servo_url::ServoUrl;
+use style::attr::AttrValue;
use style::context::QuirksMode as StyleContextQuirksMode;
use xml5ever::LocalName;
@@ -116,18 +117,34 @@ impl Tokenizer {
}
}
-fn start_element<S: Serializer>(node: &Element, serializer: &mut S) -> io::Result<()> {
- let name = QualName::new(None, node.namespace().clone(), node.local_name().clone());
- let attrs = node
- .attrs()
- .iter()
- .map(|attr| {
- let qname = QualName::new(None, attr.namespace().clone(), attr.local_name().clone());
- let value = attr.value().clone();
- (qname, value)
- })
- .collect::<Vec<_>>();
- let attr_refs = attrs.iter().map(|(qname, value)| {
+/// <https://html.spec.whatwg.org/multipage/#html-fragment-serialisation-algorithm>
+fn start_element<S: Serializer>(element: &Element, serializer: &mut S) -> io::Result<()> {
+ let name = QualName::new(
+ None,
+ element.namespace().clone(),
+ element.local_name().clone(),
+ );
+
+ let mut attributes = vec![];
+
+ // The "is" value of an element is treated as if it was an attribute and it is serialized before all
+ // other attributes. If the element already has an "is" attribute then the "is" value is ignored.
+ if !element.has_attribute(&LocalName::from("is")) {
+ if let Some(is_value) = element.get_is() {
+ let qualified_name = QualName::new(None, ns!(), LocalName::from("is"));
+
+ attributes.push((qualified_name, AttrValue::String(is_value.to_string())));
+ }
+ }
+
+ // Collect all the "normal" attributes
+ attributes.extend(element.attrs().iter().map(|attr| {
+ let qname = QualName::new(None, attr.namespace().clone(), attr.local_name().clone());
+ let value = attr.value().clone();
+ (qname, value)
+ }));
+
+ let attr_refs = attributes.iter().map(|(qname, value)| {
let ar: AttrRef = (qname, &**value);
ar
});
@@ -302,11 +319,10 @@ pub(crate) fn serialize_html_fragment<S: Serializer>(
serializer.write_processing_instruction(pi.target(), &data)?;
},
- NodeTypeId::DocumentFragment(_) => {},
+ NodeTypeId::DocumentFragment(_) | NodeTypeId::Attr => {},
NodeTypeId::Document(_) => panic!("Can't serialize Document node itself"),
NodeTypeId::Element(_) => panic!("Element shouldn't appear here"),
- NodeTypeId::Attr => panic!("Attr shouldn't appear here"),
},
SerializationCommand::SerializeShadowRoot(shadow_root) => {
// Shadow roots are serialized as template elements with a fixed set of
diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs
index 3a1efdfb291..9e45124522a 100644
--- a/components/script/dom/servoparser/mod.rs
+++ b/components/script/dom/servoparser/mod.rs
@@ -1075,7 +1075,8 @@ impl FetchResponseListener for ParserContext {
};
let document = &parser.document;
let global = &document.global();
- global.report_csp_violations(violations);
+ // TODO(https://github.com/w3c/webappsec-csp/issues/687): Update after spec is resolved
+ global.report_csp_violations(violations, None);
}
}
diff --git a/components/script/dom/svgelement.rs b/components/script/dom/svgelement.rs
index 9c8b990826d..a380dcff5ac 100644
--- a/components/script/dom/svgelement.rs
+++ b/components/script/dom/svgelement.rs
@@ -8,12 +8,13 @@ use js::rust::HandleObject;
use script_bindings::str::DOMString;
use stylo_dom::ElementState;
+use crate::dom::attr::Attr;
use crate::dom::bindings::codegen::Bindings::SVGElementBinding::SVGElementMethods;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
use crate::dom::document::Document;
-use crate::dom::element::Element;
+use crate::dom::element::{AttributeMutation, Element};
use crate::dom::node::{Node, NodeTraits};
use crate::dom::virtualmethods::VirtualMethods;
use crate::script_runtime::CanGc;
@@ -59,11 +60,33 @@ impl SVGElement {
can_gc,
)
}
+
+ fn as_element(&self) -> &Element {
+ self.upcast::<Element>()
+ }
}
impl VirtualMethods for SVGElement {
fn super_type(&self) -> Option<&dyn VirtualMethods> {
- Some(self.upcast::<Element>() as &dyn VirtualMethods)
+ Some(self.as_element() as &dyn VirtualMethods)
+ }
+
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
+ self.super_type()
+ .unwrap()
+ .attribute_mutated(attr, mutation, can_gc);
+ let element = self.as_element();
+ if let (&local_name!("nonce"), mutation) = (attr.local_name(), mutation) {
+ match mutation {
+ AttributeMutation::Set(_) => {
+ let nonce = &**attr.value();
+ element.update_nonce_internal_slot(nonce.to_owned());
+ },
+ AttributeMutation::Removed => {
+ element.update_nonce_internal_slot(String::new());
+ },
+ }
+ }
}
}
@@ -82,13 +105,19 @@ impl SVGElementMethods<crate::DomTypeHolder> for SVGElement {
})
}
- // FIXME: The nonce should be stored in an internal slot instead of an
- // attribute (https://html.spec.whatwg.org/multipage/#cryptographicnonce)
+ // <https://html.spec.whatwg.org/multipage/#globaleventhandlers>
+ global_event_handlers!();
+
// https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce
- make_getter!(Nonce, "nonce");
+ fn Nonce(&self) -> DOMString {
+ self.as_element().nonce_value().into()
+ }
// https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce
- make_setter!(SetNonce, "nonce");
+ fn SetNonce(&self, value: DOMString) {
+ self.as_element()
+ .update_nonce_internal_slot(value.to_string())
+ }
// https://html.spec.whatwg.org/multipage/#dom-fe-autofocus
fn Autofocus(&self) -> bool {
diff --git a/components/script/dom/svgimageelement.rs b/components/script/dom/svgimageelement.rs
new file mode 100644
index 00000000000..17a5a9149d8
--- /dev/null
+++ b/components/script/dom/svgimageelement.rs
@@ -0,0 +1,96 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use dom_struct::dom_struct;
+use html5ever::{LocalName, Prefix, local_name, ns};
+use js::rust::HandleObject;
+use style::attr::AttrValue;
+
+use crate::dom::attr::Attr;
+use crate::dom::bindings::inheritance::Castable;
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::str::DOMString;
+use crate::dom::document::Document;
+use crate::dom::element::AttributeMutation;
+use crate::dom::node::{Node, NodeTraits};
+use crate::dom::svggraphicselement::SVGGraphicsElement;
+use crate::dom::virtualmethods::VirtualMethods;
+use crate::script_runtime::CanGc;
+
+/// <https://svgwg.org/svg2-draft/embedded.html#Placement>
+const DEFAULT_WIDTH: u32 = 300;
+const DEFAULT_HEIGHT: u32 = 150;
+
+#[dom_struct]
+pub(crate) struct SVGImageElement {
+ svggraphicselement: SVGGraphicsElement,
+}
+
+impl SVGImageElement {
+ fn new_inherited(
+ local_name: LocalName,
+ prefix: Option<Prefix>,
+ document: &Document,
+ ) -> SVGImageElement {
+ SVGImageElement {
+ svggraphicselement: SVGGraphicsElement::new_inherited(local_name, prefix, document),
+ }
+ }
+
+ #[cfg_attr(crown, allow(crown::unrooted_must_root))]
+ pub(crate) fn new(
+ local_name: LocalName,
+ prefix: Option<Prefix>,
+ document: &Document,
+ proto: Option<HandleObject>,
+ can_gc: CanGc,
+ ) -> DomRoot<SVGImageElement> {
+ Node::reflect_node_with_proto(
+ Box::new(SVGImageElement::new_inherited(local_name, prefix, document)),
+ document,
+ proto,
+ can_gc,
+ )
+ }
+
+ /// <https://svgwg.org/svg2-draft/linking.html#processingURL>
+ fn fetch_image_resource(&self) {
+ // TODO: Process and fetch the image resource (as HTMLImageElement).
+ // Reject any resource fetching request immediately.
+ self.owner_global()
+ .task_manager()
+ .dom_manipulation_task_source()
+ .queue_simple_event(self.upcast(), atom!("error"));
+ }
+}
+
+impl VirtualMethods for SVGImageElement {
+ fn super_type(&self) -> Option<&dyn VirtualMethods> {
+ Some(self.upcast::<SVGGraphicsElement>() as &dyn VirtualMethods)
+ }
+
+ fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
+ self.super_type()
+ .unwrap()
+ .attribute_mutated(attr, mutation, can_gc);
+ if attr.local_name() == &local_name!("href") &&
+ matches!(attr.namespace(), &ns!() | &ns!(xlink))
+ {
+ if let AttributeMutation::Set(_) = mutation {
+ self.fetch_image_resource();
+ }
+ }
+ }
+
+ fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
+ match *name {
+ local_name!("width") => AttrValue::from_u32(value.into(), DEFAULT_WIDTH),
+ local_name!("height") => AttrValue::from_u32(value.into(), DEFAULT_HEIGHT),
+ _ => self
+ .super_type()
+ .unwrap()
+ .parse_plain_attribute(name, value),
+ }
+ }
+}
diff --git a/components/script/dom/transformstream.rs b/components/script/dom/transformstream.rs
new file mode 100644
index 00000000000..0251498980d
--- /dev/null
+++ b/components/script/dom/transformstream.rs
@@ -0,0 +1,1105 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use std::cell::Cell;
+use std::collections::HashMap;
+use std::ptr::{self};
+use std::rc::Rc;
+
+use base::id::{MessagePortId, MessagePortIndex};
+use constellation_traits::MessagePortImpl;
+use dom_struct::dom_struct;
+use js::jsapi::{Heap, IsPromiseObject, JSObject};
+use js::jsval::{JSVal, ObjectValue, UndefinedValue};
+use js::rust::{HandleObject as SafeHandleObject, HandleValue as SafeHandleValue, IntoHandle};
+use script_bindings::callback::ExceptionHandling;
+use script_bindings::realms::InRealm;
+
+use super::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategySize;
+use super::bindings::structuredclone::StructuredData;
+use super::bindings::transferable::Transferable;
+use super::messageport::MessagePort;
+use super::promisenativehandler::Callback;
+use super::types::{TransformStreamDefaultController, WritableStream};
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategy;
+use crate::dom::bindings::codegen::Bindings::TransformStreamBinding::TransformStreamMethods;
+use crate::dom::bindings::codegen::Bindings::TransformerBinding::Transformer;
+use crate::dom::bindings::conversions::ConversionResult;
+use crate::dom::bindings::error::{Error, Fallible};
+use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
+use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
+use crate::dom::countqueuingstrategy::{extract_high_water_mark, extract_size_algorithm};
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::promise::Promise;
+use crate::dom::readablestream::{ReadableStream, create_readable_stream};
+use crate::dom::types::PromiseNativeHandler;
+use crate::dom::underlyingsourcecontainer::UnderlyingSourceType;
+use crate::dom::writablestream::create_writable_stream;
+use crate::dom::writablestreamdefaultcontroller::UnderlyingSinkType;
+use crate::realms::enter_realm;
+use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
+
+impl js::gc::Rootable for TransformBackPressureChangePromiseFulfillment {}
+
+/// Reacting to backpressureChangePromise as part of
+/// <https://streams.spec.whatwg.org/#transform-stream-default-sink-write-algorithm>
+#[derive(JSTraceable, MallocSizeOf)]
+#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
+struct TransformBackPressureChangePromiseFulfillment {
+ /// The result of reacting to backpressureChangePromise.
+ #[ignore_malloc_size_of = "Rc is hard"]
+ result_promise: Rc<Promise>,
+
+ #[ignore_malloc_size_of = "mozjs"]
+ chunk: Box<Heap<JSVal>>,
+
+ /// The writable used in the fulfillment steps
+ writable: Dom<WritableStream>,
+
+ controller: Dom<TransformStreamDefaultController>,
+}
+
+impl Callback for TransformBackPressureChangePromiseFulfillment {
+ /// Reacting to backpressureChangePromise with the following fulfillment steps:
+ fn callback(&self, cx: SafeJSContext, _v: SafeHandleValue, _realm: InRealm, can_gc: CanGc) {
+ // Let writable be stream.[[writable]].
+ // Let state be writable.[[state]].
+ // If state is "erroring", throw writable.[[storedError]].
+ if self.writable.is_erroring() {
+ rooted!(in(*cx) let mut error = UndefinedValue());
+ self.writable.get_stored_error(error.handle_mut());
+ self.result_promise.reject(cx, error.handle(), can_gc);
+ return;
+ }
+
+ // Assert: state is "writable".
+ assert!(self.writable.is_writable());
+
+ // Return ! TransformStreamDefaultControllerPerformTransform(controller, chunk).
+ rooted!(in(*cx) let mut chunk = UndefinedValue());
+ chunk.set(self.chunk.get());
+ let transform_result = self
+ .controller
+ .transform_stream_default_controller_perform_transform(
+ cx,
+ &self.writable.global(),
+ chunk.handle(),
+ can_gc,
+ )
+ .expect("perform transform failed");
+
+ // PerformTransformFulfillment and PerformTransformRejection do not need
+ // to be rooted because they only contain an Rc.
+ let handler = PromiseNativeHandler::new(
+ &self.writable.global(),
+ Some(Box::new(PerformTransformFulfillment {
+ result_promise: self.result_promise.clone(),
+ })),
+ Some(Box::new(PerformTransformRejection {
+ result_promise: self.result_promise.clone(),
+ })),
+ can_gc,
+ );
+
+ let realm = enter_realm(&*self.writable.global());
+ let comp = InRealm::Entered(&realm);
+ transform_result.append_native_handler(&handler, comp, can_gc);
+ }
+}
+
+#[derive(JSTraceable, MallocSizeOf)]
+#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
+/// Reacting to fulfillment of performTransform as part of
+/// <https://streams.spec.whatwg.org/#transform-stream-default-sink-write-algorithm>
+struct PerformTransformFulfillment {
+ #[ignore_malloc_size_of = "Rc is hard"]
+ result_promise: Rc<Promise>,
+}
+
+impl Callback for PerformTransformFulfillment {
+ fn callback(&self, _cx: SafeJSContext, _v: SafeHandleValue, _realm: InRealm, can_gc: CanGc) {
+ // Fulfilled: resolve the outer promise
+ self.result_promise.resolve_native(&(), can_gc);
+ }
+}
+
+#[derive(JSTraceable, MallocSizeOf)]
+#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
+/// Reacting to rejection of performTransform as part of
+/// <https://streams.spec.whatwg.org/#transform-stream-default-sink-write-algorithm>
+struct PerformTransformRejection {
+ #[ignore_malloc_size_of = "Rc is hard"]
+ result_promise: Rc<Promise>,
+}
+
+impl Callback for PerformTransformRejection {
+ fn callback(&self, cx: SafeJSContext, v: SafeHandleValue, _realm: InRealm, can_gc: CanGc) {
+ // Stream already errored in perform_transform, just reject result_promise
+ self.result_promise.reject(cx, v, can_gc);
+ }
+}
+
+#[derive(JSTraceable, MallocSizeOf)]
+#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
+/// Reacting to rejection of backpressureChangePromise as part of
+/// <https://streams.spec.whatwg.org/#transform-stream-default-sink-write-algorithm>
+struct BackpressureChangeRejection {
+ #[ignore_malloc_size_of = "Rc is hard"]
+ result_promise: Rc<Promise>,
+}
+
+impl Callback for BackpressureChangeRejection {
+ fn callback(&self, cx: SafeJSContext, reason: SafeHandleValue, _realm: InRealm, can_gc: CanGc) {
+ self.result_promise.reject(cx, reason, can_gc);
+ }
+}
+
+impl js::gc::Rootable for CancelPromiseFulfillment {}
+
+/// Reacting to fulfillment of the cancelpromise as part of
+/// <https://streams.spec.whatwg.org/#transform-stream-default-sink-abort-algorithm>
+#[derive(JSTraceable, MallocSizeOf)]
+#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
+struct CancelPromiseFulfillment {
+ readable: Dom<ReadableStream>,
+ controller: Dom<TransformStreamDefaultController>,
+ #[ignore_malloc_size_of = "mozjs"]
+ reason: Box<Heap<JSVal>>,
+}
+
+impl Callback for CancelPromiseFulfillment {
+ /// Reacting to backpressureChangePromise with the following fulfillment steps:
+ fn callback(&self, cx: SafeJSContext, _v: SafeHandleValue, _realm: InRealm, can_gc: CanGc) {
+ // If readable.[[state]] is "errored", reject controller.[[finishPromise]] with readable.[[storedError]].
+ if self.readable.is_errored() {
+ rooted!(in(*cx) let mut error = UndefinedValue());
+ self.readable.get_stored_error(error.handle_mut());
+ self.controller
+ .get_finish_promise()
+ .expect("finish promise is not set")
+ .reject_native(&error.handle(), can_gc);
+ } else {
+ // Otherwise:
+ // Perform ! ReadableStreamDefaultControllerError(readable.[[controller]], reason).
+ rooted!(in(*cx) let mut reason = UndefinedValue());
+ reason.set(self.reason.get());
+ self.readable
+ .get_default_controller()
+ .error(reason.handle(), can_gc);
+
+ // Resolve controller.[[finishPromise]] with undefined.
+ self.controller
+ .get_finish_promise()
+ .expect("finish promise is not set")
+ .resolve_native(&(), can_gc);
+ }
+ }
+}
+
+impl js::gc::Rootable for CancelPromiseRejection {}
+
+/// Reacting to rejection of cancelpromise as part of
+/// <https://streams.spec.whatwg.org/#transform-stream-default-sink-abort-algorithm>
+#[derive(JSTraceable, MallocSizeOf)]
+#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
+struct CancelPromiseRejection {
+ readable: Dom<ReadableStream>,
+ controller: Dom<TransformStreamDefaultController>,
+}
+
+impl Callback for CancelPromiseRejection {
+ /// Reacting to backpressureChangePromise with the following fulfillment steps:
+ fn callback(&self, cx: SafeJSContext, v: SafeHandleValue, _realm: InRealm, can_gc: CanGc) {
+ // Perform ! ReadableStreamDefaultControllerError(readable.[[controller]], r).
+ self.readable.get_default_controller().error(v, can_gc);
+
+ // Reject controller.[[finishPromise]] with r.
+ self.controller
+ .get_finish_promise()
+ .expect("finish promise is not set")
+ .reject(cx, v, can_gc);
+ }
+}
+
+impl js::gc::Rootable for SourceCancelPromiseFulfillment {}
+
+/// Reacting to fulfillment of the cancelpromise as part of
+/// <https://streams.spec.whatwg.org/#transform-stream-default-source-cancel>
+#[derive(JSTraceable, MallocSizeOf)]
+#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
+struct SourceCancelPromiseFulfillment {
+ writeable: Dom<WritableStream>,
+ controller: Dom<TransformStreamDefaultController>,
+ stream: Dom<TransformStream>,
+ #[ignore_malloc_size_of = "mozjs"]
+ reason: Box<Heap<JSVal>>,
+}
+
+impl Callback for SourceCancelPromiseFulfillment {
+ /// Reacting to backpressureChangePromise with the following fulfillment steps:
+ fn callback(&self, cx: SafeJSContext, _v: SafeHandleValue, _realm: InRealm, can_gc: CanGc) {
+ // If cancelPromise was fulfilled, then:
+ let finish_promise = self
+ .controller
+ .get_finish_promise()
+ .expect("finish promise is not set");
+
+ let global = &self.writeable.global();
+ // If writable.[[state]] is "errored", reject controller.[[finishPromise]] with writable.[[storedError]].
+ if self.writeable.is_errored() {
+ rooted!(in(*cx) let mut error = UndefinedValue());
+ self.writeable.get_stored_error(error.handle_mut());
+ finish_promise.reject(cx, error.handle(), can_gc);
+ } else {
+ // Otherwise:
+ // Perform ! WritableStreamDefaultControllerErrorIfNeeded(writable.[[controller]], reason).
+ rooted!(in(*cx) let mut reason = UndefinedValue());
+ reason.set(self.reason.get());
+ self.writeable.get_default_controller().error_if_needed(
+ cx,
+ reason.handle(),
+ global,
+ can_gc,
+ );
+
+ // Perform ! TransformStreamUnblockWrite(stream).
+ self.stream.unblock_write(global, can_gc);
+
+ // Resolve controller.[[finishPromise]] with undefined.
+ finish_promise.resolve_native(&(), can_gc);
+ }
+ }
+}
+
+impl js::gc::Rootable for SourceCancelPromiseRejection {}
+
+/// Reacting to rejection of cancelpromise as part of
+/// <https://streams.spec.whatwg.org/#transform-stream-default-source-cancel>
+#[derive(JSTraceable, MallocSizeOf)]
+#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
+struct SourceCancelPromiseRejection {
+ writeable: Dom<WritableStream>,
+ controller: Dom<TransformStreamDefaultController>,
+ stream: Dom<TransformStream>,
+}
+
+impl Callback for SourceCancelPromiseRejection {
+ /// Reacting to backpressureChangePromise with the following fulfillment steps:
+ fn callback(&self, cx: SafeJSContext, v: SafeHandleValue, _realm: InRealm, can_gc: CanGc) {
+ // Perform ! WritableStreamDefaultControllerErrorIfNeeded(writable.[[controller]], r).
+ let global = &self.writeable.global();
+
+ self.writeable
+ .get_default_controller()
+ .error_if_needed(cx, v, global, can_gc);
+
+ // Perform ! TransformStreamUnblockWrite(stream).
+ self.stream.unblock_write(global, can_gc);
+
+ // Reject controller.[[finishPromise]] with r.
+ self.controller
+ .get_finish_promise()
+ .expect("finish promise is not set")
+ .reject(cx, v, can_gc);
+ }
+}
+
+impl js::gc::Rootable for FlushPromiseFulfillment {}
+
+/// Reacting to fulfillment of the flushpromise as part of
+/// <https://streams.spec.whatwg.org/#transform-stream-default-sink-close-algorithm>
+#[derive(JSTraceable, MallocSizeOf)]
+#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
+struct FlushPromiseFulfillment {
+ readable: Dom<ReadableStream>,
+ controller: Dom<TransformStreamDefaultController>,
+}
+
+impl Callback for FlushPromiseFulfillment {
+ /// Reacting to flushpromise with the following fulfillment steps:
+ fn callback(&self, cx: SafeJSContext, _v: SafeHandleValue, _realm: InRealm, can_gc: CanGc) {
+ // If flushPromise was fulfilled, then:
+ let finish_promise = self
+ .controller
+ .get_finish_promise()
+ .expect("finish promise is not set");
+
+ // If readable.[[state]] is "errored", reject controller.[[finishPromise]] with readable.[[storedError]].
+ if self.readable.is_errored() {
+ rooted!(in(*cx) let mut error = UndefinedValue());
+ self.readable.get_stored_error(error.handle_mut());
+ finish_promise.reject(cx, error.handle(), can_gc);
+ } else {
+ // Otherwise:
+ // Perform ! ReadableStreamDefaultControllerClose(readable.[[controller]]).
+ self.readable.get_default_controller().close(can_gc);
+
+ // Resolve controller.[[finishPromise]] with undefined.
+ finish_promise.resolve_native(&(), can_gc);
+ }
+ }
+}
+
+impl js::gc::Rootable for FlushPromiseRejection {}
+/// Reacting to rejection of flushpromise as part of
+/// <https://streams.spec.whatwg.org/#transform-stream-default-sink-close-algorithm>
+
+#[derive(JSTraceable, MallocSizeOf)]
+#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
+struct FlushPromiseRejection {
+ readable: Dom<ReadableStream>,
+ controller: Dom<TransformStreamDefaultController>,
+}
+
+impl Callback for FlushPromiseRejection {
+ /// Reacting to flushpromise with the following fulfillment steps:
+ fn callback(&self, cx: SafeJSContext, v: SafeHandleValue, _realm: InRealm, can_gc: CanGc) {
+ // If flushPromise was rejected with reason r, then:
+ // Perform ! ReadableStreamDefaultControllerError(readable.[[controller]], r).
+ self.readable.get_default_controller().error(v, can_gc);
+
+ // Reject controller.[[finishPromise]] with r.
+ self.controller
+ .get_finish_promise()
+ .expect("finish promise is not set")
+ .reject(cx, v, can_gc);
+ }
+}
+
+/// <https://streams.spec.whatwg.org/#ts-class>
+#[dom_struct]
+pub struct TransformStream {
+ reflector_: Reflector,
+
+ /// <https://streams.spec.whatwg.org/#transformstream-backpressure>
+ backpressure: Cell<bool>,
+
+ /// <https://streams.spec.whatwg.org/#transformstream-backpressurechangepromise>
+ #[ignore_malloc_size_of = "Rc is hard"]
+ backpressure_change_promise: DomRefCell<Option<Rc<Promise>>>,
+
+ /// <https://streams.spec.whatwg.org/#transformstream-controller>
+ controller: MutNullableDom<TransformStreamDefaultController>,
+
+ /// <https://streams.spec.whatwg.org/#transformstream-detached>
+ detached: Cell<bool>,
+
+ /// <https://streams.spec.whatwg.org/#transformstream-readable>
+ readable: MutNullableDom<ReadableStream>,
+
+ /// <https://streams.spec.whatwg.org/#transformstream-writable>
+ writable: MutNullableDom<WritableStream>,
+}
+
+impl TransformStream {
+ #[cfg_attr(crown, allow(crown::unrooted_must_root))]
+ /// <https://streams.spec.whatwg.org/#initialize-transform-stream>
+ fn new_inherited() -> TransformStream {
+ TransformStream {
+ reflector_: Reflector::new(),
+ backpressure: Default::default(),
+ backpressure_change_promise: DomRefCell::new(None),
+ controller: MutNullableDom::new(None),
+ detached: Cell::new(false),
+ readable: MutNullableDom::new(None),
+ writable: MutNullableDom::new(None),
+ }
+ }
+
+ pub(crate) fn new_with_proto(
+ global: &GlobalScope,
+ proto: Option<SafeHandleObject>,
+ can_gc: CanGc,
+ ) -> DomRoot<TransformStream> {
+ reflect_dom_object_with_proto(
+ Box::new(TransformStream::new_inherited()),
+ global,
+ proto,
+ can_gc,
+ )
+ }
+
+ pub(crate) fn get_controller(&self) -> DomRoot<TransformStreamDefaultController> {
+ self.controller.get().expect("controller is not set")
+ }
+
+ pub(crate) fn get_writable(&self) -> DomRoot<WritableStream> {
+ self.writable.get().expect("writable stream is not set")
+ }
+
+ pub(crate) fn get_readable(&self) -> DomRoot<ReadableStream> {
+ self.readable.get().expect("readable stream is not set")
+ }
+
+ pub(crate) fn get_backpressure(&self) -> bool {
+ self.backpressure.get()
+ }
+
+ /// <https://streams.spec.whatwg.org/#initialize-transform-stream>
+ #[allow(clippy::too_many_arguments)]
+ fn initialize(
+ &self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ start_promise: Rc<Promise>,
+ writable_high_water_mark: f64,
+ writable_size_algorithm: Rc<QueuingStrategySize>,
+ readable_high_water_mark: f64,
+ readable_size_algorithm: Rc<QueuingStrategySize>,
+ can_gc: CanGc,
+ ) -> Fallible<()> {
+ // Let startAlgorithm be an algorithm that returns startPromise.
+ // Let writeAlgorithm be the following steps, taking a chunk argument:
+ // Return ! TransformStreamDefaultSinkWriteAlgorithm(stream, chunk).
+ // Let abortAlgorithm be the following steps, taking a reason argument:
+ // Return ! TransformStreamDefaultSinkAbortAlgorithm(stream, reason).
+ // Let closeAlgorithm be the following steps:
+ // Return ! TransformStreamDefaultSinkCloseAlgorithm(stream).
+ // Set stream.[[writable]] to ! CreateWritableStream(startAlgorithm, writeAlgorithm,
+ // closeAlgorithm, abortAlgorithm, writableHighWaterMark, writableSizeAlgorithm).
+ // Note: Those steps are implemented using UnderlyingSinkType::Transform.
+
+ let writable = create_writable_stream(
+ cx,
+ global,
+ writable_high_water_mark,
+ writable_size_algorithm,
+ UnderlyingSinkType::Transform(Dom::from_ref(self), start_promise.clone()),
+ can_gc,
+ )?;
+ self.writable.set(Some(&writable));
+
+ // Let pullAlgorithm be the following steps:
+
+ // Return ! TransformStreamDefaultSourcePullAlgorithm(stream).
+
+ // Let cancelAlgorithm be the following steps, taking a reason argument:
+
+ // Return ! TransformStreamDefaultSourceCancelAlgorithm(stream, reason).
+
+ // Set stream.[[readable]] to ! CreateReadableStream(startAlgorithm, pullAlgorithm,
+ // cancelAlgorithm, readableHighWaterMark, readableSizeAlgorithm).
+
+ let readable = create_readable_stream(
+ global,
+ UnderlyingSourceType::Transform(Dom::from_ref(self), start_promise.clone()),
+ Some(readable_size_algorithm),
+ Some(readable_high_water_mark),
+ can_gc,
+ );
+ self.readable.set(Some(&readable));
+
+ // Set stream.[[backpressure]] and stream.[[backpressureChangePromise]] to undefined.
+ // Note: This is done in the constructor.
+
+ // Perform ! TransformStreamSetBackpressure(stream, true).
+ self.set_backpressure(global, true, can_gc);
+
+ // Set stream.[[controller]] to undefined.
+ self.controller.set(None);
+
+ Ok(())
+ }
+
+ /// <https://streams.spec.whatwg.org/#transform-stream-set-backpressure>
+ pub(crate) fn set_backpressure(&self, global: &GlobalScope, backpressure: bool, can_gc: CanGc) {
+ // Assert: stream.[[backpressure]] is not backpressure.
+ assert!(self.backpressure.get() != backpressure);
+
+ // If stream.[[backpressureChangePromise]] is not undefined, resolve
+ // stream.[[backpressureChangePromise]] with undefined.
+ if let Some(promise) = self.backpressure_change_promise.borrow_mut().take() {
+ promise.resolve_native(&(), can_gc);
+ }
+
+ // Set stream.[[backpressureChangePromise]] to a new promise.;
+ *self.backpressure_change_promise.borrow_mut() = Some(Promise::new(global, can_gc));
+
+ // Set stream.[[backpressure]] to backpressure.
+ self.backpressure.set(backpressure);
+ }
+
+ /// <https://streams.spec.whatwg.org/#set-up-transform-stream-default-controller>
+ fn set_up_transform_stream_default_controller(
+ &self,
+ controller: &TransformStreamDefaultController,
+ ) {
+ // Assert: stream implements TransformStream.
+ // Note: this is checked with type.
+
+ // Assert: stream.[[controller]] is undefined.
+ assert!(self.controller.get().is_none());
+
+ // Set controller.[[stream]] to stream.
+ controller.set_stream(self);
+
+ // Set stream.[[controller]] to controller.
+ self.controller.set(Some(controller));
+
+ // Set controller.[[transformAlgorithm]] to transformAlgorithm.
+ // Set controller.[[flushAlgorithm]] to flushAlgorithm.
+ // Set controller.[[cancelAlgorithm]] to cancelAlgorithm.
+ // Note: These are set in the constructor.
+ }
+
+ /// <https://streams.spec.whatwg.org/#set-up-transform-stream-default-controller-from-transformer>
+ fn set_up_transform_stream_default_controller_from_transformer(
+ &self,
+ global: &GlobalScope,
+ transformer_obj: SafeHandleObject,
+ transformer: &Transformer,
+ can_gc: CanGc,
+ ) {
+ // Let controller be a new TransformStreamDefaultController.
+ let controller = TransformStreamDefaultController::new(global, transformer, can_gc);
+
+ // Let transformAlgorithm be the following steps, taking a chunk argument:
+ // Let result be TransformStreamDefaultControllerEnqueue(controller, chunk).
+ // If result is an abrupt completion, return a promise rejected with result.[[Value]].
+ // Otherwise, return a promise resolved with undefined.
+
+ // Let flushAlgorithm be an algorithm which returns a promise resolved with undefined.
+ // Let cancelAlgorithm be an algorithm which returns a promise resolved with undefined.
+
+ // If transformerDict["transform"] exists, set transformAlgorithm to an algorithm which
+ // takes an argument
+ // chunk and returns the result of invoking transformerDict["transform"] with argument
+ // list « chunk, controller »
+ // and callback this value transformer.
+
+ // If transformerDict["flush"] exists, set flushAlgorithm to an algorithm which returns
+ // the result
+ // of invoking transformerDict["flush"] with argument list « controller » and callback
+ // this value transformer.
+
+ // If transformerDict["cancel"] exists, set cancelAlgorithm to an algorithm which takes an argument
+ // reason and returns the result of invoking transformerDict["cancel"] with argument list « reason »
+ // and callback this value transformer.
+ controller.set_transform_obj(transformer_obj);
+
+ // Perform ! SetUpTransformStreamDefaultController(stream, controller,
+ // transformAlgorithm, flushAlgorithm, cancelAlgorithm).
+ self.set_up_transform_stream_default_controller(&controller);
+ }
+
+ /// <https://streams.spec.whatwg.org/#transform-stream-default-sink-write-algorithm>
+ pub(crate) fn transform_stream_default_sink_write_algorithm(
+ &self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ chunk: SafeHandleValue,
+ can_gc: CanGc,
+ ) -> Fallible<Rc<Promise>> {
+ // Assert: stream.[[writable]].[[state]] is "writable".
+ assert!(self.writable.get().is_some());
+
+ // Let controller be stream.[[controller]].
+ let controller = self.controller.get().expect("controller is not set");
+
+ // If stream.[[backpressure]] is true,
+ if self.backpressure.get() {
+ // Let backpressureChangePromise be stream.[[backpressureChangePromise]].
+ let backpressure_change_promise = self.backpressure_change_promise.borrow();
+
+ // Assert: backpressureChangePromise is not undefined.
+ assert!(backpressure_change_promise.is_some());
+
+ // Return the result of reacting to backpressureChangePromise with the following fulfillment steps:
+ let result_promise = Promise::new(global, can_gc);
+ rooted!(in(*cx) let mut fulfillment_handler = Some(TransformBackPressureChangePromiseFulfillment {
+ controller: Dom::from_ref(&controller),
+ writable: Dom::from_ref(&self.writable.get().expect("writable stream")),
+ chunk: Heap::boxed(chunk.get()),
+ result_promise: result_promise.clone(),
+ }));
+
+ let handler = PromiseNativeHandler::new(
+ global,
+ fulfillment_handler.take().map(|h| Box::new(h) as Box<_>),
+ Some(Box::new(BackpressureChangeRejection {
+ result_promise: result_promise.clone(),
+ })),
+ can_gc,
+ );
+ let realm = enter_realm(global);
+ let comp = InRealm::Entered(&realm);
+ backpressure_change_promise
+ .as_ref()
+ .expect("Promise must be some by now.")
+ .append_native_handler(&handler, comp, can_gc);
+
+ return Ok(result_promise);
+ }
+
+ // Return ! TransformStreamDefaultControllerPerformTransform(controller, chunk).
+ controller.transform_stream_default_controller_perform_transform(cx, global, chunk, can_gc)
+ }
+
+ /// <https://streams.spec.whatwg.org/#transform-stream-default-sink-abort-algorithm>
+ pub(crate) fn transform_stream_default_sink_abort_algorithm(
+ &self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ reason: SafeHandleValue,
+ can_gc: CanGc,
+ ) -> Fallible<Rc<Promise>> {
+ // Let controller be stream.[[controller]].
+ let controller = self.controller.get().expect("controller is not set");
+
+ // If controller.[[finishPromise]] is not undefined, return controller.[[finishPromise]].
+ if let Some(finish_promise) = controller.get_finish_promise() {
+ return Ok(finish_promise);
+ }
+
+ // Let readable be stream.[[readable]].
+ let readable = self.readable.get().expect("readable stream is not set");
+
+ // Let controller.[[finishPromise]] be a new promise.
+ controller.set_finish_promise(Promise::new(global, can_gc));
+
+ // Let cancelPromise be the result of performing controller.[[cancelAlgorithm]], passing reason.
+ let cancel_promise = controller.perform_cancel(cx, global, reason, can_gc)?;
+
+ // Perform ! TransformStreamDefaultControllerClearAlgorithms(controller).
+ controller.clear_algorithms();
+
+ // React to cancelPromise:
+ let handler = PromiseNativeHandler::new(
+ global,
+ Some(Box::new(CancelPromiseFulfillment {
+ readable: Dom::from_ref(&readable),
+ controller: Dom::from_ref(&controller),
+ reason: Heap::boxed(reason.get()),
+ })),
+ Some(Box::new(CancelPromiseRejection {
+ readable: Dom::from_ref(&readable),
+ controller: Dom::from_ref(&controller),
+ })),
+ can_gc,
+ );
+ let realm = enter_realm(global);
+ let comp = InRealm::Entered(&realm);
+ cancel_promise.append_native_handler(&handler, comp, can_gc);
+
+ // Return controller.[[finishPromise]].
+ let finish_promise = controller
+ .get_finish_promise()
+ .expect("finish promise is not set");
+ Ok(finish_promise)
+ }
+
+ /// <https://streams.spec.whatwg.org/#transform-stream-default-sink-close-algorithm>
+ pub(crate) fn transform_stream_default_sink_close_algorithm(
+ &self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ can_gc: CanGc,
+ ) -> Fallible<Rc<Promise>> {
+ // Let controller be stream.[[controller]].
+ let controller = self
+ .controller
+ .get()
+ .ok_or(Error::Type("controller is not set".to_string()))?;
+
+ // If controller.[[finishPromise]] is not undefined, return controller.[[finishPromise]].
+ if let Some(finish_promise) = controller.get_finish_promise() {
+ return Ok(finish_promise);
+ }
+
+ // Let readable be stream.[[readable]].
+ let readable = self
+ .readable
+ .get()
+ .ok_or(Error::Type("readable stream is not set".to_string()))?;
+
+ // Let controller.[[finishPromise]] be a new promise.
+ controller.set_finish_promise(Promise::new(global, can_gc));
+
+ // Let flushPromise be the result of performing controller.[[flushAlgorithm]].
+ let flush_promise = controller.perform_flush(cx, global, can_gc)?;
+
+ // Perform ! TransformStreamDefaultControllerClearAlgorithms(controller).
+ controller.clear_algorithms();
+
+ // React to flushPromise:
+ let handler = PromiseNativeHandler::new(
+ global,
+ Some(Box::new(FlushPromiseFulfillment {
+ readable: Dom::from_ref(&readable),
+ controller: Dom::from_ref(&controller),
+ })),
+ Some(Box::new(FlushPromiseRejection {
+ readable: Dom::from_ref(&readable),
+ controller: Dom::from_ref(&controller),
+ })),
+ can_gc,
+ );
+
+ let realm = enter_realm(global);
+ let comp = InRealm::Entered(&realm);
+ flush_promise.append_native_handler(&handler, comp, can_gc);
+ // Return controller.[[finishPromise]].
+ let finish_promise = controller
+ .get_finish_promise()
+ .expect("finish promise is not set");
+ Ok(finish_promise)
+ }
+
+ /// <https://streams.spec.whatwg.org/#transform-stream-default-source-cancel>
+ pub(crate) fn transform_stream_default_source_cancel(
+ &self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ reason: SafeHandleValue,
+ can_gc: CanGc,
+ ) -> Fallible<Rc<Promise>> {
+ // Let controller be stream.[[controller]].
+ let controller = self
+ .controller
+ .get()
+ .ok_or(Error::Type("controller is not set".to_string()))?;
+
+ // If controller.[[finishPromise]] is not undefined, return controller.[[finishPromise]].
+ if let Some(finish_promise) = controller.get_finish_promise() {
+ return Ok(finish_promise);
+ }
+
+ // Let writable be stream.[[writable]].
+ let writable = self
+ .writable
+ .get()
+ .ok_or(Error::Type("writable stream is not set".to_string()))?;
+
+ // Let controller.[[finishPromise]] be a new promise.
+ controller.set_finish_promise(Promise::new(global, can_gc));
+
+ // Let cancelPromise be the result of performing controller.[[cancelAlgorithm]], passing reason.
+ let cancel_promise = controller.perform_cancel(cx, global, reason, can_gc)?;
+
+ // Perform ! TransformStreamDefaultControllerClearAlgorithms(controller).
+ controller.clear_algorithms();
+
+ // React to cancelPromise:
+ let handler = PromiseNativeHandler::new(
+ global,
+ Some(Box::new(SourceCancelPromiseFulfillment {
+ writeable: Dom::from_ref(&writable),
+ controller: Dom::from_ref(&controller),
+ stream: Dom::from_ref(self),
+ reason: Heap::boxed(reason.get()),
+ })),
+ Some(Box::new(SourceCancelPromiseRejection {
+ writeable: Dom::from_ref(&writable),
+ controller: Dom::from_ref(&controller),
+ stream: Dom::from_ref(self),
+ })),
+ can_gc,
+ );
+
+ // Return controller.[[finishPromise]].
+ let finish_promise = controller
+ .get_finish_promise()
+ .expect("finish promise is not set");
+ let realm = enter_realm(global);
+ let comp = InRealm::Entered(&realm);
+ cancel_promise.append_native_handler(&handler, comp, can_gc);
+ Ok(finish_promise)
+ }
+
+ /// <https://streams.spec.whatwg.org/#transform-stream-default-source-pull>
+ pub(crate) fn transform_stream_default_source_pull(
+ &self,
+ global: &GlobalScope,
+ can_gc: CanGc,
+ ) -> Fallible<Rc<Promise>> {
+ // Assert: stream.[[backpressure]] is true.
+ assert!(self.backpressure.get());
+
+ // Assert: stream.[[backpressureChangePromise]] is not undefined.
+ assert!(self.backpressure_change_promise.borrow().is_some());
+
+ // Perform ! TransformStreamSetBackpressure(stream, false).
+ self.set_backpressure(global, false, can_gc);
+
+ // Return stream.[[backpressureChangePromise]].
+ Ok(self
+ .backpressure_change_promise
+ .borrow()
+ .clone()
+ .expect("Promise must be some by now."))
+ }
+
+ /// <https://streams.spec.whatwg.org/#transform-stream-error-writable-and-unblock-write>
+ pub(crate) fn error_writable_and_unblock_write(
+ &self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ error: SafeHandleValue,
+ can_gc: CanGc,
+ ) {
+ // Perform ! TransformStreamDefaultControllerClearAlgorithms(stream.[[controller]]).
+ self.get_controller().clear_algorithms();
+
+ // Perform ! WritableStreamDefaultControllerErrorIfNeeded(stream.[[writable]].[[controller]], e).
+ self.get_writable()
+ .get_default_controller()
+ .error_if_needed(cx, error, global, can_gc);
+
+ // Perform ! TransformStreamUnblockWrite(stream).
+ self.unblock_write(global, can_gc)
+ }
+
+ /// <https://streams.spec.whatwg.org/#transform-stream-unblock-write>
+ pub(crate) fn unblock_write(&self, global: &GlobalScope, can_gc: CanGc) {
+ // If stream.[[backpressure]] is true, perform ! TransformStreamSetBackpressure(stream, false).
+ if self.backpressure.get() {
+ self.set_backpressure(global, false, can_gc);
+ }
+ }
+
+ /// <https://streams.spec.whatwg.org/#transform-stream-error>
+ pub(crate) fn error(
+ &self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ error: SafeHandleValue,
+ can_gc: CanGc,
+ ) {
+ // Perform ! ReadableStreamDefaultControllerError(stream.[[readable]].[[controller]], e).
+ self.get_readable()
+ .get_default_controller()
+ .error(error, can_gc);
+
+ // Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, e).
+ self.error_writable_and_unblock_write(cx, global, error, can_gc);
+ }
+}
+
+impl TransformStreamMethods<crate::DomTypeHolder> for TransformStream {
+ /// <https://streams.spec.whatwg.org/#ts-constructor>
+ #[allow(unsafe_code)]
+ fn Constructor(
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ proto: Option<SafeHandleObject>,
+ can_gc: CanGc,
+ transformer: Option<*mut JSObject>,
+ writable_strategy: &QueuingStrategy,
+ readable_strategy: &QueuingStrategy,
+ ) -> Fallible<DomRoot<TransformStream>> {
+ // If transformer is missing, set it to null.
+ rooted!(in(*cx) let transformer_obj = transformer.unwrap_or(ptr::null_mut()));
+
+ // Let underlyingSinkDict be underlyingSink,
+ // converted to an IDL value of type UnderlyingSink.
+ let transformer_dict = if !transformer_obj.is_null() {
+ rooted!(in(*cx) let obj_val = ObjectValue(transformer_obj.get()));
+ match Transformer::new(cx, obj_val.handle()) {
+ Ok(ConversionResult::Success(val)) => val,
+ Ok(ConversionResult::Failure(error)) => return Err(Error::Type(error.to_string())),
+ _ => {
+ return Err(Error::JSFailed);
+ },
+ }
+ } else {
+ Transformer::empty()
+ };
+
+ // If transformerDict["readableType"] exists, throw a RangeError exception.
+ if !transformer_dict.readableType.handle().is_undefined() {
+ return Err(Error::Range("readableType is set".to_string()));
+ }
+
+ // If transformerDict["writableType"] exists, throw a RangeError exception.
+ if !transformer_dict.writableType.handle().is_undefined() {
+ return Err(Error::Range("writableType is set".to_string()));
+ }
+
+ // Let readableHighWaterMark be ? ExtractHighWaterMark(readableStrategy, 0).
+ let readable_high_water_mark = extract_high_water_mark(readable_strategy, 0.0)?;
+
+ // Let readableSizeAlgorithm be ! ExtractSizeAlgorithm(readableStrategy).
+ let readable_size_algorithm = extract_size_algorithm(readable_strategy, can_gc);
+
+ // Let writableHighWaterMark be ? ExtractHighWaterMark(writableStrategy, 1).
+ let writable_high_water_mark = extract_high_water_mark(writable_strategy, 1.0)?;
+
+ // Let writableSizeAlgorithm be ! ExtractSizeAlgorithm(writableStrategy).
+ let writable_size_algorithm = extract_size_algorithm(writable_strategy, can_gc);
+
+ // Let startPromise be a new promise.
+ let start_promise = Promise::new(global, can_gc);
+
+ // Perform ! InitializeTransformStream(this, startPromise, writableHighWaterMark,
+ // writableSizeAlgorithm, readableHighWaterMark, readableSizeAlgorithm).
+ let stream = TransformStream::new_with_proto(global, proto, can_gc);
+ stream.initialize(
+ cx,
+ global,
+ start_promise.clone(),
+ writable_high_water_mark,
+ writable_size_algorithm,
+ readable_high_water_mark,
+ readable_size_algorithm,
+ can_gc,
+ )?;
+
+ // Perform ? SetUpTransformStreamDefaultControllerFromTransformer(this, transformer, transformerDict).
+ stream.set_up_transform_stream_default_controller_from_transformer(
+ global,
+ transformer_obj.handle(),
+ &transformer_dict,
+ can_gc,
+ );
+
+ // If transformerDict["start"] exists, then resolve startPromise with the
+ // result of invoking transformerDict["start"]
+ // with argument list « this.[[controller]] » and callback this value transformer.
+ if let Some(start) = &transformer_dict.start {
+ rooted!(in(*cx) let mut result_object = ptr::null_mut::<JSObject>());
+ rooted!(in(*cx) let mut result: JSVal);
+ rooted!(in(*cx) let this_object = transformer_obj.get());
+ start.Call_(
+ &this_object.handle(),
+ &stream.get_controller(),
+ result.handle_mut(),
+ ExceptionHandling::Rethrow,
+ can_gc,
+ )?;
+ let is_promise = unsafe {
+ if result.is_object() {
+ result_object.set(result.to_object());
+ IsPromiseObject(result_object.handle().into_handle())
+ } else {
+ false
+ }
+ };
+ let promise = if is_promise {
+ let promise = Promise::new_with_js_promise(result_object.handle(), cx);
+ promise
+ } else {
+ Promise::new_resolved(global, cx, result.get(), can_gc)
+ };
+ start_promise.resolve_native(&promise, can_gc);
+ } else {
+ // Otherwise, resolve startPromise with undefined.
+ start_promise.resolve_native(&(), can_gc);
+ };
+
+ Ok(stream)
+ }
+
+ /// <https://streams.spec.whatwg.org/#ts-readable>
+ fn Readable(&self) -> DomRoot<ReadableStream> {
+ // Return this.[[readable]].
+ self.readable.get().expect("readable stream is not set")
+ }
+
+ /// <https://streams.spec.whatwg.org/#ts-writable>
+ fn Writable(&self) -> DomRoot<WritableStream> {
+ // Return this.[[writable]].
+ self.writable.get().expect("writable stream is not set")
+ }
+}
+
+/// <https://streams.spec.whatwg.org/#ts-transfer>
+impl Transferable for TransformStream {
+ type Index = MessagePortIndex;
+ type Data = MessagePortImpl;
+
+ fn transfer(&self) -> Result<(MessagePortId, MessagePortImpl), ()> {
+ let global = self.global();
+ let realm = enter_realm(&*global);
+ let comp = InRealm::Entered(&realm);
+ let cx = GlobalScope::get_cx();
+ let can_gc = CanGc::note();
+
+ // Let readable be value.[[readable]].
+ let readable = self.get_readable();
+
+ // Let writable be value.[[writable]].
+ let writable = self.get_writable();
+
+ // If ! IsReadableStreamLocked(readable) is true, throw a "DataCloneError" DOMException.
+ if readable.is_locked() {
+ return Err(());
+ }
+
+ // If ! IsWritableStreamLocked(writable) is true, throw a "DataCloneError" DOMException.
+ if writable.is_locked() {
+ return Err(());
+ }
+
+ // Create the shared port pair
+ let port_1 = MessagePort::new(&global, can_gc);
+ global.track_message_port(&port_1, None);
+ let port_2 = MessagePort::new(&global, can_gc);
+ global.track_message_port(&port_2, None);
+ global.entangle_ports(*port_1.message_port_id(), *port_2.message_port_id());
+
+ // Create a proxy WritableStream wired to port_1
+ let proxy_writable = WritableStream::new_with_proto(&global, None, can_gc);
+ proxy_writable.setup_cross_realm_transform_writable(cx, &port_1, can_gc);
+
+ // Pipe readable into the proxy writable (→ port_1)
+ let pipe1 = readable.pipe_to(
+ cx,
+ &global,
+ &proxy_writable,
+ false,
+ false,
+ false,
+ comp,
+ can_gc,
+ );
+ pipe1.set_promise_is_handled();
+
+ // Create a proxy ReadableStream wired to port_1
+ let proxy_readable = ReadableStream::new_with_proto(&global, None, can_gc);
+ proxy_readable.setup_cross_realm_transform_readable(cx, &port_1, can_gc);
+
+ // Pipe proxy readable (← port_1) into writable
+ let pipe2 =
+ proxy_readable.pipe_to(cx, &global, &writable, false, false, false, comp, can_gc);
+ pipe2.set_promise_is_handled();
+
+ // Set dataHolder.[[readable]] to ! StructuredSerializeWithTransfer(readable, « readable »).
+ // Set dataHolder.[[writable]] to ! StructuredSerializeWithTransfer(writable, « writable »).
+ port_2.transfer()
+ }
+
+ fn transfer_receive(
+ owner: &GlobalScope,
+ id: MessagePortId,
+ port_impl: MessagePortImpl,
+ ) -> Result<DomRoot<Self>, ()> {
+ let can_gc = CanGc::note();
+
+ // Let readableRecord be ! StructuredDeserializeWithTransfer(dataHolder.[[readable]], the current Realm).
+ // Set value.[[readable]] to readableRecord.[[Deserialized]].
+ let readable = ReadableStream::transfer_receive(owner, id, port_impl.clone())?;
+
+ // Let writableRecord be ! StructuredDeserializeWithTransfer(dataHolder.[[writable]], the current Realm).
+ let writable = WritableStream::transfer_receive(owner, id, port_impl)?;
+
+ // Set value.[[readable]] to readableRecord.[[Deserialized]].
+ // Set value.[[writable]] to writableRecord.[[Deserialized]].
+ // Set value.[[backpressure]], value.[[backpressureChangePromise]], and value.[[controller]] to undefined.
+ let stream = TransformStream::new_with_proto(owner, None, can_gc);
+ stream.readable.set(Some(&readable));
+ stream.writable.set(Some(&writable));
+
+ Ok(stream)
+ }
+
+ fn serialized_storage<'a>(
+ data: StructuredData<'a, '_>,
+ ) -> &'a mut Option<HashMap<MessagePortId, Self::Data>> {
+ match data {
+ StructuredData::Reader(r) => &mut r.port_impls,
+ StructuredData::Writer(w) => &mut w.ports,
+ }
+ }
+}
diff --git a/components/script/dom/transformstreamdefaultcontroller.rs b/components/script/dom/transformstreamdefaultcontroller.rs
new file mode 100644
index 00000000000..41813ef6f96
--- /dev/null
+++ b/components/script/dom/transformstreamdefaultcontroller.rs
@@ -0,0 +1,420 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use std::cell::RefCell;
+use std::rc::Rc;
+
+use dom_struct::dom_struct;
+use js::jsapi::{
+ ExceptionStackBehavior, Heap, JS_IsExceptionPending, JS_SetPendingException, JSObject,
+};
+use js::jsval::UndefinedValue;
+use js::rust::{HandleObject as SafeHandleObject, HandleValue as SafeHandleValue};
+
+use super::bindings::cell::DomRefCell;
+use super::bindings::codegen::Bindings::TransformerBinding::{
+ Transformer, TransformerCancelCallback, TransformerFlushCallback, TransformerTransformCallback,
+};
+use super::types::TransformStream;
+use crate::dom::bindings::callback::ExceptionHandling;
+use crate::dom::bindings::codegen::Bindings::TransformStreamDefaultControllerBinding::TransformStreamDefaultControllerMethods;
+use crate::dom::bindings::error::{Error, ErrorToJsval, Fallible};
+use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
+use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::promise::Promise;
+use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
+use crate::realms::{InRealm, enter_realm};
+use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
+
+impl js::gc::Rootable for TransformTransformPromiseRejection {}
+
+/// Reacting to transformPromise as part of
+/// <https://streams.spec.whatwg.org/#transform-stream-default-controller-perform-transform>
+#[derive(JSTraceable, MallocSizeOf)]
+#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
+struct TransformTransformPromiseRejection {
+ controller: Dom<TransformStreamDefaultController>,
+}
+
+impl Callback for TransformTransformPromiseRejection {
+ /// Reacting to transformPromise with the following fulfillment steps:
+ #[allow(unsafe_code)]
+ fn callback(&self, cx: SafeJSContext, v: SafeHandleValue, _realm: InRealm, can_gc: CanGc) {
+ // Perform ! TransformStreamError(controller.[[stream]], r).
+ self.controller
+ .error(cx, &self.controller.global(), v, can_gc);
+
+ // Throw r.
+ // Note: this is done part of perform_transform().
+ }
+}
+
+/// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller>
+#[dom_struct]
+pub struct TransformStreamDefaultController {
+ reflector_: Reflector,
+
+ /// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-cancelalgorithm>
+ #[ignore_malloc_size_of = "Rc is hard"]
+ cancel: RefCell<Option<Rc<TransformerCancelCallback>>>,
+
+ /// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-flushalgorithm>
+ #[ignore_malloc_size_of = "Rc is hard"]
+ flush: RefCell<Option<Rc<TransformerFlushCallback>>>,
+
+ /// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-transformalgorithm>
+ #[ignore_malloc_size_of = "Rc is hard"]
+ transform: RefCell<Option<Rc<TransformerTransformCallback>>>,
+
+ /// The JS object used as `this` when invoking sink algorithms.
+ #[ignore_malloc_size_of = "mozjs"]
+ transform_obj: Heap<*mut JSObject>,
+
+ /// <https://streams.spec.whatwg.org/#TransformStreamDefaultController-stream>
+ stream: MutNullableDom<TransformStream>,
+
+ /// <https://streams.spec.whatwg.org/#transformstreamdefaultcontroller-finishpromise>
+ #[ignore_malloc_size_of = "Rc is hard"]
+ finish_promise: DomRefCell<Option<Rc<Promise>>>,
+}
+
+impl TransformStreamDefaultController {
+ #[cfg_attr(crown, allow(crown::unrooted_must_root))]
+ fn new_inherited(transformer: &Transformer) -> TransformStreamDefaultController {
+ TransformStreamDefaultController {
+ reflector_: Reflector::new(),
+ cancel: RefCell::new(transformer.cancel.clone()),
+ flush: RefCell::new(transformer.flush.clone()),
+ transform: RefCell::new(transformer.transform.clone()),
+ finish_promise: DomRefCell::new(None),
+ stream: MutNullableDom::new(None),
+ transform_obj: Default::default(),
+ }
+ }
+
+ #[cfg_attr(crown, allow(crown::unrooted_must_root))]
+ pub(crate) fn new(
+ global: &GlobalScope,
+ transformer: &Transformer,
+ can_gc: CanGc,
+ ) -> DomRoot<TransformStreamDefaultController> {
+ reflect_dom_object(
+ Box::new(TransformStreamDefaultController::new_inherited(transformer)),
+ global,
+ can_gc,
+ )
+ }
+
+ /// Setting the JS object after the heap has settled down.
+ pub(crate) fn set_transform_obj(&self, this_object: SafeHandleObject) {
+ self.transform_obj.set(*this_object);
+ }
+
+ pub(crate) fn set_stream(&self, stream: &TransformStream) {
+ self.stream.set(Some(stream));
+ }
+
+ pub(crate) fn get_finish_promise(&self) -> Option<Rc<Promise>> {
+ self.finish_promise.borrow().clone()
+ }
+
+ pub(crate) fn set_finish_promise(&self, promise: Rc<Promise>) {
+ *self.finish_promise.borrow_mut() = Some(promise);
+ }
+
+ /// <https://streams.spec.whatwg.org/#transform-stream-default-controller-perform-transform>
+ pub(crate) fn transform_stream_default_controller_perform_transform(
+ &self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ chunk: SafeHandleValue,
+ can_gc: CanGc,
+ ) -> Fallible<Rc<Promise>> {
+ // Let transformPromise be the result of performing controller.[[transformAlgorithm]], passing chunk.
+ let transform_promise = self.perform_transform(cx, global, chunk, can_gc)?;
+
+ // Return the result of reacting to transformPromise with the following rejection steps given the argument r:
+ rooted!(in(*cx) let mut reject_handler = Some(TransformTransformPromiseRejection {
+ controller: Dom::from_ref(self),
+ }));
+
+ let handler = PromiseNativeHandler::new(
+ global,
+ None,
+ reject_handler.take().map(|h| Box::new(h) as Box<_>),
+ can_gc,
+ );
+ let realm = enter_realm(global);
+ let comp = InRealm::Entered(&realm);
+ transform_promise.append_native_handler(&handler, comp, can_gc);
+
+ Ok(transform_promise)
+ }
+
+ pub(crate) fn perform_transform(
+ &self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ chunk: SafeHandleValue,
+ can_gc: CanGc,
+ ) -> Fallible<Rc<Promise>> {
+ // If transformerDict["transform"] exists, set transformAlgorithm to an algorithm which
+ // takes an argument chunk and returns the result of invoking transformerDict["transform"] with argument list
+ // « chunk, controller » and callback this value transformer.
+ let algo = self.transform.borrow().clone();
+ let result = if let Some(transform) = algo {
+ rooted!(in(*cx) let this_object = self.transform_obj.get());
+ let call_result = transform.Call_(
+ &this_object.handle(),
+ chunk,
+ self,
+ ExceptionHandling::Rethrow,
+ can_gc,
+ );
+ match call_result {
+ Ok(p) => p,
+ Err(e) => {
+ let p = Promise::new(global, can_gc);
+ p.reject_error(e, can_gc);
+ p
+ },
+ }
+ } else {
+ // Let transformAlgorithm be the following steps, taking a chunk argument:
+ // Let result be TransformStreamDefaultControllerEnqueue(controller, chunk).
+ // If result is an abrupt completion, return a promise rejected with result.[[Value]].
+ let promise = if let Err(error) = self.enqueue(cx, global, chunk, can_gc) {
+ rooted!(in(*cx) let mut error_val = UndefinedValue());
+ error.to_jsval(cx, global, error_val.handle_mut(), can_gc);
+ Promise::new_rejected(global, cx, error_val.handle(), can_gc)
+ } else {
+ // Otherwise, return a promise resolved with undefined.
+ Promise::new_resolved(global, cx, (), can_gc)
+ };
+
+ promise
+ };
+
+ Ok(result)
+ }
+
+ pub(crate) fn perform_cancel(
+ &self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ chunk: SafeHandleValue,
+ can_gc: CanGc,
+ ) -> Fallible<Rc<Promise>> {
+ // If transformerDict["cancel"] exists, set cancelAlgorithm to an algorithm which takes an argument
+ // reason and returns the result of invoking transformerDict["cancel"] with argument list « reason »
+ // and callback this value transformer.
+ let algo = self.cancel.borrow().clone();
+ let result = if let Some(cancel) = algo {
+ rooted!(in(*cx) let this_object = self.transform_obj.get());
+ let call_result = cancel.Call_(
+ &this_object.handle(),
+ chunk,
+ ExceptionHandling::Rethrow,
+ can_gc,
+ );
+ match call_result {
+ Ok(p) => p,
+ Err(e) => {
+ let p = Promise::new(global, can_gc);
+ p.reject_error(e, can_gc);
+ p
+ },
+ }
+ } else {
+ // Let cancelAlgorithm be an algorithm which returns a promise resolved with undefined.
+ Promise::new_resolved(global, cx, (), can_gc)
+ };
+
+ Ok(result)
+ }
+
+ pub(crate) fn perform_flush(
+ &self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ can_gc: CanGc,
+ ) -> Fallible<Rc<Promise>> {
+ // If transformerDict["flush"] exists, set flushAlgorithm to an algorithm which returns the result of
+ // invoking transformerDict["flush"] with argument list « controller » and callback this value transformer.
+ let algo = self.flush.borrow().clone();
+ let result = if let Some(flush) = algo {
+ rooted!(in(*cx) let this_object = self.transform_obj.get());
+ let call_result = flush.Call_(
+ &this_object.handle(),
+ self,
+ ExceptionHandling::Rethrow,
+ can_gc,
+ );
+ match call_result {
+ Ok(p) => p,
+ Err(e) => {
+ let p = Promise::new(global, can_gc);
+ p.reject_error(e, can_gc);
+ p
+ },
+ }
+ } else {
+ // Let flushAlgorithm be an algorithm which returns a promise resolved with undefined.
+ Promise::new_resolved(global, cx, (), can_gc)
+ };
+
+ Ok(result)
+ }
+
+ /// <https://streams.spec.whatwg.org/#transform-stream-default-controller-enqueue>
+ #[allow(unsafe_code)]
+ pub(crate) fn enqueue(
+ &self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ chunk: SafeHandleValue,
+ can_gc: CanGc,
+ ) -> Fallible<()> {
+ // Let stream be controller.[[stream]].
+ let stream = self.stream.get().expect("stream is null");
+
+ // Let readableController be stream.[[readable]].[[controller]].
+ let readable = stream.get_readable();
+ let readable_controller = readable.get_default_controller();
+
+ // If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(readableController)
+ // is false, throw a TypeError exception.
+ if !readable_controller.can_close_or_enqueue() {
+ return Err(Error::Type(
+ "ReadableStreamDefaultControllerCanCloseOrEnqueue is false".to_owned(),
+ ));
+ }
+
+ // Let enqueueResult be ReadableStreamDefaultControllerEnqueue(readableController, chunk).
+ // If enqueueResult is an abrupt completion,
+ if let Err(error) = readable_controller.enqueue(cx, chunk, can_gc) {
+ // Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, enqueueResult.[[Value]]).
+ rooted!(in(*cx) let mut rooted_error = UndefinedValue());
+ error
+ .clone()
+ .to_jsval(cx, global, rooted_error.handle_mut(), can_gc);
+ stream.error_writable_and_unblock_write(cx, global, rooted_error.handle(), can_gc);
+
+ // Throw stream.[[readable]].[[storedError]].
+ unsafe {
+ if !JS_IsExceptionPending(*cx) {
+ rooted!(in(*cx) let mut stored_error = UndefinedValue());
+ readable.get_stored_error(stored_error.handle_mut());
+
+ JS_SetPendingException(
+ *cx,
+ stored_error.handle().into(),
+ ExceptionStackBehavior::Capture,
+ );
+ }
+ }
+ return Err(error);
+ }
+
+ // Let backpressure be ! ReadableStreamDefaultControllerHasBackpressure(readableController).
+ let backpressure = readable_controller.has_backpressure();
+
+ // If backpressure is not stream.[[backpressure]],
+ if backpressure != stream.get_backpressure() {
+ // Assert: backpressure is true.
+ assert!(backpressure);
+
+ // Perform ! TransformStreamSetBackpressure(stream, true).
+ stream.set_backpressure(global, true, can_gc);
+ }
+ Ok(())
+ }
+
+ /// <https://streams.spec.whatwg.org/#transform-stream-default-controller-error>
+ pub(crate) fn error(
+ &self,
+ cx: SafeJSContext,
+ global: &GlobalScope,
+ reason: SafeHandleValue,
+ can_gc: CanGc,
+ ) {
+ // Perform ! TransformStreamError(controller.[[stream]], e).
+ self.stream
+ .get()
+ .expect("stream is undefined")
+ .error(cx, global, reason, can_gc);
+ }
+
+ /// <https://streams.spec.whatwg.org/#transform-stream-default-controller-clear-algorithms>
+ pub(crate) fn clear_algorithms(&self) {
+ // Set controller.[[transformAlgorithm]] to undefined.
+ self.transform.replace(None);
+
+ // Set controller.[[flushAlgorithm]] to undefined.
+ self.flush.replace(None);
+
+ // Set controller.[[cancelAlgorithm]] to undefined.
+ self.cancel.replace(None);
+ }
+
+ /// <https://streams.spec.whatwg.org/#transform-stream-default-controller-terminate>
+ pub(crate) fn terminate(&self, cx: SafeJSContext, global: &GlobalScope, can_gc: CanGc) {
+ // Let stream be controller.[[stream]].
+ let stream = self.stream.get().expect("stream is null");
+
+ // Let readableController be stream.[[readable]].[[controller]].
+ let readable = stream.get_readable();
+ let readable_controller = readable.get_default_controller();
+
+ // Perform ! ReadableStreamDefaultControllerClose(readableController).
+ readable_controller.close(can_gc);
+
+ // Let error be a TypeError exception indicating that the stream has been terminated.
+ let error = Error::Type("stream has been terminated".to_owned());
+
+ // Perform ! TransformStreamErrorWritableAndUnblockWrite(stream, error).
+ rooted!(in(*cx) let mut rooted_error = UndefinedValue());
+ error.to_jsval(cx, global, rooted_error.handle_mut(), can_gc);
+ stream.error_writable_and_unblock_write(cx, global, rooted_error.handle(), can_gc);
+ }
+}
+
+impl TransformStreamDefaultControllerMethods<crate::DomTypeHolder>
+ for TransformStreamDefaultController
+{
+ /// <https://streams.spec.whatwg.org/#ts-default-controller-desired-size>
+ fn GetDesiredSize(&self) -> Option<f64> {
+ // Let readableController be this.[[stream]].[[readable]].[[controller]].
+ let readable_controller = self
+ .stream
+ .get()
+ .expect("stream is null")
+ .get_readable()
+ .get_default_controller();
+
+ // Return ! ReadableStreamDefaultControllerGetDesiredSize(readableController).
+ readable_controller.get_desired_size()
+ }
+
+ /// <https://streams.spec.whatwg.org/#ts-default-controller-enqueue>
+ fn Enqueue(&self, cx: SafeJSContext, chunk: SafeHandleValue, can_gc: CanGc) -> Fallible<()> {
+ // Perform ? TransformStreamDefaultControllerEnqueue(this, chunk).
+ self.enqueue(cx, &self.global(), chunk, can_gc)
+ }
+
+ /// <https://streams.spec.whatwg.org/#ts-default-controller-error>
+ fn Error(&self, cx: SafeJSContext, reason: SafeHandleValue, can_gc: CanGc) -> Fallible<()> {
+ // Perform ? TransformStreamDefaultControllerError(this, e).
+ self.error(cx, &self.global(), reason, can_gc);
+ Ok(())
+ }
+
+ /// <https://streams.spec.whatwg.org/#ts-default-controller-terminate>
+ fn Terminate(&self, can_gc: CanGc) -> Fallible<()> {
+ // Perform ? TransformStreamDefaultControllerTerminate(this).
+ self.terminate(GlobalScope::get_cx(), &self.global(), can_gc);
+ Ok(())
+ }
+}
diff --git a/components/script/dom/trustedhtml.rs b/components/script/dom/trustedhtml.rs
index 8508f28c150..d1ca3cd5e71 100644
--- a/components/script/dom/trustedhtml.rs
+++ b/components/script/dom/trustedhtml.rs
@@ -6,8 +6,11 @@ use std::fmt;
use dom_struct::dom_struct;
+use crate::conversions::Convert;
use crate::dom::bindings::codegen::Bindings::TrustedHTMLBinding::TrustedHTMLMethods;
-use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrString;
+use crate::dom::bindings::codegen::UnionTypes::{
+ TrustedHTMLOrNullIsEmptyString, TrustedHTMLOrString,
+};
use crate::dom::bindings::error::Fallible;
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
use crate::dom::bindings::root::DomRoot;
@@ -80,3 +83,16 @@ impl TrustedHTMLMethods<crate::DomTypeHolder> for TrustedHTML {
DOMString::from(&*self.data)
}
}
+
+impl Convert<TrustedHTMLOrString> for TrustedHTMLOrNullIsEmptyString {
+ fn convert(self) -> TrustedHTMLOrString {
+ match self {
+ TrustedHTMLOrNullIsEmptyString::TrustedHTML(trusted_html) => {
+ TrustedHTMLOrString::TrustedHTML(trusted_html)
+ },
+ TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(str) => {
+ TrustedHTMLOrString::String(str)
+ },
+ }
+ }
+}
diff --git a/components/script/dom/trustedtypepolicyfactory.rs b/components/script/dom/trustedtypepolicyfactory.rs
index 0927446b904..284fa7045eb 100644
--- a/components/script/dom/trustedtypepolicyfactory.rs
+++ b/components/script/dom/trustedtypepolicyfactory.rs
@@ -66,7 +66,7 @@ impl TrustedTypePolicyFactory {
(CheckResult::Allowed, Vec::new())
};
- global.report_csp_violations(violations);
+ global.report_csp_violations(violations, None);
// Step 2: If allowedByCSP is "Blocked", throw a TypeError and abort further steps.
if allowed_by_csp == CheckResult::Blocked {
@@ -230,7 +230,7 @@ impl TrustedTypePolicyFactory {
.should_sink_type_mismatch_violation_be_blocked_by_csp(
sink, sink_group, &input,
);
- global.report_csp_violations(violations);
+ global.report_csp_violations(violations, None);
// Step 6.2: If disposition is “Allowed”, return stringified input and abort further steps.
if disposition == CheckResult::Allowed {
Ok(input)
diff --git a/components/script/dom/underlyingsourcecontainer.rs b/components/script/dom/underlyingsourcecontainer.rs
index 4acb58bafef..dd4b867df45 100644
--- a/components/script/dom/underlyingsourcecontainer.rs
+++ b/components/script/dom/underlyingsourcecontainer.rs
@@ -20,6 +20,7 @@ use crate::dom::defaultteeunderlyingsource::DefaultTeeUnderlyingSource;
use crate::dom::globalscope::GlobalScope;
use crate::dom::messageport::MessagePort;
use crate::dom::promise::Promise;
+use crate::dom::transformstream::TransformStream;
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
/// <https://streams.spec.whatwg.org/#underlying-source-api>
@@ -46,8 +47,7 @@ pub(crate) enum UnderlyingSourceType {
/// A struct representing a JS object as underlying source,
/// and the actual JS object for use as `thisArg` in callbacks.
/// This is used for the `TransformStream` API.
- #[allow(unused)]
- Transform(/* Dom<TransformStream>, Rc<Promise>*/),
+ Transform(Dom<TransformStream>, Rc<Promise>),
}
impl UnderlyingSourceType {
@@ -139,9 +139,9 @@ impl UnderlyingSourceContainer {
// Call the cancel algorithm for the appropriate branch.
tee_underlying_source.cancel_algorithm(cx, global, reason, can_gc)
},
- UnderlyingSourceType::Transform() => {
+ UnderlyingSourceType::Transform(stream, _) => {
// Return ! TransformStreamDefaultSourceCancelAlgorithm(stream, reason).
- todo!();
+ Some(stream.transform_stream_default_source_cancel(cx, global, reason, can_gc))
},
UnderlyingSourceType::Transfer(port) => {
// Let cancelAlgorithm be the following steps, taking a reason argument:
@@ -213,9 +213,9 @@ impl UnderlyingSourceContainer {
Some(Ok(promise))
},
// Note: other source type have no pull steps for now.
- UnderlyingSourceType::Transform() => {
+ UnderlyingSourceType::Transform(stream, _) => {
// Return ! TransformStreamDefaultSourcePullAlgorithm(stream).
- todo!();
+ Some(stream.transform_stream_default_source_pull(&self.global(), can_gc))
},
_ => None,
}
@@ -280,9 +280,9 @@ impl UnderlyingSourceContainer {
// from <https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformreadable
None
},
- UnderlyingSourceType::Transform() => {
- // Some(transform_underlying_source.start_algorithm())
- todo!();
+ UnderlyingSourceType::Transform(_, start_promise) => {
+ // Let startAlgorithm be an algorithm that returns startPromise.
+ Some(Ok(start_promise.clone()))
},
_ => None,
}
diff --git a/components/script/dom/urlpattern.rs b/components/script/dom/urlpattern.rs
new file mode 100644
index 00000000000..c811d3a9a70
--- /dev/null
+++ b/components/script/dom/urlpattern.rs
@@ -0,0 +1,204 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use dom_struct::dom_struct;
+use js::rust::HandleObject;
+use script_bindings::codegen::GenericUnionTypes::USVStringOrURLPatternInit;
+use script_bindings::error::{Error, Fallible};
+use script_bindings::reflector::Reflector;
+use script_bindings::root::DomRoot;
+use script_bindings::script_runtime::CanGc;
+use script_bindings::str::USVString;
+
+use crate::dom::bindings::codegen::Bindings::URLPatternBinding;
+use crate::dom::bindings::codegen::Bindings::URLPatternBinding::URLPatternMethods;
+use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
+use crate::dom::globalscope::GlobalScope;
+
+/// <https://urlpattern.spec.whatwg.org/#urlpattern>
+#[dom_struct]
+pub(crate) struct URLPattern {
+ reflector: Reflector,
+
+ /// <https://urlpattern.spec.whatwg.org/#urlpattern-associated-url-pattern>
+ #[no_trace]
+ associated_url_pattern: urlpattern::UrlPattern,
+}
+
+impl URLPattern {
+ #[cfg_attr(crown, allow(crown::unrooted_must_root))]
+ fn new_inherited(associated_url_pattern: urlpattern::UrlPattern) -> URLPattern {
+ URLPattern {
+ reflector: Reflector::new(),
+ associated_url_pattern,
+ }
+ }
+
+ /// <https://urlpattern.spec.whatwg.org/#urlpattern-initialize>
+ pub(crate) fn initialize(
+ global: &GlobalScope,
+ proto: Option<HandleObject>,
+ input: USVStringOrURLPatternInit,
+ base_url: Option<USVString>,
+ options: &URLPatternBinding::URLPatternOptions,
+ can_gc: CanGc,
+ ) -> Fallible<DomRoot<URLPattern>> {
+ // The section below converts from servos types to the types used in the urlpattern crate
+ let base_url = base_url.map(|usv_string| usv_string.0);
+ let input = bindings_to_third_party::map_urlpattern_input(input, base_url.clone());
+ let options = urlpattern::UrlPatternOptions {
+ ignore_case: options.ignoreCase,
+ };
+
+ // Parse and initialize the URL pattern.
+ let pattern_init =
+ urlpattern::quirks::process_construct_pattern_input(input, base_url.as_deref())
+ .map_err(|error| Error::Type(format!("{error}")))?;
+
+ let pattern = urlpattern::UrlPattern::parse(pattern_init, options)
+ .map_err(|error| Error::Type(format!("{error}")))?;
+
+ let url_pattern = reflect_dom_object_with_proto(
+ Box::new(URLPattern::new_inherited(pattern)),
+ global,
+ proto,
+ can_gc,
+ );
+ Ok(url_pattern)
+ }
+}
+
+impl URLPatternMethods<crate::DomTypeHolder> for URLPattern {
+ // <https://urlpattern.spec.whatwg.org/#dom-urlpattern-urlpattern>
+ fn Constructor(
+ global: &GlobalScope,
+ proto: Option<HandleObject>,
+ can_gc: CanGc,
+ input: USVStringOrURLPatternInit,
+ base_url: USVString,
+ options: &URLPatternBinding::URLPatternOptions,
+ ) -> Fallible<DomRoot<URLPattern>> {
+ URLPattern::initialize(global, proto, input, Some(base_url), options, can_gc)
+ }
+
+ /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-urlpattern-input-options>
+ fn Constructor_(
+ global: &GlobalScope,
+ proto: Option<HandleObject>,
+ can_gc: CanGc,
+ input: USVStringOrURLPatternInit,
+ options: &URLPatternBinding::URLPatternOptions,
+ ) -> Fallible<DomRoot<URLPattern>> {
+ // Step 1. Run initialize given this, input, null, and options.
+ URLPattern::initialize(global, proto, input, None, options, can_gc)
+ }
+
+ /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-protocol>
+ fn Protocol(&self) -> USVString {
+ // Step 1. Return this’s associated URL pattern’s protocol component’s pattern string.
+ USVString(self.associated_url_pattern.protocol().to_owned())
+ }
+
+ /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-username>
+ fn Username(&self) -> USVString {
+ // Step 1. Return this’s associated URL pattern’s username component’s pattern string.
+ USVString(self.associated_url_pattern.username().to_owned())
+ }
+
+ /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-password>
+ fn Password(&self) -> USVString {
+ // Step 1. Return this’s associated URL pattern’s password component’s pattern string.
+ USVString(self.associated_url_pattern.password().to_owned())
+ }
+
+ /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-hostname>
+ fn Hostname(&self) -> USVString {
+ // Step 1. Return this’s associated URL pattern’s hostname component’s pattern string.
+ USVString(self.associated_url_pattern.hostname().to_owned())
+ }
+
+ /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-port>
+ fn Port(&self) -> USVString {
+ // Step 1. Return this’s associated URL pattern’s port component’s pattern string.
+ USVString(self.associated_url_pattern.port().to_owned())
+ }
+
+ /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-pathname>
+ fn Pathname(&self) -> USVString {
+ // Step 1. Return this’s associated URL pattern’s pathname component’s pattern string.
+ USVString(self.associated_url_pattern.pathname().to_owned())
+ }
+
+ /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-search>
+ fn Search(&self) -> USVString {
+ // Step 1. Return this’s associated URL pattern’s search component’s pattern string.
+ USVString(self.associated_url_pattern.search().to_owned())
+ }
+
+ /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-hash>
+ fn Hash(&self) -> USVString {
+ // Step 1. Return this’s associated URL pattern’s hash component’s pattern string.
+ USVString(self.associated_url_pattern.hash().to_owned())
+ }
+
+ /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-hasregexpgroups>
+ fn HasRegExpGroups(&self) -> bool {
+ // Step 1. If this’s associated URL pattern’s has regexp groups, then return true.
+ // Step 2. Return false.
+ self.associated_url_pattern.has_regexp_groups()
+ }
+}
+
+mod bindings_to_third_party {
+ use crate::dom::urlpattern::USVStringOrURLPatternInit;
+
+ pub(super) fn map_urlpattern_input(
+ input: USVStringOrURLPatternInit,
+ base_url: Option<String>,
+ ) -> urlpattern::quirks::StringOrInit {
+ match input {
+ USVStringOrURLPatternInit::USVString(usv_string) => {
+ urlpattern::quirks::StringOrInit::String(usv_string.0)
+ },
+ USVStringOrURLPatternInit::URLPatternInit(pattern_init) => {
+ let pattern_init = urlpattern::quirks::UrlPatternInit {
+ protocol: pattern_init
+ .protocol
+ .as_ref()
+ .map(|usv_string| usv_string.to_string()),
+ username: pattern_init
+ .username
+ .as_ref()
+ .map(|usv_string| usv_string.to_string()),
+ password: pattern_init
+ .password
+ .as_ref()
+ .map(|usv_string| usv_string.to_string()),
+ hostname: pattern_init
+ .hostname
+ .as_ref()
+ .map(|usv_string| usv_string.to_string()),
+ port: pattern_init
+ .port
+ .as_ref()
+ .map(|usv_string| usv_string.to_string()),
+ pathname: pattern_init
+ .pathname
+ .as_ref()
+ .map(|usv_string| usv_string.to_string()),
+ search: pattern_init
+ .search
+ .as_ref()
+ .map(|usv_string| usv_string.to_string()),
+ hash: pattern_init
+ .hash
+ .as_ref()
+ .map(|usv_string| usv_string.to_string()),
+ base_url,
+ };
+ urlpattern::quirks::StringOrInit::Init(pattern_init)
+ },
+ }
+ }
+}
diff --git a/components/script/dom/urlpattern/mod.rs b/components/script/dom/urlpattern/mod.rs
deleted file mode 100644
index e92963c672b..00000000000
--- a/components/script/dom/urlpattern/mod.rs
+++ /dev/null
@@ -1,810 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-mod pattern_parser;
-mod preprocessing;
-mod tokenizer;
-
-use std::ptr;
-
-use dom_struct::dom_struct;
-use js::jsapi::{Heap, JSObject, RegExpFlag_IgnoreCase, RegExpFlag_UnicodeSets, RegExpFlags};
-use js::rust::HandleObject;
-use pattern_parser::parse_a_pattern_string;
-use preprocessing::{
- canonicalize_a_hash, canonicalize_a_hostname, canonicalize_a_password, canonicalize_a_pathname,
- canonicalize_a_port, canonicalize_a_protocol, canonicalize_a_search, canonicalize_a_username,
- escape_a_regexp_string, process_a_url_pattern_init,
-};
-use script_bindings::error::{Error, Fallible};
-use script_bindings::reflector::Reflector;
-use script_bindings::root::DomRoot;
-use script_bindings::script_runtime::CanGc;
-use script_bindings::str::USVString;
-
-use crate::dom::bindings::cell::RefCell;
-use crate::dom::bindings::codegen::Bindings::URLPatternBinding::{
- URLPatternInit, URLPatternMethods, URLPatternOptions,
-};
-use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
-use crate::dom::globalscope::GlobalScope;
-use crate::dom::htmlinputelement::new_js_regex;
-
-/// <https://urlpattern.spec.whatwg.org/#full-wildcard-regexp-value>
-const FULL_WILDCARD_REGEXP_VALUE: &str = ".*";
-
-/// <https://urlpattern.spec.whatwg.org/#urlpattern>
-#[dom_struct]
-pub(crate) struct URLPattern {
- reflector: Reflector,
-
- /// <https://urlpattern.spec.whatwg.org/#urlpattern-associated-url-pattern>
- associated_url_pattern: RefCell<URLPatternInternal>,
-}
-
-#[derive(JSTraceable, MallocSizeOf)]
-#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
-struct URLPatternInternal {
- /// <https://urlpattern.spec.whatwg.org/#url-pattern-protocol-component>
- protocol: Component,
-
- /// <https://urlpattern.spec.whatwg.org/#url-pattern-username-component>
- username: Component,
-
- /// <https://urlpattern.spec.whatwg.org/#url-pattern-password-component>
- password: Component,
-
- /// <https://urlpattern.spec.whatwg.org/#url-pattern-hostname-component>
- hostname: Component,
-
- /// <https://urlpattern.spec.whatwg.org/#url-pattern-port-component>
- port: Component,
-
- /// <https://urlpattern.spec.whatwg.org/#url-pattern-pathname-component>
- pathname: Component,
-
- /// <https://urlpattern.spec.whatwg.org/#url-pattern-search-component>
- search: Component,
-
- /// <https://urlpattern.spec.whatwg.org/#url-pattern-hash-component>
- hash: Component,
-}
-
-/// <https://urlpattern.spec.whatwg.org/#component>
-#[derive(JSTraceable, MallocSizeOf)]
-#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
-struct Component {
- /// <https://urlpattern.spec.whatwg.org/#component-pattern-string>
- pattern_string: USVString,
-
- /// <https://urlpattern.spec.whatwg.org/#component-regular-expression>
- #[ignore_malloc_size_of = "mozjs"]
- regular_expression: Box<Heap<*mut JSObject>>,
-
- /// <https://urlpattern.spec.whatwg.org/#component-group-name-list>
- group_name_list: Vec<USVString>,
-
- /// <https://urlpattern.spec.whatwg.org/#component-has-regexp-groups>
- has_regexp_groups: bool,
-}
-
-/// <https://urlpattern.spec.whatwg.org/#part>
-#[derive(Debug)]
-struct Part {
- /// <https://urlpattern.spec.whatwg.org/#part-type>
- part_type: PartType,
-
- /// <https://urlpattern.spec.whatwg.org/#part-value>
- value: String,
-
- /// <https://urlpattern.spec.whatwg.org/#part-modifier>
- modifier: PartModifier,
-
- /// <https://urlpattern.spec.whatwg.org/#part-name>
- name: String,
-
- /// <https://urlpattern.spec.whatwg.org/#part-prefix>
- prefix: String,
-
- /// <https://urlpattern.spec.whatwg.org/#part-suffix>
- suffix: String,
-}
-
-/// <https://urlpattern.spec.whatwg.org/#part-type>
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-enum PartType {
- /// <https://urlpattern.spec.whatwg.org/#part-type-fixed-text>
- FixedText,
-
- /// <https://urlpattern.spec.whatwg.org/#part-type-regexp>
- Regexp,
-
- /// <https://urlpattern.spec.whatwg.org/#part-type-segment-wildcard>
- SegmentWildcard,
-
- /// <https://urlpattern.spec.whatwg.org/#part-type-full-wildcard>
- FullWildcard,
-}
-
-/// <https://urlpattern.spec.whatwg.org/#part-modifier>
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-#[allow(dead_code)] // Parser is not implemented yet
-enum PartModifier {
- /// <https://urlpattern.spec.whatwg.org/#part-modifier-none>
- None,
-
- /// <https://urlpattern.spec.whatwg.org/#part-modifier-optional>
- Optional,
-
- /// <https://urlpattern.spec.whatwg.org/#part-modifier-zero-or-more>
- ZeroOrMore,
-
- /// <https://urlpattern.spec.whatwg.org/#part-modifier-one-or-more>
- OneOrMore,
-}
-
-/// <https://urlpattern.spec.whatwg.org/#options>
-#[derive(Clone, Copy, Default)]
-#[allow(dead_code)] // Parser is not fully implemented yet
-struct Options {
- /// <https://urlpattern.spec.whatwg.org/#options-delimiter-code-point>
- delimiter_code_point: Option<char>,
-
- /// <https://urlpattern.spec.whatwg.org/#options-prefix-code-point>
- prefix_code_point: Option<char>,
-
- /// <https://urlpattern.spec.whatwg.org/#options-ignore-case>
- ignore_case: bool,
-}
-
-impl Component {
- fn new_unrooted() -> Self {
- Self {
- pattern_string: Default::default(),
- regular_expression: Heap::boxed(ptr::null_mut()),
- group_name_list: Default::default(),
- has_regexp_groups: false,
- }
- }
-}
-
-impl URLPattern {
- #[cfg_attr(crown, allow(crown::unrooted_must_root))]
- fn new_inherited() -> URLPattern {
- let associated_url_pattern = URLPatternInternal {
- protocol: Component::new_unrooted(),
- username: Component::new_unrooted(),
- password: Component::new_unrooted(),
- hostname: Component::new_unrooted(),
- port: Component::new_unrooted(),
- pathname: Component::new_unrooted(),
- search: Component::new_unrooted(),
- hash: Component::new_unrooted(),
- };
-
- URLPattern {
- reflector: Reflector::new(),
- associated_url_pattern: RefCell::new(associated_url_pattern),
- }
- }
-
- #[cfg_attr(crown, allow(crown::unrooted_must_root))]
- pub(crate) fn new_with_proto(
- global: &GlobalScope,
- proto: Option<HandleObject>,
- can_gc: CanGc,
- ) -> DomRoot<URLPattern> {
- reflect_dom_object_with_proto(Box::new(URLPattern::new_inherited()), global, proto, can_gc)
- }
-
- /// <https://urlpattern.spec.whatwg.org/#urlpattern-initialize>
- fn initialize(
- global: &GlobalScope,
- proto: Option<HandleObject>,
- input: &URLPatternInit,
- options: &URLPatternOptions,
- can_gc: CanGc,
- ) -> Fallible<DomRoot<URLPattern>> {
- // Step 1. Set this’s associated URL pattern to the result of create given input, baseURL, and options.
- let pattern = URLPattern::new_with_proto(global, proto, can_gc);
- URLPatternInternal::create(
- input,
- options,
- &mut pattern.associated_url_pattern.borrow_mut(),
- )?;
-
- Ok(pattern)
- }
-}
-
-impl URLPatternMethods<crate::DomTypeHolder> for URLPattern {
- /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-urlpattern-input-options>
- fn Constructor(
- global: &GlobalScope,
- proto: Option<HandleObject>,
- can_gc: CanGc,
- input: &URLPatternInit,
- options: &URLPatternOptions,
- ) -> Fallible<DomRoot<URLPattern>> {
- // Step 1. Run initialize given this, input, null, and options.
- URLPattern::initialize(global, proto, input, options, can_gc)
- }
-
- /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-protocol>
- fn Protocol(&self) -> USVString {
- // Step 1. Return this’s associated URL pattern’s protocol component’s pattern string.
- self.associated_url_pattern
- .borrow()
- .protocol
- .pattern_string
- .clone()
- }
-
- /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-username>
- fn Username(&self) -> USVString {
- // Step 1. Return this’s associated URL pattern’s username component’s pattern string.
- self.associated_url_pattern
- .borrow()
- .username
- .pattern_string
- .clone()
- }
-
- /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-password>
- fn Password(&self) -> USVString {
- // Step 1. Return this’s associated URL pattern’s password component’s pattern string.
- self.associated_url_pattern
- .borrow()
- .password
- .pattern_string
- .clone()
- }
-
- /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-hostname>
- fn Hostname(&self) -> USVString {
- // Step 1. Return this’s associated URL pattern’s hostname component’s pattern string.
- self.associated_url_pattern
- .borrow()
- .hostname
- .pattern_string
- .clone()
- }
-
- /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-port>
- fn Port(&self) -> USVString {
- // Step 1. Return this’s associated URL pattern’s port component’s pattern string.
- self.associated_url_pattern
- .borrow()
- .port
- .pattern_string
- .clone()
- }
-
- /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-pathname>
- fn Pathname(&self) -> USVString {
- // Step 1. Return this’s associated URL pattern’s pathname component’s pattern string.
- self.associated_url_pattern
- .borrow()
- .pathname
- .pattern_string
- .clone()
- }
-
- /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-search>
- fn Search(&self) -> USVString {
- // Step 1. Return this’s associated URL pattern’s search component’s pattern string.
- self.associated_url_pattern
- .borrow()
- .search
- .pattern_string
- .clone()
- }
-
- /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-hash>
- fn Hash(&self) -> USVString {
- // Step 1. Return this’s associated URL pattern’s hash component’s pattern string.
- self.associated_url_pattern
- .borrow()
- .hash
- .pattern_string
- .clone()
- }
-
- /// <https://urlpattern.spec.whatwg.org/#dom-urlpattern-hasregexpgroups>
- fn HasRegExpGroups(&self) -> bool {
- // Step 1. If this’s associated URL pattern’s has regexp groups, then return true.
- // Step 2. Return false.
- self.associated_url_pattern.borrow().has_regexp_groups()
- }
-}
-
-impl URLPatternInternal {
- /// <https://urlpattern.spec.whatwg.org/#url-pattern-create>
- fn create(input: &URLPatternInit, options: &URLPatternOptions, out: &mut Self) -> Fallible<()> {
- // Step 1. Let init be null.
- // Step 2. If input is a scalar value string then:
- // NOTE: We don't support strings as input yet
- // Step 3. Otherwise:
- // Step 3.1 Assert: input is a URLPatternInit.
- // Step 3.2 If baseURL is not null, then throw a TypeError.
- if input.baseURL.is_some() {
- return Err(Error::Type("baseURL must be none".into()));
- }
-
- // Step 3.3 Set init to input.
- let init = input;
-
- // Step 4. Let processedInit be the result of process a URLPatternInit given init, "pattern", null, null,
- // null, null, null, null, null, and null.
- let mut processed_init = process_a_url_pattern_init(init, PatternInitType::Pattern)?;
-
- // Step 5. For each componentName of « "protocol", "username", "password", "hostname", "port",
- // "pathname", "search", "hash" »:
- // Step 5.1 If processedInit[componentName] does not exist, then set processedInit[componentName] to "*".
- // NOTE: We do this later on
-
- // Step 6. If processedInit["protocol"] is a special scheme and processedInit["port"] is a string
- // which represents its corresponding default port in radix-10 using ASCII digits then set
- // processedInit["port"] to the empty string.
- let default_port = processed_init
- .protocol
- .as_deref()
- .and_then(default_port_for_special_scheme);
- let given_port = processed_init
- .port
- .as_deref()
- .map(str::parse)
- .transpose()
- .ok()
- .flatten();
- if default_port.is_some() && default_port == given_port {
- processed_init.port = Some(Default::default());
- }
-
- // Step 7. Let urlPattern be a new URL pattern.
- // NOTE: We construct the pattern provided as the out parameter.
-
- // Step 8. Set urlPattern’s protocol component to the result of compiling a component given
- // processedInit["protocol"], canonicalize a protocol, and default options.
- Component::compile(
- processed_init.protocol.as_deref().unwrap_or("*"),
- Box::new(canonicalize_a_protocol),
- Options::default(),
- &mut out.protocol,
- )?;
-
- // Step 9. Set urlPattern’s username component to the result of compiling a component given
- // processedInit["username"], canonicalize a username, and default options.
- Component::compile(
- processed_init.username.as_deref().unwrap_or("*"),
- Box::new(|i| Ok(canonicalize_a_username(i))),
- Options::default(),
- &mut out.username,
- )?;
-
- // Step 10. Set urlPattern’s password component to the result of compiling a component given
- // processedInit["password"], canonicalize a password, and default options.
- Component::compile(
- processed_init.password.as_deref().unwrap_or("*"),
- Box::new(|i| Ok(canonicalize_a_password(i))),
- Options::default(),
- &mut out.password,
- )?;
-
- // FIXME: Steps 11 and 12: Compile host pattern correctly
- Component::compile(
- processed_init.hostname.as_deref().unwrap_or("*"),
- Box::new(canonicalize_a_hostname),
- Options::HOSTNAME,
- &mut out.hostname,
- )?;
-
- // Step 13. Set urlPattern’s port component to the result of compiling a component given
- // processedInit["port"], canonicalize a port, and default options.
- Component::compile(
- processed_init.port.as_deref().unwrap_or("*"),
- Box::new(|i| canonicalize_a_port(i, None)),
- Options::default(),
- &mut out.port,
- )?;
-
- // FIXME: Step 14: respect ignore case option from here on out
- let _ = options;
-
- // FIXME: Steps 15-16: Compile path pattern correctly
- Component::compile(
- processed_init.pathname.as_deref().unwrap_or("*"),
- Box::new(|i| Ok(canonicalize_a_pathname(i))),
- Options::PATHNAME,
- &mut out.pathname,
- )?;
-
- // Step 17. Set urlPattern’s search component to the result of compiling a component given
- // processedInit["search"], canonicalize a search, and compileOptions.
- Component::compile(
- processed_init.search.as_deref().unwrap_or("*"),
- Box::new(|i| Ok(canonicalize_a_search(i))),
- Options::default(),
- &mut out.search,
- )?;
-
- // Step 18. Set urlPattern’s hash component to the result of compiling a component given
- // processedInit["hash"], canonicalize a hash, and compileOptions.
- Component::compile(
- processed_init.hash.as_deref().unwrap_or("*"),
- Box::new(|i| Ok(canonicalize_a_hash(i))),
- Options::default(),
- &mut out.hash,
- )?;
-
- // Step 19. Return urlPattern.
- // NOTE: not necessary since we use an out parameter
- Ok(())
- }
-
- /// <https://urlpattern.spec.whatwg.org/#url-pattern-has-regexp-groups>
- fn has_regexp_groups(&self) -> bool {
- self.protocol.has_regexp_groups ||
- self.username.has_regexp_groups ||
- self.password.has_regexp_groups ||
- self.hostname.has_regexp_groups ||
- self.port.has_regexp_groups ||
- self.pathname.has_regexp_groups ||
- self.search.has_regexp_groups ||
- self.hash.has_regexp_groups
- }
-}
-
-impl Component {
- /// <https://urlpattern.spec.whatwg.org/#compile-a-component>
- fn compile(
- input: &str,
- encoding_callback: EncodingCallback,
- options: Options,
- out: &mut Self,
- ) -> Fallible<()> {
- // Step 1. Let part list be the result of running parse a pattern string given input, options,
- // and encoding callback.
- let part_list = parse_a_pattern_string(input, options, encoding_callback)?;
-
- // Step 2. Let (regular expression string, name list) be the result of running generate a regular expression and
- // name list given part list and options.
- let (regular_expression_string, name_list) =
- generate_a_regular_expression_and_name_list(&part_list, options);
-
- log::debug!("Compiled {input:?} (URLPattern) to {regular_expression_string:?} (Regex)");
-
- // Step 3. Let flags be an empty string.
- // Step 4. If options’s ignore case is true then set flags to "vi".
- let flags = if options.ignore_case {
- RegExpFlags {
- flags_: RegExpFlag_UnicodeSets | RegExpFlag_IgnoreCase,
- }
- }
- // Step 5. Otherwise set flags to "v"
- else {
- RegExpFlags {
- flags_: RegExpFlag_UnicodeSets,
- }
- };
-
- // Step 6. Let regular expression be RegExpCreate(regular expression string, flags).
- // If this throws an exception, catch it, and throw a TypeError.
- let cx = GlobalScope::get_cx();
- rooted!(in(*cx) let mut regular_expression: *mut JSObject = ptr::null_mut());
- let succeeded = new_js_regex(
- cx,
- &regular_expression_string,
- flags,
- regular_expression.handle_mut(),
- );
- if !succeeded {
- return Err(Error::Type(format!(
- "Failed to compile {regular_expression_string:?} as a regular expression"
- )));
- }
-
- // TODO Step 7. Let pattern string be the result of running generate a pattern string given
- // part list and options.
- let pattern_string = Default::default();
-
- // Step 8. Let has regexp groups be false.
- // Step 9. For each part of part list:
- // Step 9.1 If part’s type is "regexp", then set has regexp groups to true.
- let has_regexp_groups = part_list
- .iter()
- .any(|part| part.part_type == PartType::Regexp);
-
- // Step 10. Return a new component whose pattern string is pattern string, regular expression
- // is regular expression, group name list is name list, and has regexp groups is has regexp groups.
- out.pattern_string = pattern_string;
- out.regular_expression.set(*regular_expression.handle());
- out.group_name_list = name_list;
- out.has_regexp_groups = has_regexp_groups;
-
- Ok(())
- }
-}
-
-/// <https://urlpattern.spec.whatwg.org/#generate-a-regular-expression-and-name-list>
-fn generate_a_regular_expression_and_name_list(
- part_list: &[Part],
- options: Options,
-) -> (String, Vec<USVString>) {
- // Step 1. Let result be "^".
- let mut result = String::from("^");
-
- // Step 2. Let name list be a new list.
- let mut name_list = vec![];
-
- // Step 3. For each part of part list:
- for part in part_list {
- // Step 3.1 If part’s type is "fixed-text":
- if part.part_type == PartType::FixedText {
- // Step 3.1.1 If part’s modifier is "none", then append the result of running escape a regexp string given
- // part’s value to the end of result.
- if part.modifier == PartModifier::None {
- result.push_str(&escape_a_regexp_string(&part.value));
- }
- // Step 3.1.2 Otherwise:
- else {
- // Step 3.1.2.1 Append "(?:" to the end of result.
- result.push_str("(?:");
-
- // Step 3.1.2.2 Append the result of running escape a regexp string given part’s value
- // to the end of result.
- result.push_str(&escape_a_regexp_string(&part.value));
-
- // Step 3.1.2.3 Append ")" to the end of result.
- result.push(')');
-
- // Step 3.1.2.4 Append the result of running convert a modifier to a string given part’s
- // modifier to the end of result.
- result.push_str(part.modifier.convert_to_string());
- }
-
- // Step 3.1.3 Continue.
- continue;
- }
-
- // Step 3.2 Assert: part’s name is not the empty string.
- debug_assert!(!part.name.is_empty());
-
- // Step 3.3 Append part’s name to name list.
- name_list.push(USVString(part.name.to_string()));
-
- // Step 3.4 Let regexp value be part’s value.
- let mut regexp_value = part.value.clone();
-
- // Step 3.5 If part’s type is "segment-wildcard", then set regexp value to the result of running
- // generate a segment wildcard regexp given options.
- if part.part_type == PartType::SegmentWildcard {
- regexp_value = generate_a_segment_wildcard_regexp(options);
- }
- // Step 3.6 Otherwise if part’s type is "full-wildcard", then set regexp value to full wildcard regexp value.
- else if part.part_type == PartType::FullWildcard {
- regexp_value = FULL_WILDCARD_REGEXP_VALUE.into();
- }
-
- // Step 3.7 If part’s prefix is the empty string and part’s suffix is the empty string:
- if part.prefix.is_empty() && part.suffix.is_empty() {
- // Step 3.7.1 If part’s modifier is "none" or "optional", then:
- if matches!(part.modifier, PartModifier::None | PartModifier::Optional) {
- // Step 3.7.1.1 Append "(" to the end of result.
- result.push('(');
-
- // Step 3.7.1.2 Append regexp value to the end of result.
- result.push_str(&regexp_value);
-
- // Step 3.7.1.3 Append ")" to the end of result.
- result.push(')');
-
- // Step 3.7.1.4 Append the result of running convert a modifier to a string given part’s modifier
- // to the end of result.
- result.push_str(part.modifier.convert_to_string());
- }
- // Step 3.7.2 Otherwise:
- else {
- // Step 3.7.2.1 Append "((?:" to the end of result.
- result.push_str("((?:");
-
- // Step 3.7.2.2 Append regexp value to the end of result.
- result.push_str(&regexp_value);
-
- // Step 3.7.2.3 Append ")" to the end of result.
- result.push(')');
-
- // Step 3.7.2.4 Append the result of running convert a modifier to a string given part’s modifier
- // to the end of result.
- result.push_str(part.modifier.convert_to_string());
-
- // Step 3.7.2.5 Append ")" to the end of result.
- result.push(')');
- }
-
- // Step 3.7.3 Continue.
- continue;
- }
-
- // Step 3.8 If part’s modifier is "none" or "optional":
- if matches!(part.modifier, PartModifier::None | PartModifier::Optional) {
- // Step 3.8.1 Append "(?:" to the end of result.
- result.push_str("(?:");
-
- // Step 3.8.2 Append the result of running escape a regexp string given part’s prefix
- // to the end of result.
- result.push_str(&escape_a_regexp_string(&part.prefix));
-
- // Step 3.8.3 Append "(" to the end of result.
- result.push('(');
-
- // Step 3.8.4 Append regexp value to the end of result.
- result.push_str(&regexp_value);
-
- // Step 3.8.5 Append ")" to the end of result.
- result.push(')');
-
- // Step 3.8.6 Append the result of running escape a regexp string given part’s suffix
- // to the end of result.
- result.push_str(&escape_a_regexp_string(&part.suffix));
-
- // Step 3.8.7 Append ")" to the end of result.
- result.push(')');
-
- // Step 3.8.8 Append the result of running convert a modifier to a string given part’s modifier to
- // the end of result.
- result.push_str(part.modifier.convert_to_string());
-
- // Step 3.8.9 Continue.
- continue;
- }
-
- // Step 3.9 Assert: part’s modifier is "zero-or-more" or "one-or-more".
- debug_assert!(matches!(
- part.modifier,
- PartModifier::ZeroOrMore | PartModifier::OneOrMore
- ));
-
- // Step 3.10 Assert: part’s prefix is not the empty string or part’s suffix is not the empty string.
- debug_assert!(!part.prefix.is_empty() || !part.suffix.is_empty());
-
- // Step 3.11 Append "(?:" to the end of result.
- result.push_str("(?:");
-
- // Step 3.12 Append the result of running escape a regexp string given part’s prefix to the end of result.
- result.push_str(&escape_a_regexp_string(&part.prefix));
-
- // Step 3.13 Append "((?:" to the end of result.
- result.push_str("((?:");
-
- // Step 3.14 Append regexp value to the end of result.
- result.push_str(&regexp_value);
-
- // Step 3.15 Append ")(?:" to the end of result.
- result.push_str(")(?:");
-
- // Step 3.16 Append the result of running escape a regexp string given part’s suffix to the end of result.
- result.push_str(&escape_a_regexp_string(&part.suffix));
-
- // Step 3.17 Append the result of running escape a regexp string given part’s prefix to the end of result.
- result.push_str(&escape_a_regexp_string(&part.prefix));
-
- // Step 3.18 Append "(?:" to the end of result.
- result.push_str("(?:");
-
- // Step 3.19 Append regexp value to the end of result.
- result.push_str(&regexp_value);
-
- // Step 3.20 Append "))*)" to the end of result.
- result.push_str("))*)");
-
- // Step 3.21 Append the result of running escape a regexp string given part’s suffix to the end of result.
- result.push_str(&escape_a_regexp_string(&part.suffix));
-
- // Step 3.22 Append ")" to the end of result.
- result.push(')');
-
- // Step 3.23 If part’s modifier is "zero-or-more" then append "?" to the end of result.
- if part.modifier == PartModifier::ZeroOrMore {
- result.push('?');
- }
- }
-
- // Step 4. Append "$" to the end of result.
- result.push('$');
-
- // Step 5. Return (result, name list).
- (result, name_list)
-}
-
-/// <https://urlpattern.spec.whatwg.org/#encoding-callback>
-type EncodingCallback = Box<dyn Fn(&str) -> Fallible<String>>;
-
-// FIXME: Deduplicate this with the url crate
-/// <https://url.spec.whatwg.org/#special-scheme>
-fn default_port_for_special_scheme(scheme: &str) -> Option<u16> {
- match scheme {
- "ftp" => Some(21),
- "http" | "ws" => Some(80),
- "https" | "wss" => Some(443),
- _ => None,
- }
-}
-
-/// <https://url.spec.whatwg.org/#special-scheme>
-fn is_special_scheme(scheme: &str) -> bool {
- matches!(scheme, "ftp" | "http" | "https" | "ws" | "wss")
-}
-
-/// <https://urlpattern.spec.whatwg.org/#generate-a-segment-wildcard-regexp>
-fn generate_a_segment_wildcard_regexp(options: Options) -> String {
- // Step 1. Let result be "[^".
- let mut result = String::from("[^");
-
- // Step 2. Append the result of running escape a regexp string given options’s
- // delimiter code point to the end of result.
- result.push_str(&escape_a_regexp_string(
- &options
- .delimiter_code_point
- .map(|c| c.to_string())
- .unwrap_or_default(),
- ));
-
- // Step 3. Append "]+?" to the end of result.
- result.push_str("]+?");
-
- // Step 4. Return result.
- result
-}
-
-impl PartModifier {
- /// <https://urlpattern.spec.whatwg.org/#convert-a-modifier-to-a-string>
- fn convert_to_string(&self) -> &'static str {
- match self {
- // Step 1. If modifier is "zero-or-more", then return "*".
- Self::ZeroOrMore => "*",
- // Step 2. If modifier is "optional", then return "?".
- Self::Optional => "?",
- // Step 3. If modifier is "one-or-more", then return "+".
- Self::OneOrMore => "+",
- // Step 4. Return the empty string.
- _ => "",
- }
- }
-}
-
-impl Options {
- /// <https://urlpattern.spec.whatwg.org/#hostname-options>
- const HOSTNAME: Self = Self {
- delimiter_code_point: Some('.'),
- prefix_code_point: None,
- ignore_case: false,
- };
-
- /// <https://urlpattern.spec.whatwg.org/#pathname-options>
- const PATHNAME: Self = Self {
- delimiter_code_point: Some('/'),
- prefix_code_point: Some('/'),
- ignore_case: false,
- };
-}
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-enum PatternInitType {
- Pattern,
- Url,
-}
-
-impl Part {
- fn new(part_type: PartType, value: String, modifier: PartModifier) -> Self {
- Self {
- part_type,
- value,
- modifier,
- name: String::new(),
- prefix: String::new(),
- suffix: String::new(),
- }
- }
-}
diff --git a/components/script/dom/urlpattern/pattern_parser.rs b/components/script/dom/urlpattern/pattern_parser.rs
deleted file mode 100644
index 3147c5649f4..00000000000
--- a/components/script/dom/urlpattern/pattern_parser.rs
+++ /dev/null
@@ -1,473 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use script_bindings::error::{Error, Fallible};
-
-use crate::dom::urlpattern::tokenizer::{Token, TokenType, TokenizePolicy, tokenize};
-use crate::dom::urlpattern::{
- EncodingCallback, FULL_WILDCARD_REGEXP_VALUE, Options, Part, PartModifier, PartType,
- generate_a_segment_wildcard_regexp,
-};
-
-/// <https://urlpattern.spec.whatwg.org/#parse-a-pattern-string>
-pub(super) fn parse_a_pattern_string(
- input: &str,
- options: Options,
- encoding_callback: EncodingCallback,
-) -> Fallible<Vec<Part>> {
- // Step 1. Let parser be a new pattern parser whose encoding callback is encoding callback and
- // segment wildcard regexp is the result of running generate a segment wildcard regexp given options.
- let mut parser = PatternParser::new(
- generate_a_segment_wildcard_regexp(options),
- encoding_callback,
- );
-
- // Step 2. Set parser’s token list to the result of running tokenize given input and "strict".
- parser.token_list = tokenize(input, TokenizePolicy::Strict)?;
-
- // Step 3. While parser’s index is less than parser’s token list’s size:
- while parser.index < parser.token_list.len() {
- // Step 3.1 Let char token be the result of running try to consume a token given parser and "char".
- let char_token = parser.try_to_consume_a_token(TokenType::Char);
-
- // Step 3.2 Let name token be the result of running try to consume a token given parser and "name".
- let mut name_token = parser.try_to_consume_a_token(TokenType::Name);
-
- // Step 3.3 Let regexp or wildcard token be the result of running try to consume a
- // regexp or wildcard token given parser and name token.
- let mut regexp_or_wildcard_token =
- parser.try_to_consume_a_regexp_or_wildcard_token(name_token);
-
- // Step 3.4 If name token is not null or regexp or wildcard token is not null:
- if name_token.is_some() || regexp_or_wildcard_token.is_some() {
- // Step 3.4.1 Let prefix be the empty string.
- let mut prefix = "";
-
- // Step 3.4.2 If char token is not null then set prefix to char token’s value.
- if let Some(char_token) = char_token {
- prefix = char_token.value;
- }
-
- // Step 3.4.3 If prefix is not the empty string and not options’s prefix code point:
- let prefix_is_prefix_code_point = options.prefix_code_point.is_some_and(|c| {
- let mut buffer = [0; 4];
- prefix == c.encode_utf8(&mut buffer)
- });
- if !prefix.is_empty() && !prefix_is_prefix_code_point {
- // Step 3.4.3.1 Append prefix to the end of parser’s pending fixed value.
- parser.pending_fixed_value.push_str(prefix);
-
- // Step 3.4.3.2 Set prefix to the empty string.
- prefix = "";
- }
-
- // Step 3.4.4 Run maybe add a part from the pending fixed value given parser.
- parser.maybe_add_a_part_from_the_pending_fixed_value()?;
-
- // Step 3.4.5 Let modifier token be the result of running try to consume a modifier token given parser.
- let modifier_token = parser.try_to_consume_a_modifier_token();
-
- // Step 3.4.6 Run add a part given parser, prefix, name token, regexp or wildcard token,
- // the empty string, and modifier token.
- parser.add_a_part(
- prefix,
- name_token,
- regexp_or_wildcard_token,
- "",
- modifier_token,
- )?;
-
- // Step 3.4.7 Continue.
- continue;
- }
-
- // Step 3.5 Let fixed token be char token.
- let mut fixed_token = char_token;
-
- // Step 3.6 If fixed token is null, then set fixed token to the result of running
- // try to consume a token given parser and "escaped-char".
- if fixed_token.is_none() {
- fixed_token = parser.try_to_consume_a_token(TokenType::EscapedChar);
- }
-
- // Step 3.7 If fixed token is not null:
- if let Some(fixed_token) = fixed_token {
- // Step 3.7.1 Append fixed token’s value to parser’s pending fixed value.
- parser.pending_fixed_value.push_str(fixed_token.value);
-
- // Step 3.7.2 Continue.
- continue;
- }
-
- // Step 3.8 Let open token be the result of running try to consume a token given parser and "open".
- let open_token = parser.try_to_consume_a_token(TokenType::Open);
-
- // Step 3.9 If open token is not null:
- if open_token.is_some() {
- // Step 3.9.1 Let prefix be the result of running consume text given parser.
- let prefix = parser.consume_text();
-
- // Step 3.9.2 Set name token to the result of running try to consume a token given parser and "name".
- name_token = parser.try_to_consume_a_token(TokenType::Name);
-
- // Step 3.9.3 Set regexp or wildcard token to the result of running try to consume a regexp or wildcard
- // token given parser and name token.
- regexp_or_wildcard_token = parser.try_to_consume_a_regexp_or_wildcard_token(name_token);
-
- // Step 3.9.4 Let suffix be the result of running consume text given parser.
- let suffix = parser.consume_text();
-
- // Step 3.9.5 Run consume a required token given parser and "close".
- parser.consume_a_required_token(TokenType::Close)?;
-
- // Step 3.9.6 Let modifier token be the result of running try to consume a modifier token given parser.
- let modifier_token = parser.try_to_consume_a_modifier_token();
-
- // Step 3.9.7 Run add a part given parser, prefix, name token, regexp or wildcard token,
- // suffix, and modifier token.
- parser.add_a_part(
- &prefix,
- name_token,
- regexp_or_wildcard_token,
- &suffix,
- modifier_token,
- )?;
-
- // Step 3.9.8 Continue.
- continue;
- }
-
- // Step 3.10 Run maybe add a part from the pending fixed value given parser.
- parser.maybe_add_a_part_from_the_pending_fixed_value()?;
-
- // Step 3.11 Run consume a required token given parser and "end".
- parser.consume_a_required_token(TokenType::End)?;
- }
-
- Ok(parser.part_list)
-}
-
-/// <https://urlpattern.spec.whatwg.org/#pattern-parser>
-struct PatternParser<'a> {
- /// <https://urlpattern.spec.whatwg.org/#pattern-parser-token-list>
- token_list: Vec<Token<'a>>,
-
- /// <https://urlpattern.spec.whatwg.org/#pattern-parser-encoding-callback>
- encoding_callback: EncodingCallback,
-
- /// <https://urlpattern.spec.whatwg.org/#pattern-parser-segment-wildcard-regexp>
- segment_wildcard_regexp: String,
-
- /// <https://urlpattern.spec.whatwg.org/#pattern-parser-part-list>
- part_list: Vec<Part>,
-
- /// <https://urlpattern.spec.whatwg.org/#pattern-parser-pending-fixed-value>
- pending_fixed_value: String,
-
- /// <https://urlpattern.spec.whatwg.org/#pattern-parser-index>
- index: usize,
-
- /// <https://urlpattern.spec.whatwg.org/#pattern-parser-next-numeric-name>
- next_numeric_name: usize,
-}
-
-impl<'a> PatternParser<'a> {
- fn new(segment_wildcard_regexp: String, encoding_callback: EncodingCallback) -> Self {
- Self {
- token_list: vec![],
- segment_wildcard_regexp,
- part_list: vec![],
- pending_fixed_value: String::new(),
- index: 0,
- next_numeric_name: 0,
- encoding_callback,
- }
- }
-
- /// <https://urlpattern.spec.whatwg.org/#try-to-consume-a-token>
- fn try_to_consume_a_token(&mut self, token_type: TokenType) -> Option<Token<'a>> {
- // Step 1. Assert: parser’s index is less than parser’s token list size.
- debug_assert!(self.index < self.token_list.len());
-
- // Step 2. Let next token be parser’s token list[parser’s index].
- let next_token = self.token_list[self.index];
-
- // Step 3. If next token’s type is not type return null.
- if next_token.token_type != token_type {
- return None;
- }
-
- // Step 4. Increment parser’s index by 1.
- self.index += 1;
-
- // Step 5. Return next token.
- Some(next_token)
- }
-
- /// <https://urlpattern.spec.whatwg.org/#try-to-consume-a-modifier-token>
- fn try_to_consume_a_modifier_token(&mut self) -> Option<Token<'a>> {
- // Step 1. Let token be the result of running try to consume a token given parser and "other-modifier".
- let token = self.try_to_consume_a_token(TokenType::OtherModifier);
-
- // Step 2. If token is not null, then return token.
- if token.is_some() {
- return token;
- }
-
- // Step 3. Set token to the result of running try to consume a token given parser and "asterisk".
- let token = self.try_to_consume_a_token(TokenType::Asterisk);
-
- // Step 4. Return token.
- token
- }
-
- /// <https://urlpattern.spec.whatwg.org/#consume-a-required-token>
- fn consume_a_required_token(&mut self, token_type: TokenType) -> Fallible<Token<'a>> {
- // Step 1. Let result be the result of running try to consume a token given parser and type.
- let result = self.try_to_consume_a_token(token_type);
-
- // Step 2. If result is null, then throw a TypeError.
- let Some(result) = result else {
- return Err(Error::Type(format!(
- "Missing required token {token_type:?}"
- )));
- };
-
- // Step 3. Return result.
- Ok(result)
- }
-
- /// <https://urlpattern.spec.whatwg.org/#try-to-consume-a-regexp-or-wildcard-token>
- fn try_to_consume_a_regexp_or_wildcard_token(
- &mut self,
- name_token: Option<Token<'a>>,
- ) -> Option<Token<'a>> {
- // Step 1. Let token be the result of running try to consume a token given parser and "regexp".
- let mut token = self.try_to_consume_a_token(TokenType::Regexp);
-
- // Step 2. If name token is null and token is null, then set token to the result of running
- // try to consume a token given parser and "asterisk".
- if name_token.is_none() && token.is_none() {
- token = self.try_to_consume_a_token(TokenType::Asterisk);
- }
-
- // Step 3. Return token.
- token
- }
-
- /// <https://urlpattern.spec.whatwg.org/#maybe-add-a-part-from-the-pending-fixed-value>
- fn maybe_add_a_part_from_the_pending_fixed_value(&mut self) -> Fallible<()> {
- // Step 1. If parser’s pending fixed value is the empty string, then return.
- if self.pending_fixed_value.is_empty() {
- return Ok(());
- }
-
- // Step 2. Let encoded value be the result of running parser’s encoding callback
- // given parser’s pending fixed value.
- let encoded_value = (self.encoding_callback)(&self.pending_fixed_value)?;
-
- // Step 3. Set parser’s pending fixed value to the empty string.
- self.pending_fixed_value.clear();
-
- // Step 4. Let part be a new part whose type is "fixed-text", value is encoded value, and modifier is "none".
- let part = Part::new(PartType::FixedText, encoded_value, PartModifier::None);
-
- // Step 5. Append part to parser’s part list.
- self.part_list.push(part);
-
- Ok(())
- }
-
- /// <https://urlpattern.spec.whatwg.org/#add-a-part>
- fn add_a_part(
- &mut self,
- prefix: &str,
- name_token: Option<Token<'a>>,
- regexp_or_wildcard_token: Option<Token<'a>>,
- suffix: &str,
- modifier_token: Option<Token<'a>>,
- ) -> Fallible<()> {
- // Step 1. Let modifier be "none".
- let mut modifier = PartModifier::None;
-
- // Step 2. If modifier token is not null:
- if let Some(modifier_token) = modifier_token {
- // Step 2.1 If modifier token’s value is "?" then set modifier to "optional".
- if modifier_token.value == "?" {
- modifier = PartModifier::Optional;
- }
- // Step 2.2 Otherwise if modifier token’s value is "*" then set modifier to "zero-or-more".
- else if modifier_token.value == "*" {
- modifier = PartModifier::ZeroOrMore;
- }
- // Step 2.3 Otherwise if modifier token’s value is "+" then set modifier to "one-or-more".
- else if modifier_token.value == "+" {
- modifier = PartModifier::OneOrMore;
- }
- }
-
- // Step 3. If name token is null and regexp or wildcard token is null and modifier is "none":
- if name_token.is_none() &&
- regexp_or_wildcard_token.is_none() &&
- modifier == PartModifier::None
- {
- // Step 3.1 Append prefix to the end of parser’s pending fixed value.
- self.pending_fixed_value.push_str(prefix);
-
- // Step 3.2 Return
- return Ok(());
- }
-
- // Step 4. Run maybe add a part from the pending fixed value given parser.
- self.maybe_add_a_part_from_the_pending_fixed_value()?;
-
- // Step 5. If name token is null and regexp or wildcard token is null:
- if name_token.is_none() && regexp_or_wildcard_token.is_none() {
- // Step 5.1 Assert: suffix is the empty string.
- debug_assert!(suffix.is_empty());
-
- // Step 5.2 If prefix is the empty string, then return.
- if prefix.is_empty() {
- return Ok(());
- }
-
- // Step 5.3 Let encoded value be the result of running parser’s encoding callback given prefix.
- let encoded_value = (self.encoding_callback)(prefix)?;
-
- // Step 5.4 Let part be a new part whose type is "fixed-text",
- // value is encoded value, and modifier is modifier.
- let part = Part::new(PartType::FixedText, encoded_value, modifier);
-
- // Step 5.5 Append part to parser’s part list.
- self.part_list.push(part);
-
- // Step 6. Return.
- return Ok(());
- }
-
- // Step 6. Let regexp value be the empty string.
- let mut regexp_value = {
- // Step 7. If regexp or wildcard token is null, then set regexp value to parser’s segment wildcard regexp.
- match regexp_or_wildcard_token {
- None => self.segment_wildcard_regexp.clone(),
- Some(token) => {
- // Step 8. Otherwise if regexp or wildcard token’s type is "asterisk",
- // then set regexp value to the full wildcard regexp value.
- if token.token_type == TokenType::Asterisk {
- FULL_WILDCARD_REGEXP_VALUE.into()
- }
- // Step 9. Otherwise set regexp value to regexp or wildcard token’s value.
- else {
- token.value.to_owned()
- }
- },
- }
- };
-
- // Step 10. Let type be "regexp".
- let mut part_type = PartType::Regexp;
-
- // Step 11. If regexp value is parser’s segment wildcard regexp:
- if regexp_value == self.segment_wildcard_regexp {
- // Step 11.1 Set type to "segment-wildcard".
- part_type = PartType::SegmentWildcard;
-
- // Step 11.2 Set regexp value to the empty string.
- regexp_value.clear();
- }
- // Step 12. Otherwise if regexp value is the full wildcard regexp value:
- else if regexp_value == FULL_WILDCARD_REGEXP_VALUE {
- // Step 12.1 Set type to "full-wildcard".
- part_type = PartType::FullWildcard;
-
- // Step 12.2 Set regexp value to the empty string.
- regexp_value.clear();
- }
-
- // Step 13. Let name be the empty string.
- let mut name = String::new();
-
- // Step 14. If name token is not null, then set name to name token’s value.
- if let Some(name_token) = name_token {
- name = name_token.value.to_owned();
- }
- // Step 15. Otherwise if regexp or wildcard token is not null:
- else if regexp_or_wildcard_token.is_some() {
- // Step 15.1 Set name to parser’s next numeric name, serialized.
- name = self.next_numeric_name.to_string();
-
- // Step 15.2 Increment parser’s next numeric name by 1.
- self.next_numeric_name = self.next_numeric_name.wrapping_add(1);
- }
-
- // Step 16. If the result of running is a duplicate name given parser and name is true, then throw a TypeError.
- if self.is_a_duplicate_name(&name) {
- return Err(Error::Type(format!("Duplicate part name: {name:?}")));
- }
-
- // Step 17. Let encoded prefix be the result of running parser’s encoding callback given prefix.
- let encoded_prefix = (self.encoding_callback)(prefix)?;
-
- // Step 18. Let encoded suffix be the result of running parser’s encoding callback given suffix.
- let encoded_suffix = (self.encoding_callback)(suffix)?;
-
- // Step 19. Let part be a new part whose type is type, value is regexp value, modifier is modifier,
- // name is name, prefix is encoded prefix, and suffix is encoded suffix.
- let part = Part {
- part_type,
- value: regexp_value,
- modifier,
- name,
- prefix: encoded_prefix,
- suffix: encoded_suffix,
- };
-
- // Step 20. Append part to parser’s part list.
- self.part_list.push(part);
-
- Ok(())
- }
-
- // <https://urlpattern.spec.whatwg.org/#is-a-duplicate-name>
- fn is_a_duplicate_name(&self, name: &str) -> bool {
- // Step 1. For each part of parser’s part list:
- for part in &self.part_list {
- // Step 1.1 If part’s name is name, then return true.
- if part.name == name {
- return true;
- }
- }
-
- // Step 2. Return false.
- false
- }
-
- /// <https://urlpattern.spec.whatwg.org/#consume-text>
- fn consume_text(&mut self) -> String {
- // Step 1. Let result be the empty string.
- let mut result = String::new();
-
- // Step 2. While true:
- loop {
- // Step 2.1 Let token be the result of running try to consume a token given parser and "char".
- let mut token = self.try_to_consume_a_token(TokenType::Char);
-
- // Step 2.2 If token is null, then set token to the result of running
- // try to consume a token given parser and "escaped-char".
- if token.is_none() {
- token = self.try_to_consume_a_token(TokenType::EscapedChar);
- }
-
- // Step 2.3 If token is null, then break.
- let Some(token) = token else {
- break;
- };
-
- // Step 2.4 Append token’s value to the end of result.
- result.push_str(token.value);
- }
-
- result
- }
-}
diff --git a/components/script/dom/urlpattern/preprocessing.rs b/components/script/dom/urlpattern/preprocessing.rs
deleted file mode 100644
index 7fc3c136315..00000000000
--- a/components/script/dom/urlpattern/preprocessing.rs
+++ /dev/null
@@ -1,659 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use script_bindings::error::{Error, Fallible};
-use script_bindings::str::USVString;
-use url::Url;
-
-use crate::dom::bindings::codegen::Bindings::URLPatternBinding::URLPatternInit;
-use crate::dom::urlpattern::{PatternInitType, default_port_for_special_scheme, is_special_scheme};
-
-/// <https://urlpattern.spec.whatwg.org/#process-a-urlpatterninit>
-pub(super) fn process_a_url_pattern_init(
- init: &URLPatternInit,
- init_type: PatternInitType,
-) -> Fallible<URLPatternInit> {
- // Step 1. Let result be the result of creating a new URLPatternInit.
- let mut result = URLPatternInit::default();
-
- // TODO Step 2. If protocol is not null, set result["protocol"] to protocol.
- // TODO Step 3. If username is not null, set result["username"] to username.
- // TODO Step 4. If password is not null, set result["password"] to password.
- // TODO Step 5. If hostname is not null, set result["hostname"] to hostname.
- // TODO Step 6. If port is not null, set result["port"] to port.
- // TODO Step 7. If pathname is not null, set result["pathname"] to pathname.
- // TODO Step 8. If search is not null, set result["search"] to search.
- // TODO Step 9. If hash is not null, set result["hash"] to hash.
-
- // Step 10. Let baseURL be null.
- let mut base_url: Option<Url> = None;
-
- // Step 11. If init["baseURL"] exists:
- if let Some(init_base_url) = init.baseURL.as_ref() {
- // Step 11.1 Set baseURL to the result of running the basic URL parser on init["baseURL"].
- let Ok(parsed_base_url) = init_base_url.0.parse() else {
- // Step 11.2 If baseURL is failure, then throw a TypeError.
- return Err(Error::Type(format!(
- "Failed to parse {:?} as URL",
- init_base_url.0
- )));
- };
- let base_url = base_url.insert(parsed_base_url);
-
- // Step 11.3 If init["protocol"] does not exist, then set result["protocol"] to the result of
- // processing a base URL string given baseURL’s scheme and type.
- if init.protocol.is_none() {
- result.protocol = Some(USVString(process_a_base_url_string(
- base_url.scheme(),
- init_type,
- )));
- }
-
- // Step 11.4. If type is not "pattern" and init contains none of "protocol", "hostname",
- // "port" and "username", then set result["username"] to the result of processing a base URL string
- // given baseURL’s username and type.
- if init_type != PatternInitType::Pattern &&
- init.protocol.is_none() &&
- init.hostname.is_none() &&
- init.port.is_none() &&
- init.username.is_none()
- {
- result.username = Some(USVString(process_a_base_url_string(
- base_url.username(),
- init_type,
- )));
- }
-
- // Step 11.5 If type is not "pattern" and init contains none of "protocol", "hostname", "port",
- // "username" and "password", then set result["password"] to the result of processing a base URL string
- // given baseURL’s password and type.
- if init_type != PatternInitType::Pattern &&
- init.protocol.is_none() &&
- init.hostname.is_none() &&
- init.port.is_none() &&
- init.username.is_none() &&
- init.password.is_none()
- {
- result.password = Some(USVString(process_a_base_url_string(
- base_url.password().unwrap_or_default(),
- init_type,
- )));
- }
-
- // Step 11.6 If init contains neither "protocol" nor "hostname", then:
- if init.protocol.is_none() && init.hostname.is_none() {
- // Step 11.6.1 Let baseHost be the empty string.
- // Step 11.6.2 If baseURL’s host is not null, then set baseHost to its serialization.
- let base_host = base_url
- .host()
- .map(|host| host.to_string())
- .unwrap_or_default();
-
- // Step 11.6.3 Set result["hostname"] to the result of processing a base URL string given baseHost and type.
- result.hostname = Some(USVString(process_a_base_url_string(&base_host, init_type)));
- }
-
- // Step 11.7 If init contains none of "protocol", "hostname", and "port", then:
- if init.protocol.is_none() && init.hostname.is_none() && init.port.is_none() {
- match base_url.port() {
- // Step 11.7.1 If baseURL’s port is null, then set result["port"] to the empty string.
- None => {
- result.port = Some(USVString(String::new()));
- },
- // Step 11.7.2 Otherwise, set result["port"] to baseURL’s port, serialized.
- Some(port) => {
- result.port = Some(USVString(port.to_string()));
- },
- }
- }
-
- // Step 11.8 If init contains none of "protocol", "hostname", "port", and "pathname", then set
- // result["pathname"] to the result of processing a base URL string given the result of
- // URL path serializing baseURL and type.
- if init.protocol.is_none() &&
- init.hostname.is_none() &&
- init.port.is_none() &&
- init.pathname.is_none()
- {
- result.pathname = Some(USVString(process_a_base_url_string(
- base_url.path(),
- init_type,
- )));
- }
-
- // Step 11.9 If init contains none of "protocol", "hostname", "port", "pathname",
- // and "search", then:
- if init.protocol.is_none() &&
- init.hostname.is_none() &&
- init.port.is_none() &&
- init.pathname.is_none() &&
- init.search.is_none()
- {
- // Step 11.9.1 Let baseQuery be baseURL’s query.
- let base_query = base_url.query();
-
- // Step 11.9.2 If baseQuery is null, then set baseQuery to the empty string.
- let base_query = base_query.unwrap_or_default();
-
- // Step 11.9.3 Set result["search"] to the result of processing a base URL string given baseQuery and type.
- result.search = Some(USVString(process_a_base_url_string(base_query, init_type)));
- }
-
- // Step 11.10 If init contains none of "protocol", "hostname",
- // "port", "pathname", "search", and "hash", then:
- if init.protocol.is_none() &&
- init.hostname.is_none() &&
- init.port.is_none() &&
- init.pathname.is_none() &&
- init.search.is_none() &&
- init.hash.is_none()
- {
- // Step 11.10.1 Let baseFragment be baseURL’s fragment.
- let base_fragment = base_url.fragment();
-
- // Step 11.10.2 If baseFragment is null, then set baseFragment to the empty string.
- let base_fragment = base_fragment.unwrap_or_default();
-
- // Step 11.10.3 Set result["hash"] to the result of processing a base URL string
- // given baseFragment and type.
- result.hash = Some(USVString(process_a_base_url_string(
- base_fragment,
- init_type,
- )));
- }
- }
-
- // Step 12. If init["protocol"] exists, then set result["protocol"] to the result of process protocol for init
- // given init["protocol"] and type.
- if let Some(protocol) = &init.protocol {
- result.protocol = Some(USVString(process_a_protocol_for_init(protocol, init_type)?));
- }
-
- // Step 13. If init["username"] exists, then set result["username"] to the result of
- // process username for init given init["username"] and type.
- if let Some(username) = &init.username {
- result.username = Some(USVString(process_username_for_init(username, init_type)));
- }
-
- // Step 14. If init["password"] exists, then set result["password"] to the result of
- // process password for init given init["password"] and type.
- if let Some(password) = &init.password {
- result.password = Some(USVString(process_password_for_init(password, init_type)));
- }
-
- // Step 15. If init["hostname"] exists, then set result["hostname"] to the result of
- // process hostname for init given init["hostname"] and type.
- if let Some(hostname) = &init.hostname {
- result.hostname = Some(USVString(process_hostname_for_init(hostname, init_type)?));
- }
-
- // Step 16. Let resultProtocolString be result["protocol"] if it exists; otherwise the empty string.
- let result_protocol_string = result.protocol.as_deref().unwrap_or_default();
-
- // Step 17. If init["port"] exists, then set result["port"] to the result of process port for init
- // given init["port"], resultProtocolString, and type.
- if let Some(port) = &init.port {
- result.port = Some(USVString(process_port_for_init(
- port,
- result_protocol_string,
- init_type,
- )?));
- }
-
- // Step 18. If init["pathname"] exists:
- if let Some(path_name) = &init.pathname {
- // Step 18.1 Set result["pathname"] to init["pathname"].
- // NOTE: This is not necessary - the spec uses result["pathname"] in the following section,
- // but it could just as well use init["pathname"]. Storing the string in an intermediate
- // variable makes the code simpler
- let mut result_pathname = path_name.to_string();
-
- // Step 18.2 If the following are all true:
- // * baseURL is not null;
- // * baseURL does not have an opaque path; and
- // * the result of running is an absolute pathname given result["pathname"] and type is false,
- if let Some(base_url) = base_url {
- if !base_url.cannot_be_a_base() && !is_an_absolute_pathname(path_name, init_type) {
- // Step 18.2.1 Let baseURLPath be the result of running process a base URL string given the result
- // of URL path serializing baseURL and type.
- let base_url_path = process_a_base_url_string(base_url.path(), init_type);
-
- // Step 18.2.2 Let slash index be the index of the last U+002F (/) code point found in baseURLPath,
- // interpreted as a sequence of code points, or null if there are no instances of the code point.
- let slash_index = base_url_path.rfind('/');
-
- // Step 18.2.3 If slash index is not null:
- if let Some(slash_index) = slash_index {
- // Step 18.2.3.1 Let new pathname be the code point substring from 0 to slash index + 1
- // within baseURLPath.
- let mut new_pathname = base_url_path[..=slash_index].to_owned();
-
- // Step 18.2.3.2 Append result["pathname"] to the end of new pathname.
- new_pathname.push_str(path_name);
-
- // Step 18.2.3.3 Set result["pathname"] to new pathname.
- result_pathname = new_pathname;
- }
- }
- }
-
- // Step 18.3 Set result["pathname"] to the result of process pathname for init given result["pathname"],
- // resultProtocolString, and type.
- result.pathname = Some(USVString(process_pathname_for_init(
- &result_pathname,
- result_protocol_string,
- init_type,
- )?));
- }
-
- // Step 19. If init["search"] exists then set result["search"] to the result of
- // process search for init given init["search"] and type.
- if let Some(search) = &init.search {
- result.search = Some(USVString(process_search_for_init(search, init_type)));
- }
-
- // Step 20. If init["hash"] exists then set result["hash"] to the result of
- // process hash for init given init["hash"] and type.
- if let Some(hash) = &init.hash {
- result.hash = Some(USVString(process_hash_for_init(hash, init_type)));
- }
-
- // Step 21. Return result.
- Ok(result)
-}
-
-/// <https://urlpattern.spec.whatwg.org/#process-protocol-for-init>
-fn process_a_protocol_for_init(input: &str, init_type: PatternInitType) -> Fallible<String> {
- // Step 1. Let strippedValue be the given value with a single trailing U+003A (:) removed, if any.
- let stripped_value = input.strip_suffix(':').unwrap_or(input);
-
- // Step 2. If type is "pattern" then return strippedValue.
- if init_type == PatternInitType::Pattern {
- return Ok(stripped_value.to_owned());
- }
-
- // Step 3. Return the result of running canonicalize a protocol given strippedValue.
- canonicalize_a_protocol(stripped_value)
-}
-
-/// <https://urlpattern.spec.whatwg.org/#process-username-for-init>
-fn process_username_for_init(value: &str, init_type: PatternInitType) -> String {
- // Step 1. If type is "pattern" then return value.
- if init_type == PatternInitType::Pattern {
- return value.to_owned();
- }
-
- // Step 2. Return the result of running canonicalize a username given value.
- canonicalize_a_username(value)
-}
-
-/// <https://urlpattern.spec.whatwg.org/#process-password-for-init>
-fn process_password_for_init(value: &str, init_type: PatternInitType) -> String {
- // Step 1. If type is "pattern" then return value.
- if init_type == PatternInitType::Pattern {
- return value.to_owned();
- }
-
- // Step 2. Return the result of running canonicalize a password given value.
- canonicalize_a_password(value)
-}
-
-/// <https://urlpattern.spec.whatwg.org/#process-hostname-for-init>
-fn process_hostname_for_init(value: &str, init_type: PatternInitType) -> Fallible<String> {
- // Step 1. If type is "pattern" then return value.
- if init_type == PatternInitType::Pattern {
- return Ok(value.to_owned());
- }
-
- // Step 2. Return the result of running canonicalize a hostname given value.
- canonicalize_a_hostname(value)
-}
-
-/// <https://urlpattern.spec.whatwg.org/#process-port-for-init>
-fn process_port_for_init(
- port_value: &str,
- protocol_value: &str,
- init_type: PatternInitType,
-) -> Fallible<String> {
- // Step 1. If type is "pattern" then return portValue.
- if init_type == PatternInitType::Pattern {
- return Ok(port_value.to_owned());
- }
-
- // Step 2. Return the result of running canonicalize a port given portValue and protocolValue.
- canonicalize_a_port(port_value, Some(protocol_value))
-}
-
-/// <https://urlpattern.spec.whatwg.org/#process-pathname-for-init>
-fn process_pathname_for_init(
- path_name_value: &str,
- protocol_value: &str,
- init_type: PatternInitType,
-) -> Fallible<String> {
- // Step 1. If type is "pattern" then return pathnameValue.
- if init_type == PatternInitType::Pattern {
- return Ok(path_name_value.to_owned());
- }
-
- // Step 2. If protocolValue is a special scheme or the empty string, then return the result of
- // running canonicalize a pathname given pathnameValue.
- if is_special_scheme(protocol_value) || protocol_value.is_empty() {
- return Ok(canonicalize_a_pathname(path_name_value));
- }
-
- // Step 2. Return the result of running canonicalize an opaque pathname given pathnameValue.
- canonicalize_an_opaque_pathname(path_name_value)
-}
-
-/// <https://urlpattern.spec.whatwg.org/#process-search-for-init>
-fn process_search_for_init(value: &str, init_type: PatternInitType) -> String {
- // Step 1. Let strippedValue be the given value with a single leading U+003F (?) removed, if any.
- let stripped_value = value.strip_prefix('?').unwrap_or(value);
-
- // Step 2. If type is "pattern" then return strippedValue.
- if init_type == PatternInitType::Pattern {
- return stripped_value.to_owned();
- }
-
- // Step 3. Return the result of running canonicalize a search given strippedValue.
- canonicalize_a_search(stripped_value)
-}
-
-/// <https://urlpattern.spec.whatwg.org/#process-hash-for-init>
-fn process_hash_for_init(value: &str, init_type: PatternInitType) -> String {
- // Step 1. Let strippedValue be the given value with a single leading U+0023 (#) removed, if any.
- let stripped_value = value.strip_prefix('#').unwrap_or(value);
-
- // Step 2. If type is "pattern" then return strippedValue.
- if init_type == PatternInitType::Pattern {
- return stripped_value.to_owned();
- }
-
- // Step 3. Return the result of running canonicalize a hash given strippedValue.
- canonicalize_a_hash(stripped_value)
-}
-
-/// <https://urlpattern.spec.whatwg.org/#url-pattern-create-a-dummy-url>
-fn create_a_dummy_url() -> Url {
- // Step 1. Let dummyInput be "https://dummy.invalid/".
- let dummy_input = "https://dummy.invalid/";
-
- // Step 2. Return the result of running the basic URL parser on dummyInput.
- dummy_input
- .parse()
- .expect("parsing dummy input cannot fail")
-}
-
-/// <https://urlpattern.spec.whatwg.org/#canonicalize-a-protocol>
-pub(super) fn canonicalize_a_protocol(value: &str) -> Fallible<String> {
- // Step 1. If value is the empty string, return value.
- if value.is_empty() {
- return Ok(String::new());
- }
-
- // Step 2. Let parseResult be the result of running the basic URL parser
- // given value followed by "://dummy.invalid/".
- let Ok(parse_result) = Url::parse(&format!("{value}://dummy.invalid/")) else {
- // Step 3. If parseResult is failure, then throw a TypeError.
- return Err(Error::Type(format!(
- "Failed to canonicalize {value:?} as a protocol"
- )));
- };
-
- // Step 4. Return parseResult’s scheme.
- Ok(parse_result.scheme().to_owned())
-}
-
-/// <https://urlpattern.spec.whatwg.org/#canonicalize-a-username>
-pub(super) fn canonicalize_a_username(input: &str) -> String {
- // Step 1. If value is the empty string, return value.
- if input.is_empty() {
- return input.to_owned();
- }
-
- // Step 2. Let dummyURL be the result of creating a dummy URL.
- let mut dummy_url = create_a_dummy_url();
-
- // Step 3. Set the username given dummyURL and value.
- dummy_url.set_username(input).unwrap();
-
- // Step 4. Return dummyURL’s username.
- dummy_url.username().to_owned()
-}
-
-/// <https://urlpattern.spec.whatwg.org/#canonicalize-a-password>
-pub(super) fn canonicalize_a_password(input: &str) -> String {
- // Step 1. If value is the empty string, return value.
- if input.is_empty() {
- return input.to_owned();
- }
-
- // Step 2. Let dummyURL be the result of creating a dummy URL.
- let mut dummy_url = create_a_dummy_url();
-
- // Step 3. Set the password given dummyURL and value.
- dummy_url.set_password(Some(input)).unwrap();
-
- // Step 4. Return dummyURL’s password.
- dummy_url.password().unwrap().to_owned()
-}
-
-/// <https://urlpattern.spec.whatwg.org/#canonicalize-a-hostname>
-pub(super) fn canonicalize_a_hostname(input: &str) -> Fallible<String> {
- // Step 1. If value is the empty string, return value.
- if input.is_empty() {
- return Ok(String::new());
- }
-
- // Step 2. Let dummyURL be the result of creating a dummy URL.
- let mut dummy_url = create_a_dummy_url();
-
- // FIXME: The rest of the algorithm needs functionality that the url crate
- // does not expose. We need to figure out if there's a way around that or
- // if we want to reimplement that functionality here
-
- if dummy_url.set_host(Some(input)).is_err() {
- return Err(Error::Type(format!(
- "Failed to canonicalize hostname: {input:?}"
- )));
- }
-
- Ok(dummy_url.host_str().unwrap().to_owned())
-}
-
-/// <https://urlpattern.spec.whatwg.org/#canonicalize-a-port>
-pub(super) fn canonicalize_a_port(
- port_value: &str,
- protocol_value: Option<&str>,
-) -> Fallible<String> {
- // Step 1. If portValue is the empty string, return portValue.
- if port_value.is_empty() {
- return Ok(String::new());
- }
-
- // Step 2. Let dummyURL be the result of creating a dummy URL.
- let mut dummy_url = create_a_dummy_url();
-
- // Step 3. If protocolValue was given, then set dummyURL’s scheme to protocolValue.
- if let Some(protocol_value) = protocol_value {
- dummy_url.set_scheme(protocol_value).unwrap();
- }
-
- // Step 4. Let parseResult be the result of running basic URL parser given portValue
- // with dummyURL as url and port state as state override.
- // NOTE: The url crate does not expose these parsing concepts, so we try
- // to recreate the parsing step here.
- let port_value = port_value.trim();
- let Ok(port) = port_value.parse::<u16>() else {
- // Step 5. If parseResult is failure, then throw a TypeError.
- return Err(Error::Type(format!(
- "{port_value:?} is not a valid port number"
- )));
- };
-
- // Step 6. Return dummyURL’s port, serialized, or empty string if it is null.
- if let Some(scheme) = protocol_value {
- if default_port_for_special_scheme(scheme) == Some(port) {
- return Ok(String::new());
- }
- }
- Ok(port.to_string())
-}
-
-/// <https://urlpattern.spec.whatwg.org/#canonicalize-a-pathname>
-pub(super) fn canonicalize_a_pathname(value: &str) -> String {
- // Step 1. If value is the empty string, then return value.
- if value.is_empty() {
- return String::new();
- }
-
- // NOTE: This is not what the spec says, but the url crate does not expose the required functionality.
- // TODO: Investigate whether this is different in practice
- let mut dummy_url = create_a_dummy_url();
- dummy_url.set_path(value);
-
- dummy_url.path().to_owned()
-}
-
-/// <https://urlpattern.spec.whatwg.org/#canonicalize-an-opaque-pathname>
-pub(super) fn canonicalize_an_opaque_pathname(value: &str) -> Fallible<String> {
- // NOTE: The url crate doesn't expose the functionality needed by this algorithm.
- // Instead we create a url with an opaque path that is value and then return that opaque path,
- // which should be equivalent.
- let Ok(url) = Url::parse(&format!("foo:{value}")) else {
- return Err(Error::Type(format!(
- "Could not parse {value:?} as opaque path"
- )));
- };
-
- Ok(url.path().to_owned())
-}
-
-/// <https://urlpattern.spec.whatwg.org/#canonicalize-a-search>
-pub(super) fn canonicalize_a_search(value: &str) -> String {
- if value.is_empty() {
- return String::new();
- }
-
- let Ok(url) = Url::parse(&format!("http://example.com?{value}")) else {
- log::warn!("canonicalizing a search should never fail");
- return String::new();
- };
-
- url.query().unwrap_or_default().to_owned()
-}
-
-/// <https://urlpattern.spec.whatwg.org/#canonicalize-a-hash>
-pub(super) fn canonicalize_a_hash(value: &str) -> String {
- if value.is_empty() {
- return String::new();
- }
-
- let Ok(url) = Url::parse(&format!("http://example.com#{value}")) else {
- log::warn!("canonicalizing a hash should never fail");
- return String::new();
- };
-
- url.fragment().unwrap_or_default().to_owned()
-}
-
-/// <https://urlpattern.spec.whatwg.org/#is-an-absolute-pathname>
-fn is_an_absolute_pathname(input: &str, init_type: PatternInitType) -> bool {
- let mut chars = input.chars();
-
- // Step 1. If input is the empty string, then return false.
- let Some(first_char) = chars.next() else {
- return false;
- };
-
- // Step 2. If input[0] is U+002F (/), then return true.
- if first_char == '/' {
- return true;
- }
-
- // Step 3. If type is "url", then return false.
- if init_type == PatternInitType::Url {
- return false;
- }
-
- // Step 4. If input’s code point length is less than 2, then return false.
- let Some(second_char) = chars.next() else {
- return false;
- };
-
- // Step 5. If input[0] is U+005C (\) and input[1] is U+002F (/), then return true.
- if first_char == '\\' && second_char == '/' {
- return true;
- }
-
- // Step 6. If input[0] is U+007B ({) and input[1] is U+002F (/), then return true.
- if first_char == '{' && second_char == '/' {
- return true;
- }
-
- // Step 7. Return false.
- false
-}
-
-/// <https://urlpattern.spec.whatwg.org/#process-a-base-url-string>
-fn process_a_base_url_string(input: &str, init_type: PatternInitType) -> String {
- // Step 1. Assert: input is not null.
- // NOTE: The type system ensures that already
-
- // Step 2. If type is not "pattern" return input.
- if init_type != PatternInitType::Pattern {
- return input.to_owned();
- }
-
- // Step 3. Return the result of escaping a pattern string given input.
- escape_a_pattern_string(input)
-}
-
-/// Implements functionality that is shared between <https://urlpattern.spec.whatwg.org/#escape-a-pattern-string>
-/// and <https://urlpattern.spec.whatwg.org/#escape-a-regexp-string>.
-///
-/// These two algorithms are identical except for the set of characters that they escape, so implementing them
-/// seperately does not make sense.
-fn escape_a_string(input: &str, to_escape: &[char]) -> String {
- // Step 1. Assert: input is an ASCII string.
- debug_assert!(
- input.is_ascii(),
- "Expected input to be ASCII, got {input:?}"
- );
-
- // Step 2. Let result be the empty string.
- let mut result = String::with_capacity(input.len());
-
- // Step 3. Let index be 0.
- // Step 4. While index is less than input’s length:
- // Step 4.1 Let c be input[index].
- // Step 4.2 Increment index by 1.
- for c in input.chars() {
- // Step 4.3 If c is one of: [..] then append "\" to the end of result.
- if to_escape.contains(&c) {
- result.push('\\');
- }
-
- // Step 4.4 Append c to the end of result.
- result.push(c);
- }
-
- // Step 5. Return result.
- result
-}
-
-/// <https://urlpattern.spec.whatwg.org/#escape-a-pattern-string>
-fn escape_a_pattern_string(input: &str) -> String {
- escape_a_string(input, &['+', '*', '?', ':', '{', '}', '(', ')', '\\'])
-}
-
-/// <https://urlpattern.spec.whatwg.org/#escape-a-regexp-string>
-pub(super) fn escape_a_regexp_string(input: &str) -> String {
- escape_a_string(
- input,
- &[
- '.', '+', '*', '?', '^', '$', '{', '}', '(', ')', '[', ']', '|', '/', '\\',
- ],
- )
-}
diff --git a/components/script/dom/urlpattern/tokenizer.rs b/components/script/dom/urlpattern/tokenizer.rs
deleted file mode 100644
index e2d70217c3f..00000000000
--- a/components/script/dom/urlpattern/tokenizer.rs
+++ /dev/null
@@ -1,524 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-
-use script_bindings::error::{Error, Fallible};
-
-/// <https://urlpattern.spec.whatwg.org/#tokenize>
-pub(super) fn tokenize(input: &str, policy: TokenizePolicy) -> Fallible<Vec<Token>> {
- // Step 1. Let tokenizer be a new tokenizer.
- // Step 2. Set tokenizer’s input to input.
- // Step 3. Set tokenizer’s policy to policy.
- let mut tokenizer = Tokenizer {
- input,
- policy,
- index: 0,
- next_index: 0,
- token_list: vec![],
- code_point: char::MIN,
- };
-
- // Step 4. While tokenizer’s index is less than tokenizer’s input’s code point length:
- while tokenizer.index < tokenizer.input.len() {
- // Step 4.1 Run seek and get the next code point given tokenizer and tokenizer’s index.
- tokenizer.seek_and_get_the_next_code_point(tokenizer.index);
-
- match tokenizer.code_point {
- // Step 4.2 If tokenizer’s code point is U+002A (*):
- '*' => {
- // Step 4.2.1 Run add a token with default position and length given tokenizer and "asterisk".
- tokenizer.add_a_token_with_default_position_and_length(TokenType::Asterisk);
-
- // Step 4.2.2 Continue.
- continue;
- },
- // Step 4.3 If tokenizer’s code point is U+002B (+) or U+003F (?):
- '+' | '?' => {
- // Step 4.3.1 Run add a token with default position and length given tokenizer and "other-modifier".
- tokenizer.add_a_token_with_default_position_and_length(TokenType::OtherModifier);
-
- // Step 4.3.2 Continue.
- continue;
- },
- // Step 4.4 If tokenizer’s code point is U+005C (\):
- '\\' => {
- // Step 4.4.1 If tokenizer’s index is equal to tokenizer’s input’s code point length − 1:
- if tokenizer.is_done() {
- // Step 4.4.1.1 Run process a tokenizing error given tokenizer, tokenizer’s next index,
- // and tokenizer’s index.
- tokenizer.process_a_tokenizing_error(tokenizer.next_index, tokenizer.index)?;
-
- // Step 4.4.1.2 Continue.
- continue;
- }
-
- // Step 4.4.2 Let escaped index be tokenizer’s next index.
- let escaped_index = tokenizer.index;
-
- // Step 4.4.3 Run get the next code point given tokenizer.
- tokenizer.get_the_next_code_point();
-
- // Step 4.4.4 Run add a token with default length given tokenizer, "escaped-char",
- // tokenizer’s next index, and escaped index.
- tokenizer.add_a_token_with_default_length(
- TokenType::EscapedChar,
- tokenizer.next_index,
- escaped_index,
- );
-
- // Step 4.4.5 Continue.
- continue;
- },
- // Step 4.5 If tokenizer’s code point is U+007B ({):
- '{' => {
- // Step 4.5.1 Run add a token with default position and length given tokenizer and "open".
- tokenizer.add_a_token_with_default_position_and_length(TokenType::Open);
-
- // Step 4.5.2 Continue.
- continue;
- },
- // Step 4.6 If tokenizer’s code point is U+007D (}):
- '}' => {
- // Step 4.6.1 Run add a token with default position and length given tokenizer and "close".
- tokenizer.add_a_token_with_default_position_and_length(TokenType::Close);
-
- // Step 4.6.2 Continue.
- continue;
- },
- // Step 4.7 If tokenizer’s code point is U+003A (:):
- ':' => {
- // Step 4.7.1 Let name position be tokenizer’s next index.
- let mut name_position = tokenizer.next_index;
-
- // Step 4.7.2 Let name start be name position.
- let name_start = name_position;
-
- // Step 4.7.3 While name position is less than tokenizer’s input’s code point length:
- while name_position < tokenizer.input.len() {
- // Step 4.7.3.1 Run seek and get the next code point given tokenizer and name position.
- tokenizer.seek_and_get_the_next_code_point(name_position);
-
- // Step 4.7.3.2 Let first code point be true if name position equals name start
- // and false otherwise.
- let first_code_point = name_position == name_start;
-
- // Step 4.7.3.3 Let valid code point be the result of running is a valid name
- // code point given tokenizer’s code point and first code point.
- let valid_code_point =
- is_a_valid_name_code_point(tokenizer.code_point, first_code_point);
-
- // Step 4.7.3.4 If valid code point is false break.
- if !valid_code_point {
- break;
- }
-
- // Step 4.6.3.5 Set name position to tokenizer’s next index.
- name_position = tokenizer.next_index;
- }
-
- // Step 4.7.4 If name position is less than or equal to name start:
- if name_position <= name_start {
- // Step 4.7.4.1 Run process a tokenizing error given tokenizer, name start, and tokenizer’s index.
- tokenizer.process_a_tokenizing_error(name_start, tokenizer.index)?;
-
- // Step 4.7.4.2 Continue.
- continue;
- }
-
- // Step 4.7.5 Run add a token with default length given tokenizer, "name", name position,
- // and name start.
- tokenizer.add_a_token_with_default_length(
- TokenType::Name,
- name_position,
- name_start,
- );
-
- // Step 4.7.6 Continue.
- continue;
- },
- // Step 4.8 If tokenizer’s code point is U+0028 (():
- '(' => {
- // Step 4.8.1 Let depth be 1.
- let mut depth = 1;
-
- // Step 4.8.2 Let regexp position be tokenizer’s next index.
- let mut regexp_position = tokenizer.next_index;
-
- // Step 4.8.3 Let regexp start be regexp position.
- let regexp_start = regexp_position;
-
- // Step 4.8.4 Let error be false.
- let mut error = false;
-
- // Step 4.8.5 While regexp position is less than tokenizer’s input’s code point length:
- while regexp_position < tokenizer.input.len() {
- // Step 4.8.5.1 Run seek and get the next code point given tokenizer and regexp position.
- tokenizer.seek_and_get_the_next_code_point(regexp_position);
-
- // Step 4.8.5.2 If tokenizer’s code point is not an ASCII code point:
- if !tokenizer.code_point.is_ascii() {
- // Step 4.8.5.1.1 Run process a tokenizing error given tokenizer, regexp start,
- // and tokenizer’s index.
- tokenizer.process_a_tokenizing_error(regexp_start, tokenizer.index)?;
-
- // Step 4.8.5.1.2 Set error to true.
- error = true;
-
- // Step 4.8.5.1.2 Break.
- break;
- }
-
- // Step 4.8.5.3 If regexp position equals regexp start and tokenizer’s code point is U+003F (?):
- if regexp_position == regexp_start && tokenizer.code_point == '?' {
- // Step 4.8.5.3.1 Run process a tokenizing error given tokenizer, regexp start,
- // and tokenizer’s index.
- tokenizer.process_a_tokenizing_error(regexp_start, tokenizer.index)?;
-
- // Step 4.8.5.3.2 Set error to true.
- error = true;
-
- // Step 4.8.5.3.3 Break.
- break;
- }
-
- // Step 4.8.5.4 If tokenizer’s code point is U+005C (\):
- if tokenizer.code_point == '\\' {
- // Step 4.8.5.4.1 If regexp position equals tokenizer’s input’s code point length − 1:
- if tokenizer.is_last_character(regexp_position) {
- // Step 4.8.5.4.1.1 Run process a tokenizing error given tokenizer, regexp start,
- // and tokenizer’s index.
- tokenizer.process_a_tokenizing_error(regexp_start, tokenizer.index)?;
-
- // Step 4.8.5.4.1.2 Set error to true.
- error = true;
-
- // Step 4.8.5.4.1.3 Break
- break;
- }
-
- // Step 4.8.5.4.2 Run get the next code point given tokenizer.
- tokenizer.get_the_next_code_point();
-
- // Step 4.8.5.4.3 If tokenizer’s code point is not an ASCII code point:
- if !tokenizer.code_point.is_ascii() {
- // Step 4.8.5.4.3.1 Run process a tokenizing error given tokenizer, regexp start,
- // and tokenizer’s index.
- tokenizer.process_a_tokenizing_error(regexp_start, tokenizer.index)?;
-
- // Step 4.8.5.4.3.2 Set error to true.
- error = true;
-
- // Step 4.8.5.4.3.3 Break
- break;
- }
-
- // Step 4.8.5.4.4 Set regexp position to tokenizer’s next index.
- regexp_position = tokenizer.next_index;
-
- // Step 4.8.5.4.5 Continue.
- continue;
- }
-
- // Step 4.8.5.5 If tokenizer’s code point is U+0029 ()):
- if tokenizer.code_point == ')' {
- // Step 4.8.5.5.1 Decrement depth by 1.
- depth -= 1;
-
- // Step 4.8.5.5.2 If depth is 0:
- if depth == 0 {
- // Step 4.8.5.5.2.1 Set regexp position to tokenizer’s next index.
- regexp_position = tokenizer.next_index;
-
- // Step 4.8.5.5.2.2 Break.
- break;
- }
- }
- // Step 4.8.5.6 Otherwise if tokenizer’s code point is U+0028 (():
- else if tokenizer.code_point == '(' {
- // Step 4.8.5.6.1 Increment depth by 1.
- depth += 1;
-
- // Step 4.8.5.6.2 If regexp position equals tokenizer’s input’s code point length − 1:
- if tokenizer.is_last_character(regexp_position) {
- // Step 4.8.5.6.2.1 Run process a tokenizing error given tokenizer, regexp start,
- // and tokenizer’s index.
- tokenizer.process_a_tokenizing_error(regexp_start, tokenizer.index)?;
-
- // Step 4.8.5.6.2.2 Set error to true.
- error = true;
-
- // Step 4.8.5.6.2.3 Break
- break;
- }
-
- // Step 4.8.5.6.3 Let temporary position be tokenizer’s next index.
- let temporary_position = tokenizer.next_index;
-
- // Step 4.8.5.6.4 Run get the next code point given tokenizer.
- tokenizer.get_the_next_code_point();
-
- // Step 4.8.5.6.5 If tokenizer’s code point is not U+003F (?):
- if tokenizer.code_point != '?' {
- // Step 4.8.5.6.5.1 Run process a tokenizing error given tokenizer, regexp start,
- // and tokenizer’s index.
- tokenizer.process_a_tokenizing_error(regexp_start, tokenizer.index)?;
-
- // Step 4.8.5.6.5.2 Set error to true.
- error = true;
-
- // Step 4.8.5.6.5.3 Break.
- break;
- }
-
- // Step 4.8.5.6.6 Set tokenizer’s next index to temporary position.
- tokenizer.next_index = temporary_position;
- }
-
- // Step 4.8.5.7 Set regexp position to tokenizer’s next index.
- regexp_position = tokenizer.next_index;
- }
-
- // Step 4.8.6 If error is true continue.
- if error {
- continue;
- }
-
- // Step 4.8.7 If depth is not zero:
- if depth != 0 {
- // Step 4.8.7.1 Run process a tokenizing error given tokenizer, regexp start,
- // and tokenizer’s index
- tokenizer.process_a_tokenizing_error(regexp_start, tokenizer.index)?;
-
- // Step 4.8.7.2 Continue.
- continue;
- }
-
- // Step 4.8.8 Let regexp length be regexp position − regexp start − 1.
- let regexp_length = regexp_position - regexp_start - 1;
-
- // Step 4.8.9 If regexp length is zero:
- if regexp_length == 0 {
- // Step 4.8.9.1 Run process a tokenizing error given tokenizer, regexp start,
- // and tokenizer’s index.
- tokenizer.process_a_tokenizing_error(regexp_start, tokenizer.index)?;
-
- // Step 4.8.9.2 Continue.
- continue;
- }
-
- // Step 4.8.10 Run add a token given tokenizer, "regexp", regexp position,
- // regexp start, and regexp length.
- tokenizer.add_a_token(
- TokenType::Regexp,
- regexp_position,
- regexp_start,
- regexp_length,
- );
-
- // Step 4.8.11 Continue.
- continue;
- },
- _ => {
- // Step 4.9 Run add a token with default position and length given tokenizer and "char".
- tokenizer.add_a_token_with_default_position_and_length(TokenType::Char);
- },
- }
- }
-
- // Step 5. Run add a token with default length given tokenizer, "end", tokenizer’s index, and tokenizer’s index.
- tokenizer.add_a_token_with_default_length(TokenType::End, tokenizer.index, tokenizer.index);
-
- // Step 6.Return tokenizer’s token list.
- Ok(tokenizer.token_list)
-}
-
-/// <https://urlpattern.spec.whatwg.org/#tokenizer>
-struct Tokenizer<'a> {
- /// <https://urlpattern.spec.whatwg.org/#tokenizer-input>
- input: &'a str,
-
- /// <https://urlpattern.spec.whatwg.org/#tokenizer-policy>
- policy: TokenizePolicy,
-
- /// <https://urlpattern.spec.whatwg.org/#tokenizer-index>
- ///
- /// Note that we deviate the from the spec and index bytes, not code points.
- index: usize,
-
- /// <https://urlpattern.spec.whatwg.org/#tokenizer-next-index>
- ///
- /// Note that we deviate the from the spec and index bytes, not code points.
- next_index: usize,
-
- /// <https://urlpattern.spec.whatwg.org/#tokenizer-token-list>
- token_list: Vec<Token<'a>>,
-
- /// <https://urlpattern.spec.whatwg.org/#tokenizer-code-point>
- code_point: char,
-}
-
-/// <https://urlpattern.spec.whatwg.org/#token>
-#[derive(Clone, Copy, Debug)]
-#[allow(dead_code)] // index isn't used yet, because constructor strings aren't parsed
-pub(super) struct Token<'a> {
- /// <https://urlpattern.spec.whatwg.org/#token-index>
- pub(super) index: usize,
-
- /// <https://urlpattern.spec.whatwg.org/#token-value>
- pub(super) value: &'a str,
-
- /// <https://urlpattern.spec.whatwg.org/#token-type>
- pub(super) token_type: TokenType,
-}
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub(super) enum TokenType {
- /// <https://urlpattern.spec.whatwg.org/#token-type-open>
- Open,
-
- /// <https://urlpattern.spec.whatwg.org/#token-type-close>
- Close,
-
- /// <https://urlpattern.spec.whatwg.org/#token-type-regexp>
- Regexp,
-
- /// <https://urlpattern.spec.whatwg.org/#token-type-name>
- Name,
-
- /// <https://urlpattern.spec.whatwg.org/#token-type-char>
- Char,
-
- /// <https://urlpattern.spec.whatwg.org/#token-type-escaped-char>
- EscapedChar,
-
- /// <https://urlpattern.spec.whatwg.org/#token-type-other-modifier>
- OtherModifier,
-
- /// <https://urlpattern.spec.whatwg.org/#token-type-asterisk>
- Asterisk,
-
- /// <https://urlpattern.spec.whatwg.org/#token-type-end>
- End,
-
- /// <https://urlpattern.spec.whatwg.org/#token-type-invalid-char>
- InvalidChar,
-}
-
-/// <https://urlpattern.spec.whatwg.org/#tokenize-policy>
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub(super) enum TokenizePolicy {
- /// <https://urlpattern.spec.whatwg.org/#tokenize-policy-strict>
- Strict,
-
- /// <https://urlpattern.spec.whatwg.org/#tokenize-policy-lenient>
- Lenient,
-}
-
-impl Tokenizer<'_> {
- fn is_last_character(&self, position: usize) -> bool {
- self.input[position..].chars().count() == 1
- }
-
- fn is_done(&self) -> bool {
- self.input[self.next_index..].is_empty()
- }
-
- /// <https://urlpattern.spec.whatwg.org/#get-the-next-code-point>
- fn get_the_next_code_point(&mut self) {
- // Step 1. Set tokenizer’s code point to the Unicode code point in tokenizer’s
- // input at the position indicated by tokenizer’s next index.
- self.code_point = self.input[self.next_index..]
- .chars()
- .next()
- .expect("URLPattern tokenizer is trying to read out of bounds");
-
- // Step 2. Increment tokenizer’s next index by 1.
- // NOTE: Because our next_index is indexing bytes (not code points) we use
- // the utf8 length of the code point instead.
- self.next_index = self.next_index.wrapping_add(self.code_point.len_utf8());
- }
-
- /// <https://urlpattern.spec.whatwg.org/#seek-and-get-the-next-code-point>
- fn seek_and_get_the_next_code_point(&mut self, index: usize) {
- // Step 1. Set tokenizer’s next index to index.
- self.next_index = index;
-
- // Step 2. Run get the next code point given tokenizer.
- self.get_the_next_code_point();
- }
-
- /// <https://urlpattern.spec.whatwg.org/#add-a-token>
- fn add_a_token(
- &mut self,
- token_type: TokenType,
- next_position: usize,
- value_position: usize,
- value_length: usize,
- ) {
- // Step 1. Let token be a new token.
- // Step 2. Set token’s type to type.
- // Step 3. Set token’s index to tokenizer’s index.
- // Step 4. Set token’s value to the code point substring from value position
- // with length value length within tokenizer’s input.
- let token = Token {
- token_type,
- index: self.index,
- value: &self.input[value_position..][..value_length],
- };
-
- // Step 5. Append token to the back of tokenizer’s token list.
- self.token_list.push(token);
-
- // Step 6. Set tokenizer’s index to next position.
- self.index = next_position;
- }
-
- /// <https://urlpattern.spec.whatwg.org/#add-a-token-with-default-position-and-length>
- fn add_a_token_with_default_position_and_length(&mut self, token_type: TokenType) {
- // Step 1. Run add a token with default length given tokenizer, type,
- // tokenizer’s next index, and tokenizer’s index.
- self.add_a_token_with_default_length(token_type, self.next_index, self.index);
- }
-
- /// <https://urlpattern.spec.whatwg.org/#add-a-token-with-default-length>
- fn add_a_token_with_default_length(
- &mut self,
- token_type: TokenType,
- next_position: usize,
- value_position: usize,
- ) {
- // Step 1. Let computed length be next position − value position.
- let computed_length = next_position - value_position;
-
- // Step 2. Run add a token given tokenizer, type, next position, value position, and computed length.
- self.add_a_token(token_type, next_position, value_position, computed_length);
- }
-
- /// <https://urlpattern.spec.whatwg.org/#process-a-tokenizing-error>
- fn process_a_tokenizing_error(
- &mut self,
- next_position: usize,
- value_position: usize,
- ) -> Fallible<()> {
- // Step 1. If tokenizer’s policy is "strict", then throw a TypeError.
- if self.policy == TokenizePolicy::Strict {
- return Err(Error::Type("Failed to tokenize URL pattern".into()));
- }
-
- // Step 2. Assert: tokenizer’s policy is "lenient".
- debug_assert_eq!(self.policy, TokenizePolicy::Lenient);
-
- // Step 3. Run add a token with default length given tokenizer, "invalid-char",
- // next position, and value position.
- self.add_a_token_with_default_length(TokenType::InvalidChar, next_position, value_position);
-
- Ok(())
- }
-}
-
-/// <https://urlpattern.spec.whatwg.org/#is-a-valid-name-code-point>
-fn is_a_valid_name_code_point(code_point: char, first: bool) -> bool {
- // FIXME: implement this check
- _ = first;
- code_point.is_alphabetic()
-}
diff --git a/components/script/dom/virtualmethods.rs b/components/script/dom/virtualmethods.rs
index 57ecba7b172..1d992b1f301 100644
--- a/components/script/dom/virtualmethods.rs
+++ b/components/script/dom/virtualmethods.rs
@@ -61,6 +61,7 @@ use crate::dom::htmlvideoelement::HTMLVideoElement;
use crate::dom::node::{BindContext, ChildrenMutation, CloneChildrenFlag, Node, UnbindContext};
use crate::dom::shadowroot::ShadowRoot;
use crate::dom::svgelement::SVGElement;
+use crate::dom::svgimageelement::SVGImageElement;
use crate::dom::svgsvgelement::SVGSVGElement;
/// Trait to allow DOM nodes to opt-in to overriding (or adding to) common
@@ -299,6 +300,9 @@ pub(crate) fn vtable_for(node: &Node) -> &dyn VirtualMethods {
node.downcast::<HTMLTitleElement>().unwrap() as &dyn VirtualMethods
},
NodeTypeId::Element(ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement(
+ SVGGraphicsElementTypeId::SVGImageElement,
+ ))) => node.downcast::<SVGImageElement>().unwrap() as &dyn VirtualMethods,
+ NodeTypeId::Element(ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement(
SVGGraphicsElementTypeId::SVGSVGElement,
))) => node.downcast::<SVGSVGElement>().unwrap() as &dyn VirtualMethods,
NodeTypeId::Element(ElementTypeId::SVGElement(SVGElementTypeId::SVGElement)) => {
diff --git a/components/script/dom/websocket.rs b/components/script/dom/websocket.rs
index b9b1b50c901..bbb637dfe28 100644
--- a/components/script/dom/websocket.rs
+++ b/components/script/dom/websocket.rs
@@ -471,7 +471,7 @@ struct ReportCSPViolationTask {
impl TaskOnce for ReportCSPViolationTask {
fn run_once(self) {
let global = self.websocket.root().global();
- global.report_csp_violations(self.violations);
+ global.report_csp_violations(self.violations, None);
}
}
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index d888cc8d917..24e694b4f06 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -65,6 +65,7 @@ use profile_traits::time::ProfilerChan as TimeProfilerChan;
use script_bindings::codegen::GenericBindings::NavigatorBinding::NavigatorMethods;
use script_bindings::codegen::GenericBindings::PerformanceBinding::PerformanceMethods;
use script_bindings::interfaces::WindowHelpers;
+use script_bindings::root::Root;
use script_layout_interface::{
FragmentType, Layout, PendingImageState, QueryMsg, Reflow, ReflowGoal, ReflowRequest,
TrustedNodeAddress, combine_id_with_fragment_type,
@@ -146,6 +147,7 @@ use crate::dom::performance::Performance;
use crate::dom::promise::Promise;
use crate::dom::screen::Screen;
use crate::dom::selection::Selection;
+use crate::dom::shadowroot::ShadowRoot;
use crate::dom::storage::Storage;
#[cfg(feature = "bluetooth")]
use crate::dom::testrunner::TestRunner;
@@ -165,7 +167,7 @@ use crate::script_runtime::{CanGc, JSContext, Runtime};
use crate::script_thread::ScriptThread;
use crate::timers::{IsInterval, TimerCallback};
use crate::unminify::unminified_path;
-use crate::webdriver_handlers::jsval_to_webdriver;
+use crate::webdriver_handlers::{find_node_by_unique_id_in_document, jsval_to_webdriver};
use crate::{fetch, window_named_properties};
/// A callback to call when a response comes back from the `ImageCache`.
@@ -1394,6 +1396,30 @@ impl WindowMethods<crate::DomTypeHolder> for Window {
}
}
+ fn WebdriverElement(&self, id: DOMString) -> Option<DomRoot<Element>> {
+ find_node_by_unique_id_in_document(&self.Document(), id.into())
+ .ok()
+ .and_then(Root::downcast)
+ }
+
+ fn WebdriverFrame(&self, id: DOMString) -> Option<DomRoot<Element>> {
+ find_node_by_unique_id_in_document(&self.Document(), id.into())
+ .ok()
+ .and_then(Root::downcast::<HTMLIFrameElement>)
+ .map(Root::upcast::<Element>)
+ }
+
+ fn WebdriverWindow(&self, _id: DOMString) -> Option<DomRoot<Window>> {
+ warn!("Window references are not supported in webdriver yet");
+ None
+ }
+
+ fn WebdriverShadowRoot(&self, id: DOMString) -> Option<DomRoot<ShadowRoot>> {
+ find_node_by_unique_id_in_document(&self.Document(), id.into())
+ .ok()
+ .and_then(Root::downcast)
+ }
+
// https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle
fn GetComputedStyle(
&self,
diff --git a/components/script/dom/writablestream.rs b/components/script/dom/writablestream.rs
index 1b029f592de..f528f4fbde2 100644
--- a/components/script/dom/writablestream.rs
+++ b/components/script/dom/writablestream.rs
@@ -962,14 +962,13 @@ impl WritableStream {
/// <https://streams.spec.whatwg.org/#create-writable-stream>
#[cfg_attr(crown, allow(crown::unrooted_must_root))]
-#[allow(unused)]
pub(crate) fn create_writable_stream(
cx: SafeJSContext,
global: &GlobalScope,
- can_gc: CanGc,
writable_high_water_mark: f64,
writable_size_algorithm: Rc<QueuingStrategySize>,
underlying_sink_type: UnderlyingSinkType,
+ can_gc: CanGc,
) -> Fallible<DomRoot<WritableStream>> {
// Assert: ! IsNonNegativeNumber(highWaterMark) is true.
assert!(writable_high_water_mark >= 0.0);
diff --git a/components/script/dom/writablestreamdefaultcontroller.rs b/components/script/dom/writablestreamdefaultcontroller.rs
index 084165a6892..135ee6faa59 100644
--- a/components/script/dom/writablestreamdefaultcontroller.rs
+++ b/components/script/dom/writablestreamdefaultcontroller.rs
@@ -12,6 +12,7 @@ use js::jsval::{JSVal, UndefinedValue};
use js::rust::{HandleObject as SafeHandleObject, HandleValue as SafeHandleValue, IntoHandle};
use super::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategySize;
+use super::types::TransformStream;
use crate::dom::bindings::callback::ExceptionHandling;
use crate::dom::bindings::codegen::Bindings::UnderlyingSinkBinding::{
UnderlyingSinkAbortCallback, UnderlyingSinkCloseCallback, UnderlyingSinkStartCallback,
@@ -290,8 +291,7 @@ pub enum UnderlyingSinkType {
port: Dom<MessagePort>,
},
/// Algorithms supporting transform streams are implemented in Rust.
- #[allow(unused)]
- Transform(/*Dom<TransformStream>, Rc<Promise>*/),
+ Transform(Dom<TransformStream>, Rc<Promise>),
}
impl UnderlyingSinkType {
@@ -413,7 +413,7 @@ impl WritableStreamDefaultController {
} => {
backpressure_promise.borrow_mut().take();
},
- UnderlyingSinkType::Transform() => {
+ UnderlyingSinkType::Transform(_, _) => {
return;
},
}
@@ -423,7 +423,6 @@ impl WritableStreamDefaultController {
}
/// <https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller>
- #[allow(unsafe_code)]
pub(crate) fn setup(
&self,
cx: SafeJSContext,
@@ -560,9 +559,9 @@ impl WritableStreamDefaultController {
// Let startAlgorithm be an algorithm that returns undefined.
Ok(Promise::new_resolved(global, cx, (), can_gc))
},
- UnderlyingSinkType::Transform() => {
+ UnderlyingSinkType::Transform(_, start_promise) => {
// Let startAlgorithm be an algorithm that returns startPromise.
- todo!()
+ Ok(start_promise.clone())
},
}
}
@@ -622,9 +621,11 @@ impl WritableStreamDefaultController {
}
promise
},
- UnderlyingSinkType::Transform() => {
+ UnderlyingSinkType::Transform(stream, _) => {
// Return ! TransformStreamDefaultSinkAbortAlgorithm(stream, reason).
- todo!()
+ stream
+ .transform_stream_default_sink_abort_algorithm(cx, global, reason, can_gc)
+ .expect("Transform stream default sink abort algorithm should not fail.")
},
};
@@ -707,9 +708,11 @@ impl WritableStreamDefaultController {
.append_native_handler(&handler, comp, can_gc);
result_promise
},
- UnderlyingSinkType::Transform() => {
+ UnderlyingSinkType::Transform(stream, _) => {
// Return ! TransformStreamDefaultSinkWriteAlgorithm(stream, chunk).
- todo!()
+ stream
+ .transform_stream_default_sink_write_algorithm(cx, global, chunk, can_gc)
+ .expect("Transform stream default sink write algorithm should not fail.")
},
}
}
@@ -757,9 +760,11 @@ impl WritableStreamDefaultController {
// Return a promise resolved with undefined.
Promise::new_resolved(global, cx, (), can_gc)
},
- UnderlyingSinkType::Transform() => {
+ UnderlyingSinkType::Transform(stream, _) => {
// Return ! TransformStreamDefaultSinkCloseAlgorithm(stream).
- todo!()
+ stream
+ .transform_stream_default_sink_close_algorithm(cx, global, can_gc)
+ .expect("Transform stream default sink close algorithm should not fail.")
},
}
}
@@ -1038,7 +1043,7 @@ impl WritableStreamDefaultController {
}
/// <https://streams.spec.whatwg.org/#writable-stream-default-controller-error>
- fn error(
+ pub(crate) fn error(
&self,
stream: &WritableStream,
cx: SafeJSContext,
diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs
index 9cef58acc9a..ca5bb72a1dc 100644
--- a/components/script/dom/xmlhttprequest.rs
+++ b/components/script/dom/xmlhttprequest.rs
@@ -148,7 +148,7 @@ impl FetchResponseListener for XHRContext {
fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<csp::Violation>) {
let global = &self.resource_timing_global();
- global.report_csp_violations(violations);
+ global.report_csp_violations(violations, None);
}
}
diff --git a/components/script/fetch.rs b/components/script/fetch.rs
index 9192a030b66..989cdba862a 100644
--- a/components/script/fetch.rs
+++ b/components/script/fetch.rs
@@ -313,7 +313,7 @@ impl FetchResponseListener for FetchContext {
fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<csp::Violation>) {
let global = &self.resource_timing_global();
- global.report_csp_violations(violations);
+ global.report_csp_violations(violations, None);
}
}
diff --git a/components/script/layout_image.rs b/components/script/layout_image.rs
index df542b4b759..546e758e38c 100644
--- a/components/script/layout_image.rs
+++ b/components/script/layout_image.rs
@@ -81,7 +81,7 @@ impl FetchResponseListener for LayoutImageContext {
fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<csp::Violation>) {
let global = &self.resource_timing_global();
- global.report_csp_violations(violations);
+ global.report_csp_violations(violations, None);
}
}
diff --git a/components/script/messaging.rs b/components/script/messaging.rs
index e0ea9e30af2..08d6fc841cf 100644
--- a/components/script/messaging.rs
+++ b/components/script/messaging.rs
@@ -91,6 +91,7 @@ impl MixedMessage {
#[cfg(feature = "webgpu")]
ScriptThreadMessage::SetWebGPUPort(..) => None,
ScriptThreadMessage::SetScrollStates(id, ..) => Some(*id),
+ ScriptThreadMessage::EvaluateJavaScript(id, _, _) => Some(*id),
},
MixedMessage::FromScript(inner_msg) => match inner_msg {
MainThreadScriptMsg::Common(CommonScriptMsg::Task(_, _, pipeline_id, _)) => {
diff --git a/components/script/script_module.rs b/components/script/script_module.rs
index 0aa35a2eda8..449f17901ed 100644
--- a/components/script/script_module.rs
+++ b/components/script/script_module.rs
@@ -1277,7 +1277,7 @@ impl FetchResponseListener for ModuleContext {
fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<csp::Violation>) {
let global = &self.resource_timing_global();
- global.report_csp_violations(violations);
+ global.report_csp_violations(violations, None);
}
}
diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs
index 1f05c15d74e..c407f9cfc73 100644
--- a/components/script/script_runtime.rs
+++ b/components/script/script_runtime.rs
@@ -390,7 +390,7 @@ unsafe extern "C" fn content_security_policy_allows(
RuntimeCode::WASM => csp_list.is_wasm_evaluation_allowed(),
};
- global.report_csp_violations(violations);
+ global.report_csp_violations(violations, None);
allowed = is_evaluation_allowed == CheckResult::Allowed;
});
allowed
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 54cf89a213f..4815e6feae1 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -50,9 +50,9 @@ use devtools_traits::{
};
use embedder_traits::user_content_manager::UserContentManager;
use embedder_traits::{
- CompositorHitTestResult, EmbedderMsg, FocusSequenceNumber, InputEvent, MediaSessionActionType,
- MouseButton, MouseButtonAction, MouseButtonEvent, Theme, ViewportDetails,
- WebDriverScriptCommand,
+ CompositorHitTestResult, EmbedderMsg, FocusSequenceNumber, InputEvent,
+ JavaScriptEvaluationError, JavaScriptEvaluationId, MediaSessionActionType, MouseButton,
+ MouseButtonAction, MouseButtonEvent, Theme, ViewportDetails, WebDriverScriptCommand,
};
use euclid::Point2D;
use euclid::default::Rect;
@@ -156,6 +156,7 @@ use crate::script_runtime::{
};
use crate::task_queue::TaskQueue;
use crate::task_source::{SendableTaskSource, TaskSourceName};
+use crate::webdriver_handlers::jsval_to_webdriver;
use crate::{devtools, webdriver_handlers};
thread_local!(static SCRIPT_THREAD_ROOT: Cell<Option<*const ScriptThread>> = const { Cell::new(None) });
@@ -1878,6 +1879,9 @@ impl ScriptThread {
ScriptThreadMessage::SetScrollStates(pipeline_id, scroll_states) => {
self.handle_set_scroll_states(pipeline_id, scroll_states)
},
+ ScriptThreadMessage::EvaluateJavaScript(pipeline_id, evaluation_id, script) => {
+ self.handle_evaluate_javascript(pipeline_id, evaluation_id, script, can_gc);
+ },
}
}
@@ -3606,7 +3610,8 @@ impl ScriptThread {
fn handle_csp_violations(&self, id: PipelineId, _: RequestId, violations: Vec<csp::Violation>) {
if let Some(global) = self.documents.borrow().find_global(id) {
- global.report_csp_violations(violations);
+ // TODO(https://github.com/w3c/webappsec-csp/issues/687): Update after spec is resolved
+ global.report_csp_violations(violations, None);
}
}
@@ -3814,6 +3819,53 @@ impl ScriptThread {
)
}
}
+
+ fn handle_evaluate_javascript(
+ &self,
+ pipeline_id: PipelineId,
+ evaluation_id: JavaScriptEvaluationId,
+ script: String,
+ can_gc: CanGc,
+ ) {
+ let Some(window) = self.documents.borrow().find_window(pipeline_id) else {
+ let _ = self.senders.pipeline_to_constellation_sender.send((
+ pipeline_id,
+ ScriptToConstellationMessage::FinishJavaScriptEvaluation(
+ evaluation_id,
+ Err(JavaScriptEvaluationError::WebViewNotReady),
+ ),
+ ));
+ return;
+ };
+
+ let global_scope = window.as_global_scope();
+ let realm = enter_realm(global_scope);
+ let context = window.get_cx();
+
+ rooted!(in(*context) let mut return_value = UndefinedValue());
+ global_scope.evaluate_js_on_global_with_result(
+ &script,
+ return_value.handle_mut(),
+ ScriptFetchOptions::default_classic_script(global_scope),
+ global_scope.api_base_url(),
+ can_gc,
+ );
+ let result = match jsval_to_webdriver(
+ context,
+ global_scope,
+ return_value.handle(),
+ (&realm).into(),
+ can_gc,
+ ) {
+ Ok(ref value) => Ok(value.into()),
+ Err(_) => Err(JavaScriptEvaluationError::SerializationError),
+ };
+
+ let _ = self.senders.pipeline_to_constellation_sender.send((
+ pipeline_id,
+ ScriptToConstellationMessage::FinishJavaScriptEvaluation(evaluation_id, result),
+ ));
+ }
}
impl Drop for ScriptThread {
diff --git a/components/script/security_manager.rs b/components/script/security_manager.rs
index ee320206de2..ee062594eb8 100644
--- a/components/script/security_manager.rs
+++ b/components/script/security_manager.rs
@@ -14,8 +14,7 @@ use crate::dom::bindings::codegen::Bindings::SecurityPolicyViolationEventBinding
};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
-use crate::dom::bindings::reflector::DomGlobal;
-use crate::dom::event::{Event, EventBubbles, EventCancelable};
+use crate::dom::event::{Event, EventBubbles, EventCancelable, EventComposed};
use crate::dom::eventtarget::EventTarget;
use crate::dom::securitypolicyviolationevent::SecurityPolicyViolationEvent;
use crate::dom::types::GlobalScope;
@@ -23,6 +22,7 @@ use crate::script_runtime::CanGc;
use crate::task::TaskOnce;
pub(crate) struct CSPViolationReportTask {
+ global: Trusted<GlobalScope>,
event_target: Trusted<EventTarget>,
violation_report: SecurityPolicyViolationReport,
}
@@ -159,28 +159,31 @@ impl CSPViolationReportBuilder {
impl CSPViolationReportTask {
pub fn new(
- global: &GlobalScope,
- report: SecurityPolicyViolationReport,
+ global: Trusted<GlobalScope>,
+ event_target: Trusted<EventTarget>,
+ violation_report: SecurityPolicyViolationReport,
) -> CSPViolationReportTask {
CSPViolationReportTask {
- violation_report: report,
- event_target: Trusted::new(global.upcast::<EventTarget>()),
+ global,
+ event_target,
+ violation_report,
}
}
fn fire_violation_event(self, can_gc: CanGc) {
- let target = self.event_target.root();
- let global = &target.global();
let event = SecurityPolicyViolationEvent::new(
- global,
+ &self.global.root(),
Atom::from("securitypolicyviolation"),
EventBubbles::Bubbles,
EventCancelable::Cancelable,
+ EventComposed::Composed,
&self.violation_report.convert(),
can_gc,
);
- event.upcast::<Event>().fire(&target, can_gc);
+ event
+ .upcast::<Event>()
+ .fire(&self.event_target.root(), can_gc);
}
}
diff --git a/components/script/stylesheet_loader.rs b/components/script/stylesheet_loader.rs
index 67e186c7f6a..a18d63e323b 100644
--- a/components/script/stylesheet_loader.rs
+++ b/components/script/stylesheet_loader.rs
@@ -286,7 +286,7 @@ impl FetchResponseListener for StylesheetContext {
fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<csp::Violation>) {
let global = &self.resource_timing_global();
- global.report_csp_violations(violations);
+ global.report_csp_violations(violations, None);
}
}
diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs
index 781ac53f415..6b4264d945e 100644
--- a/components/script/webdriver_handlers.rs
+++ b/components/script/webdriver_handlers.rs
@@ -53,6 +53,7 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{DomGlobal, DomObject};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
+use crate::dom::document::Document;
use crate::dom::element::Element;
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
@@ -77,12 +78,27 @@ fn find_node_by_unique_id(
pipeline: PipelineId,
node_id: String,
) -> Result<DomRoot<Node>, ErrorStatus> {
- match documents.find_document(pipeline).and_then(|document| {
- document
- .upcast::<Node>()
- .traverse_preorder(ShadowIncluding::Yes)
- .find(|node| node.unique_id() == node_id)
- }) {
+ match documents.find_document(pipeline) {
+ Some(doc) => find_node_by_unique_id_in_document(&doc, node_id),
+ None => {
+ if ScriptThread::has_node_id(&node_id) {
+ Err(ErrorStatus::StaleElementReference)
+ } else {
+ Err(ErrorStatus::NoSuchElement)
+ }
+ },
+ }
+}
+
+pub(crate) fn find_node_by_unique_id_in_document(
+ document: &Document,
+ node_id: String,
+) -> Result<DomRoot<Node>, ErrorStatus> {
+ match document
+ .upcast::<Node>()
+ .traverse_preorder(ShadowIncluding::Yes)
+ .find(|node| node.unique_id() == node_id)
+ {
Some(node) => Ok(node),
None => {
if ScriptThread::has_node_id(&node_id) {
@@ -183,7 +199,7 @@ unsafe fn is_arguments_object(cx: *mut JSContext, value: HandleValue) -> bool {
jsstring_to_str(cx, class_name) == "[object Arguments]"
}
-#[derive(Eq, Hash, PartialEq)]
+#[derive(Clone, Eq, Hash, PartialEq)]
struct HashableJSVal(u64);
impl From<HandleValue<'_>> for HashableJSVal {
@@ -193,6 +209,7 @@ impl From<HandleValue<'_>> for HashableJSVal {
}
#[allow(unsafe_code)]
+/// <https://w3c.github.io/webdriver/#dfn-json-deserialize>
pub(crate) fn jsval_to_webdriver(
cx: SafeJSContext,
global_scope: &GlobalScope,
@@ -215,12 +232,6 @@ unsafe fn jsval_to_webdriver_inner(
val: HandleValue,
seen: &mut HashSet<HashableJSVal>,
) -> WebDriverJSResult {
- let hashable = val.into();
- if seen.contains(&hashable) {
- return Err(WebDriverJSError::JSError);
- }
- seen.insert(hashable);
-
let _ac = enter_realm(global_scope);
if val.get().is_undefined() {
Ok(WebDriverJSValue::Undefined)
@@ -252,14 +263,26 @@ unsafe fn jsval_to_webdriver_inner(
_ => unreachable!(),
};
Ok(WebDriverJSValue::String(String::from(string)))
- } else if val.get().is_object() {
+ }
+ // https://w3c.github.io/webdriver/#dfn-clone-an-object
+ else if val.get().is_object() {
+ let hashable = val.into();
+ // Step 1. If value is in `seen`, return error with error code javascript error.
+ if seen.contains(&hashable) {
+ return Err(WebDriverJSError::JSError);
+ }
+ //Step 2. Append value to `seen`.
+ seen.insert(hashable.clone());
+
rooted!(in(cx) let object = match FromJSValConvertible::from_jsval(cx, val, ()).unwrap() {
ConversionResult::Success(object) => object,
_ => unreachable!(),
});
let _ac = JSAutoRealm::new(cx, *object);
- if is_array_like::<crate::DomTypeHolder>(cx, val) || is_arguments_object(cx, val) {
+ let return_val = if is_array_like::<crate::DomTypeHolder>(cx, val) ||
+ is_arguments_object(cx, val)
+ {
let mut result: Vec<WebDriverJSValue> = Vec::new();
let length = match get_property::<u32>(
@@ -282,7 +305,7 @@ unsafe fn jsval_to_webdriver_inner(
return Err(WebDriverJSError::JSError);
},
};
-
+ // Step 4. For each enumerable property in value, run the following substeps:
for i in 0..length {
rooted!(in(cx) let mut item = UndefinedValue());
match get_property_jsval(cx, object.handle(), &i.to_string(), item.handle_mut()) {
@@ -303,7 +326,6 @@ unsafe fn jsval_to_webdriver_inner(
},
}
}
-
Ok(WebDriverJSValue::ArrayLike(result))
} else if let Ok(element) = root_from_object::<Element>(*object, cx) {
Ok(WebDriverJSValue::Element(WebElement(
@@ -312,7 +334,7 @@ unsafe fn jsval_to_webdriver_inner(
} else if let Ok(window) = root_from_object::<Window>(*object, cx) {
let window_proxy = window.window_proxy();
if window_proxy.is_browsing_context_discarded() {
- Err(WebDriverJSError::StaleElementReference)
+ return Err(WebDriverJSError::StaleElementReference);
} else if window_proxy.browsing_context_id() == window_proxy.webview_id() {
Ok(WebDriverJSValue::Window(WebWindow(
window.Document().upcast::<Node>().unique_id(),
@@ -332,7 +354,12 @@ unsafe fn jsval_to_webdriver_inner(
&HandleValueArray::empty(),
value.handle_mut(),
) {
- jsval_to_webdriver_inner(cx, global_scope, value.handle(), seen)
+ Ok(jsval_to_webdriver_inner(
+ cx,
+ global_scope,
+ value.handle(),
+ seen,
+ )?)
} else {
throw_dom_exception(
SafeJSContext::from_ptr(cx),
@@ -340,7 +367,7 @@ unsafe fn jsval_to_webdriver_inner(
Error::JSFailed,
CanGc::note(),
);
- Err(WebDriverJSError::JSError)
+ return Err(WebDriverJSError::JSError);
}
} else {
let mut result = HashMap::new();
@@ -392,9 +419,12 @@ unsafe fn jsval_to_webdriver_inner(
}
}
}
-
Ok(WebDriverJSValue::Object(result))
- }
+ };
+ // Step 5. Remove the last element of `seen`.
+ seen.remove(&hashable);
+ // Step 6. Return success with data `result`.
+ return_val
} else {
Err(WebDriverJSError::UnknownType)
}
@@ -872,7 +902,7 @@ pub(crate) fn handle_get_page_source(
.find_document(pipeline)
.ok_or(ErrorStatus::UnknownError)
.and_then(|document| match document.GetDocumentElement() {
- Some(element) => match element.GetOuterHTML(can_gc) {
+ Some(element) => match element.outer_html(can_gc) {
Ok(source) => Ok(source.to_string()),
Err(_) => {
match XMLSerializer::new(document.window(), None, can_gc)
diff --git a/components/script_bindings/codegen/Bindings.conf b/components/script_bindings/codegen/Bindings.conf
index cb33188804f..92871bc54aa 100644
--- a/components/script_bindings/codegen/Bindings.conf
+++ b/components/script_bindings/codegen/Bindings.conf
@@ -88,7 +88,7 @@ DOMInterfaces = {
},
'Clipboard': {
- 'canGc': ['WriteText']
+ 'canGc': ['ReadText', 'WriteText']
},
'ClipboardItem': {
@@ -371,7 +371,7 @@ DOMInterfaces = {
},
'HTMLIFrameElement': {
- 'canGc': ['Sandbox'],
+ 'canGc': ['Sandbox', 'SetSrcdoc'],
},
'HTMLImageElement': {
@@ -538,7 +538,7 @@ DOMInterfaces = {
'Promise': {
'spiderMonkeyInterface': True,
- 'additionalTraits': ["crate::interfaces::PromiseHelpers<Self>", "js::conversions::FromJSValConvertibleRc"]
+ 'additionalTraits': ["js::conversions::FromJSValConvertibleRc"]
},
'Range': {
diff --git a/components/script_bindings/codegen/CodegenRust.py b/components/script_bindings/codegen/CodegenRust.py
index 48f024be70f..458aa7508b0 100644
--- a/components/script_bindings/codegen/CodegenRust.py
+++ b/components/script_bindings/codegen/CodegenRust.py
@@ -742,6 +742,19 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
"}")
return templateBody
+ # A helper function for types that implement FromJSValConvertible trait
+ def fromJSValTemplate(config, errorHandler, exceptionCode):
+ return f"""match FromJSValConvertible::from_jsval(*cx, ${{val}}, {config}) {{
+ Ok(ConversionResult::Success(value)) => value,
+ Ok(ConversionResult::Failure(error)) => {{
+ {errorHandler}
+ }}
+ _ => {{
+ {exceptionCode}
+ }},
+}}
+"""
+
assert not (isEnforceRange and isClamp) # These are mutually exclusive
if type.isSequence() or type.isRecord():
@@ -755,13 +768,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if type.nullable():
declType = CGWrapper(declType, pre="Option<", post=" >")
- templateBody = (f"match FromJSValConvertible::from_jsval(*cx, ${{val}}, {config}) {{\n"
- " Ok(ConversionResult::Success(value)) => value,\n"
- " Ok(ConversionResult::Failure(error)) => {\n"
- f"{indent(failOrPropagate, 8)}\n"
- " }\n"
- f" _ => {{ {exceptionCode} }},\n"
- "}")
+ templateBody = fromJSValTemplate(config, failOrPropagate, exceptionCode)
return handleOptional(templateBody, declType, handleDefault("None"))
@@ -770,13 +777,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if type.nullable():
declType = CGWrapper(declType, pre="Option<", post=" >")
- templateBody = ("match FromJSValConvertible::from_jsval(*cx, ${val}, ()) {\n"
- " Ok(ConversionResult::Success(value)) => value,\n"
- " Ok(ConversionResult::Failure(error)) => {\n"
- f"{indent(failOrPropagate, 8)}\n"
- " }\n"
- f" _ => {{ {exceptionCode} }},\n"
- "}")
+ templateBody = fromJSValTemplate("()", failOrPropagate, exceptionCode)
dictionaries = [
memberType
@@ -836,21 +837,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
# once again be providing a Promise to signal completion of an
# operation, which would then not be exposed to anyone other than
# our own implementation code.
- templateBody = fill(
- """
- { // Scope for our JSAutoRealm.
-
- rooted!(in(*cx) let globalObj = CurrentGlobalOrNull(*cx));
- let promiseGlobal = D::GlobalScope::from_object_maybe_wrapped(globalObj.handle().get(), *cx);
-
- rooted!(in(*cx) let mut valueToResolve = $${val}.get());
- if !JS_WrapValue(*cx, valueToResolve.handle_mut()) {
- $*{exceptionCode}
- }
- D::Promise::new_resolved(&promiseGlobal, cx, valueToResolve.handle())
- }
- """,
- exceptionCode=exceptionCode)
+ templateBody = fromJSValTemplate("()", failOrPropagate, exceptionCode)
if isArgument:
declType = CGGeneric("&D::Promise")
@@ -960,14 +947,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if type.isDOMString():
nullBehavior = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs)
- conversionCode = (
- f"match FromJSValConvertible::from_jsval(*cx, ${{val}}, {nullBehavior}) {{\n"
- " Ok(ConversionResult::Success(strval)) => strval,\n"
- " Ok(ConversionResult::Failure(error)) => {\n"
- f"{indent(failOrPropagate, 8)}\n"
- " }\n"
- f" _ => {{ {exceptionCode} }},\n"
- "}")
+ conversionCode = fromJSValTemplate(nullBehavior, failOrPropagate, exceptionCode)
if defaultValue is None:
default = None
@@ -989,14 +969,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if type.isUSVString():
assert not isEnforceRange and not isClamp
- conversionCode = (
- "match FromJSValConvertible::from_jsval(*cx, ${val}, ()) {\n"
- " Ok(ConversionResult::Success(strval)) => strval,\n"
- " Ok(ConversionResult::Failure(error)) => {\n"
- f"{indent(failOrPropagate, 8)}\n"
- " }\n"
- f" _ => {{ {exceptionCode} }},\n"
- "}")
+ conversionCode = fromJSValTemplate("()", failOrPropagate, exceptionCode)
if defaultValue is None:
default = None
@@ -1018,14 +991,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if type.isByteString():
assert not isEnforceRange and not isClamp
- conversionCode = (
- "match FromJSValConvertible::from_jsval(*cx, ${val}, ()) {\n"
- " Ok(ConversionResult::Success(strval)) => strval,\n"
- " Ok(ConversionResult::Failure(error)) => {\n"
- f"{indent(failOrPropagate, 8)}\n"
- " }\n"
- f" _ => {{ {exceptionCode} }},\n"
- "}")
+ conversionCode = fromJSValTemplate("()", failOrPropagate, exceptionCode)
if defaultValue is None:
default = None
@@ -1056,12 +1022,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
else:
handleInvalidEnumValueCode = "return true;"
- template = (
- "match FromJSValConvertible::from_jsval(*cx, ${val}, ()) {"
- f" Err(_) => {{ {exceptionCode} }},\n"
- " Ok(ConversionResult::Success(v)) => v,\n"
- f" Ok(ConversionResult::Failure(error)) => {{ {handleInvalidEnumValueCode} }},\n"
- "}")
+ template = fromJSValTemplate("()", handleInvalidEnumValueCode, exceptionCode)
if defaultValue is not None:
assert defaultValue.type.tag() == IDLType.Tags.domstring
@@ -1192,14 +1153,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if type_needs_tracing(type):
declType = CGTemplatedType("RootedTraceableBox", declType)
- template = (
- "match FromJSValConvertible::from_jsval(*cx, ${val}, ()) {\n"
- " Ok(ConversionResult::Success(dictionary)) => dictionary,\n"
- " Ok(ConversionResult::Failure(error)) => {\n"
- f"{indent(failOrPropagate, 8)}\n"
- " }\n"
- f" _ => {{ {exceptionCode} }},\n"
- "}")
+ template = fromJSValTemplate("()", failOrPropagate, exceptionCode)
return handleOptional(template, declType, handleDefault(empty))
@@ -1220,14 +1174,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if type.nullable():
declType = CGWrapper(declType, pre="Option<", post=">")
- template = (
- f"match FromJSValConvertible::from_jsval(*cx, ${{val}}, {conversionBehavior}) {{\n"
- " Ok(ConversionResult::Success(v)) => v,\n"
- " Ok(ConversionResult::Failure(error)) => {\n"
- f"{indent(failOrPropagate, 8)}\n"
- " }\n"
- f" _ => {{ {exceptionCode} }}\n"
- "}")
+ template = fromJSValTemplate(conversionBehavior, failOrPropagate, exceptionCode)
if defaultValue is not None:
if isinstance(defaultValue, IDLNullValue):
diff --git a/components/script_bindings/conversions.rs b/components/script_bindings/conversions.rs
index 4d8e7aa595c..953dca889a8 100644
--- a/components/script_bindings/conversions.rs
+++ b/components/script_bindings/conversions.rs
@@ -532,7 +532,7 @@ where
/// Returns whether `value` is an array-like object (Array, FileList,
/// HTMLCollection, HTMLFormControlsCollection, HTMLOptionsCollection,
-/// NodeList).
+/// NodeList, DOMTokenList).
///
/// # Safety
/// `cx` must point to a valid, non-null JSContext.
@@ -548,6 +548,10 @@ pub unsafe fn is_array_like<D: crate::DomTypes>(cx: *mut JSContext, value: Handl
_ => return false,
};
+ // TODO: HTMLAllCollection
+ if root_from_object::<D::DOMTokenList>(object, cx).is_ok() {
+ return true;
+ }
if root_from_object::<D::FileList>(object, cx).is_ok() {
return true;
}
diff --git a/components/script_bindings/import.rs b/components/script_bindings/import.rs
index 16cc92f07bf..25bd6c38669 100644
--- a/components/script_bindings/import.rs
+++ b/components/script_bindings/import.rs
@@ -11,12 +11,12 @@ pub(crate) mod base {
};
pub(crate) use js::error::throw_type_error;
pub(crate) use js::jsapi::{
- CurrentGlobalOrNull, HandleValue as RawHandleValue, HandleValueArray, Heap, IsCallable,
- JS_NewObject, JSContext, JSObject,
+ HandleValue as RawHandleValue, HandleValueArray, Heap, IsCallable, JS_NewObject, JSContext,
+ JSObject,
};
pub(crate) use js::jsval::{JSVal, NullValue, ObjectOrNullValue, ObjectValue, UndefinedValue};
pub(crate) use js::panic::maybe_resume_unwind;
- pub(crate) use js::rust::wrappers::{Call, JS_WrapValue};
+ pub(crate) use js::rust::wrappers::Call;
pub(crate) use js::rust::{HandleObject, HandleValue, MutableHandleObject, MutableHandleValue};
pub(crate) use crate::callback::{
diff --git a/components/script_bindings/interfaces.rs b/components/script_bindings/interfaces.rs
index b289737143e..58917283170 100644
--- a/components/script_bindings/interfaces.rs
+++ b/components/script_bindings/interfaces.rs
@@ -3,10 +3,8 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::cell::RefCell;
-use std::rc::Rc;
use std::thread::LocalKey;
-use js::conversions::ToJSValConvertible;
use js::glue::JSPrincipalsCallbacks;
use js::jsapi::{CallArgs, HandleObject as RawHandleObject, JSContext as RawJSContext, JSObject};
use js::rust::{HandleObject, MutableHandleObject};
@@ -78,14 +76,6 @@ pub trait GlobalScopeHelpers<D: DomTypes> {
unsafe fn from_object(obj: *mut JSObject) -> DomRoot<D::GlobalScope>;
fn from_reflector(reflector: &impl DomObject, realm: InRealm) -> DomRoot<D::GlobalScope>;
- /// # Safety
- /// `obj` must point to a valid, non-null JSObject.
- /// `cx` must point to a valid, non-null RawJSContext.
- unsafe fn from_object_maybe_wrapped(
- obj: *mut JSObject,
- cx: *mut RawJSContext,
- ) -> DomRoot<D::GlobalScope>;
-
fn origin(&self) -> &MutableOrigin;
fn incumbent() -> Option<DomRoot<D::GlobalScope>>;
@@ -101,15 +91,6 @@ pub trait DocumentHelpers {
fn ensure_safe_to_run_script_or_layout(&self);
}
-/// Operations that must be invoked from the generated bindings.
-pub trait PromiseHelpers<D: crate::DomTypes> {
- fn new_resolved(
- global: &D::GlobalScope,
- cx: JSContext,
- value: impl ToJSValConvertible,
- ) -> Rc<D::Promise>;
-}
-
pub trait ServoInternalsHelpers {
fn is_servo_internal(cx: JSContext, global: HandleObject) -> bool;
}
diff --git a/components/script_bindings/webidls/CanvasRenderingContext2D.webidl b/components/script_bindings/webidls/CanvasRenderingContext2D.webidl
index 0c4960fe6ad..46d91683577 100644
--- a/components/script_bindings/webidls/CanvasRenderingContext2D.webidl
+++ b/components/script_bindings/webidls/CanvasRenderingContext2D.webidl
@@ -254,7 +254,7 @@ interface CanvasPattern {
Serializable]
interface ImageData {
[Throws] constructor(unsigned long sw, unsigned long sh/*, optional ImageDataSettings settings = {}*/);
- [Throws] constructor(/* Uint8ClampedArray */ object data, unsigned long sw, optional unsigned long sh
+ [Throws] constructor(Uint8ClampedArray data, unsigned long sw, optional unsigned long sh
/*, optional ImageDataSettings settings = {}*/);
readonly attribute unsigned long width;
diff --git a/components/script_bindings/webidls/Clipboard.webidl b/components/script_bindings/webidls/Clipboard.webidl
index 7562adbfa38..b77e975917e 100644
--- a/components/script_bindings/webidls/Clipboard.webidl
+++ b/components/script_bindings/webidls/Clipboard.webidl
@@ -9,7 +9,7 @@ typedef sequence<ClipboardItem> ClipboardItems;
[SecureContext, Exposed=Window, Pref="dom_async_clipboard_enabled"]
interface Clipboard : EventTarget {
// Promise<ClipboardItems> read();
- // Promise<DOMString> readText();
+ Promise<DOMString> readText();
// Promise<undefined> write(ClipboardItems data);
Promise<undefined> writeText(DOMString data);
};
diff --git a/components/script_bindings/webidls/Document.webidl b/components/script_bindings/webidls/Document.webidl
index 737e74d3bf2..e878b1642e4 100644
--- a/components/script_bindings/webidls/Document.webidl
+++ b/components/script_bindings/webidls/Document.webidl
@@ -154,7 +154,6 @@ partial /*sealed*/ interface Document {
// also has obsolete members
};
Document includes GlobalEventHandlers;
-Document includes DocumentAndElementEventHandlers;
// https://html.spec.whatwg.org/multipage/#Document-partial
partial interface Document {
diff --git a/components/script_bindings/webidls/Element.webidl b/components/script_bindings/webidls/Element.webidl
index 0d2e204ae52..42733b91929 100644
--- a/components/script_bindings/webidls/Element.webidl
+++ b/components/script_bindings/webidls/Element.webidl
@@ -82,7 +82,7 @@ interface Element : Node {
[Throws]
undefined insertAdjacentText(DOMString where_, DOMString data);
[CEReactions, Throws]
- undefined insertAdjacentHTML(DOMString position, DOMString html);
+ undefined insertAdjacentHTML(DOMString position, (TrustedHTML or DOMString) string);
[Throws, Pref="dom_shadowdom_enabled"] ShadowRoot attachShadow(ShadowRootInit init);
readonly attribute ShadowRoot? shadowRoot;
@@ -122,11 +122,11 @@ partial interface Element {
// https://html.spec.whatwg.org/multipage/#dom-parsing-and-serialization
partial interface Element {
- [CEReactions] undefined setHTMLUnsafe(DOMString html);
+ [CEReactions, Throws] undefined setHTMLUnsafe((TrustedHTML or DOMString) html);
DOMString getHTML(optional GetHTMLOptions options = {});
- [CEReactions, Throws] attribute [LegacyNullToEmptyString] DOMString innerHTML;
- [CEReactions, Throws] attribute [LegacyNullToEmptyString] DOMString outerHTML;
+ [CEReactions, Throws] attribute (TrustedHTML or [LegacyNullToEmptyString] DOMString) innerHTML;
+ [CEReactions, Throws] attribute (TrustedHTML or [LegacyNullToEmptyString] DOMString) outerHTML;
};
dictionary GetHTMLOptions {
diff --git a/components/script_bindings/webidls/EventHandler.webidl b/components/script_bindings/webidls/EventHandler.webidl
index f597ce237d3..d32302f4b37 100644
--- a/components/script_bindings/webidls/EventHandler.webidl
+++ b/components/script_bindings/webidls/EventHandler.webidl
@@ -28,6 +28,10 @@ typedef OnBeforeUnloadEventHandlerNonNull? OnBeforeUnloadEventHandler;
[Exposed=Window]
interface mixin GlobalEventHandlers {
attribute EventHandler onabort;
+ attribute EventHandler onauxclick;
+ attribute EventHandler onbeforeinput;
+ attribute EventHandler onbeforematch;
+ attribute EventHandler onbeforetoggle;
attribute EventHandler onblur;
attribute EventHandler oncancel;
attribute EventHandler oncanplay;
@@ -35,13 +39,17 @@ interface mixin GlobalEventHandlers {
attribute EventHandler onchange;
attribute EventHandler onclick;
attribute EventHandler onclose;
+ attribute EventHandler oncommand;
+ attribute EventHandler oncontextlost;
attribute EventHandler oncontextmenu;
+ attribute EventHandler oncontextrestored;
+ attribute EventHandler oncopy;
attribute EventHandler oncuechange;
+ attribute EventHandler oncut;
attribute EventHandler ondblclick;
attribute EventHandler ondrag;
attribute EventHandler ondragend;
attribute EventHandler ondragenter;
- attribute EventHandler ondragexit;
attribute EventHandler ondragleave;
attribute EventHandler ondragover;
attribute EventHandler ondragstart;
@@ -68,7 +76,7 @@ interface mixin GlobalEventHandlers {
attribute EventHandler onmouseout;
attribute EventHandler onmouseover;
attribute EventHandler onmouseup;
- attribute EventHandler onwheel;
+ attribute EventHandler onpaste;
attribute EventHandler onpause;
attribute EventHandler onplay;
attribute EventHandler onplaying;
@@ -77,11 +85,12 @@ interface mixin GlobalEventHandlers {
attribute EventHandler onreset;
attribute EventHandler onresize;
attribute EventHandler onscroll;
+ attribute EventHandler onscrollend;
attribute EventHandler onsecuritypolicyviolation;
attribute EventHandler onseeked;
attribute EventHandler onseeking;
attribute EventHandler onselect;
- attribute EventHandler onshow;
+ attribute EventHandler onslotchange;
attribute EventHandler onstalled;
attribute EventHandler onsubmit;
attribute EventHandler onsuspend;
@@ -89,6 +98,11 @@ interface mixin GlobalEventHandlers {
attribute EventHandler ontoggle;
attribute EventHandler onvolumechange;
attribute EventHandler onwaiting;
+ attribute EventHandler onwebkitanimationend;
+ attribute EventHandler onwebkitanimationiteration;
+ attribute EventHandler onwebkitanimationstart;
+ attribute EventHandler onwebkittransitionend;
+ attribute EventHandler onwheel;
};
// https://drafts.csswg.org/css-animations/#interface-globaleventhandlers-idl
@@ -123,18 +137,12 @@ interface mixin WindowEventHandlers {
attribute EventHandler onoffline;
attribute EventHandler ononline;
attribute EventHandler onpagehide;
+ attribute EventHandler onpagereveal;
attribute EventHandler onpageshow;
+ attribute EventHandler onpageswap;
attribute EventHandler onpopstate;
attribute EventHandler onrejectionhandled;
attribute EventHandler onstorage;
attribute EventHandler onunhandledrejection;
attribute EventHandler onunload;
};
-
-// https://html.spec.whatwg.org/multipage/#documentandelementeventhandlers
-[Exposed=Window]
-interface mixin DocumentAndElementEventHandlers {
- attribute EventHandler oncopy;
- attribute EventHandler oncut;
- attribute EventHandler onpaste;
-};
diff --git a/components/script_bindings/webidls/HTMLElement.webidl b/components/script_bindings/webidls/HTMLElement.webidl
index 76bfada1b94..19a4b515d11 100644
--- a/components/script_bindings/webidls/HTMLElement.webidl
+++ b/components/script_bindings/webidls/HTMLElement.webidl
@@ -73,7 +73,6 @@ partial interface HTMLElement {
};
HTMLElement includes GlobalEventHandlers;
-HTMLElement includes DocumentAndElementEventHandlers;
HTMLElement includes ElementContentEditable;
HTMLElement includes ElementCSSInlineStyle;
HTMLElement includes HTMLOrSVGElement;
diff --git a/components/script_bindings/webidls/HTMLIFrameElement.webidl b/components/script_bindings/webidls/HTMLIFrameElement.webidl
index 8ba58a20f33..b083f51c0f1 100644
--- a/components/script_bindings/webidls/HTMLIFrameElement.webidl
+++ b/components/script_bindings/webidls/HTMLIFrameElement.webidl
@@ -9,8 +9,8 @@ interface HTMLIFrameElement : HTMLElement {
[CEReactions]
attribute USVString src;
- [CEReactions]
- attribute DOMString srcdoc;
+ [CEReactions, SetterThrows]
+ attribute (TrustedHTML or DOMString) srcdoc;
[CEReactions]
attribute DOMString name;
diff --git a/components/script_bindings/webidls/SVGElement.webidl b/components/script_bindings/webidls/SVGElement.webidl
index e6bc468d5dc..08bcb4a8c99 100644
--- a/components/script_bindings/webidls/SVGElement.webidl
+++ b/components/script_bindings/webidls/SVGElement.webidl
@@ -18,7 +18,7 @@ interface SVGElement : Element {
//void blur();
};
-//SVGElement includes GlobalEventHandlers;
+SVGElement includes GlobalEventHandlers;
//SVGElement includes SVGElementInstance;
SVGElement includes ElementCSSInlineStyle;
SVGElement includes HTMLOrSVGElement;
diff --git a/components/script_bindings/webidls/SVGImageElement.webidl b/components/script_bindings/webidls/SVGImageElement.webidl
new file mode 100644
index 00000000000..bced6277c5e
--- /dev/null
+++ b/components/script_bindings/webidls/SVGImageElement.webidl
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+// https://svgwg.org/svg2-draft/embedded.html#InterfaceSVGImageElement
+[Exposed=Window, Pref="dom_svg_enabled"]
+interface SVGImageElement : SVGGraphicsElement {
+ //[SameObject] readonly attribute SVGAnimatedLength x;
+ //[SameObject] readonly attribute SVGAnimatedLength y;
+ //[SameObject] readonly attribute SVGAnimatedLength width;
+ //[SameObject] readonly attribute SVGAnimatedLength height;
+ //[SameObject] readonly attribute SVGAnimatedPreserveAspectRatio preserveAspectRatio;
+ //attribute DOMString? crossOrigin;
+};
+
+//SVGImageElement includes SVGURIReference;
diff --git a/components/script_bindings/webidls/TransformStream.webidl b/components/script_bindings/webidls/TransformStream.webidl
new file mode 100644
index 00000000000..c36a49b114a
--- /dev/null
+++ b/components/script_bindings/webidls/TransformStream.webidl
@@ -0,0 +1,18 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * https://streams.spec.whatwg.org/#ts-class-definition
+ */
+
+[Exposed=*] // [Transferable] - See Bug 1562065
+interface TransformStream {
+ [Throws]
+ constructor(optional object transformer,
+ optional QueuingStrategy writableStrategy = {},
+ optional QueuingStrategy readableStrategy = {});
+
+ readonly attribute ReadableStream readable;
+ readonly attribute WritableStream writable;
+};
diff --git a/components/script_bindings/webidls/TransformStreamDefaultController.webidl b/components/script_bindings/webidls/TransformStreamDefaultController.webidl
new file mode 100644
index 00000000000..5f5511ed4b6
--- /dev/null
+++ b/components/script_bindings/webidls/TransformStreamDefaultController.webidl
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * https://streams.spec.whatwg.org/#ts-default-controller-class-definition
+ */
+
+[Exposed=*]
+interface TransformStreamDefaultController {
+ readonly attribute unrestricted double? desiredSize;
+ [Throws] undefined enqueue(optional any chunk);
+ [Throws] undefined error(optional any reason);
+ [Throws] undefined terminate();
+};
diff --git a/components/script_bindings/webidls/Transformer.webidl b/components/script_bindings/webidls/Transformer.webidl
new file mode 100644
index 00000000000..652511450a4
--- /dev/null
+++ b/components/script_bindings/webidls/Transformer.webidl
@@ -0,0 +1,22 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * https://streams.spec.whatwg.org/#transformer-api
+ */
+
+[GenerateInit]
+dictionary Transformer {
+ TransformerStartCallback start;
+ TransformerTransformCallback transform;
+ TransformerFlushCallback flush;
+ TransformerCancelCallback cancel;
+ any readableType;
+ any writableType;
+};
+
+callback TransformerStartCallback = any (TransformStreamDefaultController controller);
+callback TransformerFlushCallback = Promise<undefined> (TransformStreamDefaultController controller);
+callback TransformerTransformCallback = Promise<undefined> (any chunk, TransformStreamDefaultController controller);
+callback TransformerCancelCallback = Promise<undefined> (any reason);
diff --git a/components/script_bindings/webidls/URLPattern.webidl b/components/script_bindings/webidls/URLPattern.webidl
index 85c0c468fb5..f61b65702bc 100644
--- a/components/script_bindings/webidls/URLPattern.webidl
+++ b/components/script_bindings/webidls/URLPattern.webidl
@@ -4,16 +4,16 @@
// https://urlpattern.spec.whatwg.org/#urlpattern
-typedef /* USVString or */ URLPatternInit URLPatternInput;
+typedef (USVString or URLPatternInit) URLPatternInput;
[Exposed=(Window,Worker), Pref="dom_urlpattern_enabled"]
interface URLPattern {
-// constructor(URLPatternInput input, USVString baseURL, optional URLPatternOptions options = {});
+ [Throws] constructor(URLPatternInput input, USVString baseURL, optional URLPatternOptions options = {});
[Throws] constructor(optional URLPatternInput input = {}, optional URLPatternOptions options = {});
-// boolean test(optional URLPatternInput input = {}, optional USVString baseURL);
+ // [Throws] boolean test(optional URLPatternInput input = {}, optional USVString baseURL);
-// URLPatternResult? exec(optional URLPatternInput input = {}, optional USVString baseURL);
+ // [Throws] URLPatternResult? exec(optional URLPatternInput input = {}, optional USVString baseURL);
readonly attribute USVString protocol;
readonly attribute USVString username;
diff --git a/components/script_bindings/webidls/Window.webidl b/components/script_bindings/webidls/Window.webidl
index eb7c3e1d03d..929279f7951 100644
--- a/components/script_bindings/webidls/Window.webidl
+++ b/components/script_bindings/webidls/Window.webidl
@@ -150,6 +150,10 @@ partial interface Window {
undefined webdriverCallback(optional any result);
undefined webdriverException(optional any result);
undefined webdriverTimeout();
+ Element? webdriverElement(DOMString id);
+ Element? webdriverFrame(DOMString id);
+ Window? webdriverWindow(DOMString id);
+ ShadowRoot? webdriverShadowRoot(DOMString id);
};
// https://html.spec.whatwg.org/multipage/#dom-sessionstorage
diff --git a/components/servo/javascript_evaluator.rs b/components/servo/javascript_evaluator.rs
new file mode 100644
index 00000000000..41cb5539b05
--- /dev/null
+++ b/components/servo/javascript_evaluator.rs
@@ -0,0 +1,65 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use std::collections::HashMap;
+
+use base::id::WebViewId;
+use constellation_traits::EmbedderToConstellationMessage;
+use embedder_traits::{JSValue, JavaScriptEvaluationError, JavaScriptEvaluationId};
+
+use crate::ConstellationProxy;
+
+struct PendingEvaluation {
+ callback: Box<dyn FnOnce(Result<JSValue, JavaScriptEvaluationError>)>,
+}
+
+pub(crate) struct JavaScriptEvaluator {
+ current_id: JavaScriptEvaluationId,
+ constellation_proxy: ConstellationProxy,
+ pending_evaluations: HashMap<JavaScriptEvaluationId, PendingEvaluation>,
+}
+
+impl JavaScriptEvaluator {
+ pub(crate) fn new(constellation_proxy: ConstellationProxy) -> Self {
+ Self {
+ current_id: JavaScriptEvaluationId(0),
+ constellation_proxy,
+ pending_evaluations: Default::default(),
+ }
+ }
+
+ fn generate_id(&mut self) -> JavaScriptEvaluationId {
+ let next_id = JavaScriptEvaluationId(self.current_id.0 + 1);
+ std::mem::replace(&mut self.current_id, next_id)
+ }
+
+ pub(crate) fn evaluate(
+ &mut self,
+ webview_id: WebViewId,
+ script: String,
+ callback: Box<dyn FnOnce(Result<JSValue, JavaScriptEvaluationError>)>,
+ ) {
+ let evaluation_id = self.generate_id();
+ self.constellation_proxy
+ .send(EmbedderToConstellationMessage::EvaluateJavaScript(
+ webview_id,
+ evaluation_id,
+ script,
+ ));
+ self.pending_evaluations
+ .insert(evaluation_id, PendingEvaluation { callback });
+ }
+
+ pub(crate) fn finish_evaluation(
+ &mut self,
+ evaluation_id: JavaScriptEvaluationId,
+ result: Result<JSValue, JavaScriptEvaluationError>,
+ ) {
+ (self
+ .pending_evaluations
+ .remove(&evaluation_id)
+ .expect("Received request to finish unknown JavaScript evaluation.")
+ .callback)(result)
+ }
+}
diff --git a/components/servo/lib.rs b/components/servo/lib.rs
index 366685e1123..d2c65429ba9 100644
--- a/components/servo/lib.rs
+++ b/components/servo/lib.rs
@@ -18,6 +18,7 @@
//! `WindowMethods` trait.
mod clipboard_delegate;
+mod javascript_evaluator;
mod proxies;
mod responders;
mod servo_delegate;
@@ -82,6 +83,7 @@ pub use gleam::gl;
use gleam::gl::RENDERER;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
+use javascript_evaluator::JavaScriptEvaluator;
pub use keyboard_types::*;
use layout::LayoutFactoryImpl;
use log::{Log, Metadata, Record, debug, warn};
@@ -196,6 +198,9 @@ pub struct Servo {
compositor: Rc<RefCell<IOCompositor>>,
constellation_proxy: ConstellationProxy,
embedder_receiver: Receiver<EmbedderMsg>,
+ /// A struct that tracks ongoing JavaScript evaluations and is responsible for
+ /// calling the callback when the evaluation is complete.
+ javascript_evaluator: Rc<RefCell<JavaScriptEvaluator>>,
/// Tracks whether we are in the process of shutting down, or have shut down.
/// This is shared with `WebView`s and the `ServoRenderer`.
shutdown_state: Rc<Cell<ShutdownState>>,
@@ -487,10 +492,14 @@ impl Servo {
opts.debug.convert_mouse_to_touch,
);
+ let constellation_proxy = ConstellationProxy::new(constellation_chan);
Self {
delegate: RefCell::new(Rc::new(DefaultServoDelegate)),
compositor: Rc::new(RefCell::new(compositor)),
- constellation_proxy: ConstellationProxy::new(constellation_chan),
+ javascript_evaluator: Rc::new(RefCell::new(JavaScriptEvaluator::new(
+ constellation_proxy.clone(),
+ ))),
+ constellation_proxy,
embedder_receiver,
shutdown_state,
webviews: Default::default(),
@@ -738,6 +747,11 @@ impl Servo {
webview.delegate().request_unload(webview, request);
}
},
+ EmbedderMsg::FinishJavaScriptEvaluation(evaluation_id, result) => {
+ self.javascript_evaluator
+ .borrow_mut()
+ .finish_evaluation(evaluation_id, result);
+ },
EmbedderMsg::Keyboard(webview_id, keyboard_event) => {
if let Some(webview) = self.get_webview_handle(webview_id) {
webview
@@ -1044,7 +1058,11 @@ fn create_constellation(
);
let system_font_service = Arc::new(
- SystemFontService::spawn(compositor_proxy.cross_process_compositor_api.clone()).to_proxy(),
+ SystemFontService::spawn(
+ compositor_proxy.cross_process_compositor_api.clone(),
+ mem_profiler_chan.clone(),
+ )
+ .to_proxy(),
);
let (canvas_create_sender, canvas_ipc_sender) = CanvasPaintThread::start(
diff --git a/components/servo/tests/webview.rs b/components/servo/tests/webview.rs
index 89fbe2025a3..41900015b94 100644
--- a/components/servo/tests/webview.rs
+++ b/components/servo/tests/webview.rs
@@ -11,12 +11,14 @@
mod common;
-use std::cell::Cell;
+use std::cell::{Cell, RefCell};
use std::rc::Rc;
use anyhow::ensure;
use common::{ServoTest, run_api_tests};
-use servo::{WebViewBuilder, WebViewDelegate};
+use servo::{
+ JSValue, JavaScriptEvaluationError, LoadStatus, WebView, WebViewBuilder, WebViewDelegate,
+};
#[derive(Default)]
struct WebViewDelegateImpl {
@@ -44,6 +46,81 @@ fn test_create_webview(servo_test: &ServoTest) -> Result<(), anyhow::Error> {
Ok(())
}
+fn evaluate_javascript(
+ servo_test: &ServoTest,
+ webview: WebView,
+ script: impl ToString,
+) -> Result<JSValue, JavaScriptEvaluationError> {
+ let load_webview = webview.clone();
+ let _ = servo_test.spin(move || Ok(load_webview.load_status() != LoadStatus::Complete));
+
+ let saved_result = Rc::new(RefCell::new(None));
+ let callback_result = saved_result.clone();
+ webview.evaluate_javascript(script, move |result| {
+ *callback_result.borrow_mut() = Some(result)
+ });
+
+ let spin_result = saved_result.clone();
+ let _ = servo_test.spin(move || Ok(spin_result.borrow().is_none()));
+
+ (*saved_result.borrow())
+ .clone()
+ .expect("Should have waited until value available")
+}
+
+fn test_evaluate_javascript_basic(servo_test: &ServoTest) -> Result<(), anyhow::Error> {
+ let delegate = Rc::new(WebViewDelegateImpl::default());
+ let webview = WebViewBuilder::new(servo_test.servo())
+ .delegate(delegate.clone())
+ .build();
+
+ let result = evaluate_javascript(servo_test, webview.clone(), "undefined");
+ ensure!(result == Ok(JSValue::Undefined));
+
+ let result = evaluate_javascript(servo_test, webview.clone(), "null");
+ ensure!(result == Ok(JSValue::Null));
+
+ let result = evaluate_javascript(servo_test, webview.clone(), "42");
+ ensure!(result == Ok(JSValue::Number(42.0)));
+
+ let result = evaluate_javascript(servo_test, webview.clone(), "3 + 4");
+ ensure!(result == Ok(JSValue::Number(7.0)));
+
+ let result = evaluate_javascript(servo_test, webview.clone(), "'abc' + 'def'");
+ ensure!(result == Ok(JSValue::String("abcdef".into())));
+
+ let result = evaluate_javascript(servo_test, webview.clone(), "let foo = {blah: 123}; foo");
+ ensure!(matches!(result, Ok(JSValue::Object(_))));
+ if let Ok(JSValue::Object(values)) = result {
+ ensure!(values.len() == 1);
+ ensure!(values.get("blah") == Some(&JSValue::Number(123.0)));
+ }
+
+ let result = evaluate_javascript(servo_test, webview.clone(), "[1, 2, 3, 4]");
+ let expected = JSValue::Array(vec![
+ JSValue::Number(1.0),
+ JSValue::Number(2.0),
+ JSValue::Number(3.0),
+ JSValue::Number(4.0),
+ ]);
+ ensure!(result == Ok(expected));
+
+ let result = evaluate_javascript(servo_test, webview.clone(), "window");
+ ensure!(matches!(result, Ok(JSValue::Window(..))));
+
+ let result = evaluate_javascript(servo_test, webview.clone(), "document.body");
+ ensure!(matches!(result, Ok(JSValue::Element(..))));
+
+ let result = evaluate_javascript(
+ servo_test,
+ webview.clone(),
+ "document.body.innerHTML += '<iframe>'; frames[0]",
+ );
+ ensure!(matches!(result, Ok(JSValue::Frame(..))));
+
+ Ok(())
+}
+
fn test_create_webview_and_immediately_drop_webview_before_shutdown(
servo_test: &ServoTest,
) -> Result<(), anyhow::Error> {
@@ -54,6 +131,7 @@ fn test_create_webview_and_immediately_drop_webview_before_shutdown(
fn main() {
run_api_tests!(
test_create_webview,
+ test_evaluate_javascript_basic,
// This test needs to be last, as it tests creating and dropping
// a WebView right before shutdown.
test_create_webview_and_immediately_drop_webview_before_shutdown
diff --git a/components/servo/webview.rs b/components/servo/webview.rs
index 95eb6dfd154..10786ad8b69 100644
--- a/components/servo/webview.rs
+++ b/components/servo/webview.rs
@@ -13,8 +13,8 @@ use compositing_traits::WebViewTrait;
use constellation_traits::{EmbedderToConstellationMessage, TraversalDirection};
use dpi::PhysicalSize;
use embedder_traits::{
- Cursor, InputEvent, LoadStatus, MediaSessionActionType, ScreenGeometry, Theme, TouchEventType,
- ViewportDetails,
+ Cursor, InputEvent, JSValue, JavaScriptEvaluationError, LoadStatus, MediaSessionActionType,
+ ScreenGeometry, Theme, TouchEventType, ViewportDetails,
};
use euclid::{Point2D, Scale, Size2D};
use servo_geometry::DeviceIndependentPixel;
@@ -23,6 +23,7 @@ use webrender_api::ScrollLocation;
use webrender_api::units::{DeviceIntPoint, DevicePixel, DeviceRect};
use crate::clipboard_delegate::{ClipboardDelegate, DefaultClipboardDelegate};
+use crate::javascript_evaluator::JavaScriptEvaluator;
use crate::webview_delegate::{DefaultWebViewDelegate, WebViewDelegate};
use crate::{ConstellationProxy, Servo, WebRenderDebugOption};
@@ -75,6 +76,7 @@ pub(crate) struct WebViewInner {
pub(crate) compositor: Rc<RefCell<IOCompositor>>,
pub(crate) delegate: Rc<dyn WebViewDelegate>,
pub(crate) clipboard_delegate: Rc<dyn ClipboardDelegate>,
+ javascript_evaluator: Rc<RefCell<JavaScriptEvaluator>>,
rect: DeviceRect,
hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
@@ -117,9 +119,10 @@ impl WebView {
compositor: servo.compositor.clone(),
delegate: builder.delegate,
clipboard_delegate: Rc::new(DefaultClipboardDelegate),
+ javascript_evaluator: servo.javascript_evaluator.clone(),
rect: DeviceRect::from_origin_and_size(Point2D::origin(), size),
hidpi_scale_factor: builder.hidpi_scale_factor,
- load_status: LoadStatus::Complete,
+ load_status: LoadStatus::Started,
url: None,
status_text: None,
page_title: None,
@@ -549,6 +552,20 @@ impl WebView {
pub fn paint(&self) -> bool {
self.inner().compositor.borrow_mut().render()
}
+
+ /// Evaluate the specified string of JavaScript code. Once execution is complete or an error
+ /// occurs, Servo will call `callback`.
+ pub fn evaluate_javascript<T: ToString>(
+ &self,
+ script: T,
+ callback: impl FnOnce(Result<JSValue, JavaScriptEvaluationError>) + 'static,
+ ) {
+ self.inner().javascript_evaluator.borrow_mut().evaluate(
+ self.id(),
+ script.to_string(),
+ Box::new(callback),
+ );
+ }
}
/// A structure used to expose a view of the [`WebView`] to the Servo
diff --git a/components/shared/compositing/lib.rs b/components/shared/compositing/lib.rs
index 31371f87529..a6701ca2b52 100644
--- a/components/shared/compositing/lib.rs
+++ b/components/shared/compositing/lib.rs
@@ -14,6 +14,7 @@ use embedder_traits::{
use euclid::Rect;
use ipc_channel::ipc::IpcSender;
use log::warn;
+use malloc_size_of_derive::MallocSizeOf;
use pixels::Image;
use strum_macros::IntoStaticStr;
use style_traits::CSSPixel;
@@ -103,6 +104,8 @@ pub enum CompositorMsg {
WebDriverMouseButtonEvent(WebViewId, MouseButtonAction, MouseButton, f32, f32),
/// WebDriver mouse move event
WebDriverMouseMoveEvent(WebViewId, f32, f32),
+ // Webdriver wheel scroll event
+ WebDriverWheelScrollEvent(WebViewId, f32, f32, f64, f64),
/// Inform WebRender of the existence of this pipeline.
SendInitialTransaction(WebRenderPipelineId),
@@ -188,7 +191,7 @@ pub struct CompositionPipeline {
}
/// A mechanism to send messages from ScriptThread to the parent process' WebRender instance.
-#[derive(Clone, Deserialize, Serialize)]
+#[derive(Clone, Deserialize, MallocSizeOf, Serialize)]
pub struct CrossProcessCompositorApi(pub IpcSender<CompositorMsg>);
impl CrossProcessCompositorApi {
diff --git a/components/shared/constellation/from_script_message.rs b/components/shared/constellation/from_script_message.rs
index 21665c24e57..3856def660e 100644
--- a/components/shared/constellation/from_script_message.rs
+++ b/components/shared/constellation/from_script_message.rs
@@ -15,8 +15,8 @@ use base::id::{
use canvas_traits::canvas::{CanvasId, CanvasMsg};
use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId};
use embedder_traits::{
- AnimationState, EmbedderMsg, FocusSequenceNumber, MediaSessionEvent, TouchEventResult,
- ViewportDetails,
+ AnimationState, EmbedderMsg, FocusSequenceNumber, JSValue, JavaScriptEvaluationError,
+ JavaScriptEvaluationId, MediaSessionEvent, TouchEventResult, ViewportDetails,
};
use euclid::default::Size2D as UntypedSize2D;
use http::{HeaderMap, Method};
@@ -644,6 +644,11 @@ pub enum ScriptToConstellationMessage {
IFrameSizes(Vec<IFrameSizeMsg>),
/// Request results from the memory reporter.
ReportMemory(IpcSender<MemoryReportResult>),
+ /// Return the result of the evaluated JavaScript with the given [`JavaScriptEvaluationId`].
+ FinishJavaScriptEvaluation(
+ JavaScriptEvaluationId,
+ Result<JSValue, JavaScriptEvaluationError>,
+ ),
}
impl fmt::Debug for ScriptToConstellationMessage {
diff --git a/components/shared/constellation/lib.rs b/components/shared/constellation/lib.rs
index 559bc2dd2d1..d85fbe31bdf 100644
--- a/components/shared/constellation/lib.rs
+++ b/components/shared/constellation/lib.rs
@@ -19,8 +19,8 @@ use base::Epoch;
use base::cross_process_instant::CrossProcessInstant;
use base::id::{MessagePortId, PipelineId, WebViewId};
use embedder_traits::{
- CompositorHitTestResult, Cursor, InputEvent, MediaSessionActionType, Theme, ViewportDetails,
- WebDriverCommandMsg,
+ CompositorHitTestResult, Cursor, InputEvent, JavaScriptEvaluationId, MediaSessionActionType,
+ Theme, ViewportDetails, WebDriverCommandMsg,
};
use euclid::Vector2D;
pub use from_script_message::*;
@@ -92,6 +92,9 @@ pub enum EmbedderToConstellationMessage {
SetScrollStates(PipelineId, Vec<ScrollState>),
/// Notify the constellation that a particular paint metric event has happened for the given pipeline.
PaintMetric(PipelineId, PaintMetricEvent),
+ /// Evaluate a JavaScript string in the context of a `WebView`. When execution is complete or an
+ /// error is encountered, a correpsonding message will be sent to the embedding layer.
+ EvaluateJavaScript(WebViewId, JavaScriptEvaluationId, String),
}
/// A description of a paint metric that is sent from the Servo renderer to the
@@ -149,7 +152,7 @@ pub enum TraversalDirection {
}
/// A task on the <https://html.spec.whatwg.org/multipage/#port-message-queue>
-#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
+#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
pub struct PortMessageTask {
/// The origin of this task.
pub origin: ImmutableOrigin,
diff --git a/components/shared/constellation/structured_data/mod.rs b/components/shared/constellation/structured_data/mod.rs
index 41fc05493a2..3fb9d0c5f67 100644
--- a/components/shared/constellation/structured_data/mod.rs
+++ b/components/shared/constellation/structured_data/mod.rs
@@ -20,7 +20,7 @@ pub use transferable::*;
/// A data-holder for serialized data and transferred objects.
/// <https://html.spec.whatwg.org/multipage/#structuredserializewithtransfer>
-#[derive(Debug, Default, Deserialize, MallocSizeOf, Serialize)]
+#[derive(Clone, Debug, Default, Deserialize, MallocSizeOf, Serialize)]
pub struct StructuredSerializedData {
/// Data serialized by SpiderMonkey.
pub serialized: Vec<u8>,
@@ -43,6 +43,7 @@ impl StructuredSerializedData {
Transferrable::MessagePort => is_field_empty(&self.ports),
Transferrable::ReadableStream => is_field_empty(&self.ports),
Transferrable::WritableStream => is_field_empty(&self.ports),
+ Transferrable::TransformStream => is_field_empty(&self.ports),
}
}
diff --git a/components/shared/constellation/structured_data/serializable.rs b/components/shared/constellation/structured_data/serializable.rs
index 22370087665..194f0567c51 100644
--- a/components/shared/constellation/structured_data/serializable.rs
+++ b/components/shared/constellation/structured_data/serializable.rs
@@ -88,7 +88,7 @@ impl Clone for BroadcastMsg {
}
/// File-based blob
-#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
+#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
pub struct FileBlob {
#[ignore_malloc_size_of = "Uuid are hard(not really)"]
id: Uuid,
@@ -164,7 +164,7 @@ impl BroadcastClone for BlobImpl {
}
/// The data backing a DOM Blob.
-#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
+#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
pub struct BlobImpl {
/// UUID of the blob.
blob_id: BlobId,
@@ -177,7 +177,7 @@ pub struct BlobImpl {
}
/// Different backends of Blob
-#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
+#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
pub enum BlobData {
/// File-based blob, whose content lives in the net process
File(FileBlob),
diff --git a/components/shared/constellation/structured_data/transferable.rs b/components/shared/constellation/structured_data/transferable.rs
index 7e4fe0e6d2d..528c1e79e65 100644
--- a/components/shared/constellation/structured_data/transferable.rs
+++ b/components/shared/constellation/structured_data/transferable.rs
@@ -24,9 +24,11 @@ pub enum Transferrable {
ReadableStream,
/// The `WritableStream` interface.
WritableStream,
+ /// The `TransformStream` interface.
+ TransformStream,
}
-#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
+#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
enum MessagePortState {
/// <https://html.spec.whatwg.org/multipage/#detached>
Detached,
@@ -40,7 +42,7 @@ enum MessagePortState {
Disabled(bool),
}
-#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
+#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
/// The data and logic backing the DOM managed MessagePort.
pub struct MessagePortImpl {
/// The current state of the port.
diff --git a/components/shared/embedder/input_events.rs b/components/shared/embedder/input_events.rs
index 0268be6dd9c..acaa9afb3ff 100644
--- a/components/shared/embedder/input_events.rs
+++ b/components/shared/embedder/input_events.rs
@@ -61,15 +61,16 @@ pub enum MouseButton {
Other(u16),
}
-impl From<u16> for MouseButton {
- fn from(value: u16) -> Self {
+impl<T: Into<u64>> From<T> for MouseButton {
+ fn from(value: T) -> Self {
+ let value = value.into();
match value {
0 => MouseButton::Left,
1 => MouseButton::Middle,
2 => MouseButton::Right,
3 => MouseButton::Back,
4 => MouseButton::Forward,
- _ => MouseButton::Other(value),
+ _ => MouseButton::Other(value as u16),
}
}
}
diff --git a/components/shared/embedder/lib.rs b/components/shared/embedder/lib.rs
index c87fa9019ef..e9427fcc719 100644
--- a/components/shared/embedder/lib.rs
+++ b/components/shared/embedder/lib.rs
@@ -13,8 +13,10 @@ pub mod resources;
pub mod user_content_manager;
mod webdriver;
+use std::collections::HashMap;
use std::ffi::c_void;
use std::fmt::{Debug, Display, Error, Formatter};
+use std::hash::Hash;
use std::path::PathBuf;
use std::sync::Arc;
@@ -372,6 +374,12 @@ pub enum EmbedderMsg {
DeviceIntRect,
IpcSender<Option<usize>>,
),
+ /// Inform the embedding layer that a JavaScript evaluation has
+ /// finished with the given result.
+ FinishJavaScriptEvaluation(
+ JavaScriptEvaluationId,
+ Result<JSValue, JavaScriptEvaluationError>,
+ ),
}
impl Debug for EmbedderMsg {
@@ -857,3 +865,59 @@ impl Display for FocusSequenceNumber {
Display::fmt(&self.0, f)
}
}
+
+/// An identifier for a particular JavaScript evaluation that is used to track the
+/// evaluation from the embedding layer to the script layer and then back.
+#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)]
+pub struct JavaScriptEvaluationId(pub usize);
+
+#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
+pub enum JSValue {
+ Undefined,
+ Null,
+ Boolean(bool),
+ Number(f64),
+ String(String),
+ Element(String),
+ Frame(String),
+ Window(String),
+ Array(Vec<JSValue>),
+ Object(HashMap<String, JSValue>),
+}
+
+impl From<&WebDriverJSValue> for JSValue {
+ fn from(value: &WebDriverJSValue) -> Self {
+ match value {
+ WebDriverJSValue::Undefined => Self::Undefined,
+ WebDriverJSValue::Null => Self::Null,
+ WebDriverJSValue::Boolean(value) => Self::Boolean(*value),
+ WebDriverJSValue::Int(value) => Self::Number(*value as f64),
+ WebDriverJSValue::Number(value) => Self::Number(*value),
+ WebDriverJSValue::String(value) => Self::String(value.clone()),
+ WebDriverJSValue::Element(web_element) => Self::Element(web_element.0.clone()),
+ WebDriverJSValue::Frame(web_frame) => Self::Frame(web_frame.0.clone()),
+ WebDriverJSValue::Window(web_window) => Self::Window(web_window.0.clone()),
+ WebDriverJSValue::ArrayLike(vector) => {
+ Self::Array(vector.iter().map(Into::into).collect())
+ },
+ WebDriverJSValue::Object(map) => Self::Object(
+ map.iter()
+ .map(|(key, value)| (key.clone(), value.into()))
+ .collect(),
+ ),
+ }
+ }
+}
+
+#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
+pub enum JavaScriptEvaluationError {
+ /// An internal Servo error prevented the JavaSript evaluation from completing properly.
+ /// This indicates a bug in Servo.
+ InternalError,
+ /// The `WebView` on which this evaluation request was triggered is not ready. This might
+ /// happen if the `WebView`'s `Document` is changing due to ongoing load events, for instance.
+ WebViewNotReady,
+ /// The script executed successfully, but Servo could not serialize the JavaScript return
+ /// value into a [`JSValue`].
+ SerializationError,
+}
diff --git a/components/shared/embedder/webdriver.rs b/components/shared/embedder/webdriver.rs
index 9577163411e..3716a29951a 100644
--- a/components/shared/embedder/webdriver.rs
+++ b/components/shared/embedder/webdriver.rs
@@ -44,6 +44,8 @@ pub enum WebDriverCommandMsg {
MouseButtonAction(WebViewId, MouseButtonAction, MouseButton, f32, f32),
/// Act as if the mouse was moved in the browsing context with the given ID.
MouseMoveAction(WebViewId, f32, f32),
+ /// Act as if the mouse wheel is scrolled in the browsing context given the given ID.
+ WheelScrollAction(WebViewId, f32, f32, f64, f64),
/// Set the window size.
SetWindowSize(WebViewId, DeviceIntSize, IpcSender<Size2D<f32, CSSPixel>>),
/// Take a screenshot of the window.
diff --git a/components/shared/net/request.rs b/components/shared/net/request.rs
index 9c3693316b0..259132b55c4 100644
--- a/components/shared/net/request.rs
+++ b/components/shared/net/request.rs
@@ -8,7 +8,7 @@ use base::id::{PipelineId, WebViewId};
use content_security_policy::{self as csp};
use http::header::{AUTHORIZATION, HeaderName};
use http::{HeaderMap, Method};
-use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
+use ipc_channel::ipc::{self, IpcReceiver, IpcSender, IpcSharedMemory};
use malloc_size_of_derive::MallocSizeOf;
use mime::Mime;
use serde::{Deserialize, Serialize};
@@ -156,7 +156,7 @@ pub enum BodySource {
#[derive(Debug, Deserialize, Serialize)]
pub enum BodyChunkResponse {
/// A chunk of bytes.
- Chunk(Vec<u8>),
+ Chunk(IpcSharedMemory),
/// The body is done.
Done,
/// There was an error streaming the body,
diff --git a/components/shared/profile/mem.rs b/components/shared/profile/mem.rs
index 1be4eb5abc4..b626facd042 100644
--- a/components/shared/profile/mem.rs
+++ b/components/shared/profile/mem.rs
@@ -279,7 +279,6 @@ thread_local!(static SEEN_POINTERS: LazyCell<RefCell<HashSet<*const c_void>>> =
/// The function is expected to call all the desired [MallocSizeOf::size_of]
/// for allocations reachable from the current thread.
pub fn perform_memory_report<F: FnOnce(&mut MallocSizeOfOps)>(f: F) {
- SEEN_POINTERS.with(|pointers| pointers.borrow_mut().clear());
let seen_pointer = move |ptr| SEEN_POINTERS.with(|pointers| !pointers.borrow_mut().insert(ptr));
let mut ops = MallocSizeOfOps::new(
servo_allocator::usable_size,
@@ -287,4 +286,9 @@ pub fn perform_memory_report<F: FnOnce(&mut MallocSizeOfOps)>(f: F) {
Some(Box::new(seen_pointer)),
);
f(&mut ops);
+ SEEN_POINTERS.with(|pointers| {
+ let mut pointers = pointers.borrow_mut();
+ pointers.clear();
+ pointers.shrink_to_fit();
+ });
}
diff --git a/components/shared/script/lib.rs b/components/shared/script/lib.rs
index 748c42400a8..29acc51765c 100644
--- a/components/shared/script/lib.rs
+++ b/components/shared/script/lib.rs
@@ -27,8 +27,8 @@ use crossbeam_channel::{RecvTimeoutError, Sender};
use devtools_traits::ScriptToDevtoolsControlMsg;
use embedder_traits::user_content_manager::UserContentManager;
use embedder_traits::{
- CompositorHitTestResult, FocusSequenceNumber, InputEvent, MediaSessionActionType, Theme,
- ViewportDetails, WebDriverScriptCommand,
+ CompositorHitTestResult, FocusSequenceNumber, InputEvent, JavaScriptEvaluationId,
+ MediaSessionActionType, Theme, ViewportDetails, WebDriverScriptCommand,
};
use euclid::{Rect, Scale, Size2D, UnknownUnit};
use ipc_channel::ipc::{IpcReceiver, IpcSender};
@@ -245,6 +245,9 @@ pub enum ScriptThreadMessage {
/// The compositor scrolled and is updating the scroll states of the nodes in the given
/// pipeline via the Constellation.
SetScrollStates(PipelineId, Vec<ScrollState>),
+ /// Evaluate the given JavaScript and return a result via a corresponding message
+ /// to the Constellation.
+ EvaluateJavaScript(PipelineId, JavaScriptEvaluationId, String),
}
impl fmt::Debug for ScriptThreadMessage {
diff --git a/components/shared/script_layout/lib.rs b/components/shared/script_layout/lib.rs
index 8c5d4edc4e0..1f526b64240 100644
--- a/components/shared/script_layout/lib.rs
+++ b/components/shared/script_layout/lib.rs
@@ -114,6 +114,7 @@ pub enum LayoutElementType {
HTMLTableRowElement,
HTMLTableSectionElement,
HTMLTextAreaElement,
+ SVGImageElement,
SVGSVGElement,
}
diff --git a/components/url/lib.rs b/components/url/lib.rs
index 8aa04fd5ae4..f1ae1083c8e 100644
--- a/components/url/lib.rs
+++ b/components/url/lib.rs
@@ -35,7 +35,7 @@ pub enum UrlError {
}
#[derive(Clone, Deserialize, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize)]
-pub struct ServoUrl(#[ignore_malloc_size_of = "Arc"] Arc<Url>);
+pub struct ServoUrl(#[conditional_malloc_size_of] Arc<Url>);
impl ServoUrl {
pub fn from_url(url: Url) -> Self {
diff --git a/components/webdriver_server/actions.rs b/components/webdriver_server/actions.rs
index fbede5b5887..43dd0e183dc 100644
--- a/components/webdriver_server/actions.rs
+++ b/components/webdriver_server/actions.rs
@@ -13,28 +13,31 @@ use keyboard_types::webdriver::KeyInputState;
use webdriver::actions::{
ActionSequence, ActionsType, GeneralAction, KeyAction, KeyActionItem, KeyDownAction,
KeyUpAction, NullActionItem, PointerAction, PointerActionItem, PointerActionParameters,
- PointerDownAction, PointerMoveAction, PointerOrigin, PointerType, PointerUpAction,
+ PointerDownAction, PointerMoveAction, PointerOrigin, PointerType, PointerUpAction, WheelAction,
+ WheelActionItem, WheelScrollAction,
};
-use webdriver::error::ErrorStatus;
+use webdriver::error::{ErrorStatus, WebDriverError};
-use crate::Handler;
+use crate::{Handler, WebElement, wait_for_script_response};
-// Interval between pointerMove increments in ms, based on common vsync
+// Interval between wheelScroll and pointerMove increments in ms, based on common vsync
static POINTERMOVE_INTERVAL: u64 = 17;
+static WHEELSCROLL_INTERVAL: u64 = 17;
// https://w3c.github.io/webdriver/#dfn-input-source-state
pub(crate) enum InputSourceState {
Null,
Key(KeyInputState),
Pointer(PointerInputState),
+ Wheel,
}
// https://w3c.github.io/webdriver/#dfn-pointer-input-source
pub(crate) struct PointerInputState {
subtype: PointerType,
pressed: HashSet<u64>,
- x: i64,
- y: i64,
+ x: f64,
+ y: f64,
}
impl PointerInputState {
@@ -46,8 +49,8 @@ impl PointerInputState {
PointerType::Touch => PointerType::Touch,
},
pressed: HashSet::new(),
- x: 0,
- y: 0,
+ x: 0.0,
+ y: 0.0,
}
}
}
@@ -76,7 +79,15 @@ fn compute_tick_duration(tick_actions: &ActionSequence) -> u64 {
}
},
ActionsType::Key { actions: _ } => (),
- ActionsType::Wheel { .. } => log::error!("not implemented"),
+ ActionsType::Wheel { actions } => {
+ for action in actions.iter() {
+ let action_duration = match action {
+ WheelActionItem::General(GeneralAction::Pause(action)) => action.duration,
+ WheelActionItem::Wheel(WheelAction::Scroll(action)) => action.duration,
+ };
+ duration = cmp::max(duration, action_duration.unwrap_or(0));
+ }
+ },
}
duration
}
@@ -176,9 +187,26 @@ impl Handler {
}
}
},
- ActionsType::Wheel { .. } => {
- log::error!("not yet implemented");
- return Err(ErrorStatus::UnsupportedOperation);
+ ActionsType::Wheel { actions } => {
+ for action in actions.iter() {
+ match action {
+ WheelActionItem::General(_action) => {
+ self.dispatch_general_action(source_id)
+ },
+ WheelActionItem::Wheel(action) => {
+ self.session_mut()
+ .unwrap()
+ .input_state_table
+ .entry(source_id.to_string())
+ .or_insert(InputSourceState::Wheel);
+ match action {
+ WheelAction::Scroll(action) => {
+ self.dispatch_scroll_action(action, tick_duration)?
+ },
+ }
+ },
+ }
+ }
},
}
@@ -191,9 +219,8 @@ impl Handler {
let raw_key = action.value.chars().next().unwrap();
let key_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
- InputSourceState::Null => unreachable!(),
InputSourceState::Key(key_input_state) => key_input_state,
- InputSourceState::Pointer(_) => unreachable!(),
+ _ => unreachable!(),
};
session.input_cancel_list.push(ActionSequence {
@@ -219,9 +246,8 @@ impl Handler {
let raw_key = action.value.chars().next().unwrap();
let key_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
- InputSourceState::Null => unreachable!(),
InputSourceState::Key(key_input_state) => key_input_state,
- InputSourceState::Pointer(_) => unreachable!(),
+ _ => unreachable!(),
};
session.input_cancel_list.push(ActionSequence {
@@ -251,9 +277,8 @@ impl Handler {
let session = self.session.as_mut().unwrap();
let pointer_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
- InputSourceState::Null => unreachable!(),
- InputSourceState::Key(_) => unreachable!(),
InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
+ _ => unreachable!(),
};
if pointer_input_state.pressed.contains(&action.button) {
@@ -280,11 +305,10 @@ impl Handler {
},
});
- let button = (action.button as u16).into();
let cmd_msg = WebDriverCommandMsg::MouseButtonAction(
session.webview_id,
MouseButtonAction::Down,
- button,
+ action.button.into(),
pointer_input_state.x as f32,
pointer_input_state.y as f32,
);
@@ -298,9 +322,8 @@ impl Handler {
let session = self.session.as_mut().unwrap();
let pointer_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
- InputSourceState::Null => unreachable!(),
- InputSourceState::Key(_) => unreachable!(),
InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
+ _ => unreachable!(),
};
if !pointer_input_state.pressed.contains(&action.button) {
@@ -327,11 +350,10 @@ impl Handler {
},
});
- let button = (action.button as u16).into();
let cmd_msg = WebDriverCommandMsg::MouseButtonAction(
session.webview_id,
MouseButtonAction::Up,
- button,
+ action.button.into(),
pointer_input_state.x as f32,
pointer_input_state.y as f32,
);
@@ -362,59 +384,39 @@ impl Handler {
.get(source_id)
.unwrap()
{
- InputSourceState::Null => unreachable!(),
- InputSourceState::Key(_) => unreachable!(),
InputSourceState::Pointer(pointer_input_state) => {
(pointer_input_state.x, pointer_input_state.y)
},
+ _ => unreachable!(),
};
- // Step 5 - 6
let (x, y) = match action.origin {
PointerOrigin::Viewport => (x_offset, y_offset),
PointerOrigin::Pointer => (start_x + x_offset, start_y + y_offset),
- PointerOrigin::Element(ref x) => {
- let (sender, receiver) = ipc::channel().unwrap();
- self.browsing_context_script_command(
- WebDriverScriptCommand::GetElementInViewCenterPoint(x.to_string(), sender),
- )
- .unwrap();
-
- let Some(point) = receiver.recv().unwrap()? else {
- return Err(ErrorStatus::UnknownError);
- };
- point
+ PointerOrigin::Element(ref web_element) => {
+ let point = self.get_element_origin_relative_coordinates(web_element)?;
+ (point.0 as f64, point.1 as f64)
},
};
- let (sender, receiver) = ipc::channel().unwrap();
- let cmd_msg =
- WebDriverCommandMsg::GetWindowSize(self.session.as_ref().unwrap().webview_id, sender);
- self.constellation_chan
- .send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
- .unwrap();
-
- // Steps 7 - 8
- let viewport_size = receiver.recv().unwrap();
- if x < 0 || x as f32 > viewport_size.width || y < 0 || y as f32 > viewport_size.height {
- return Err(ErrorStatus::MoveTargetOutOfBounds);
- }
+ // Step 5 - 6
+ self.check_viewport_bound(x, y)?;
- // Step 9
+ // Step 7
let duration = match action.duration {
Some(duration) => duration,
None => tick_duration,
};
- // Step 10
+ // Step 8
if duration > 0 {
thread::sleep(Duration::from_millis(POINTERMOVE_INTERVAL));
}
- // Step 11
+ // Step 9 - 18
self.perform_pointer_move(source_id, duration, start_x, start_y, x, y, tick_start);
- // Step 12
+ // Step 19
Ok(())
}
@@ -424,17 +426,16 @@ impl Handler {
&mut self,
source_id: &str,
duration: u64,
- start_x: i64,
- start_y: i64,
- target_x: i64,
- target_y: i64,
+ start_x: f64,
+ start_y: f64,
+ target_x: f64,
+ target_y: f64,
tick_start: Instant,
) {
let session = self.session.as_mut().unwrap();
let pointer_input_state = match session.input_state_table.get_mut(source_id).unwrap() {
- InputSourceState::Null => unreachable!(),
- InputSourceState::Key(_) => unreachable!(),
InputSourceState::Pointer(pointer_input_state) => pointer_input_state,
+ _ => unreachable!(),
};
loop {
@@ -456,8 +457,8 @@ impl Handler {
(target_x, target_y)
} else {
(
- (duration_ratio * (target_x - start_x) as f64) as i64 + start_x,
- (duration_ratio * (target_y - start_y) as f64) as i64 + start_y,
+ duration_ratio * (target_x - start_x) + start_x,
+ duration_ratio * (target_y - start_y) + start_y,
)
};
@@ -470,6 +471,7 @@ impl Handler {
// Step 7.2
let cmd_msg =
WebDriverCommandMsg::MouseMoveAction(session.webview_id, x as f32, y as f32);
+ //TODO: Need Synchronization here before updating `pointer_input_state`
self.constellation_chan
.send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
.unwrap();
@@ -487,4 +489,186 @@ impl Handler {
thread::sleep(Duration::from_millis(POINTERMOVE_INTERVAL));
}
}
+
+ /// <https://w3c.github.io/webdriver/#dfn-dispatch-a-scroll-action>
+ fn dispatch_scroll_action(
+ &mut self,
+ action: &WheelScrollAction,
+ tick_duration: u64,
+ ) -> Result<(), ErrorStatus> {
+ // Note: We have not implemented `extract an action sequence` which will calls
+ // `process a wheel action` that validate many of the variable used here.
+ // Hence, we do all the checking here until those functions is properly
+ // implemented.
+ // <https://w3c.github.io/webdriver/#dfn-process-a-wheel-action>
+
+ let tick_start = Instant::now();
+
+ // Step 1
+ let Some(x_offset) = action.x else {
+ return Err(ErrorStatus::InvalidArgument);
+ };
+
+ // Step 2
+ let Some(y_offset) = action.y else {
+ return Err(ErrorStatus::InvalidArgument);
+ };
+
+ // Step 3 - 4
+ // Get coordinates relative to an origin.
+ let (x, y) = match action.origin {
+ PointerOrigin::Viewport => (x_offset, y_offset),
+ PointerOrigin::Pointer => return Err(ErrorStatus::InvalidArgument),
+ PointerOrigin::Element(ref web_element) => {
+ self.get_element_origin_relative_coordinates(web_element)?
+ },
+ };
+
+ // Step 5 - 6
+ self.check_viewport_bound(x as _, y as _)?;
+
+ // Step 7 - 8
+ let Some(delta_x) = action.deltaX else {
+ return Err(ErrorStatus::InvalidArgument);
+ };
+
+ let Some(delta_y) = action.deltaY else {
+ return Err(ErrorStatus::InvalidArgument);
+ };
+
+ // Step 9
+ let duration = match action.duration {
+ Some(duration) => duration,
+ None => tick_duration,
+ };
+
+ // Step 10
+ if duration > 0 {
+ thread::sleep(Duration::from_millis(WHEELSCROLL_INTERVAL));
+ }
+
+ // Step 11
+ self.perform_scroll(duration, x, y, delta_x, delta_y, 0, 0, tick_start);
+
+ // Step 12
+ Ok(())
+ }
+
+ /// <https://w3c.github.io/webdriver/#dfn-perform-a-scroll>
+ #[allow(clippy::too_many_arguments)]
+ fn perform_scroll(
+ &mut self,
+ duration: u64,
+ x: i64,
+ y: i64,
+ target_delta_x: i64,
+ target_delta_y: i64,
+ mut curr_delta_x: i64,
+ mut curr_delta_y: i64,
+ tick_start: Instant,
+ ) {
+ let session = self.session.as_mut().unwrap();
+
+ // Step 1
+ let time_delta = tick_start.elapsed().as_millis();
+
+ // Step 2
+ let duration_ratio = if duration > 0 {
+ time_delta as f64 / duration as f64
+ } else {
+ 1.0
+ };
+
+ // Step 3
+ let last = 1.0 - duration_ratio < 0.001;
+
+ // Step 4
+ let (delta_x, delta_y) = if last {
+ (target_delta_x - curr_delta_x, target_delta_y - curr_delta_y)
+ } else {
+ (
+ (duration_ratio * target_delta_x as f64) as i64 - curr_delta_x,
+ (duration_ratio * target_delta_y as f64) as i64 - curr_delta_y,
+ )
+ };
+
+ // Step 5
+ if delta_x != 0 || delta_y != 0 {
+ // Perform implementation-specific action dispatch steps
+ let cmd_msg = WebDriverCommandMsg::WheelScrollAction(
+ session.webview_id,
+ x as f32,
+ y as f32,
+ delta_x as f64,
+ delta_y as f64,
+ );
+ self.constellation_chan
+ .send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
+ .unwrap();
+
+ curr_delta_x += delta_x;
+ curr_delta_y += delta_y;
+ }
+
+ // Step 6
+ if last {
+ return;
+ }
+
+ // Step 7
+ // TODO: The two steps should be done in parallel
+ // 7.1. Asynchronously wait for an implementation defined amount of time to pass.
+ thread::sleep(Duration::from_millis(WHEELSCROLL_INTERVAL));
+ // 7.2. Perform a scroll with arguments duration, x, y, target delta x,
+ // target delta y, current delta x, current delta y.
+ self.perform_scroll(
+ duration,
+ x,
+ y,
+ target_delta_x,
+ target_delta_y,
+ curr_delta_x,
+ curr_delta_y,
+ tick_start,
+ );
+ }
+
+ fn check_viewport_bound(&self, x: f64, y: f64) -> Result<(), ErrorStatus> {
+ let (sender, receiver) = ipc::channel().unwrap();
+ let cmd_msg =
+ WebDriverCommandMsg::GetWindowSize(self.session.as_ref().unwrap().webview_id, sender);
+ self.constellation_chan
+ .send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg))
+ .unwrap();
+
+ let viewport_size = match wait_for_script_response(receiver) {
+ Ok(response) => response,
+ Err(WebDriverError { error, .. }) => return Err(error),
+ };
+ if x < 0.0 || x > viewport_size.width.into() || y < 0.0 || y > viewport_size.height.into() {
+ Err(ErrorStatus::MoveTargetOutOfBounds)
+ } else {
+ Ok(())
+ }
+ }
+
+ fn get_element_origin_relative_coordinates(
+ &self,
+ web_element: &WebElement,
+ ) -> Result<(i64, i64), ErrorStatus> {
+ let (sender, receiver) = ipc::channel().unwrap();
+ self.browsing_context_script_command(WebDriverScriptCommand::GetElementInViewCenterPoint(
+ web_element.to_string(),
+ sender,
+ ))
+ .unwrap();
+ let response = match wait_for_script_response(receiver) {
+ Ok(response) => response,
+ Err(WebDriverError { error, .. }) => return Err(error),
+ };
+ match response? {
+ Some(point) => Ok(point),
+ None => Err(ErrorStatus::UnknownError),
+ }
+ }
}
diff --git a/components/webdriver_server/lib.rs b/components/webdriver_server/lib.rs
index d003ebf8adb..5735594b058 100644
--- a/components/webdriver_server/lib.rs
+++ b/components/webdriver_server/lib.rs
@@ -23,7 +23,7 @@ use constellation_traits::{EmbedderToConstellationMessage, TraversalDirection};
use cookie::{CookieBuilder, Expiration};
use crossbeam_channel::{Receiver, Sender, after, select, unbounded};
use embedder_traits::{
- WebDriverCommandMsg, WebDriverCookieError, WebDriverFrameId, WebDriverJSError,
+ MouseButton, WebDriverCommandMsg, WebDriverCookieError, WebDriverFrameId, WebDriverJSError,
WebDriverJSResult, WebDriverJSValue, WebDriverLoadStatus, WebDriverScriptCommand,
};
use euclid::{Rect, Size2D};
@@ -1619,41 +1619,44 @@ impl Handler {
InputSourceState::Pointer(PointerInputState::new(&PointerType::Mouse)),
);
- // Steps 8.3 - 8.6
+ // Step 8.7. Construct a pointer move action.
+ // Step 8.8. Set a property x to 0 on pointer move action.
+ // Step 8.9. Set a property y to 0 on pointer move action.
+ // Step 8.10. Set a property origin to element on pointer move action.
let pointer_move_action = PointerMoveAction {
duration: None,
origin: PointerOrigin::Element(WebElement(element_id)),
- x: 0,
- y: 0,
+ x: 0.0,
+ y: 0.0,
..Default::default()
};
- // Steps 8.7 - 8.8
+ // Step 8.11. Construct pointer down action.
+ // Step 8.12. Set a property button to 0 on pointer down action.
let pointer_down_action = PointerDownAction {
- button: 1,
+ button: i16::from(MouseButton::Left) as u64,
..Default::default()
};
- // Steps 8.9 - 8.10
+ // Step 8.13. Construct pointer up action.
+ // Step 8.14. Set a property button to 0 on pointer up action.
let pointer_up_action = PointerUpAction {
- button: 1,
+ button: i16::from(MouseButton::Left) as u64,
..Default::default()
};
- // Step 8.11
+ // Step 8.16 Dispatch a list of actions with input state,
+ // actions, session's current browsing context, and actions options.
if let Err(error) =
self.dispatch_pointermove_action(&id, &pointer_move_action, 0)
{
return Err(WebDriverError::new(error, ""));
}
- // Steps 8.12
self.dispatch_pointerdown_action(&id, &pointer_down_action);
-
- // Steps 8.13
self.dispatch_pointerup_action(&id, &pointer_up_action);
- // Step 8.14
+ // Step 8.17 Remove an input source with input state and input id.
self.session_mut()?.input_state_table.remove(&id);
// Step 13
@@ -1915,6 +1918,15 @@ impl WebDriverHandler<ServoExtensionRoute> for Handler {
}
}
+/// <https://w3c.github.io/webdriver/#dfn-web-element-identifier>
+const ELEMENT_IDENTIFIER: &str = "element-6066-11e4-a52e-4f735466cecf";
+/// <https://w3c.github.io/webdriver/#dfn-web-frame-identifier>
+const FRAME_IDENTIFIER: &str = "frame-075b-4da1-b6ba-e579c2d3230a";
+/// <https://w3c.github.io/webdriver/#dfn-web-window-identifier>
+const WINDOW_IDENTIFIER: &str = "window-fcc6-11e5-b4f8-330a88ab9d7f";
+/// <https://w3c.github.io/webdriver/#dfn-shadow-root-identifier>
+const SHADOW_ROOT_IDENTIFIER: &str = "shadow-6066-11e4-a52e-4f735466cecf";
+
fn webdriver_value_to_js_argument(v: &Value) -> String {
match v {
Value::String(s) => format!("\"{}\"", s),
@@ -1929,6 +1941,22 @@ fn webdriver_value_to_js_argument(v: &Value) -> String {
format!("[{}]", elems.join(", "))
},
Value::Object(map) => {
+ let key = map.keys().next().map(String::as_str);
+ match (key, map.values().next()) {
+ (Some(ELEMENT_IDENTIFIER), Some(id)) => {
+ return format!("window.webdriverElement({})", id);
+ },
+ (Some(FRAME_IDENTIFIER), Some(id)) => {
+ return format!("window.webdriverFrame({})", id);
+ },
+ (Some(WINDOW_IDENTIFIER), Some(id)) => {
+ return format!("window.webdriverWindow({})", id);
+ },
+ (Some(SHADOW_ROOT_IDENTIFIER), Some(id)) => {
+ return format!("window.webdriverShadowRoot({})", id);
+ },
+ _ => {},
+ }
let elems = map
.iter()
.map(|(k, v)| format!("{}: {}", k, webdriver_value_to_js_argument(v)))
diff --git a/components/webgl/Cargo.toml b/components/webgl/Cargo.toml
index b0c1c0ceb29..542a3cb4fae 100644
--- a/components/webgl/Cargo.toml
+++ b/components/webgl/Cargo.toml
@@ -26,6 +26,7 @@ fnv = { workspace = true }
glow = { workspace = true }
half = "2"
ipc-channel = { workspace = true }
+itertools = { workspace = true }
log = { workspace = true }
pixels = { path = "../pixels" }
snapshot = { workspace = true }
diff --git a/components/webgl/webgl_thread.rs b/components/webgl/webgl_thread.rs
index b1ac2b2d3c4..9562c4cb4e0 100644
--- a/components/webgl/webgl_thread.rs
+++ b/components/webgl/webgl_thread.rs
@@ -32,6 +32,7 @@ use glow::{
};
use half::f16;
use ipc_channel::ipc::IpcSharedMemory;
+use itertools::Itertools;
use log::{debug, error, trace, warn};
use pixels::{self, PixelFormat, unmultiply_inplace};
use surfman::chains::{PreserveBuffer, SwapChains, SwapChainsAPI};
@@ -2570,24 +2571,10 @@ impl WebGLImpl {
chan.send((range_min, range_max, precision)).unwrap();
}
- fn get_extensions(gl: &Gl, chan: &WebGLSender<String>) {
- let mut ext_count = [0];
- unsafe {
- gl.get_parameter_i32_slice(gl::NUM_EXTENSIONS, &mut ext_count);
- }
- // Fall back to the depricated extensions API if that fails
- if unsafe { gl.get_error() } != gl::NO_ERROR {
- chan.send(unsafe { gl.get_parameter_string(gl::EXTENSIONS) })
- .unwrap();
- return;
- }
- let ext_count = ext_count[0] as usize;
- let mut extensions = Vec::with_capacity(ext_count);
- for idx in 0..ext_count {
- extensions.push(unsafe { gl.get_parameter_indexed_string(gl::EXTENSIONS, idx as u32) })
- }
- let extensions = extensions.join(" ");
- chan.send(extensions).unwrap();
+ /// This is an implementation of `getSupportedExtensions()` from
+ /// <https://registry.khronos.org/webgl/specs/latest/1.0/#5.14>
+ fn get_extensions(gl: &Gl, result_sender: &WebGLSender<String>) {
+ let _ = result_sender.send(gl.supported_extensions().iter().join(" "));
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6
diff --git a/deny.toml b/deny.toml
index b2e86b9e67e..5284cb80b31 100644
--- a/deny.toml
+++ b/deny.toml
@@ -159,6 +159,11 @@ skip = [
# duplicated by font-kit
"redox_users",
"dirs-sys",
+
+ # duplicated by winit
+ "objc2-app-kit",
+ "objc2-foundation",
+ "objc2",
]
# github.com organizations to allow git sources for
diff --git a/ports/servoshell/Cargo.toml b/ports/servoshell/Cargo.toml
index e000900e4ea..a77bd885549 100644
--- a/ports/servoshell/Cargo.toml
+++ b/ports/servoshell/Cargo.toml
@@ -131,13 +131,13 @@ libservo = { path = "../../components/servo", features = ["no-wgl"] }
windows-sys = { workspace = true, features = ["Win32_Graphics_Gdi"] }
[target.'cfg(target_os = "macos")'.dependencies]
-objc2-app-kit = { version = "0.2.2", default-features = false, features = [
+objc2-app-kit = { version = "0.3.1", default-features = false, features = [
"std",
"NSColorSpace",
"NSResponder",
"NSView",
"NSWindow",
] }
-objc2-foundation = { version = "0.2.2", default-features = false, features = [
+objc2-foundation = { version = "0.3.1", default-features = false, features = [
"std",
] }
diff --git a/ports/servoshell/egl/app_state.rs b/ports/servoshell/egl/app_state.rs
index a65ed7424f4..737a2f23b7d 100644
--- a/ports/servoshell/egl/app_state.rs
+++ b/ports/servoshell/egl/app_state.rs
@@ -38,6 +38,14 @@ impl Coordinates {
viewport: Rect::new(Point2D::new(x, y), Size2D::new(width, height)),
}
}
+
+ pub fn origin(&self) -> Point2D<i32, DevicePixel> {
+ self.viewport.origin
+ }
+
+ pub fn size(&self) -> Size2D<i32, DevicePixel> {
+ self.viewport.size
+ }
}
pub(super) struct ServoWindowCallbacks {
@@ -115,11 +123,13 @@ impl ServoDelegate for ServoShellServoDelegate {
impl WebViewDelegate for RunningAppState {
fn screen_geometry(&self, _webview: WebView) -> Option<ScreenGeometry> {
let coord = self.callbacks.coordinates.borrow();
- let screen_size = DeviceIntSize::new(coord.viewport.size.width, coord.viewport.size.height);
+ let offset = coord.origin();
+ let available_size = coord.size();
+ let screen_size = coord.size();
Some(ScreenGeometry {
size: screen_size,
- available_size: screen_size,
- offset: Point2D::zero(),
+ available_size,
+ offset,
})
}
diff --git a/ports/servoshell/egl/ohos.rs b/ports/servoshell/egl/ohos.rs
index 236f1d842da..ceccbdbe900 100644
--- a/ports/servoshell/egl/ohos.rs
+++ b/ports/servoshell/egl/ohos.rs
@@ -28,8 +28,9 @@ use servo::{
use xcomponent_sys::{
OH_NativeXComponent, OH_NativeXComponent_Callback, OH_NativeXComponent_GetKeyEvent,
OH_NativeXComponent_GetKeyEventAction, OH_NativeXComponent_GetKeyEventCode,
- OH_NativeXComponent_GetTouchEvent, OH_NativeXComponent_GetXComponentSize,
- OH_NativeXComponent_KeyAction, OH_NativeXComponent_KeyCode, OH_NativeXComponent_KeyEvent,
+ OH_NativeXComponent_GetTouchEvent, OH_NativeXComponent_GetXComponentOffset,
+ OH_NativeXComponent_GetXComponentSize, OH_NativeXComponent_KeyAction,
+ OH_NativeXComponent_KeyCode, OH_NativeXComponent_KeyEvent,
OH_NativeXComponent_RegisterCallback, OH_NativeXComponent_RegisterKeyEventCallback,
OH_NativeXComponent_TouchEvent, OH_NativeXComponent_TouchEventType,
};
@@ -216,6 +217,8 @@ static SERVO_CHANNEL: OnceLock<Sender<ServoAction>> = OnceLock::new();
#[unsafe(no_mangle)]
extern "C" fn on_surface_created_cb(xcomponent: *mut OH_NativeXComponent, window: *mut c_void) {
info!("on_surface_created_cb");
+ #[cfg(feature = "tracing-hitrace")]
+ let _ = hitrace::ScopedTrace::start_trace(&c"on_surface_created_cb");
let xc_wrapper = XComponentWrapper(xcomponent);
let window_wrapper = WindowWrapper(window);
@@ -267,6 +270,33 @@ extern "C" fn on_surface_created_cb(xcomponent: *mut OH_NativeXComponent, window
info!("Returning from on_surface_created_cb");
}
+/// Returns the offset of the surface relative to its parent's top left corner
+///
+/// # Safety
+///
+/// `xcomponent` and `native_window` must be valid, non-null and aligned pointers to a
+/// live xcomponent and associated native window surface.
+unsafe fn get_xcomponent_offset(
+ xcomponent: *mut OH_NativeXComponent,
+ native_window: *mut c_void,
+) -> Result<(i32, i32), i32> {
+ let mut x: f64 = 0.0;
+ let mut y: f64 = 0.0;
+
+ let result = unsafe {
+ OH_NativeXComponent_GetXComponentOffset(xcomponent, native_window, &raw mut x, &raw mut y)
+ };
+ if result != 0 {
+ error!("OH_NativeXComponent_GetXComponentOffset failed with {result}");
+ return Err(result);
+ }
+
+ Ok((
+ (x.round() as i64).try_into().expect("X offset too large"),
+ (y.round() as i64).try_into().expect("Y offset too large"),
+ ))
+}
+
/// Returns the size of the surface
///
/// # Safety
@@ -387,6 +417,7 @@ extern "C" fn on_dispatch_key_event(xc: *mut OH_NativeXComponent, _window: *mut
static LOGGER: LazyLock<hilog::Logger> = LazyLock::new(|| {
let mut builder = hilog::Builder::new();
+ builder.set_domain(hilog::LogDomain::new(0xE0C3));
let filters = [
"fonts",
"servo",
@@ -678,7 +709,7 @@ impl EventLoopWaker for WakeupCallback {
}
fn wake(&self) {
- info!("wake called!");
+ log::trace!("wake called!");
self.chan.send(ServoAction::WakeUp).unwrap_or_else(|e| {
error!("Failed to send wake message with: {e}");
});
@@ -783,6 +814,12 @@ impl HostTrait for HostCallbacks {
#[cfg(feature = "tracing-hitrace")]
let _scope = hitrace::ScopedTrace::start_trace(&c"PageLoadEndedPrompt");
self.show_alert("Page finished loading!".to_string());
+ } else {
+ #[cfg(feature = "tracing-hitrace")]
+ let _ = hitrace::ScopedTrace::start_trace_str(&format!(
+ "load status changed {:?}",
+ load_status
+ ));
}
}
diff --git a/ports/servoshell/egl/ohos/simpleservo.rs b/ports/servoshell/egl/ohos/simpleservo.rs
index 9815c0fe4fd..c867c7a5330 100644
--- a/ports/servoshell/egl/ohos/simpleservo.rs
+++ b/ports/servoshell/egl/ohos/simpleservo.rs
@@ -97,7 +97,10 @@ pub fn init(
let Ok(window_size) = (unsafe { super::get_xcomponent_size(xcomponent, native_window) }) else {
return Err("Failed to get xcomponent size");
};
- let coordinates = Coordinates::new(0, 0, window_size.width, window_size.height);
+ let Ok((x, y)) = (unsafe { super::get_xcomponent_offset(xcomponent, native_window) }) else {
+ return Err("Failed to get xcomponent offset");
+ };
+ let coordinates = Coordinates::new(x, y, window_size.width, window_size.height);
let display_handle = RawDisplayHandle::Ohos(OhosDisplayHandle::new());
let display_handle = unsafe { DisplayHandle::borrow_raw(display_handle) };
diff --git a/ports/servoshell/prefs.rs b/ports/servoshell/prefs.rs
index 306aaa197d2..a602939cb65 100644
--- a/ports/servoshell/prefs.rs
+++ b/ports/servoshell/prefs.rs
@@ -428,7 +428,11 @@ pub(crate) fn parse_command_line_arguments(args: Vec<String>) -> ArgumentParsing
#[cfg(target_env = "ohos")]
let log_filter = {
let filters = opt_match.opt_strs("log-filter").join(",");
- (!filters.is_empty()).then_some(filters)
+ let log_filter = (!filters.is_empty()).then_some(filters).or_else(|| {
+ (!preferences.log_filter.is_empty()).then_some(preferences.log_filter.clone())
+ });
+ log::debug!("Set log_filter to: {:?}", log_filter);
+ log_filter
};
let mut debug_options = DebugOptions::default();
@@ -574,7 +578,6 @@ pub(crate) fn parse_command_line_arguments(args: Vec<String>) -> ArgumentParsing
"dom_offscreen_canvas_enabled",
"dom_permissions_enabled",
"dom_resize_observer_enabled",
- "dom_serviceworker_enabled",
"dom_svg_enabled",
"dom_trusted_types_enabled",
"dom_webgl2_enabled",
@@ -733,7 +736,7 @@ fn print_debug_options_usage(app: &str) {
);
print_option(
"dump-flow-tree",
- "Print the flow tree (Layout 2013) or fragment tree (Layout 2020) after each layout.",
+ "Print the fragment tree after each layout.",
);
print_option(
"dump-rule-tree",
diff --git a/python/requirements.txt b/python/requirements.txt
index 20d42065dde..d026ce3ee5f 100644
--- a/python/requirements.txt
+++ b/python/requirements.txt
@@ -37,3 +37,6 @@ types-requests
# For mach package on macOS.
Mako == 1.2.2
+
+# For devtools tests.
+geckordp == 1.0.3
diff --git a/python/servo/build_commands.py b/python/servo/build_commands.py
index b21f89980c0..42988debf35 100644
--- a/python/servo/build_commands.py
+++ b/python/servo/build_commands.py
@@ -13,10 +13,11 @@ import os.path as path
import pathlib
import shutil
import stat
+import subprocess
import sys
from time import time
-from typing import Optional
+from typing import Optional, List
import notifypy
@@ -40,6 +41,32 @@ SUPPORTED_ASAN_TARGETS = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu",
"x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]
+def get_rustc_llvm_version() -> Optional[List[int]]:
+ """Determine the LLVM version of `rustc` and return it as a List[major, minor, patch, ...]
+
+ In some cases we want to ensure that the LLVM version of rustc and clang match, e.g.
+ when using ASAN for both C/C++ and Rust code, we want to use the same ASAN implementation.
+ This function assumes that rustc points to the rust compiler we are interested in, which should
+ be valid in both rustup managed environment and on nix.
+ """
+ try:
+ result = subprocess.run(['rustc', '--version', '--verbose'], encoding='utf-8', capture_output=True)
+ result.check_returncode()
+ for line in result.stdout.splitlines():
+ line_lowercase = line.lower()
+ if line_lowercase.startswith("llvm version:"):
+ llvm_version = line_lowercase.strip("llvm version:")
+ llvm_version = llvm_version.strip()
+ version = llvm_version.split('.')
+ print(f"Info: rustc is using LLVM version {'.'.join(version)}")
+ return version
+ else:
+ print(f"Error: Couldn't find LLVM version in output of `rustc --version --verbose`: `{result.stdout}`")
+ except Exception as e:
+ print(f"Error: Failed to determine rustc version: {e}")
+ return None
+
+
@CommandProvider
class MachCommands(CommandBase):
@Command('build', description='Build Servo', category='build')
@@ -99,11 +126,30 @@ class MachCommands(CommandBase):
env["RUSTFLAGS"] = env.get("RUSTFLAGS", "") + " -Zsanitizer=address"
opts += ["-Zbuild-std"]
kwargs["target_override"] = target_triple
- # TODO: Investigate sanitizers in C/C++ code:
- # env.setdefault("CFLAGS", "")
- # env.setdefault("CXXFLAGS", "")
- # env["CFLAGS"] += " -fsanitize=address"
- # env["CXXFLAGS"] += " -fsanitize=address"
+
+ # Note: We want to use the same clang/LLVM version as rustc.
+ rustc_llvm_version = get_rustc_llvm_version()
+ if rustc_llvm_version is None:
+ raise RuntimeError("Unable to determine necessary clang version for ASAN support")
+ llvm_major: int = rustc_llvm_version[0]
+ target_clang = f"clang-{llvm_major}"
+ target_cxx = f"clang++-{llvm_major}"
+ if shutil.which(target_clang) is None or shutil.which(target_cxx) is None:
+ raise RuntimeError(f"--with-asan requires `{target_clang}` and `{target_cxx}` to be in PATH")
+ env.setdefault("TARGET_CC", target_clang)
+ env.setdefault("TARGET_CXX", target_cxx)
+ # TODO: We should also parse the LLVM version from the clang compiler we chose.
+ # It's unclear if the major version being the same is sufficient.
+
+ # We need to use `TARGET_CFLAGS`, since we don't want to compile host dependencies with ASAN,
+ # since that causes issues when building build-scripts / proc macros.
+ env.setdefault("TARGET_CFLAGS", "")
+ env.setdefault("TARGET_CXXFLAGS", "")
+ env["TARGET_CFLAGS"] += " -fsanitize=address"
+ env["TARGET_CXXFLAGS"] += " -fsanitize=address"
+ env["TARGET_LDFLAGS"] = "-static-libasan"
+ # By default build mozjs from source to enable ASAN with mozjs.
+ env.setdefault("MOZJS_FROM_SOURCE", "1")
# asan replaces system allocator with asan allocator
# we need to make sure that we do not replace it with jemalloc
diff --git a/python/servo/devtools_tests.py b/python/servo/devtools_tests.py
new file mode 100644
index 00000000000..0d336fbcb9c
--- /dev/null
+++ b/python/servo/devtools_tests.py
@@ -0,0 +1,207 @@
+# Copyright 2013 The Servo Project Developers. See the COPYRIGHT
+# file at the top-level directory of this distribution.
+#
+# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+from concurrent.futures import Future
+import logging
+from geckordp.actors.root import RootActor
+from geckordp.actors.descriptors.tab import TabActor
+from geckordp.actors.watcher import WatcherActor
+from geckordp.actors.resources import Resources
+from geckordp.actors.events import Events
+from geckordp.rdp_client import RDPClient
+import http.server
+import os.path
+import socketserver
+import subprocess
+import time
+from threading import Thread
+from typing import Optional
+import unittest
+
+
+# Set this to true to log requests in the internal web servers.
+LOG_REQUESTS = False
+
+
+class DevtoolsTests(unittest.IsolatedAsyncioTestCase):
+ # /path/to/servo/python/servo
+ script_path = None
+
+ def __init__(self, methodName="runTest"):
+ super().__init__(methodName)
+ self.servoshell = None
+ self.base_url = None
+ self.web_server = None
+ self.web_server_thread = None
+
+ # Classic script vs module script:
+ # - <https://html.spec.whatwg.org/multipage/#classic-script>
+ # - <https://html.spec.whatwg.org/multipage/#module-script>
+ # Worker scripts can be classic or module:
+ # - <https://html.spec.whatwg.org/multipage/#fetch-a-classic-worker-script>
+ # - <https://html.spec.whatwg.org/multipage/#fetch-a-module-worker-script-tree>
+ # Non-worker(?) script sources can be inline, external, or blob.
+ # Worker script sources can be external or blob.
+ def test_sources_list(self):
+ self.start_web_server(test_dir=os.path.join(DevtoolsTests.script_path, "devtools_tests/sources"))
+ self.run_servoshell()
+ self.assert_sources_list(2, set([
+ tuple([f"{self.base_url}/classic.js", f"{self.base_url}/test.html", "https://servo.org/js/load-table.js"]),
+ tuple([f"{self.base_url}/worker.js"]),
+ ]))
+
+ def test_sources_list_with_data_no_scripts(self):
+ self.run_servoshell(url="data:text/html,")
+ self.assert_sources_list(1, set([tuple()]))
+
+ def test_sources_list_with_data_empty_inline_classic_script(self):
+ self.run_servoshell(url="data:text/html,<script></script>")
+ self.assert_sources_list(1, set([tuple()]))
+
+ def test_sources_list_with_data_inline_classic_script(self):
+ self.run_servoshell(url="data:text/html,<script>;</script>")
+ self.assert_sources_list(1, set([tuple(["data:text/html,<script>;</script>"])]))
+
+ def test_sources_list_with_data_external_classic_script(self):
+ self.start_web_server(test_dir=os.path.join(DevtoolsTests.script_path, "devtools_tests/sources"))
+ self.run_servoshell(url=f"data:text/html,<script src=\"{self.base_url}/classic.js\"></script>")
+ self.assert_sources_list(1, set([tuple([f"{self.base_url}/classic.js"])]))
+
+ def test_sources_list_with_data_empty_inline_module_script(self):
+ self.run_servoshell(url="data:text/html,<script type=module></script>")
+ self.assert_sources_list(1, set([tuple()]))
+
+ def test_sources_list_with_data_inline_module_script(self):
+ self.run_servoshell(url="data:text/html,<script type=module>;</script>")
+ self.assert_sources_list(1, set([tuple(["data:text/html,<script type=module>;</script>"])]))
+
+ # Sets `base_url` and `web_server` and `web_server_thread`.
+ def start_web_server(self, *, test_dir=None):
+ if test_dir is None:
+ test_dir = os.path.join(DevtoolsTests.script_path, "devtools_tests")
+ base_url = Future()
+
+ class Handler(http.server.SimpleHTTPRequestHandler):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, directory=test_dir, **kwargs)
+
+ def log_message(self, format, *args):
+ if LOG_REQUESTS:
+ return super().log_message(format, *args)
+
+ def server_thread():
+ self.web_server = socketserver.TCPServer(("0.0.0.0", 0), Handler)
+ base_url.set_result(f"http://127.0.0.1:{self.web_server.server_address[1]}")
+ self.web_server.serve_forever()
+
+ # Start a web server for the test.
+ self.web_server_thread = Thread(target=server_thread)
+ self.web_server_thread.start()
+ self.base_url = base_url.result(1)
+
+ # Sets `servoshell`.
+ def run_servoshell(self, *, url=None):
+ # Change this setting if you want to debug Servo.
+ os.environ["RUST_LOG"] = "error,devtools=warn"
+
+ # Run servoshell.
+ if url is None:
+ url = f"{self.base_url}/test.html"
+ self.servoshell = subprocess.Popen(["target/release/servo", "--devtools=6080", url])
+
+ # FIXME: Don’t do this
+ time.sleep(1)
+
+ def tearDown(self):
+ # Terminate servoshell.
+ if self.servoshell is not None:
+ self.servoshell.terminate()
+ self.servoshell = None
+
+ # Stop the web server.
+ if self.web_server is not None:
+ self.web_server.shutdown()
+ self.web_server = None
+ if self.web_server_thread is not None:
+ self.web_server_thread.join()
+ self.web_server_thread = None
+ if self.base_url is not None:
+ self.base_url = None
+
+ def assert_sources_list(self, expected_targets: int, expected_urls_by_target: set[tuple[str]]):
+ client = RDPClient()
+ client.connect("127.0.0.1", 6080)
+ root = RootActor(client)
+ tabs = root.list_tabs()
+ tab_dict = tabs[0]
+ tab = TabActor(client, tab_dict["actor"])
+ watcher = tab.get_watcher()
+ watcher = WatcherActor(client, watcher["actor"])
+
+ done = Future()
+ targets = []
+
+ def on_target(data):
+ try:
+ targets.append(data["target"])
+ if len(targets) == expected_targets:
+ done.set_result(None)
+ except Exception as e:
+ # Raising here does nothing, for some reason.
+ # Send the exception back so it can be raised.
+ done.set_result(e)
+
+ client.add_event_listener(
+ watcher.actor_id, Events.Watcher.TARGET_AVAILABLE_FORM, on_target,
+ )
+ watcher.watch_targets(WatcherActor.Targets.FRAME)
+ watcher.watch_targets(WatcherActor.Targets.WORKER)
+
+ result: Optional[Exception] = done.result(1)
+ if result:
+ raise result
+ done = Future()
+ # NOTE: breaks if two targets have the same list of source urls.
+ # This should really be a multiset, but Python does not have multisets.
+ actual_urls_by_target: set[tuple[str]] = set()
+
+ def on_source_resource(data):
+ for [resource_type, sources] in data["array"]:
+ try:
+ self.assertEqual(resource_type, "source")
+ source_urls = tuple([source["url"] for source in sources])
+ self.assertFalse(source_urls in sources) # See NOTE above
+ actual_urls_by_target.add(source_urls)
+ if len(actual_urls_by_target) == expected_targets:
+ done.set_result(None)
+ except Exception as e:
+ # Raising here does nothing, for some reason.
+ # Send the exception back so it can be raised.
+ done.set_result(e)
+
+ for target in targets:
+ client.add_event_listener(
+ target["actor"],
+ Events.Watcher.RESOURCES_AVAILABLE_ARRAY,
+ on_source_resource,
+ )
+ watcher.watch_resources([Resources.SOURCE])
+
+ result: Optional[Exception] = done.result(1)
+ if result:
+ raise result
+ self.assertEqual(actual_urls_by_target, expected_urls_by_target)
+ client.disconnect()
+
+
+def run_tests(script_path):
+ DevtoolsTests.script_path = script_path
+ verbosity = 1 if logging.getLogger().level >= logging.WARN else 2
+ suite = unittest.TestLoader().loadTestsFromTestCase(DevtoolsTests)
+ return unittest.TextTestRunner(verbosity=verbosity).run(suite).wasSuccessful()
diff --git a/python/servo/devtools_tests/sources/classic.js b/python/servo/devtools_tests/sources/classic.js
new file mode 100644
index 00000000000..84fd1671805
--- /dev/null
+++ b/python/servo/devtools_tests/sources/classic.js
@@ -0,0 +1 @@
+console.log("external classic");
diff --git a/python/servo/devtools_tests/sources/module.js b/python/servo/devtools_tests/sources/module.js
new file mode 100644
index 00000000000..a1d0f1f37cf
--- /dev/null
+++ b/python/servo/devtools_tests/sources/module.js
@@ -0,0 +1,2 @@
+export default 1;
+console.log("external module");
diff --git a/python/servo/devtools_tests/sources/test.html b/python/servo/devtools_tests/sources/test.html
new file mode 100644
index 00000000000..b8e1aa0e334
--- /dev/null
+++ b/python/servo/devtools_tests/sources/test.html
@@ -0,0 +1,11 @@
+<!doctype html><meta charset=utf-8>
+<script src="classic.js"></script>
+<script>
+ console.log("inline classic");
+ new Worker("worker.js");
+</script>
+<script type="module">
+ import module from "./module.js";
+ console.log("inline module");
+</script>
+<script src="https://servo.org/js/load-table.js"></script>
diff --git a/python/servo/devtools_tests/sources/worker.js b/python/servo/devtools_tests/sources/worker.js
new file mode 100644
index 00000000000..a7993a8b5fb
--- /dev/null
+++ b/python/servo/devtools_tests/sources/worker.js
@@ -0,0 +1 @@
+console.log("external classic worker");
diff --git a/python/servo/testing_commands.py b/python/servo/testing_commands.py
index 860217d062a..5e5a8c2e8d2 100644
--- a/python/servo/testing_commands.py
+++ b/python/servo/testing_commands.py
@@ -19,6 +19,7 @@ import subprocess
import textwrap
import json
+import servo.devtools_tests
from servo.post_build_commands import PostBuildCommands
import wpt
import wpt.manifestupdate
@@ -326,6 +327,14 @@ class MachCommands(CommandBase):
return 0 if passed else 1
+ @Command('test-devtools',
+ description='Run tests for devtools.',
+ category='testing')
+ def test_devtools(self):
+ print("Running devtools tests...")
+ passed = servo.devtools_tests.run_tests(SCRIPT_PATH)
+ return 0 if passed else 1
+
@Command('test-wpt-failure',
description='Run the tests harness that verifies that the test failures are reported correctly',
category='testing',
diff --git a/python/servo/try_parser.py b/python/servo/try_parser.py
index 9a6b31693b0..0d78599c06f 100644
--- a/python/servo/try_parser.py
+++ b/python/servo/try_parser.py
@@ -39,11 +39,12 @@ class JobConfig(object):
unit_tests: bool = False
build_libservo: bool = False
bencher: bool = False
+ build_args: str = ""
wpt_args: str = ""
number_of_wpt_chunks: int = 20
# These are the fields that must match in between two JobConfigs for them to be able to be
# merged. If you modify any of the fields above, make sure to update this line as well.
- merge_compatibility_fields: ClassVar[List[str]] = ['workflow', 'profile', 'wpt_args']
+ merge_compatibility_fields: ClassVar[List[str]] = ['workflow', 'profile', 'wpt_args', 'build_args']
def merge(self, other: JobConfig) -> bool:
"""Try to merge another job with this job. Returns True if merging is successful
@@ -209,7 +210,8 @@ class TestParser(unittest.TestCase):
'build_libservo': False,
'workflow': 'linux',
'wpt': False,
- 'wpt_args': ''
+ 'wpt_args': '',
+ 'build_args': ''
}]
})
@@ -225,7 +227,8 @@ class TestParser(unittest.TestCase):
"unit_tests": True,
'build_libservo': False,
'bencher': True,
- "wpt_args": ""
+ "wpt_args": "",
+ 'build_args': ''
},
{
"name": "MacOS (Unit Tests)",
@@ -236,7 +239,8 @@ class TestParser(unittest.TestCase):
"unit_tests": True,
'build_libservo': False,
'bencher': False,
- "wpt_args": ""
+ "wpt_args": "",
+ 'build_args': ''
},
{
"name": "Windows (Unit Tests)",
@@ -247,7 +251,8 @@ class TestParser(unittest.TestCase):
"unit_tests": True,
'build_libservo': False,
'bencher': False,
- "wpt_args": ""
+ "wpt_args": "",
+ 'build_args': ''
},
{
"name": "Android",
@@ -258,7 +263,8 @@ class TestParser(unittest.TestCase):
"unit_tests": False,
'build_libservo': False,
'bencher': False,
- "wpt_args": ""
+ "wpt_args": "",
+ 'build_args': ''
},
{
"name": "OpenHarmony",
@@ -269,7 +275,8 @@ class TestParser(unittest.TestCase):
"unit_tests": False,
'build_libservo': False,
'bencher': False,
- "wpt_args": ""
+ "wpt_args": "",
+ 'build_args': ''
},
{
"name": "Lint",
@@ -280,7 +287,9 @@ class TestParser(unittest.TestCase):
"unit_tests": False,
'build_libservo': False,
'bencher': False,
- "wpt_args": ""}
+ "wpt_args": "",
+ 'build_args': ''
+ }
]})
def test_job_merging(self):
@@ -295,7 +304,8 @@ class TestParser(unittest.TestCase):
'build_libservo': False,
'workflow': 'linux',
'wpt': True,
- 'wpt_args': ''
+ 'wpt_args': '',
+ 'build_args': ''
}]
})
@@ -327,6 +337,11 @@ class TestParser(unittest.TestCase):
self.assertFalse(a.merge(b), "Should not merge jobs that run different WPT tests.")
self.assertEqual(a, JobConfig("Linux (Unit Tests)", Workflow.LINUX, unit_tests=True))
+ a = JobConfig("Linux (Unit Tests)", Workflow.LINUX, unit_tests=True)
+ b = JobConfig("Linux (Unit Tests)", Workflow.LINUX, unit_tests=True, build_args="--help")
+ self.assertFalse(a.merge(b), "Should not merge jobs with different build arguments.")
+ self.assertEqual(a, JobConfig("Linux (Unit Tests)", Workflow.LINUX, unit_tests=True))
+
def test_full(self):
self.assertDictEqual(json.loads(Config("full").to_json()),
json.loads(Config("").to_json()))
diff --git a/servobuild.example b/servobuild.example
index 40dd62e2421..8efc1e2907a 100644
--- a/servobuild.example
+++ b/servobuild.example
@@ -36,9 +36,6 @@ webgl-backtrace = false
# that triggered it.
dom-backtrace = false
-# Default to the “2020” implementation of CSS layout instead of the “2013” one.
-layout-2020 = true
-
# Pick a media stack based on the target. Other values are "gstreamer" and "dummy"
media-stack = "auto"
diff --git a/tests/wpt/include.ini b/tests/wpt/include.ini
index ba81ab64b83..8ea1e50b8d0 100644
--- a/tests/wpt/include.ini
+++ b/tests/wpt/include.ini
@@ -256,6 +256,8 @@ skip: true
skip: true
[writable-streams]
skip: false
+ [transform-streams]
+ skip: false
[subresource-integrity]
skip: false
[touch-events]
diff --git a/tests/wpt/meta/MANIFEST.json b/tests/wpt/meta/MANIFEST.json
index 1469a123dd5..f428e76b966 100644
--- a/tests/wpt/meta/MANIFEST.json
+++ b/tests/wpt/meta/MANIFEST.json
@@ -605,6 +605,13 @@
{}
]
],
+ "root-element-background-image-opaque-crash.html": [
+ "d380310d0968c3ad63eb6adc52acd929b3b43b14",
+ [
+ null,
+ {}
+ ]
+ ],
"root-element-filter-background-clip-text-crash.html": [
"e12e4bb80f40e09e189f7f239486be246ea67ade",
[
@@ -799,6 +806,13 @@
{}
]
],
+ "chrome-bug-415627003.html": [
+ "af942fbcc9701955f3778dc2e57521d10bb1ce41",
+ [
+ null,
+ {}
+ ]
+ ],
"pseudo-element-animation-with-marker.html": [
"ba7fc2371abfdc463f8ed36e6f1bb39fb9ef6ef5",
[
@@ -4794,6 +4808,13 @@
{}
]
],
+ "table-col-and-dead-row-group-crash.html": [
+ "e6e90a63a345fe2bb9499b729d2cf2df6ed65d9d",
+ [
+ null,
+ {}
+ ]
+ ],
"table-collapsed-borders-crash.html": [
"aa699317e2734fece3d662ad102fdd2772680e25",
[
@@ -5866,7 +5887,32 @@
null,
{}
]
- ]
+ ],
+ "root-preserve3d-crash.html": [
+ "1123280187506b326adc9378465bfc560af4415e",
+ [
+ null,
+ {}
+ ]
+ ],
+ "root-replace-crash.html": [
+ "165eec41b6bc56db131cc7fc19f23701bc291b8c",
+ [
+ null,
+ {}
+ ]
+ ],
+ "scoped": {
+ "crashtests": {
+ "shadow-dom.html": [
+ "a2faafc1694e15f5c513655e57e320325bd4f45a",
+ [
+ null,
+ {}
+ ]
+ ]
+ }
+ }
},
"css-viewport": {
"zoom": {
@@ -6114,6 +6160,13 @@
{}
]
],
+ "backdrop-filter-transform-popover-crash.html": [
+ "582f0c119656020aaa5167b0547cfbde20d11b95",
+ [
+ null,
+ {}
+ ]
+ ],
"crashtests": {
"broken-reference-crash-001.html": [
"6e18e06c317e329a58b2b5132cf17dc6086d444f",
@@ -33290,6 +33343,19 @@
{}
]
],
+ "root-margin-001-print.html": [
+ "b2ec34fe0799d7cab5ac4dacb237780b6f7503e0",
+ [
+ null,
+ [
+ [
+ "/css/css-break/root-margin-001-print-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
"table": {
"repeated-section": {
"fixedpos-in-footer-forced-break-print.html": [
@@ -38040,6 +38106,19 @@
{}
]
],
+ "webkit-box-ignores-flex-wrap.tentative.html": [
+ "98086f83570d4c5b4c6251f31093744442a907ca",
+ [
+ null,
+ [
+ [
+ "/compat/green-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
"webkit-box-rtl-flex.html": [
"d20b3a0a9dbab237a799f62504b9b1ea7b90a564",
[
@@ -124754,6 +124833,19 @@
{}
]
],
+ "justify-items-anonymous.tentative.html": [
+ "641dea1f54b2f0cea29bc4c4c82e023a479e3d3c",
+ [
+ null,
+ [
+ [
+ "/css/reference/ref-filled-green-100px-square.xht",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
"justify-self-auto-margins-1.html": [
"b54eb775114808ff22b82a858426e764a623e432",
[
@@ -159361,7 +159453,7 @@
]
],
"contain-paint-stacking-context-001a.html": [
- "71102b6c73a3d88cdd953ecf82c69602ff87baa2",
+ "5e588cb61742ca406b23f573dd3609a52a191a1e",
[
null,
[
@@ -159374,7 +159466,7 @@
]
],
"contain-paint-stacking-context-001b.html": [
- "0c4d3323bf7cb28b5a31de8aac41563013b2ecd4",
+ "e03323782a7fbfbb924184fa71b0ab1031381671",
[
null,
[
@@ -167131,6 +167223,19 @@
{}
]
],
+ "display-contents-inline-002.html": [
+ "f40a34129f348aca37fca83f70598053cf22785b",
+ [
+ null,
+ [
+ [
+ "/css/css-display/display-contents-pass-no-red-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
"display-contents-inline-flex-001.html": [
"43b502731aefbaa348671e32171f0e1eb4bca04b",
[
@@ -184678,6 +184783,19 @@
{}
]
],
+ "font-variant-emoji-005.html": [
+ "0f5c2d7a98a615323b1adc0babb479fdb738537d",
+ [
+ null,
+ [
+ [
+ "/css/css-fonts/font-variant-emoji-005-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
"font-variant-emoji-1.html": [
"53cf539edd8b61a7f08f8b95ae83f4f7a5179308",
[
@@ -186484,6 +186602,19 @@
],
{}
]
+ ],
+ "flex-gap-decorations-022.html": [
+ "5f3b512ef76214b3645c329c92bd06ebf23efd9d",
+ [
+ null,
+ [
+ [
+ "/css/css-gaps/agnostic/gap-decorations-002-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
]
},
"grid": {
@@ -186837,6 +186968,84 @@
],
{}
]
+ ],
+ "grid-gap-decorations-029.html": [
+ "6da75548d94b5e17e1ad7ba1011662c42b818d83",
+ [
+ null,
+ [
+ [
+ "/css/css-gaps/grid/grid-gap-decorations-029-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "grid-gap-decorations-030.html": [
+ "ef4507ef92638eeff660e67e2024bbce323c1aa8",
+ [
+ null,
+ [
+ [
+ "/css/css-gaps/grid/grid-gap-decorations-030-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "grid-gap-decorations-031.html": [
+ "530b661bfe32c5af7687954f3f4b2cbdfa61380a",
+ [
+ null,
+ [
+ [
+ "/css/css-gaps/grid/grid-gap-decorations-031-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "grid-gap-decorations-032.html": [
+ "ac2d38fdda0ec6a1f4a3b7b091d03c896e3e5e47",
+ [
+ null,
+ [
+ [
+ "/css/css-gaps/grid/grid-gap-decorations-032-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "grid-gap-decorations-033.html": [
+ "47ef35cc3688b2fcd2329558b1d0a27880983b38",
+ [
+ null,
+ [
+ [
+ "/css/css-gaps/grid/grid-gap-decorations-033-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "grid-gap-decorations-38.html": [
+ "8b87bcd4860ac50a1c2f3de98cec9599bebb77a3",
+ [
+ null,
+ [
+ [
+ "/css/css-gaps/agnostic/gap-decorations-002-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
]
},
"multicol": {
@@ -187047,6 +187256,19 @@
],
{}
]
+ ],
+ "multicol-gap-decorations-017.html": [
+ "c7d1fe42584f77e77acd3676f249ffa6510ada3b",
+ [
+ null,
+ [
+ [
+ "/css/css-gaps/multicol/multicol-gap-decorations-017-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
]
}
},
@@ -201929,6 +202151,45 @@
{}
]
],
+ "linear-gradient-body-sibling-index.html": [
+ "299b86d464ad54f383ce8f384ec6677b3d3e305b",
+ [
+ null,
+ [
+ [
+ "/css/css-images/linear-gradient-body-sibling-index-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "linear-gradient-calc-em-units.html": [
+ "e764e62890dad81adfda9d60d50d82a1e07c5de5",
+ [
+ null,
+ [
+ [
+ "/css/css-images/linear-gradient-calc-em-units-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
+ "linear-gradient-sibling-index.html": [
+ "b92897e0697441df6876b88e53daa148a5edb009",
+ [
+ null,
+ [
+ [
+ "/css/css-images/linear-gradient-sibling-index-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
"multiple-position-color-stop-conic-2.html": [
"f3171bf3fb9823095312ab080ee3d0481a8545f6",
[
@@ -256915,6 +257176,19 @@
{}
]
],
+ "letter-spacing-cursive-001.html": [
+ "5700c600981ab815e2ecf33577a6408d8e54dee1",
+ [
+ null,
+ [
+ [
+ "/css/css-text/letter-spacing/reference/letter-spacing-cursive-001-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
"letter-spacing-end-of-line-001.html": [
"645ef10509803cb86b31c7250928c007acb21431",
[
@@ -277158,6 +277432,35 @@
{}
]
],
+ "css-scale-of-clip-path.html": [
+ "dda72c0d139979213741174da8c9572f1237bcc3",
+ [
+ null,
+ [
+ [
+ "/css/css-transforms/css-scale-of-clip-path-ref.html",
+ "=="
+ ]
+ ],
+ {
+ "fuzzy": [
+ [
+ null,
+ [
+ [
+ 0,
+ 40
+ ],
+ [
+ 0,
+ 3000
+ ]
+ ]
+ ]
+ ]
+ }
+ ]
+ ],
"css-skew-001.html": [
"f209834121bb00ff3542029d44ba1ce878b0dd77",
[
@@ -281127,6 +281430,35 @@
{}
]
],
+ "scale-transform-filtered-text.html": [
+ "a479b12528a0bb6903d58d46a4b2a0d073b9ee97",
+ [
+ null,
+ [
+ [
+ "/css/css-transforms/scale-transform-filtered-text-ref.html",
+ "=="
+ ]
+ ],
+ {
+ "fuzzy": [
+ [
+ null,
+ [
+ [
+ 0,
+ 25
+ ],
+ [
+ 0,
+ 500
+ ]
+ ]
+ ]
+ ]
+ }
+ ]
+ ],
"scale-transform-overlap.html": [
"0cb8db40b66accf337babe82d281d0a77c0ffa0d",
[
@@ -328573,6 +328905,19 @@
{}
]
],
+ "backdrop-filter-scale-transform.html": [
+ "813fb95d5ad3b5e4844e480980b4c712d6b8f28d",
+ [
+ null,
+ [
+ [
+ "/css/filter-effects/backdrop-filter-scale-transform-ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
"backdrop-filter-svg-background-image-blur.html": [
"9cd18b0542c86911b3a0b774fe892a61102c8b85",
[
@@ -356396,7 +356741,7 @@
"the-button-element": {
"interest-target": {
"interesttarget-keyboard-invalidation.tentative.html": [
- "8c0ebd7a2cdcbab16e13618dbd5b8e36c58359f8",
+ "b6d0ff10932f1717fb038efb28c42c2bbf57cbf8",
[
null,
[
@@ -356410,19 +356755,6 @@
}
]
],
- "interesttarget-outline-appearance.tentative.html": [
- "28cb2052a8d430b5dfadd31171b17d32a567d073",
- [
- null,
- [
- [
- "/html/semantics/the-button-element/interest-target/interesttarget-outline-appearance-ref.html",
- "!="
- ]
- ],
- {}
- ]
- ],
"interesttarget-plain-inline-element.tentative.html": [
"68ce00193f70c021cc392e0e2460662d7eb38ad9",
[
@@ -372429,6 +372761,14 @@
[]
]
}
+ },
+ "translator": {
+ "resources": {
+ "util.js": [
+ "ad06086a123bccbe2c852dffd1a36ffde0613ada",
+ []
+ ]
+ }
}
},
"ambient-light": {
@@ -374690,6 +375030,10 @@
"840e187cbad1cca4ea09e144c88df2a14032136a",
[]
],
+ "WEB_FEATURES.yml": [
+ "3c4f69200f67b3cd856f56913025c14de17a0e73",
+ []
+ ],
"resources": {
"setAppBadge_iframe.html": [
"8738099658fbb2de893658e70242e855ecccd4b1",
@@ -375026,6 +375370,10 @@
"65ca96dbb9d774e97cc3a60f6b6dde952104893b",
[]
],
+ "WEB_FEATURES.yml": [
+ "d55b5faaf11be9aa92df01f814d5ce165b23025f",
+ []
+ ],
"support": {
"clear-cache-helper.sub.js": [
"5db0caf834cbf6a598f02761beda267dc8f1bcf8",
@@ -417931,6 +418279,10 @@
"8c19f1cf4c6c0173b45face28d624368d365ec70",
[]
],
+ "root-margin-001-print-ref.html": [
+ "320da2e0f551b82e2e699500179fe06db612151b",
+ []
+ ],
"rounded-clipped-border-ref.html": [
"be08126fd2c3bab356a3a4fd5ff1cc1579184662",
[]
@@ -419214,7 +419566,7 @@
[]
],
"contain-paint-stacking-context-001-ref.html": [
- "c7553716ab6c257bb0d9407582eed186bd763369",
+ "e2516f3d3788fd7f28356d266ee73e6b7ec0a4e3",
[]
],
"contain-size-block-001-ref.html": [
@@ -424631,6 +424983,10 @@
"633a94c4e51f32328bc68eeb74837aada6f4aff0",
[]
],
+ "font-variant-emoji-005-ref.html": [
+ "d4275b5c951c05ea270f1a2a474ff4ccaf9afc4a",
+ []
+ ],
"font-variant-emoji-1-notref.html": [
"bbf3654ccfcdd415fe5a28468e3329ba94ce63da",
[]
@@ -431505,6 +431861,10 @@
"gap-decorations-001-ref.html": [
"a7314fc7dca1545dacd710c261c7594117a3894a",
[]
+ ],
+ "gap-decorations-002-ref.html": [
+ "42c5ef128b47fc9cdd14c9ce4f8abdffb209d94e",
+ []
]
},
"flex": {
@@ -431669,6 +432029,26 @@
"grid-gap-decorations-027-ref.html": [
"7e0d9cbcd6ff64b17bc8fe57616a37ba29a9fd8e",
[]
+ ],
+ "grid-gap-decorations-029-ref.html": [
+ "35450e07ae5579707c94b4a0a58f29eb83d7f7fa",
+ []
+ ],
+ "grid-gap-decorations-030-ref.html": [
+ "d4953ae2ddda701324d350ccf25a113b8e67fc37",
+ []
+ ],
+ "grid-gap-decorations-031-ref.html": [
+ "0615305bc9f8de791f1c2061ad6d90039932b157",
+ []
+ ],
+ "grid-gap-decorations-032-ref.html": [
+ "f90e3dd68132078e2557f33b7e32605d278cd0eb",
+ []
+ ],
+ "grid-gap-decorations-033-ref.html": [
+ "e26143a6b93e9fdd53d4055234d89e40d98dc2af",
+ []
]
},
"multicol": {
@@ -431735,6 +432115,10 @@
"multicol-gap-decorations-016-ref.html": [
"8b2458fccce5a190c2c804b7e552dc0d85a8849a",
[]
+ ],
+ "multicol-gap-decorations-017-ref.html": [
+ "23fd089ac8e575e4035c5614c10ae55d7c3b06a4",
+ []
]
}
},
@@ -434305,10 +434689,22 @@
"a80236dcf75c22ecd8e43935d004f1fddf0550cb",
[]
],
+ "linear-gradient-body-sibling-index-ref.html": [
+ "7325685511a542964859c34a359b107823db18df",
+ []
+ ],
+ "linear-gradient-calc-em-units-ref.html": [
+ "be13be8e260ef8d89be878d811edf83411dd2cee",
+ []
+ ],
"linear-gradient-ref.html": [
"033f1767799d718f072b48b6a99a4eaf7070f90e",
[]
],
+ "linear-gradient-sibling-index-ref.html": [
+ "eea8518d95b9393a5c1e1d34afc20744b8834c34",
+ []
+ ],
"multiple-position-color-stop-conic-2-ref.html": [
"2ee92a11ee43a60031302ecb24b5a1e2865590ec",
[]
@@ -439016,7 +439412,7 @@
[]
],
"scroll-buttons-appearance-ref.html": [
- "462186407d55da46e14f5ebbc8f90218cd0b80b7",
+ "c382f738a6fc01402668747529ce7ab7b46f5f4f",
[]
],
"scroll-buttons-disabled-ref.html": [
@@ -445745,6 +446141,10 @@
"977d8211457a6e7b4976b596be91068cdf518869",
[]
],
+ "letter-spacing-cursive-001-ref.html": [
+ "720c9491b4d8cfdba5c2279dacdc297d495c2b67",
+ []
+ ],
"letter-spacing-end-of-line-001-ref.html": [
"eb474ab8d234d0836c3466e29bfe9b313e10d302",
[]
@@ -449499,6 +449899,10 @@
"54940566cd107ba443d3183dccecb505a811d14e",
[]
],
+ "css-scale-of-clip-path-ref.html": [
+ "d6fa480b0408ffc9c3687515e25a30ddc68f199f",
+ []
+ ],
"css-skew-001-ref.html": [
"70c256ebec653128a2d91555e817b54880666a35",
[]
@@ -449949,6 +450353,10 @@
]
}
},
+ "scale-transform-filtered-text-ref.html": [
+ "e8b564ce3ddd6560299d91f347b4407fe6a5db6b",
+ []
+ ],
"scale-transform-overlap-ref.html": [
"4750764ae93092e7b7e9f7d151eb1b7904c32ca8",
[]
@@ -457138,6 +457546,10 @@
"f8947e0472f489ca1a98a9e231b17e7d8ce6585e",
[]
],
+ "scrollIntoView-frame.html": [
+ "ee2be2fd3ab04ad9a1bde48ee188789d92d9ca8a",
+ []
+ ],
"simultaneousScrollIntoViews.js": [
"32de35fb544c5a83b0c8159de21f9a6ebfed19ba",
[]
@@ -457465,6 +457877,10 @@
"c92741df4300e5416cbe26339d52b819ec35347c",
[]
],
+ "backdrop-filter-scale-transform-ref.html": [
+ "d7faa0e97a175fc5dabf6f2d89ce71ba0fd498e1",
+ []
+ ],
"backdrop-filter-transform-ref.html": [
"1af8776cf0dc663ec7bac1f83ba361ccb8b2b5d4",
[]
@@ -458240,7 +458656,7 @@
[]
],
"WEB_FEATURES.yml": [
- "24027360aab4a27b72975f0d77062ffe8fb1a060",
+ "7f15b7fa475230b42981a3f92f60f434e9ccf1af",
[]
],
"prefers-color-scheme-svg-as-image-ref.html": [
@@ -460304,7 +460720,7 @@
[]
],
"testdriver.md": [
- "67757af63f2ed6af4026577dd35000c183509d44",
+ "e0fae529875ee89d5e8a2457cdab35e1866f663b",
[]
],
"testharness-api.md": [
@@ -460874,6 +461290,12 @@
]
}
},
+ "observable": {
+ "WEB_FEATURES.yml": [
+ "3e872791165e41402949da13fedd7a8780aaf660",
+ []
+ ]
+ },
"parts": {
"resources": {
"domparts-utils.js": [
@@ -461338,7 +461760,7 @@
[]
],
"element-timing-helpers.js": [
- "2c1bd195ea76f8126d739e705fed7af3a562fc45",
+ "5ff01bfa1209a26d84d69bb5a80d7fd21d671fab",
[]
],
"iframe-stores-entry.html": [
@@ -462656,7 +463078,7 @@
[]
],
"event-timing-test-utils.js": [
- "59c78ebf39c8a8e0f31aafa1eccd0809bea54376",
+ "afad67bb6fc2d90e02bf7b40633be891bb2fe3d0",
[]
],
"slow-image.py": [
@@ -464352,6 +464774,10 @@
]
},
"request": {
+ "WEB_FEATURES.yml": [
+ "69b2ea582a2281403c4899e122343262e43976db",
+ []
+ ],
"destination": {
"resources": {
"dummy": [
@@ -465226,6 +465652,10 @@
[]
],
"resources": {
+ "cached_pragma_rand.py": [
+ "18c7d25159fa3041b8aa278c8ac022003eee0e36",
+ []
+ ],
"http-cache.py": [
"3ab610dd1421cea5289ad14a8ef472ade27f07f1",
[]
@@ -465244,6 +465674,30 @@
]
}
},
+ "local-network-access": {
+ "META.yml": [
+ "4c5c6983ed0467f3ebca3182a6b5d095270f85d2",
+ []
+ ],
+ "README.md": [
+ "95066cdcd0ba6f2baed3cd02d67610420367b6df",
+ []
+ ],
+ "resources": {
+ "fetch-private.html": [
+ "b96a207ec33a13e5dd4c53083ac3d73123b23cbb",
+ []
+ ],
+ "support.sub.js": [
+ "774e34d0a6fe59bab19aca14cb71b6e520acb798",
+ []
+ ],
+ "target.py": [
+ "eabcdd47517c8938d12cd08a0d66f0db2e518456",
+ []
+ ]
+ }
+ },
"metadata": {
"META.yml": [
"85f0a7d2ee12616aa7a5698b346954829d8a8bb0",
@@ -465865,7 +466319,7 @@
[]
],
"WEB_FEATURES.yml": [
- "45e40acba1dcb6b4abe11d1478c682264a4c1e90",
+ "7faf67952b5b82107a2797706e41dfdc780ef041",
[]
],
"resources": {
@@ -471334,6 +471788,10 @@
[]
],
"wide-gamut-canvas": {
+ "WEB_FEATURES.yml": [
+ "b4d11212a6c677339784cdab33e0e31b0b948379",
+ []
+ ],
"canvas-display-p3.js": [
"c6ee97b7880a4ac2333b82b60407102045d1f509",
[]
@@ -471696,6 +472154,10 @@
"2d.path.stroke.overlap.png": [
"e2a35d48d4c4363294aec671a38cbd4b39c9a53c",
[]
+ ],
+ "WEB_FEATURES.yml": [
+ "1bbef5e88d6630d820e2206f283ac38cffceb67d",
+ []
]
},
"pixel-manipulation": {
@@ -471879,6 +472341,12 @@
[]
]
}
+ },
+ "wide-gamut-canvas": {
+ "WEB_FEATURES.yml": [
+ "b4d11212a6c677339784cdab33e0e31b0b948379",
+ []
+ ]
}
},
"offscreen": {
@@ -472358,8 +472826,20 @@
"fc7265a48c393566ad8c9fe7b73281e4f5137b21",
[]
]
+ },
+ "wide-gamut-canvas": {
+ "WEB_FEATURES.yml": [
+ "b4d11212a6c677339784cdab33e0e31b0b948379",
+ []
+ ]
}
},
+ "path-objects": {
+ "WEB_FEATURES.yml": [
+ "1bbef5e88d6630d820e2206f283ac38cffceb67d",
+ []
+ ]
+ },
"reset": {
"2d.reset.after-rasterization-expected.html": [
"3162e16e63d9fa5cc495401ca53d6c0242d3ee65",
@@ -472471,6 +472951,12 @@
"00ecdccad3743db0d0a1ec99850f80ea7964d512",
[]
]
+ },
+ "wide-gamut-canvas": {
+ "WEB_FEATURES.yml": [
+ "b4d11212a6c677339784cdab33e0e31b0b948379",
+ []
+ ]
}
},
"resources": {
@@ -474038,7 +474524,7 @@
[]
],
"reflection.js": [
- "b2c3b30aae36b390a60c05b39901826ba71e0b1a",
+ "eeecd450fca8139e924affb298e7feb1a1fb46fb",
[]
],
"render-blocking": {
@@ -477418,6 +477904,14 @@
}
}
},
+ "embedded-content": {
+ "the-img-element": {
+ "WEB_FEATURES.yml": [
+ "5716baa5def9bd2af6813751aa7a93ffba64c4cd",
+ []
+ ]
+ }
+ },
"iana": {
"application-x-www-form-urlencoded": {
"original-id.json": [
@@ -478970,6 +479464,10 @@
},
"scripting": {
"the-script-element": {
+ "WEB_FEATURES.yml": [
+ "864bd3398ee21fb62665a2d5c259a52acdd04232",
+ []
+ ],
"resources": {
"script.js": [
"e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
@@ -482984,11 +483482,7 @@
},
"interest-target": {
"interesttarget-keyboard-invalidation-ref.html": [
- "0ba07b16f497203f231d03cc7908453c6ccc43c4",
- []
- ],
- "interesttarget-outline-appearance-ref.html": [
- "33b0cf668587f182caf87dfb98665a9301fca19a",
+ "9d520943d5f542a2aeb45ac6cb1417eb20c25477",
[]
],
"resources": {
@@ -483000,6 +483494,10 @@
}
},
"the-link-element": {
+ "WEB_FEATURES.yml": [
+ "6a444ee171ad29c442d573fb2131b544820b6cad",
+ []
+ ],
"resources": {
"stylesheet.css": [
"9d9d772fb468756d1a90f72325f89cd372f812ef",
@@ -486735,7 +487233,7 @@
[]
],
"largest-contentful-paint.idl": [
- "872ba552b0d3b0398be533beb578fdedd260baea",
+ "d1630cc7daae802bf27107ce0ab07185d0746d7f",
[]
],
"layout-instability.idl": [
@@ -487059,7 +487557,7 @@
[]
],
"service-workers.idl": [
- "d9ff2f651f8235968dd29bd6d7f86e9e8fdcb4cf",
+ "34af3372401eed53328b8bc5e7ea42b87ddd9b20",
[]
],
"shape-detection-api.idl": [
@@ -487211,7 +487709,7 @@
[]
],
"webauthn.idl": [
- "a33c85e7bad86753211fa7aa9270abac18b1e54e",
+ "7fbe55e67652b3fd35079d1a06423bb441657bd5",
[]
],
"webcodecs-aac-codec-registration.idl": [
@@ -487702,7 +488200,7 @@
[]
],
"largest-contentful-paint-helpers.js": [
- "a01080c00881d7b66a80886e34367624913e1d6f",
+ "99916f5c5d31110d83c91d4ce346890efb494b12",
[]
],
"lcp-sw-from-cache.js": [
@@ -491118,7 +491616,7 @@
[]
],
"utils.js": [
- "ed58c957c97132276bb813603e1d25133cf0d860",
+ "41c76d6826658c0f73301f70ef0ae08cf84572e4",
[]
]
}
@@ -494280,6 +494778,10 @@
"da9bbc387f41ab069c27edc055962f72036b96f2",
[]
],
+ "WEB_FEATURES.yml": [
+ "f59321971a7668545949ee3212e54420e424c7ae",
+ []
+ ],
"prepare-device.js": [
"a12dbaf28717cf3776d9e34f5a12e4adaa7bae18",
[]
@@ -494388,7 +494890,7 @@
[]
],
"report-helper.js": [
- "5b5438903de11c346ac3901a9de81a96e4446386",
+ "216da22eae8c0ccde223c70a110a7a892676c14f",
[]
],
"report.py": [
@@ -495204,7 +495706,7 @@
[]
],
"api-tests-1.html": [
- "9de875b0f127776e0d60df68075402d32c195749",
+ "6ec396c221b98f282b46a366e99a76bd9daef492",
[]
],
"api-tests-2.html": [
@@ -495589,7 +496091,7 @@
[]
],
"testdriver.js": [
- "6e8410b7ea46064438c8d276274b6fbb689f81d2",
+ "992b9e3ab2ce0b63b94be2a5aa671b321f65e334",
[]
],
"testdriver.js.headers": [
@@ -495609,7 +496111,7 @@
[]
],
"testharness.js": [
- "d1d9d61eab890f2090e06a286aa76aedc98db7c7",
+ "6ccede34483b227cc41fcb15235e56aa60d4022e",
[]
],
"testharness.js.headers": [
@@ -499883,7 +500385,7 @@
[]
],
"basic-service-worker.js": [
- "17fccd448d2412287515f6176b0731189eb8eede",
+ "59d7d8bae9ee4d46d8a0bb12bcc3377d041280f4",
[]
],
"conditional-status.py": [
@@ -499895,11 +500397,11 @@
[]
],
"counting-executor.py": [
- "cbcbc8eccb9bd8cf536c56cf576a4b5ea73ca44a",
+ "3511fe4905e2d8962175f0e4bc318a13c41c35f5",
[]
],
"executor.sub.html": [
- "d27acfe100a6a3567606bd64df0b8cd1c5bdbfc1",
+ "975a3e5092ed149b687f34ba6b9c6d5af11432ea",
[]
],
"executor.sub.html.headers": [
@@ -500875,14 +501377,6 @@
[]
],
"animations": {
- "reftests": {
- "reference": {
- "green-100x100.svg": [
- "120941444a4898197d6b6001f9908a6cd48b62ba",
- []
- ]
- }
- },
"support": {
"animated-path-helpers.js": [
"8fd4492265c2eb8aa00fb173bd8d625f119adaac",
@@ -500927,6 +501421,10 @@
}
},
"embedded": {
+ "WEB_FEATURES.yml": [
+ "8482e555584bf205a6afa11836b35d046976e0df",
+ []
+ ],
"image-embedding-svg-with-near-integral-width-ref.html": [
"655d982b16ce0582f6b83e26844b2a6e2437b17b",
[]
@@ -501765,6 +502263,10 @@
}
},
"scripted": {
+ "WEB_FEATURES.yml": [
+ "864bd3398ee21fb62665a2d5c259a52acdd04232",
+ []
+ ],
"script-style-attribute-csp-ref.html": [
"a7316213f13a57fa1c3ab5c058bc9d21f14d5055",
[]
@@ -513355,7 +513857,7 @@
[]
],
"emulation.py": [
- "e587a082c037d4fdcd72441616a3800d341c981a",
+ "fd5cf733ab761639493df402268429f5ab09f528",
[]
],
"input.py": [
@@ -513714,7 +514216,7 @@
[]
],
"android_webview.py": [
- "0e02d3c02cd5456d62401423e7bb7581d70dc32f",
+ "c796be18a734cfbb33fd73b0d99a7c30512c8dd6",
[]
],
"base.py": [
@@ -513726,7 +514228,7 @@
[]
],
"chrome_android.py": [
- "820323e615ad57d89df7ccdee68a4587b8acc6fc",
+ "4ed7707c3bed88e2f5061a331e49683db8a4f583",
[]
],
"chrome_ios.py": [
@@ -513750,7 +514252,7 @@
[]
],
"firefox.py": [
- "494a7514efda2f2607c7394bca5ff9ddb19adf51",
+ "c63bfa2ceebb281e01e49a79aaf6cdeb2bbd92e5",
[]
],
"firefox_android.py": [
@@ -513826,7 +514328,7 @@
[]
],
"asyncactions.py": [
- "9925a4b511def24fda82522acb5c5f0f9f3e80a1",
+ "8397d7838a3d6bd45e28dc613322d00ff8f2aebb",
[]
],
"base.py": [
@@ -513858,7 +514360,7 @@
[]
],
"executorwebdriver.py": [
- "87403c2944d57e31c2612871262af9b77ce73235",
+ "7ca46a05a7b0f2bc3da65512fdee57080946b7d0",
[]
],
"executorwktr.py": [
@@ -513874,7 +514376,7 @@
[]
],
"protocol.py": [
- "833dff45636c7e22697e47cfe692ebecfcf2d76f",
+ "16eb3cbb4a528ac9e4fc4897aa36f2a58e6d68ec",
[]
],
"pytestrunner": {
@@ -513929,18 +514431,10 @@
"e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
[]
],
- "chromium.py": [
- "95f53011bfc32de47a93e0bb7d2ac99f84be4e40",
- []
- ],
"tests": {
"__init__.py": [
"e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
[]
- ],
- "test_chromium.py": [
- "bf815d5dc761b869f89ec71fdec66c395b453fbd",
- []
]
},
"wptreport.py": [
@@ -513989,7 +514483,7 @@
[]
],
"testdriver-extra.js": [
- "ab8b04ba3cdfc3d7c351ce99c6289de6e99b8f3e",
+ "3c2dd8b42dd5b572841b5b5529b42a17f724cef9",
[]
],
"testdriver-vendor.js": [
@@ -514017,7 +514511,7 @@
[]
],
"testrunner.py": [
- "64668fc470b730ecc664c06c0c5412a657f3d2da",
+ "2c2da790e901f8e56f5f5f77673a281530c53294",
[]
],
"tests": {
@@ -514131,7 +514625,7 @@
[]
],
"wptcommandline.py": [
- "647cc7f21b1e2f35f9111650cb6eba2dce5376a4",
+ "7bfeef317170f2032152df46d9f976a1a82cbc90",
[]
],
"wptlogging.py": [
@@ -516425,6 +516919,10 @@
"afa5e4eaf032e0ad26e730615d633ce498a73c12",
[]
],
+ "simd_select.wast.js": [
+ "e997d81f75abc7ef0ba8e6e78e5435e3bd63e13c",
+ []
+ ],
"simd_splat.wast.js": [
"3ecdc10e2791eda422bb2fa657386ea476951592",
[]
@@ -518417,7 +518915,7 @@
[]
],
"__init__.py": [
- "955335ea87e7fc427104a9a4eab5ba7c9e338558",
+ "587dc91a9387d3047fa56fa85ad80612637b24fd",
[]
],
"browser": {
@@ -518641,11 +519139,11 @@
],
"set_geolocation_override": {
"__init__.py": [
- "33dcca90f047b58e9026a104b99f537678191666",
+ "5e1fd1aa8637529a20b48356fe20e4f970b8428c",
[]
],
"conftest.py": [
- "a90895c74ed90c04b5f181af22c3bddec9ac132d",
+ "5fb9451f0a23821bbd7d25ac59efeb928f84b5ac",
[]
]
}
@@ -519964,7 +520462,7 @@
},
"resources": {
"utils.js": [
- "50d7911a9181a5139d9e16f7c866112dc54d6d01",
+ "9d5cfc70c10187743807096421975a099f22afc9",
[]
],
"utils_validation.js": [
@@ -520184,7 +520682,7 @@
[]
],
"RTCPeerConnection-insertable-streams.js": [
- "0bf820acde48058711163708b05d02e89501546b",
+ "f3873e1de4b12aa668f711dfbbf41cb9b2d0ac66",
[]
],
"RTCPeerConnection-sender-worker-single-frame.js": [
@@ -529018,7 +529516,7 @@
]
],
"idbcursor-request-source.any.js": [
- "2fe8c66f2e590b170a1456648e1dca74077464d8",
+ "8e1b34ee798a69c3d61386c5ae1f9adaeba6f21a",
[
"IndexedDB/idbcursor-request-source.any.html",
{
@@ -549735,7 +550233,7 @@
]
],
"idlharness.https.any.js": [
- "ae65eb49f2120e1a6d7222a7c0ecc9836c4ff5a8",
+ "5ddf7eab6dba2116d0d3bbc6826b176463107eb0",
[
"WebCryptoAPI/idlharness.https.any.html",
{
@@ -551569,10 +552067,10 @@
]
},
"translator": {
- "translator-bad-input.tentative.https.window.js": [
- "53a184bfd196f6006e7df9c908518ba47deacda6",
+ "translator-bad-input.https.window.js": [
+ "db8905a61f660f8dc0aa7666c044794d22683799",
[
- "ai/translator/translator-bad-input.tentative.https.window.html",
+ "ai/translator/translator-bad-input.https.window.html",
{
"script_metadata": [
[
@@ -551592,15 +552090,15 @@
}
]
],
- "translator-translate.tentative.https.window.js": [
- "a8aad5e03e1f46db43a47966d66f7aecc526b255",
+ "translator.optional.https.window.js": [
+ "96eca09d28bd61ccc4d31b746216f1f252d0bf3b",
[
- "ai/translator/translator-translate.tentative.https.window.html",
+ "ai/translator/translator.optional.https.window.html",
{
"script_metadata": [
[
"title",
- "Translate from English to Japanese"
+ "Translator Translate"
],
[
"global",
@@ -551621,6 +552119,10 @@
[
"script",
"/resources/testdriver.js"
+ ],
+ [
+ "script",
+ "resources/util.js"
]
],
"timeout": "long"
@@ -569742,7 +570244,7 @@
]
],
"icon-blocked.sub.html": [
- "cc882347a1ac7b595275c2263ef266826e6f07bd",
+ "4c39e5dec735c10635a603356367610d3aad3fa2",
[
null,
{}
@@ -569797,6 +570299,13 @@
{}
]
],
+ "img-src-targeting.html": [
+ "3b4fe7c690b0b980a9626de0deb02c8950f5d4a0",
+ [
+ null,
+ {}
+ ]
+ ],
"img-src-wildcard-allowed.html": [
"050a4d14100bb1ef719d6700bfbd37a97424af59",
[
@@ -571859,6 +572368,13 @@
{}
]
],
+ "style-src-inline-style-with-csstext.html": [
+ "5e812b4aee9d0d081673a0f333f8b29187619c3d",
+ [
+ null,
+ {}
+ ]
+ ],
"style-src-multiple-policies-multiple-hashing-algorithms.html": [
"027c61d8c632f2387408b8fb6869dee69bb8913d",
[
@@ -573010,7 +573526,7 @@
]
],
"cookieListItem_attributes.https.any.js": [
- "6716d91788db746765593a2a95d34e1ecbb4e3a5",
+ "542bd6c53870b5baaa9ac0f75c2235079b7bfe88",
[
"cookie-store/cookieListItem_attributes.https.any.html",
{
@@ -573764,7 +574280,7 @@
]
],
"httponly_cookies.https.window.js": [
- "8a10e358ef6de72d5476ae8dc8a571482881d7ef",
+ "605e94e67440aaedbcaa39f185270fe77b316ad3",
[
"cookie-store/httponly_cookies.https.window.html",
{
@@ -577587,7 +578103,7 @@
]
],
"anchor-position-grid-001.html": [
- "92fb4d275b8988641ed0736969e918703e4d649d",
+ "7bd389b39d8d6642f02181e475bbc5eaa7322f2d",
[
null,
{}
@@ -577629,7 +578145,7 @@
]
],
"anchor-position-multicol-002.html": [
- "7b2691a2b904ffb7fdc3d720b07ca454e1df7fe1",
+ "1e1f0a5c95b1b5f1d86efc5f22ffc87afbbab453",
[
null,
{}
@@ -577643,7 +578159,7 @@
]
],
"anchor-position-multicol-004.html": [
- "399494120ea0ea5da5534a0a14a9b99e598222d5",
+ "8f7a3dad12915ddbea9bd88077694434d0a17b48",
[
null,
{}
@@ -577685,7 +578201,7 @@
]
],
"anchor-position-multicol-nested-001.html": [
- "35ab2cfc15cea1fa1788f9b4cb09f9af39d079ec",
+ "c5ce41299af29008356b7a049a3d479038379fae",
[
null,
{}
@@ -581618,7 +582134,7 @@
],
"table": {
"border-spacing.html": [
- "fc5e87e35dde4b4cb2ed5c457f5cd22ec73d96b0",
+ "9fd94760681f0186f86f9a55da300250499f2a50",
[
null,
{}
@@ -581662,7 +582178,7 @@
]
],
"table-parts-offsets-vertical-rl.tentative.html": [
- "9d4a472d4382825c13af35114acc48a3b5face2a",
+ "1eb751032ca4b7598ee3a413408c61c5a592b3e4",
[
null,
{}
@@ -581839,6 +582355,13 @@
{}
]
],
+ "inline-style-background.html": [
+ "11451f8cefd3e725b017b263dded4bf9a806d785",
+ [
+ null,
+ {}
+ ]
+ ],
"layer-basic.html": [
"e214bffc25f5fc3b56480ec9c8704a80bf109c17",
[
@@ -586149,6 +586672,20 @@
{}
]
],
+ "flex-container-max-content-002.tentative.html": [
+ "77a074b153a3b3fcb47c5665bab614c1720a73d2",
+ [
+ null,
+ {}
+ ]
+ ],
+ "flex-container-min-content-002.tentative.html": [
+ "92d37e3b9d8db7b57b813b49c26a3e9cd03b5179",
+ [
+ null,
+ {}
+ ]
+ ],
"flex-direction-column-overlap-001.html": [
"4d483a44fc630e46ddbe75ca79d8246eed67a94d",
[
@@ -588881,6 +589418,13 @@
{}
]
],
+ "gap-decorations-col-rule-width.html": [
+ "db7b97e74a58f30a52b22b68a92475f48f865ed7",
+ [
+ null,
+ {}
+ ]
+ ],
"gap-decorations-color-computed.html": [
"fbc3c3f4c976b344d48a9bd927547214f072e3ab",
[
@@ -595009,8 +595553,8 @@
{}
]
],
- "dashed-function-cycles.tentative.html": [
- "11e653e9b7d02d944e4bd74bda6e08e838c4ea42",
+ "dashed-function-cycles.html": [
+ "15305be2b287ca3e4fbfad928f8d101ed8463104",
[
null,
{}
@@ -595244,6 +595788,34 @@
{}
]
],
+ "getclientrects-005.html": [
+ "ee60a607dccb3fdba2ed7dc99cb344071f5bb34a",
+ [
+ null,
+ {}
+ ]
+ ],
+ "getclientrects-006.html": [
+ "9f07a7de7444de7a2f91f97ff0a4fe44b7e177af",
+ [
+ null,
+ {}
+ ]
+ ],
+ "getclientrects-007.html": [
+ "0791c5b931fe3fade4c7b4483ef2469435ffbd77",
+ [
+ null,
+ {}
+ ]
+ ],
+ "getclientrects-008.html": [
+ "21505a7e48a2d2f9947342329de19dfde974d8e5",
+ [
+ null,
+ {}
+ ]
+ ],
"going-out-of-flow-after-spanner.html": [
"2fe0e42a7522647b7eca4db1e6f710a9975faaa4",
[
@@ -596318,6 +596890,24 @@
}
]
],
+ "scroll-button-activation-without-scroller.html": [
+ "f5951efc419d62a28f661c4f7d93cd9654b42616",
+ [
+ null,
+ {
+ "testdriver": true
+ }
+ ]
+ ],
+ "scroll-button-disabled-no-focus.html": [
+ "a88977068742f03bdd4929ff1e3f96e581d9ba2d",
+ [
+ null,
+ {
+ "testdriver": true
+ }
+ ]
+ ],
"scroll-button-display-none.html": [
"8b3068be6dad6d1ccbe1134b9833ecc1c8b0f594",
[
@@ -611449,7 +612039,7 @@
]
],
"transform.html": [
- "7a852545a74a494348d90d727fc7266140033d2f",
+ "d12714a499c75ecbae8c4586f452d335594240fb",
[
null,
{}
@@ -613518,7 +614108,7 @@
]
],
"sibling-function-descriptors.tentative.html": [
- "d31d4acf499fb62f777817f24186e394626d507e",
+ "76d2ff8ee4db660e41ede9b188465a6c31843e3b",
[
null,
{}
@@ -613538,6 +614128,20 @@
{}
]
],
+ "sibling-index-keyframe-length-value-dynamic.html": [
+ "cbd34602fb3c74f3cea8a1e2192d1b5687e38a05",
+ [
+ null,
+ {}
+ ]
+ ],
+ "sibling-index-keyframe-value-dynamic.html": [
+ "286e0d3d3e2eecdd091df74c8c47f738cb700dd1",
+ [
+ null,
+ {}
+ ]
+ ],
"tree-scoped-sibling-function.html": [
"979466bb7cf3d5e5c4e9a2681f02f969858953e6",
[
@@ -614620,6 +615224,13 @@
]
]
},
+ "start-skip-start.html": [
+ "e04979472f1725b0225cdb98bbbd5222ed98c16c",
+ [
+ null,
+ {}
+ ]
+ ],
"start-view-transtion-skips-active.html": [
"971c7b38d4c816489caab9c611adc800b3638971",
[
@@ -614696,7 +615307,7 @@
]
],
"view-transition-types-mutable-no-document-element-crashtest.html": [
- "d5c0abd652cd3c673a71979a458df8183ab057ba",
+ "3ac946b273ba9fa9f9da27b10e492420daa3430a",
[
null,
{}
@@ -617053,7 +617664,7 @@
]
],
"getBoundingClientRect-newline.html": [
- "40e29181d6fed001b641b0b0050cf21d6f3c589c",
+ "ce82b7237d003d79fe4333e5a67aa74b004bb11c",
[
null,
{}
@@ -617528,6 +618139,13 @@
{}
]
],
+ "scrollIntoView-container.html": [
+ "3b241ea22517ecc5db6f286422233ed9f25cab5f",
+ [
+ null,
+ {}
+ ]
+ ],
"scrollIntoView-fixed-outside-of-viewport.html": [
"b3d61a430a3d2b566ff73b0844a79bf5ec4e92db",
[
@@ -617832,7 +618450,7 @@
]
],
"table-client-props.html": [
- "4af06d6bf71f0df75d3710ec0906445e943d340d",
+ "2895bebebbf1868ef60f947d230ecca96dd6e85a",
[
null,
{}
@@ -618022,6 +618640,13 @@
{}
]
],
+ "css-filters-opacity-hit-testing.html": [
+ "3b288fca4df13fcad7cfe0104c0f333aec73e45c",
+ [
+ null,
+ {}
+ ]
+ ],
"filter-sign-function.html": [
"97e5a26073b097728cf4571712bb9bd472ed69fd",
[
@@ -618217,7 +618842,7 @@
},
"geometry": {
"DOMMatrix-001.html": [
- "f578da076cbe6d7d7eb69b2cf438aa6f11a5c060",
+ "3436e17ced00de235f23117ed3a2bf8eba828a03",
[
null,
{}
@@ -624130,7 +624755,7 @@
}
]
],
- "overscroll-deltas.html": [
+ "overscroll-deltas.tentative.html": [
"e13e9f1cce5949da74227a1f069f64baad1f517c",
[
null,
@@ -624139,7 +624764,7 @@
}
]
],
- "overscroll-event-fired-to-document.html": [
+ "overscroll-event-fired-to-document.tentative.html": [
"c054ffca9c471f78ce5d1cbaa210fa7aad3aee3b",
[
null,
@@ -624148,7 +624773,7 @@
}
]
],
- "overscroll-event-fired-to-element-with-overscroll-behavior.html": [
+ "overscroll-event-fired-to-element-with-overscroll-behavior.tentative.html": [
"750080e6568e863a9249bc62e52971d9459f8418",
[
null,
@@ -624157,7 +624782,7 @@
}
]
],
- "overscroll-event-fired-to-scrolled-element.html": [
+ "overscroll-event-fired-to-scrolled-element.tentative.html": [
"be4176df59d6a1d59d90beee839bc5ca6a72f1c5",
[
null,
@@ -624166,7 +624791,7 @@
}
]
],
- "overscroll-event-fired-to-window.html": [
+ "overscroll-event-fired-to-window.tentative.html": [
"ef5ae3daef8158c9424cf07ad091c6a945456d0a",
[
null,
@@ -627813,7 +628438,7 @@
]
],
"XMLSerializer-serializeToString.html": [
- "6c294e464a5dc787abd4d10281ab2fe0555a0a3c",
+ "352a62c7d5db0710da6819bbc094ebfae46f4099",
[
null,
{}
@@ -646895,6 +647520,15 @@
}
]
],
+ "orphan-keydown.html": [
+ "0ab105d4d7284c19513a92241a9e16976fadefd0",
+ [
+ null,
+ {
+ "testdriver": true
+ }
+ ]
+ ],
"pointerdown.html": [
"2eb6ae898ccd006da1633a7f812dbcfc0fcbe9f1",
[
@@ -648829,7 +649463,7 @@
]
],
"fedcm-disconnect.sub.https.html": [
- "2ea2d4a2599751cad941552964c700ced2f1b7cc",
+ "04fcd272dcf261a19cf71abb049e41c8b41abbbf",
[
null,
{
@@ -662480,6 +663114,13 @@
}
]
],
+ "pragma-no-cache-with-cache-control.html": [
+ "19a80fe5edc6fcfdaad36219bc608312f5d15a3b",
+ [
+ null,
+ {}
+ ]
+ ],
"split-cache.html": [
"c822abba3acee8db1079ad08dd7fbcbf38d9e3b9",
[
@@ -662759,6 +663400,17 @@
]
]
},
+ "local-network-access": {
+ "fetch.tentative.https.html": [
+ "9c591f309b75af6731c6d6be9c0907d3b8c22618",
+ [
+ null,
+ {
+ "testdriver": true
+ }
+ ]
+ ]
+ },
"metadata": {
"audio-worklet.https.html": [
"3b768ef0b5ddd2d13361629afd011e6987cb38d0",
@@ -692441,6 +693093,13 @@
},
"windows": {
"auxiliary-browsing-contexts": {
+ "named-lookup-noopener.html": [
+ "91cf6ae6ee24611856b1a5eeb87f60b99cb82d64",
+ [
+ null,
+ {}
+ ]
+ ],
"named-lookup-scoped-to-browsing-context-group.html": [
"0450d479196a19535419c286fa2447580b344b25",
[
@@ -721858,6 +722517,15 @@
}
]
],
+ "pointerdown-add-display-none.html": [
+ "653944e155b71055761ab900f9f485ba812b46d2",
+ [
+ null,
+ {
+ "testdriver": true
+ }
+ ]
+ ],
"pointerdown-add-overflow-hidden.html": [
"ac953adfd874b19ab42379039dc26ac0efb67b7b",
[
@@ -733702,6 +734370,15 @@
}
]
],
+ "select-input-keyboard-behavior.tentative.html": [
+ "bf3fbab9d18a4747f903d082a8c11d6ebad737aa",
+ [
+ null,
+ {
+ "testdriver": true
+ }
+ ]
+ ],
"select-inside-top-layer.tentative.html": [
"6d70f3aaf8d3dc23b2f5fb784c399991913dc938",
[
@@ -740732,6 +741409,15 @@
}
]
],
+ "interesttarget-partial-interest.tentative.html": [
+ "bc7184d42d2528fe8e027e7af51463a796177ca9",
+ [
+ null,
+ {
+ "testdriver": true
+ }
+ ]
+ ],
"interesttarget-popover-focus.tentative.html": [
"9dfc39a37a60d438ecf9287d0ddffc0212e7a21c",
[
@@ -748357,7 +749043,7 @@
},
"emulation": {
"set_geolocation_override.https.html": [
- "5cbcf546423c772a98fd3725531754c366ab7b91",
+ "e339f39dcd89aa311e91c744669bf2cd9aadc103",
[
null,
{
@@ -748868,7 +749554,7 @@
]
],
"input-events-spin-button-click-on-number-input-delete-document.html": [
- "fb3655398dbd1bdde84f032df392b6352b7fcafe",
+ "9e5d9b9ef99a4290b4195ff5901eab324935c3fe",
[
null,
{
@@ -750749,7 +751435,7 @@
]
],
"idlharness.html": [
- "5f5d286b356cf5dcffd76f2ed6f689804e8426ba",
+ "0dd006e711c6d7b02fcbcd4d31a5c11381fe8d69",
[
null,
{}
@@ -750952,7 +751638,7 @@
]
],
"observe-text.html": [
- "0724a0bb92bf5248938c33fe1377d2e3a23309a5",
+ "d029d500aa1217b92c3898e90a50de83a98bc9e0",
[
null,
{}
@@ -763872,7 +764558,7 @@
]
],
"paint-timing-mixin.html": [
- "c43b7668b5c9b376774672746b6f1e31e5feb3cb",
+ "048c985c7c75cc3fe717b3b836120f170e847d1e",
[
null,
{}
@@ -785582,7 +786268,7 @@
]
],
"sanitizer-basic-filtering.tentative.html": [
- "0b6924b0980d8295d02a5ed3d552ba4e58beb212",
+ "38c764ae181418014488cf86126398158e653157",
[
null,
{}
@@ -785609,6 +786295,13 @@
{}
]
],
+ "sanitizer-parseHTML.tentative.html": [
+ "c4a31e2eb0365f67479968f4ce08117d04384889",
+ [
+ null,
+ {}
+ ]
+ ],
"sanitizer-removeUnsafe.tentative.html": [
"7ad8253ad5fdec29b79c590e50e9c0d211d17031",
[
@@ -797059,7 +797752,7 @@
]
],
"innertext.tentative.html": [
- "d40b6047924834ef09137fa975cbd2d142999bcb",
+ "7716488f25a08ea62635fceec679d97650d1f383",
[
null,
{
@@ -797068,7 +797761,7 @@
]
],
"interaction-with-paint-before-back.tentative.html": [
- "7b884f2bdb38945a346c3b5a8b178c04e88989ad",
+ "5961a6ebcdfdd2b10bf16173d3f0cece2733fa1c",
[
null,
{
@@ -797248,6 +797941,19 @@
}
]
],
+ "smoke": {
+ "tentative": {
+ "basic.html": [
+ "9bfedf09b1934e00a85cd030388ed470f575f7b0",
+ [
+ null,
+ {
+ "testdriver": true
+ }
+ ]
+ ]
+ }
+ },
"soft-navigation-detection-main-descendent.tentative.html": [
"96ff55260c3b9553ac5735cc5fe209ee7cfd5f37",
[
@@ -797355,7 +798061,7 @@
]
],
"text-lcp-followed-by-anim-image-softnav-lcp.tentative.html": [
- "0615b513e61319d61bcbed3540aa1a5d47289cb6",
+ "b34a6e81a58333c61ccedb4e25551feba4893736",
[
null,
{
@@ -797382,7 +798088,7 @@
]
],
"visited-link.tentative.html": [
- "0bb149f00e91fc2f0033a6f763543967ef542ab2",
+ "0bb31aaac1540b3d2b9f6a3157bb43d96e430a58",
[
null,
{
@@ -798302,6 +799008,75 @@
"timeout": "long"
}
]
+ ],
+ "redirect.sub.https.html": [
+ "f56f6bf09b9f61956e9297f8098677b846270d49",
+ [
+ "speculation-rules/prefetch/tentative/service-worker/redirect.sub.https.html?origin=same-site&sc=in-in&sw=fetch-handler",
+ {
+ "timeout": "long"
+ }
+ ],
+ [
+ "speculation-rules/prefetch/tentative/service-worker/redirect.sub.https.html?origin=same-site&sc=in-in&sw=fetch-handler-synthetic",
+ {
+ "timeout": "long"
+ }
+ ],
+ [
+ "speculation-rules/prefetch/tentative/service-worker/redirect.sub.https.html?origin=same-site&sc=in-in&sw=fetch-handler-to-fallback",
+ {
+ "timeout": "long"
+ }
+ ],
+ [
+ "speculation-rules/prefetch/tentative/service-worker/redirect.sub.https.html?origin=same-site&sc=in-in&sw=no-fetch-handler",
+ {
+ "timeout": "long"
+ }
+ ],
+ [
+ "speculation-rules/prefetch/tentative/service-worker/redirect.sub.https.html?origin=same-site&sc=in-out&sw=fetch-handler",
+ {
+ "timeout": "long"
+ }
+ ],
+ [
+ "speculation-rules/prefetch/tentative/service-worker/redirect.sub.https.html?origin=same-site&sc=in-out&sw=fetch-handler-synthetic",
+ {
+ "timeout": "long"
+ }
+ ],
+ [
+ "speculation-rules/prefetch/tentative/service-worker/redirect.sub.https.html?origin=same-site&sc=in-out&sw=fetch-handler-to-fallback",
+ {
+ "timeout": "long"
+ }
+ ],
+ [
+ "speculation-rules/prefetch/tentative/service-worker/redirect.sub.https.html?origin=same-site&sc=in-out&sw=no-fetch-handler",
+ {
+ "timeout": "long"
+ }
+ ],
+ [
+ "speculation-rules/prefetch/tentative/service-worker/redirect.sub.https.html?origin=same-site&sc=out-in&sw=fetch-handler",
+ {
+ "timeout": "long"
+ }
+ ],
+ [
+ "speculation-rules/prefetch/tentative/service-worker/redirect.sub.https.html?origin=same-site&sc=out-in&sw=fetch-handler-to-fallback",
+ {
+ "timeout": "long"
+ }
+ ],
+ [
+ "speculation-rules/prefetch/tentative/service-worker/redirect.sub.https.html?origin=same-site&sc=out-in&sw=no-fetch-handler",
+ {
+ "timeout": "long"
+ }
+ ]
]
}
},
@@ -800067,6 +800842,26 @@
}
]
],
+ "prefetch-eagerness-pointer-down.https.html": [
+ "cbf69cefba61f23b706c9b397308625467671237",
+ [
+ null,
+ {
+ "testdriver": true,
+ "timeout": "long"
+ }
+ ]
+ ],
+ "prefetch-eagerness-pointer-hover.https.html": [
+ "f5e749b9c6f63f80c40ed9ff7cac42570f8b8d13",
+ [
+ null,
+ {
+ "testdriver": true,
+ "timeout": "long"
+ }
+ ]
+ ],
"prerender-target-hint.https.html": [
"cab8f4586c514f562976ee41acb2cd6f74568eac",
[
@@ -800144,10 +800939,12 @@
]
],
"SpeechRecognition-installOnDevice.https.html": [
- "1d1dd35edc26d75419a49120d8a76c4ea7c84233",
+ "2f5b359c571d6d9cc97815ae6ba46e4249ec569e",
[
null,
- {}
+ {
+ "testdriver": true
+ }
]
],
"SpeechRecognition-onerror.https.html": [
@@ -807076,7 +807873,7 @@
],
"crashtests": {
"garbage-collection.any.js": [
- "cf10f7f2e8317ed16dd5fe3a967c6f26efd0a335",
+ "6e9d80c41425b13800717f9e27184f64f1514752",
[
"streams/readable-streams/crashtests/garbage-collection.any.html",
{
@@ -812273,6 +813070,71 @@
}
]
],
+ "crashtests": {
+ "garbage-collection.any.js": [
+ "a3796881c9fb544a0b446db39af233b3866ae2b9",
+ [
+ "streams/writable-streams/crashtests/garbage-collection.any.html",
+ {
+ "script_metadata": [
+ [
+ "global",
+ "window,worker"
+ ],
+ [
+ "script",
+ "/common/gc.js"
+ ]
+ ]
+ }
+ ],
+ [
+ "streams/writable-streams/crashtests/garbage-collection.any.serviceworker.html",
+ {
+ "script_metadata": [
+ [
+ "global",
+ "window,worker"
+ ],
+ [
+ "script",
+ "/common/gc.js"
+ ]
+ ]
+ }
+ ],
+ [
+ "streams/writable-streams/crashtests/garbage-collection.any.sharedworker.html",
+ {
+ "script_metadata": [
+ [
+ "global",
+ "window,worker"
+ ],
+ [
+ "script",
+ "/common/gc.js"
+ ]
+ ]
+ }
+ ],
+ [
+ "streams/writable-streams/crashtests/garbage-collection.any.worker.html",
+ {
+ "script_metadata": [
+ [
+ "global",
+ "window,worker"
+ ],
+ [
+ "script",
+ "/common/gc.js"
+ ]
+ ]
+ }
+ ]
+ ]
+ },
"error.any.js": [
"d08c8a54863bb9693e76116946bd118592d6928c",
[
@@ -813742,6 +814604,34 @@
{}
]
],
+ "tentative": {
+ "integrity-policy": {
+ "parsing.https.html": [
+ "205854419a7d58871e39a5cfb3a86ccd38cd5492",
+ [
+ "subresource-integrity/tentative/integrity-policy/parsing.https.html?type=enforce",
+ {
+ "timeout": "long"
+ }
+ ],
+ [
+ "subresource-integrity/tentative/integrity-policy/parsing.https.html?type=report",
+ {
+ "timeout": "long"
+ }
+ ]
+ ],
+ "script.https.html": [
+ "783374db920a24ae32a3492d8cfe2edc5d8eb5d3",
+ [
+ null,
+ {
+ "timeout": "long"
+ }
+ ]
+ ]
+ }
+ },
"unencoded-digest": {
"tentative": {
"fetch.any.js": [
@@ -814687,13 +815577,6 @@
{}
]
],
- "discard-check-removal-order.html": [
- "2935a69addaabdeee2eb381de847cd7f77688ce3",
- [
- null,
- {}
- ]
- ],
"end-attribute-change-end-time.html": [
"9f05d7d405d364a1eadfa104c1c4bd90a2bf3b8c",
[
@@ -815915,7 +816798,7 @@
]
],
"idlharness.window.js": [
- "b363128c25a9068df93b26966b82b50cdd9b87f7",
+ "57beca77fd45b83f40fcdf378103d6f1c1c0940d",
[
"svg/idlharness.window.html",
{
@@ -817079,7 +817962,7 @@
]
],
"presentation-attributes-special-cases.html": [
- "7bfa001030a0e0969d845e353a9d721d9ce940a0",
+ "9a5da751ce1ace3c277bfce3bf9b99352ac7786f",
[
null,
{}
@@ -825112,6 +825995,13 @@
{}
]
],
+ "simd_select.wast.js.html": [
+ "5d50138374b1ce1edaa9d8b28d03f33b6cb6bab3",
+ [
+ null,
+ {}
+ ]
+ ],
"simd_splat.wast.js.html": [
"45204220343e96015990bb8c6374c2a98aa62382",
[
@@ -851940,7 +852830,7 @@
]
],
"conv2d.https.any.js": [
- "9fe246c9395c6cacf7e2f5245dac420e295707be",
+ "45fecb2b40cc399ace227264e881d2009d467cd2",
[
"webnn/conformance_tests/conv2d.https.any.html?cpu",
{
@@ -852051,7 +852941,7 @@
]
],
"conv_transpose2d.https.any.js": [
- "67ce5dbaf65cc99ba7f2fdd02aaf2d9b3e9022ce",
+ "a17df911e4482de7e49874d84f5818694515e722",
[
"webnn/conformance_tests/conv_transpose2d.https.any.html?cpu",
{
@@ -856140,7 +857030,7 @@
]
],
"log.https.any.js": [
- "011beef53aca200f5916dc51152fe745866989ec",
+ "8ed807b3401330e961f2ae9721f6378147580ddd",
[
"webnn/conformance_tests/log.https.any.html?cpu",
{
@@ -858129,7 +859019,7 @@
]
],
"pooling.https.any.js": [
- "f385aab1f154387bbe35b171927d29684c8d5120",
+ "8f81ff565d232dec427381dfdae638cffceb0d91",
[
"webnn/conformance_tests/pooling.https.any.html?cpu",
{
@@ -858462,7 +859352,7 @@
]
],
"qdq_subgraph.https.any.js": [
- "3b59c3bb49d6441a77e83309f48a97dd982aea74",
+ "aa816cce7fb2cd3d0a3f81d0453c8c199bf052fe",
[
"webnn/conformance_tests/qdq_subgraph.https.any.html?cpu",
{
@@ -861804,7 +862694,7 @@
]
],
"sign.https.any.js": [
- "4c3a330f850ca591bcc5a1b9c8fc784469ee821e",
+ "004c03bdf1339d74a84c95d5b03b31b56876d0fe",
[
"webnn/conformance_tests/sign.https.any.html?cpu",
{
@@ -881534,7 +882424,7 @@
],
"tentative": {
"RTCEncodedAudioFrame-clone.https.html": [
- "9f07713d4439c676fd7a5cc2f7d32af92a350e19",
+ "c93f8b3e54150b94334c33f30031d0b9f3afcd19",
[
null,
{
@@ -881543,7 +882433,7 @@
]
],
"RTCEncodedAudioFrame-metadata.https.html": [
- "435e1c067834fe2050fd5c510f95516c5e6aeb61",
+ "df4577c5614a82afd7b02d988d0f15caa3b9e848",
[
null,
{
@@ -927337,7 +928227,7 @@
]
],
"user_contexts.py": [
- "64c261637342868e90ee67a2332503e00fa96b0d",
+ "fa1e748d6b2a91addeb6c0569ebd271c00865fb8",
[
null,
{}
@@ -927417,28 +928307,35 @@
"emulation": {
"set_geolocation_override": {
"contexts.py": [
- "8a0e43475da52609a362bf5f3e0853ceb2a9725f",
+ "068bb804e73bd06e2d901363280792d479cfb38f",
[
null,
{}
]
],
"coordinates.py": [
- "b23354e9e42ef1bdd9d6684d11b782ce15f4eb8c",
+ "ea4fe643c3ecfc6e3609172706b51df17456e2e7",
+ [
+ null,
+ {}
+ ]
+ ],
+ "error.py": [
+ "27c23dc7811458feb7cd43c8a286c83809d9d2d0",
[
null,
{}
]
],
"invalid.py": [
- "e804848e0be6c483340dcd8b3b4def10dddedce9",
+ "2334d37517df00cde8f845e7ec6412d1570e405b",
[
null,
{}
]
],
"user_contexts.py": [
- "008dee5698de43a3371c1f7d697bb69014cf1b65",
+ "b3038bb59587b661e75945c5b8eeb4e943c72d9e",
[
null,
{}
diff --git a/tests/wpt/meta/WebCryptoAPI/idlharness.https.any.js.ini b/tests/wpt/meta/WebCryptoAPI/idlharness.https.any.js.ini
deleted file mode 100644
index 88bb87548ec..00000000000
--- a/tests/wpt/meta/WebCryptoAPI/idlharness.https.any.js.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[idlharness.https.any.html]
- [idl_test setup]
- expected: FAIL
-
-
-[idlharness.https.any.worker.html]
- [idl_test setup]
- expected: FAIL
diff --git a/tests/wpt/meta/__dir__.ini b/tests/wpt/meta/__dir__.ini
index 5d513146b3f..057fb932df2 100644
--- a/tests/wpt/meta/__dir__.ini
+++ b/tests/wpt/meta/__dir__.ini
@@ -1,4 +1,5 @@
prefs: [
+ "dom_serviceworker_enabled:true",
"dom_testutils_enabled:true",
"dom_urlpattern_enabled:true",
]
diff --git a/tests/wpt/meta/clipboard-apis/idlharness.https.window.js.ini b/tests/wpt/meta/clipboard-apis/idlharness.https.window.js.ini
index 98bb3d8b303..e648b62a314 100644
--- a/tests/wpt/meta/clipboard-apis/idlharness.https.window.js.ini
+++ b/tests/wpt/meta/clipboard-apis/idlharness.https.window.js.ini
@@ -8,9 +8,6 @@
[Clipboard interface: operation read(optional ClipboardUnsanitizedFormats)]
expected: FAIL
- [Clipboard interface: operation readText()]
- expected: FAIL
-
[Clipboard interface: operation write(ClipboardItems)]
expected: FAIL
@@ -20,9 +17,6 @@
[Clipboard interface: calling read(optional ClipboardUnsanitizedFormats) on navigator.clipboard with too few arguments must throw TypeError]
expected: FAIL
- [Clipboard interface: navigator.clipboard must inherit property "readText()" with the proper type]
- expected: FAIL
-
[Clipboard interface: navigator.clipboard must inherit property "write(ClipboardItems)" with the proper type]
expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/default-src/default-src-sri_hash.sub.html.ini b/tests/wpt/meta/content-security-policy/default-src/default-src-sri_hash.sub.html.ini
deleted file mode 100644
index ee237b70bc4..00000000000
--- a/tests/wpt/meta/content-security-policy/default-src/default-src-sri_hash.sub.html.ini
+++ /dev/null
@@ -1,12 +0,0 @@
-[default-src-sri_hash.sub.html]
- [multiple matching integrity]
- expected: FAIL
-
- [matching integrity]
- expected: FAIL
-
- [matching integrity (case-insensitive algorithm)]
- expected: FAIL
-
- [matching plus unsupported integrity]
- expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/gen/top.http-rp/script-src-self/script-tag.http.html.ini b/tests/wpt/meta/content-security-policy/gen/top.http-rp/script-src-self/script-tag.http.html.ini
index e29f4dd5d4e..5891a18681e 100644
--- a/tests/wpt/meta/content-security-policy/gen/top.http-rp/script-src-self/script-tag.http.html.ini
+++ b/tests/wpt/meta/content-security-policy/gen/top.http-rp/script-src-self/script-tag.http.html.ini
@@ -1,13 +1,4 @@
[script-tag.http.html]
- [Content Security Policy: Expects blocked for script-tag to cross-http origin and keep-origin redirection from http context.: securitypolicyviolation]
- expected: FAIL
-
- [Content Security Policy: Expects blocked for script-tag to cross-http origin and no-redirect redirection from http context.: securitypolicyviolation]
- expected: FAIL
-
- [Content Security Policy: Expects blocked for script-tag to cross-http origin and swap-origin redirection from http context.: securitypolicyviolation]
- expected: FAIL
-
[Content Security Policy: Expects blocked for script-tag to same-http origin and swap-origin redirection from http context.]
expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/gen/top.http-rp/script-src-self/script-tag.https.html.ini b/tests/wpt/meta/content-security-policy/gen/top.http-rp/script-src-self/script-tag.https.html.ini
index bbe30519d0b..699a0dd6238 100644
--- a/tests/wpt/meta/content-security-policy/gen/top.http-rp/script-src-self/script-tag.https.html.ini
+++ b/tests/wpt/meta/content-security-policy/gen/top.http-rp/script-src-self/script-tag.https.html.ini
@@ -1,13 +1,4 @@
[script-tag.https.html]
- [Content Security Policy: Expects blocked for script-tag to cross-https origin and keep-origin redirection from https context.: securitypolicyviolation]
- expected: FAIL
-
- [Content Security Policy: Expects blocked for script-tag to cross-https origin and no-redirect redirection from https context.: securitypolicyviolation]
- expected: FAIL
-
- [Content Security Policy: Expects blocked for script-tag to cross-https origin and swap-origin redirection from https context.: securitypolicyviolation]
- expected: FAIL
-
[Content Security Policy: Expects blocked for script-tag to same-https origin and swap-origin redirection from https context.]
expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/gen/top.meta/script-src-self/script-tag.http.html.ini b/tests/wpt/meta/content-security-policy/gen/top.meta/script-src-self/script-tag.http.html.ini
index e29f4dd5d4e..5891a18681e 100644
--- a/tests/wpt/meta/content-security-policy/gen/top.meta/script-src-self/script-tag.http.html.ini
+++ b/tests/wpt/meta/content-security-policy/gen/top.meta/script-src-self/script-tag.http.html.ini
@@ -1,13 +1,4 @@
[script-tag.http.html]
- [Content Security Policy: Expects blocked for script-tag to cross-http origin and keep-origin redirection from http context.: securitypolicyviolation]
- expected: FAIL
-
- [Content Security Policy: Expects blocked for script-tag to cross-http origin and no-redirect redirection from http context.: securitypolicyviolation]
- expected: FAIL
-
- [Content Security Policy: Expects blocked for script-tag to cross-http origin and swap-origin redirection from http context.: securitypolicyviolation]
- expected: FAIL
-
[Content Security Policy: Expects blocked for script-tag to same-http origin and swap-origin redirection from http context.]
expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/gen/top.meta/script-src-self/script-tag.https.html.ini b/tests/wpt/meta/content-security-policy/gen/top.meta/script-src-self/script-tag.https.html.ini
index bbe30519d0b..699a0dd6238 100644
--- a/tests/wpt/meta/content-security-policy/gen/top.meta/script-src-self/script-tag.https.html.ini
+++ b/tests/wpt/meta/content-security-policy/gen/top.meta/script-src-self/script-tag.https.html.ini
@@ -1,13 +1,4 @@
[script-tag.https.html]
- [Content Security Policy: Expects blocked for script-tag to cross-https origin and keep-origin redirection from https context.: securitypolicyviolation]
- expected: FAIL
-
- [Content Security Policy: Expects blocked for script-tag to cross-https origin and no-redirect redirection from https context.: securitypolicyviolation]
- expected: FAIL
-
- [Content Security Policy: Expects blocked for script-tag to cross-https origin and swap-origin redirection from https context.: securitypolicyviolation]
- expected: FAIL
-
[Content Security Policy: Expects blocked for script-tag to same-https origin and swap-origin redirection from https context.]
expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/navigation/to-javascript-url-script-src.html.ini b/tests/wpt/meta/content-security-policy/navigation/to-javascript-url-script-src.html.ini
deleted file mode 100644
index 213ed2d4692..00000000000
--- a/tests/wpt/meta/content-security-policy/navigation/to-javascript-url-script-src.html.ini
+++ /dev/null
@@ -1,13 +0,0 @@
-[to-javascript-url-script-src.html]
- expected: TIMEOUT
- [<iframe src='javascript:'> blocked without 'unsafe-inline'.]
- expected: TIMEOUT
-
- [<iframe> navigated to 'javascript:' blocked without 'unsafe-inline'.]
- expected: NOTRUN
-
- [<iframe src='...'> with 'unsafe-inline' navigated to 'javascript:' blocked in this document]
- expected: NOTRUN
-
- [<iframe src='...'> without 'unsafe-inline' navigated to 'javascript:' blocked in this document.]
- expected: NOTRUN
diff --git a/tests/wpt/meta/content-security-policy/nonce-hiding/nonces.html.ini b/tests/wpt/meta/content-security-policy/nonce-hiding/nonces.html.ini
deleted file mode 100644
index dbf3c1db85e..00000000000
--- a/tests/wpt/meta/content-security-policy/nonce-hiding/nonces.html.ini
+++ /dev/null
@@ -1,24 +0,0 @@
-[nonces.html]
- [Basic nonce tests for meh in HTML namespace]
- expected: FAIL
-
- [Basic nonce tests for div in HTML namespace]
- expected: FAIL
-
- [Basic nonce tests for script in HTML namespace]
- expected: FAIL
-
- [Basic nonce tests for meh in SVG namespace]
- expected: FAIL
-
- [Basic nonce tests for svg in SVG namespace]
- expected: FAIL
-
- [Basic nonce tests for script in SVG namespace]
- expected: FAIL
-
- [Basic nonce tests for style in HTML namespace]
- expected: FAIL
-
- [Basic nonce tests for link in HTML namespace]
- expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/nonce-hiding/script-nonces-hidden-meta.sub.html.ini b/tests/wpt/meta/content-security-policy/nonce-hiding/script-nonces-hidden-meta.sub.html.ini
index 7268cf08bda..94d7bf64166 100644
--- a/tests/wpt/meta/content-security-policy/nonce-hiding/script-nonces-hidden-meta.sub.html.ini
+++ b/tests/wpt/meta/content-security-policy/nonce-hiding/script-nonces-hidden-meta.sub.html.ini
@@ -1,6 +1,3 @@
[script-nonces-hidden-meta.sub.html]
- [Writing 'nonce' IDL attribute.]
- expected: FAIL
-
[createElement.nonce.]
expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/nonce-hiding/script-nonces-hidden.html.ini b/tests/wpt/meta/content-security-policy/nonce-hiding/script-nonces-hidden.html.ini
index 8b687e4ecfb..e8896cde9c4 100644
--- a/tests/wpt/meta/content-security-policy/nonce-hiding/script-nonces-hidden.html.ini
+++ b/tests/wpt/meta/content-security-policy/nonce-hiding/script-nonces-hidden.html.ini
@@ -1,30 +1,3 @@
[script-nonces-hidden.html]
- [Reading 'nonce' content attribute and IDL attribute.]
- expected: FAIL
-
- [Cloned node retains nonce.]
- expected: FAIL
-
- [Cloned node retains nonce when inserted.]
- expected: FAIL
-
- [Writing 'nonce' IDL attribute.]
- expected: FAIL
-
- [Document-written script's nonce value.]
- expected: FAIL
-
[createElement.nonce.]
expected: FAIL
-
- [setAttribute('nonce') overwrites '.nonce' upon insertion.]
- expected: FAIL
-
- [createElement.setAttribute.]
- expected: FAIL
-
- [Custom elements expose the correct events.]
- expected: FAIL
-
- [Nonces don't leak via CSS side-channels.]
- expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/nonce-hiding/svgscript-nonces-hidden-meta.sub.html.ini b/tests/wpt/meta/content-security-policy/nonce-hiding/svgscript-nonces-hidden-meta.sub.html.ini
index 14a7cfae7aa..54fff6e8d97 100644
--- a/tests/wpt/meta/content-security-policy/nonce-hiding/svgscript-nonces-hidden-meta.sub.html.ini
+++ b/tests/wpt/meta/content-security-policy/nonce-hiding/svgscript-nonces-hidden-meta.sub.html.ini
@@ -2,9 +2,3 @@
expected: TIMEOUT
[Document-written script executes.]
expected: NOTRUN
-
- [createElement.nonce.]
- expected: FAIL
-
- [Writing 'nonce' IDL attribute.]
- expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/nonce-hiding/svgscript-nonces-hidden.html.ini b/tests/wpt/meta/content-security-policy/nonce-hiding/svgscript-nonces-hidden.html.ini
index 77270d6f4a2..277bef043c8 100644
--- a/tests/wpt/meta/content-security-policy/nonce-hiding/svgscript-nonces-hidden.html.ini
+++ b/tests/wpt/meta/content-security-policy/nonce-hiding/svgscript-nonces-hidden.html.ini
@@ -1,22 +1,4 @@
[svgscript-nonces-hidden.html]
expected: TIMEOUT
- [Reading 'nonce' content attribute and IDL attribute.]
- expected: FAIL
-
- [Cloned node retains nonce.]
- expected: FAIL
-
- [Cloned node retains nonce when inserted.]
- expected: FAIL
-
[Document-written script executes.]
expected: NOTRUN
-
- [createElement.nonce.]
- expected: FAIL
-
- [createElement.setAttribute.]
- expected: FAIL
-
- [Writing 'nonce' IDL attribute.]
- expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/reporting/report-clips-sample.https.html.ini b/tests/wpt/meta/content-security-policy/reporting/report-clips-sample.https.html.ini
index 783a58399e0..282ea4398ee 100644
--- a/tests/wpt/meta/content-security-policy/reporting/report-clips-sample.https.html.ini
+++ b/tests/wpt/meta/content-security-policy/reporting/report-clips-sample.https.html.ini
@@ -16,6 +16,3 @@
[AsyncGenerator Function constructor is also clipped.]
expected: FAIL
-
- [Trusted Types violation sample is clipped to 40 characters excluded the sink name.]
- expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/reporting/report-original-url.sub.html.ini b/tests/wpt/meta/content-security-policy/reporting/report-original-url.sub.html.ini
index 9d64a7e6bc4..66a2ee93f3b 100644
--- a/tests/wpt/meta/content-security-policy/reporting/report-original-url.sub.html.ini
+++ b/tests/wpt/meta/content-security-policy/reporting/report-original-url.sub.html.ini
@@ -1,11 +1,5 @@
[report-original-url.sub.html]
expected: TIMEOUT
- [Direct block, same-origin = full URL in report]
- expected: TIMEOUT
-
- [Direct block, cross-origin = full URL in report]
- expected: TIMEOUT
-
[Block after redirect, same-origin = original URL in report]
expected: TIMEOUT
diff --git a/tests/wpt/meta/content-security-policy/script-src/script-src-report-only-policy-works-with-hash-policy.html.ini b/tests/wpt/meta/content-security-policy/script-src/script-src-report-only-policy-works-with-hash-policy.html.ini
deleted file mode 100644
index 31bfeae4a12..00000000000
--- a/tests/wpt/meta/content-security-policy/script-src/script-src-report-only-policy-works-with-hash-policy.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[script-src-report-only-policy-works-with-hash-policy.html]
- expected: TIMEOUT
- [Test that the securitypolicyviolation event is fired]
- expected: NOTRUN
diff --git a/tests/wpt/meta/content-security-policy/script-src/script-src-sri_hash.sub.html.ini b/tests/wpt/meta/content-security-policy/script-src/script-src-sri_hash.sub.html.ini
deleted file mode 100644
index 3324bf91bd7..00000000000
--- a/tests/wpt/meta/content-security-policy/script-src/script-src-sri_hash.sub.html.ini
+++ /dev/null
@@ -1,12 +0,0 @@
-[script-src-sri_hash.sub.html]
- [multiple matching integrity]
- expected: FAIL
-
- [matching integrity]
- expected: FAIL
-
- [matching integrity (case-insensitive algorithm)]
- expected: FAIL
-
- [matching plus unsupported integrity]
- expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/script-src/scripthash-case-insensitive.sub.html.ini b/tests/wpt/meta/content-security-policy/script-src/scripthash-case-insensitive.sub.html.ini
deleted file mode 100644
index 19b7395760a..00000000000
--- a/tests/wpt/meta/content-security-policy/script-src/scripthash-case-insensitive.sub.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[scripthash-case-insensitive.sub.html]
- [Expecting alerts: ["PASS (1/6)","PASS (2/6)","PASS (3/6)","PASS (4/6)","PASS (5/6)","PASS (6/6)"\]]
- expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/script-src/scriptnonce-basic-blocked.sub.html.ini b/tests/wpt/meta/content-security-policy/script-src/scriptnonce-basic-blocked.sub.html.ini
deleted file mode 100644
index dc8de328080..00000000000
--- a/tests/wpt/meta/content-security-policy/script-src/scriptnonce-basic-blocked.sub.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[scriptnonce-basic-blocked.sub.html]
- [Expecting alerts: ["PASS (closely-quoted nonce)","PASS (nonce w/whitespace)", "violated-directive=script-src-elem", "violated-directive=script-src-elem", "violated-directive=script-src-elem"\]]
- expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/securitypolicyviolation/blockeduri-eval.html.ini b/tests/wpt/meta/content-security-policy/securitypolicyviolation/blockeduri-eval.html.ini
deleted file mode 100644
index bebd42f2743..00000000000
--- a/tests/wpt/meta/content-security-policy/securitypolicyviolation/blockeduri-eval.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[blockeduri-eval.html]
- expected: TIMEOUT
- [Eval violations have a blockedURI of 'eval']
- expected: TIMEOUT
diff --git a/tests/wpt/meta/content-security-policy/securitypolicyviolation/blockeduri-inline.html.ini b/tests/wpt/meta/content-security-policy/securitypolicyviolation/blockeduri-inline.html.ini
index 9c191e43078..3d0febce2b5 100644
--- a/tests/wpt/meta/content-security-policy/securitypolicyviolation/blockeduri-inline.html.ini
+++ b/tests/wpt/meta/content-security-policy/securitypolicyviolation/blockeduri-inline.html.ini
@@ -1,4 +1,3 @@
[blockeduri-inline.html]
- expected: TIMEOUT
[Inline violations have a blockedURI of 'inline']
- expected: TIMEOUT
+ expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/securitypolicyviolation/blockeduri-ws-wss-scheme.html.ini b/tests/wpt/meta/content-security-policy/securitypolicyviolation/blockeduri-ws-wss-scheme.html.ini
index 6ebb357445f..14fa5353a94 100644
--- a/tests/wpt/meta/content-security-policy/securitypolicyviolation/blockeduri-ws-wss-scheme.html.ini
+++ b/tests/wpt/meta/content-security-policy/securitypolicyviolation/blockeduri-ws-wss-scheme.html.ini
@@ -1,13 +1,3 @@
[blockeduri-ws-wss-scheme.html]
- expected: TIMEOUT
- [ws]
- expected: FAIL
-
- [wss]
- expected: FAIL
-
- [cross-origin]
- expected: FAIL
-
[redirect]
- expected: TIMEOUT
+ expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/securitypolicyviolation/linenumber.tentative.html.ini b/tests/wpt/meta/content-security-policy/securitypolicyviolation/linenumber.tentative.html.ini
index fed78a0aa49..e8114229ab9 100644
--- a/tests/wpt/meta/content-security-policy/securitypolicyviolation/linenumber.tentative.html.ini
+++ b/tests/wpt/meta/content-security-policy/securitypolicyviolation/linenumber.tentative.html.ini
@@ -1,4 +1,3 @@
[linenumber.tentative.html]
- expected: TIMEOUT
[linenumber]
- expected: NOTRUN
+ expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/securitypolicyviolation/script-sample-no-opt-in.html.ini b/tests/wpt/meta/content-security-policy/securitypolicyviolation/script-sample-no-opt-in.html.ini
index b12f81377d1..409022079e0 100644
--- a/tests/wpt/meta/content-security-policy/securitypolicyviolation/script-sample-no-opt-in.html.ini
+++ b/tests/wpt/meta/content-security-policy/securitypolicyviolation/script-sample-no-opt-in.html.ini
@@ -1,13 +1,7 @@
[script-sample-no-opt-in.html]
expected: TIMEOUT
- [Inline script should not have a sample.]
- expected: TIMEOUT
-
- [Inline event handlers should not have a sample.]
- expected: TIMEOUT
-
[JavaScript URLs in iframes should not have a sample.]
expected: TIMEOUT
- [eval()-alikes should not have a sample.]
+ [Inline event handlers should not have a sample.]
expected: TIMEOUT
diff --git a/tests/wpt/meta/content-security-policy/securitypolicyviolation/script-sample.html.ini b/tests/wpt/meta/content-security-policy/securitypolicyviolation/script-sample.html.ini
index f4c315396f6..8723775f27e 100644
--- a/tests/wpt/meta/content-security-policy/securitypolicyviolation/script-sample.html.ini
+++ b/tests/wpt/meta/content-security-policy/securitypolicyviolation/script-sample.html.ini
@@ -1,19 +1,7 @@
[script-sample.html]
expected: TIMEOUT
- [Inline script should have a sample.]
- expected: TIMEOUT
-
- [Inline event handlers should have a sample.]
- expected: TIMEOUT
-
[JavaScript URLs in iframes should have a sample.]
expected: TIMEOUT
- [eval() should have a sample.]
- expected: TIMEOUT
-
- [setInterval() should have a sample.]
- expected: TIMEOUT
-
- [setTimeout() should have a sample.]
+ [Inline event handlers should have a sample.]
expected: TIMEOUT
diff --git a/tests/wpt/meta/content-security-policy/securitypolicyviolation/source-file-blob-scheme.html.ini b/tests/wpt/meta/content-security-policy/securitypolicyviolation/source-file-blob-scheme.html.ini
index 03d164a4050..514f91961e3 100644
--- a/tests/wpt/meta/content-security-policy/securitypolicyviolation/source-file-blob-scheme.html.ini
+++ b/tests/wpt/meta/content-security-policy/securitypolicyviolation/source-file-blob-scheme.html.ini
@@ -1,4 +1,3 @@
[source-file-blob-scheme.html]
- expected: TIMEOUT
[Violations from data:-URL scripts have a sourceFile of 'blob']
- expected: TIMEOUT
+ expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/securitypolicyviolation/source-file-data-scheme.html.ini b/tests/wpt/meta/content-security-policy/securitypolicyviolation/source-file-data-scheme.html.ini
index 387a7e2ff98..ed4dba7d3c3 100644
--- a/tests/wpt/meta/content-security-policy/securitypolicyviolation/source-file-data-scheme.html.ini
+++ b/tests/wpt/meta/content-security-policy/securitypolicyviolation/source-file-data-scheme.html.ini
@@ -1,4 +1,3 @@
[source-file-data-scheme.html]
- expected: TIMEOUT
[Violations from data:-URL scripts have a sourceFile of 'data']
- expected: TIMEOUT
+ expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/securitypolicyviolation/style-sample-no-opt-in.html.ini b/tests/wpt/meta/content-security-policy/securitypolicyviolation/style-sample-no-opt-in.html.ini
index eb10ad61b2c..ef89e7cb1a9 100644
--- a/tests/wpt/meta/content-security-policy/securitypolicyviolation/style-sample-no-opt-in.html.ini
+++ b/tests/wpt/meta/content-security-policy/securitypolicyviolation/style-sample-no-opt-in.html.ini
@@ -1,7 +1,4 @@
[style-sample-no-opt-in.html]
expected: TIMEOUT
- [Inline style blocks should not have a sample.]
- expected: TIMEOUT
-
[Inline style attributes should not have a sample.]
expected: TIMEOUT
diff --git a/tests/wpt/meta/content-security-policy/securitypolicyviolation/style-sample.html.ini b/tests/wpt/meta/content-security-policy/securitypolicyviolation/style-sample.html.ini
index 460e21bd6cd..63e278182d8 100644
--- a/tests/wpt/meta/content-security-policy/securitypolicyviolation/style-sample.html.ini
+++ b/tests/wpt/meta/content-security-policy/securitypolicyviolation/style-sample.html.ini
@@ -1,7 +1,4 @@
[style-sample.html]
expected: TIMEOUT
- [Inline style blocks should have a sample.]
- expected: TIMEOUT
-
[Inline style attributes should have a sample.]
expected: TIMEOUT
diff --git a/tests/wpt/meta/content-security-policy/securitypolicyviolation/targeting.html.ini b/tests/wpt/meta/content-security-policy/securitypolicyviolation/targeting.html.ini
index 88da5e48238..952c5185dd8 100644
--- a/tests/wpt/meta/content-security-policy/securitypolicyviolation/targeting.html.ini
+++ b/tests/wpt/meta/content-security-policy/securitypolicyviolation/targeting.html.ini
@@ -4,13 +4,10 @@
expected: NOTRUN
[Inline violations target the right element.]
- expected: TIMEOUT
+ expected: FAIL
[Correct targeting inside shadow tree (inline handler).]
expected: TIMEOUT
- [Correct targeting inside shadow tree (style).]
- expected: TIMEOUT
-
[Elements created in this document, but pushed into a same-origin frame trigger on that frame's document, not on this frame's document.]
expected: TIMEOUT
diff --git a/tests/wpt/meta/content-security-policy/style-src-attr-elem/style-src-attr-blocked-src-allowed.html.ini b/tests/wpt/meta/content-security-policy/style-src-attr-elem/style-src-attr-blocked-src-allowed.html.ini
deleted file mode 100644
index a5cf5faf238..00000000000
--- a/tests/wpt/meta/content-security-policy/style-src-attr-elem/style-src-attr-blocked-src-allowed.html.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[style-src-attr-blocked-src-allowed.html]
- expected: TIMEOUT
- [Should fire a security policy violation event]
- expected: NOTRUN
-
- [The attribute style should not be applied]
- expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/style-src-attr-elem/style-src-elem-allowed-attr-blocked.html.ini b/tests/wpt/meta/content-security-policy/style-src-attr-elem/style-src-elem-allowed-attr-blocked.html.ini
deleted file mode 100644
index 979fc151f38..00000000000
--- a/tests/wpt/meta/content-security-policy/style-src-attr-elem/style-src-elem-allowed-attr-blocked.html.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[style-src-elem-allowed-attr-blocked.html]
- expected: TIMEOUT
- [Should fire a security policy violation for the attribute]
- expected: NOTRUN
-
- [The attribute style should not be applied and the inline style should be applied]
- expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/style-src-attr-elem/style-src-elem-blocked-attr-allowed.html.ini b/tests/wpt/meta/content-security-policy/style-src-attr-elem/style-src-elem-blocked-attr-allowed.html.ini
deleted file mode 100644
index c67fec5245c..00000000000
--- a/tests/wpt/meta/content-security-policy/style-src-attr-elem/style-src-elem-blocked-attr-allowed.html.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[style-src-elem-blocked-attr-allowed.html]
- expected: TIMEOUT
- [Should fire a security policy violation for the inline block]
- expected: NOTRUN
-
- [The inline style should not be applied and the attribute style should be applied]
- expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/style-src-attr-elem/style-src-elem-blocked-src-allowed.html.ini b/tests/wpt/meta/content-security-policy/style-src-attr-elem/style-src-elem-blocked-src-allowed.html.ini
deleted file mode 100644
index 06439a8cc0a..00000000000
--- a/tests/wpt/meta/content-security-policy/style-src-attr-elem/style-src-elem-blocked-src-allowed.html.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[style-src-elem-blocked-src-allowed.html]
- expected: TIMEOUT
- [Should fire a security policy violation event]
- expected: NOTRUN
-
- [The inline style should not be applied]
- expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/style-src/injected-inline-style-blocked.sub.html.ini b/tests/wpt/meta/content-security-policy/style-src/injected-inline-style-blocked.sub.html.ini
deleted file mode 100644
index 06132f67bfc..00000000000
--- a/tests/wpt/meta/content-security-policy/style-src/injected-inline-style-blocked.sub.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[injected-inline-style-blocked.sub.html]
- [Expecting logs: ["violated-directive=style-src-elem","violated-directive=style-src-elem","PASS"\]]
- expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/style-src/inline-style-allowed-while-cloning-objects.sub.html.ini b/tests/wpt/meta/content-security-policy/style-src/inline-style-allowed-while-cloning-objects.sub.html.ini
index 0c8111987c0..c99d7bd7844 100644
--- a/tests/wpt/meta/content-security-policy/style-src/inline-style-allowed-while-cloning-objects.sub.html.ini
+++ b/tests/wpt/meta/content-security-policy/style-src/inline-style-allowed-while-cloning-objects.sub.html.ini
@@ -1,13 +1,36 @@
[inline-style-allowed-while-cloning-objects.sub.html]
- expected: TIMEOUT
- [Test that violation report event was fired]
- expected: NOTRUN
+ [non-HTML namespace]
+ expected: FAIL
- [inline-style-allowed-while-cloning-objects 12]
+ [inline-style-allowed-while-cloning-objects 1]
expected: FAIL
- [inline-style-allowed-while-cloning-objects 14]
+ [inline-style-allowed-while-cloning-objects 3]
expected: FAIL
- [non-HTML namespace]
+ [inline-style-allowed-while-cloning-objects 5]
+ expected: FAIL
+
+ [inline-style-allowed-while-cloning-objects 7]
+ expected: FAIL
+
+ [inline-style-allowed-while-cloning-objects 8]
+ expected: FAIL
+
+ [inline-style-allowed-while-cloning-objects 9]
+ expected: FAIL
+
+ [inline-style-allowed-while-cloning-objects 10]
+ expected: FAIL
+
+ [inline-style-allowed-while-cloning-objects 11]
+ expected: FAIL
+
+ [inline-style-allowed-while-cloning-objects 17]
+ expected: FAIL
+
+ [inline-style-allowed-while-cloning-objects 18]
+ expected: FAIL
+
+ [inline-style-allowed-while-cloning-objects 19]
expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/style-src/inline-style-attribute-blocked.sub.html.ini b/tests/wpt/meta/content-security-policy/style-src/inline-style-attribute-blocked.sub.html.ini
deleted file mode 100644
index 92f00acdffe..00000000000
--- a/tests/wpt/meta/content-security-policy/style-src/inline-style-attribute-blocked.sub.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[inline-style-attribute-blocked.sub.html]
- [Expecting logs: ["violated-directive=style-src-attr","PASS"\]]
- expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/style-src/inline-style-blocked.sub.html.ini b/tests/wpt/meta/content-security-policy/style-src/inline-style-blocked.sub.html.ini
deleted file mode 100644
index 62bbd2f0e13..00000000000
--- a/tests/wpt/meta/content-security-policy/style-src/inline-style-blocked.sub.html.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[inline-style-blocked.sub.html]
- expected: TIMEOUT
- [Triggers securitypolicyviolation.]
- expected: TIMEOUT
-
- [Inline style element is blocked by CSP.]
- expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/style-src/style-src-hash-blocked.html.ini b/tests/wpt/meta/content-security-policy/style-src/style-src-hash-blocked.html.ini
deleted file mode 100644
index a79f011aec3..00000000000
--- a/tests/wpt/meta/content-security-policy/style-src/style-src-hash-blocked.html.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[style-src-hash-blocked.html]
- expected: TIMEOUT
- [Should not load style that does not match hash]
- expected: FAIL
-
- [Should fire a securitypolicyviolation event]
- expected: NOTRUN
diff --git a/tests/wpt/meta/content-security-policy/style-src/style-src-imported-style-blocked.html.ini b/tests/wpt/meta/content-security-policy/style-src/style-src-imported-style-blocked.html.ini
deleted file mode 100644
index fba57c0f24f..00000000000
--- a/tests/wpt/meta/content-security-policy/style-src/style-src-imported-style-blocked.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[style-src-imported-style-blocked.html]
- expected: TIMEOUT
- [Should fire a securitypolicyviolation event]
- expected: NOTRUN
diff --git a/tests/wpt/meta/content-security-policy/style-src/style-src-injected-inline-style-blocked.html.ini b/tests/wpt/meta/content-security-policy/style-src/style-src-injected-inline-style-blocked.html.ini
deleted file mode 100644
index bb5c48df1b0..00000000000
--- a/tests/wpt/meta/content-security-policy/style-src/style-src-injected-inline-style-blocked.html.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[style-src-injected-inline-style-blocked.html]
- expected: TIMEOUT
- [Injected style attributes should not be applied]
- expected: FAIL
-
- [Should fire a securitypolicyviolation event]
- expected: NOTRUN
diff --git a/tests/wpt/meta/content-security-policy/style-src/style-src-injected-stylesheet-blocked.sub.html.ini b/tests/wpt/meta/content-security-policy/style-src/style-src-injected-stylesheet-blocked.sub.html.ini
deleted file mode 100644
index 0b431bab548..00000000000
--- a/tests/wpt/meta/content-security-policy/style-src/style-src-injected-stylesheet-blocked.sub.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[style-src-injected-stylesheet-blocked.sub.html]
- expected: TIMEOUT
- [Should fire a securitypolicyviolation event]
- expected: NOTRUN
diff --git a/tests/wpt/meta/content-security-policy/style-src/style-src-inline-style-attribute-blocked.html.ini b/tests/wpt/meta/content-security-policy/style-src/style-src-inline-style-attribute-blocked.html.ini
deleted file mode 100644
index d910f28e56a..00000000000
--- a/tests/wpt/meta/content-security-policy/style-src/style-src-inline-style-attribute-blocked.html.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[style-src-inline-style-attribute-blocked.html]
- expected: TIMEOUT
- [Inline style attribute should not be applied without 'unsafe-inline']
- expected: FAIL
-
- [Should fire a securitypolicyviolation event]
- expected: NOTRUN
diff --git a/tests/wpt/meta/content-security-policy/style-src/style-src-inline-style-blocked.html.ini b/tests/wpt/meta/content-security-policy/style-src/style-src-inline-style-blocked.html.ini
deleted file mode 100644
index 33ee0df35af..00000000000
--- a/tests/wpt/meta/content-security-policy/style-src/style-src-inline-style-blocked.html.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[style-src-inline-style-blocked.html]
- expected: TIMEOUT
- [Inline style element should not load without 'unsafe-inline']
- expected: FAIL
-
- [Should fire a securitypolicyviolation event]
- expected: NOTRUN
diff --git a/tests/wpt/meta/content-security-policy/style-src/style-src-inline-style-nonce-blocked-error-event.html.ini b/tests/wpt/meta/content-security-policy/style-src/style-src-inline-style-nonce-blocked-error-event.html.ini
index 63a1c9b6240..eb2f1c46fbb 100644
--- a/tests/wpt/meta/content-security-policy/style-src/style-src-inline-style-nonce-blocked-error-event.html.ini
+++ b/tests/wpt/meta/content-security-policy/style-src/style-src-inline-style-nonce-blocked-error-event.html.ini
@@ -1,7 +1,4 @@
[style-src-inline-style-nonce-blocked-error-event.html]
expected: TIMEOUT
- [Should fire a securitypolicyviolation event]
- expected: NOTRUN
-
[Test that paragraph remains unmodified and error events received.]
expected: NOTRUN
diff --git a/tests/wpt/meta/content-security-policy/style-src/style-src-inline-style-nonce-blocked.html.ini b/tests/wpt/meta/content-security-policy/style-src/style-src-inline-style-nonce-blocked.html.ini
deleted file mode 100644
index df0fb590691..00000000000
--- a/tests/wpt/meta/content-security-policy/style-src/style-src-inline-style-nonce-blocked.html.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[style-src-inline-style-nonce-blocked.html]
- expected: TIMEOUT
- [Should not load inline style element with invalid nonce]
- expected: FAIL
-
- [Should fire a securitypolicyviolation event]
- expected: NOTRUN
diff --git a/tests/wpt/meta/content-security-policy/style-src/style-src-none-blocked.html.ini b/tests/wpt/meta/content-security-policy/style-src/style-src-none-blocked.html.ini
deleted file mode 100644
index c3014b37c31..00000000000
--- a/tests/wpt/meta/content-security-policy/style-src/style-src-none-blocked.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[style-src-none-blocked.html]
- expected: TIMEOUT
- [Should fire a securitypolicyviolation event]
- expected: NOTRUN
diff --git a/tests/wpt/meta/content-security-policy/style-src/style-src-stylesheet-nonce-blocked.html.ini b/tests/wpt/meta/content-security-policy/style-src/style-src-stylesheet-nonce-blocked.html.ini
deleted file mode 100644
index b8645f13da7..00000000000
--- a/tests/wpt/meta/content-security-policy/style-src/style-src-stylesheet-nonce-blocked.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[style-src-stylesheet-nonce-blocked.html]
- expected: TIMEOUT
- [Should fire a securitypolicyviolation event]
- expected: NOTRUN
diff --git a/tests/wpt/meta/content-security-policy/style-src/stylehash-basic-blocked.sub.html.ini b/tests/wpt/meta/content-security-policy/style-src/stylehash-basic-blocked.sub.html.ini
deleted file mode 100644
index 10375077b42..00000000000
--- a/tests/wpt/meta/content-security-policy/style-src/stylehash-basic-blocked.sub.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[stylehash-basic-blocked.sub.html]
- [Expecting alerts: ["PASS: The 'p' element's text is green, which means the style was correctly applied.", "violated-directive=style-src-elem"\]]
- expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/style-src/stylenonce-allowed.sub.html.ini b/tests/wpt/meta/content-security-policy/style-src/stylenonce-allowed.sub.html.ini
deleted file mode 100644
index ae2f83c41d8..00000000000
--- a/tests/wpt/meta/content-security-policy/style-src/stylenonce-allowed.sub.html.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[stylenonce-allowed.sub.html]
- expected: TIMEOUT
- [Should fire securitypolicyviolation]
- expected: NOTRUN
-
- [stylenonce-allowed]
- expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/style-src/stylenonce-blocked.sub.html.ini b/tests/wpt/meta/content-security-policy/style-src/stylenonce-blocked.sub.html.ini
deleted file mode 100644
index 97a86a69eea..00000000000
--- a/tests/wpt/meta/content-security-policy/style-src/stylenonce-blocked.sub.html.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[stylenonce-blocked.sub.html]
- expected: TIMEOUT
- [Should fire securitypolicyviolation]
- expected: NOTRUN
-
- [stylenonce-blocked]
- expected: FAIL
diff --git a/tests/wpt/meta/content-security-policy/unsafe-hashes/style_attribute_denied_missing_unsafe_hashes.html.ini b/tests/wpt/meta/content-security-policy/unsafe-hashes/style_attribute_denied_missing_unsafe_hashes.html.ini
deleted file mode 100644
index 26dc98e8f62..00000000000
--- a/tests/wpt/meta/content-security-policy/unsafe-hashes/style_attribute_denied_missing_unsafe_hashes.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[style_attribute_denied_missing_unsafe_hashes.html]
- expected: TIMEOUT
- [Test that the inline style attribute is blocked]
- expected: NOTRUN
diff --git a/tests/wpt/meta/content-security-policy/unsafe-hashes/style_attribute_denied_wrong_hash.html.ini b/tests/wpt/meta/content-security-policy/unsafe-hashes/style_attribute_denied_wrong_hash.html.ini
deleted file mode 100644
index 3031a4f6f77..00000000000
--- a/tests/wpt/meta/content-security-policy/unsafe-hashes/style_attribute_denied_wrong_hash.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[style_attribute_denied_wrong_hash.html]
- expected: TIMEOUT
- [Test that the inline style attribute is blocked]
- expected: NOTRUN
diff --git a/tests/wpt/meta/css/css-flexbox/flex-container-max-content-002.tentative.html.ini b/tests/wpt/meta/css/css-flexbox/flex-container-max-content-002.tentative.html.ini
new file mode 100644
index 00000000000..8b9bd7fdf97
--- /dev/null
+++ b/tests/wpt/meta/css/css-flexbox/flex-container-max-content-002.tentative.html.ini
@@ -0,0 +1,12 @@
+[flex-container-max-content-002.tentative.html]
+ [.flex 2]
+ expected: FAIL
+
+ [.flex 3]
+ expected: FAIL
+
+ [.flex 5]
+ expected: FAIL
+
+ [.flex 6]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/css-flexbox/flex-container-min-content-002.tentative.html.ini b/tests/wpt/meta/css/css-flexbox/flex-container-min-content-002.tentative.html.ini
new file mode 100644
index 00000000000..408a853b460
--- /dev/null
+++ b/tests/wpt/meta/css/css-flexbox/flex-container-min-content-002.tentative.html.ini
@@ -0,0 +1,21 @@
+[flex-container-min-content-002.tentative.html]
+ [.flex 2]
+ expected: FAIL
+
+ [.flex 3]
+ expected: FAIL
+
+ [.flex 5]
+ expected: FAIL
+
+ [.flex 6]
+ expected: FAIL
+
+ [.flex 13]
+ expected: FAIL
+
+ [.flex 14]
+ expected: FAIL
+
+ [.flex 15]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/css-gaps/flex/flex-gap-decorations-022.html.ini b/tests/wpt/meta/css/css-gaps/flex/flex-gap-decorations-022.html.ini
new file mode 100644
index 00000000000..1d8edacc4e1
--- /dev/null
+++ b/tests/wpt/meta/css/css-gaps/flex/flex-gap-decorations-022.html.ini
@@ -0,0 +1,2 @@
+[flex-gap-decorations-022.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-029.html.ini b/tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-029.html.ini
new file mode 100644
index 00000000000..6198f1bf534
--- /dev/null
+++ b/tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-029.html.ini
@@ -0,0 +1,2 @@
+[grid-gap-decorations-029.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-030.html.ini b/tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-030.html.ini
new file mode 100644
index 00000000000..76e055a6a0a
--- /dev/null
+++ b/tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-030.html.ini
@@ -0,0 +1,2 @@
+[grid-gap-decorations-030.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-031.html.ini b/tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-031.html.ini
new file mode 100644
index 00000000000..d9765648ea7
--- /dev/null
+++ b/tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-031.html.ini
@@ -0,0 +1,2 @@
+[grid-gap-decorations-031.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-032.html.ini b/tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-032.html.ini
new file mode 100644
index 00000000000..1e5a6bc2a68
--- /dev/null
+++ b/tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-032.html.ini
@@ -0,0 +1,2 @@
+[grid-gap-decorations-032.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-033.html.ini b/tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-033.html.ini
new file mode 100644
index 00000000000..aaeff2efe72
--- /dev/null
+++ b/tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-033.html.ini
@@ -0,0 +1,2 @@
+[grid-gap-decorations-033.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-38.html.ini b/tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-38.html.ini
new file mode 100644
index 00000000000..eb1f89bfb6d
--- /dev/null
+++ b/tests/wpt/meta/css/css-gaps/grid/grid-gap-decorations-38.html.ini
@@ -0,0 +1,2 @@
+[grid-gap-decorations-38.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/css-gaps/multicol/multicol-gap-decorations-017.html.ini b/tests/wpt/meta/css/css-gaps/multicol/multicol-gap-decorations-017.html.ini
new file mode 100644
index 00000000000..b4d67fce3e3
--- /dev/null
+++ b/tests/wpt/meta/css/css-gaps/multicol/multicol-gap-decorations-017.html.ini
@@ -0,0 +1,2 @@
+[multicol-gap-decorations-017.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/css-gaps/parsing/gap-decorations-col-rule-width.html.ini b/tests/wpt/meta/css/css-gaps/parsing/gap-decorations-col-rule-width.html.ini
new file mode 100644
index 00000000000..02d385579b9
--- /dev/null
+++ b/tests/wpt/meta/css/css-gaps/parsing/gap-decorations-col-rule-width.html.ini
@@ -0,0 +1,9 @@
+[gap-decorations-col-rule-width.html]
+ [`column-rule-width` should be `0px` when `column-rule-style` is `none` with single value]
+ expected: FAIL
+
+ [`column-rule-width` should be as specified regardless of `column-rule-style` with multiple values]
+ expected: FAIL
+
+ [`column-rule-width` should be as specified regardless of `column-rule-style` with multiple (repeat) values]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/css-images/linear-gradient-body-sibling-index.html.ini b/tests/wpt/meta/css/css-images/linear-gradient-body-sibling-index.html.ini
new file mode 100644
index 00000000000..638b6c21891
--- /dev/null
+++ b/tests/wpt/meta/css/css-images/linear-gradient-body-sibling-index.html.ini
@@ -0,0 +1,2 @@
+[linear-gradient-body-sibling-index.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/css-images/linear-gradient-sibling-index.html.ini b/tests/wpt/meta/css/css-images/linear-gradient-sibling-index.html.ini
new file mode 100644
index 00000000000..d81ff383b17
--- /dev/null
+++ b/tests/wpt/meta/css/css-images/linear-gradient-sibling-index.html.ini
@@ -0,0 +1,2 @@
+[linear-gradient-sibling-index.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/css-mixins/dashed-function-cycles.tentative.html.ini b/tests/wpt/meta/css/css-mixins/dashed-function-cycles.html.ini
index e16c79da6ad..7faf9c2b2e4 100644
--- a/tests/wpt/meta/css/css-mixins/dashed-function-cycles.tentative.html.ini
+++ b/tests/wpt/meta/css/css-mixins/dashed-function-cycles.html.ini
@@ -1,4 +1,4 @@
-[dashed-function-cycles.tentative.html]
+[dashed-function-cycles.html]
[Local with self-cycle]
expected: FAIL
@@ -11,7 +11,10 @@
[Local shadowing cyclic property --x]
expected: FAIL
- [Local shadowing cyclic outer local --x ]
+ [Local shadowing cyclic outer local --x]
+ expected: FAIL
+
+ [Argument shadowing cyclic outer local --x]
expected: FAIL
[Arguments shadowing cyclic properties]
@@ -53,6 +56,9 @@
[Cycle through local, other function]
expected: FAIL
+ [Cycle through local, other function, fallback in function]
+ expected: FAIL
+
[Cycle through various variables and other functions]
expected: FAIL
@@ -62,15 +68,6 @@
[Cyclic defaults]
expected: FAIL
- [Local shadowing cyclic outer local --x]
- expected: FAIL
-
- [Argument shadowing cyclic outer local --x]
- expected: FAIL
-
- [Cycle through local, other function, fallback in function]
- expected: FAIL
-
[Cyclic outer --b shadows custom property]
expected: FAIL
diff --git a/tests/wpt/meta/css/css-transforms/transform-translate-background-001.html.ini b/tests/wpt/meta/css/css-transforms/transform-translate-background-001.html.ini
deleted file mode 100644
index abb0cf51515..00000000000
--- a/tests/wpt/meta/css/css-transforms/transform-translate-background-001.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[transform-translate-background-001.html]
- expected: FAIL
diff --git a/tests/wpt/meta/css/css-transforms/transform-translate-background-002.html.ini b/tests/wpt/meta/css/css-transforms/transform-translate-background-002.html.ini
deleted file mode 100644
index bd8e02d68f5..00000000000
--- a/tests/wpt/meta/css/css-transforms/transform-translate-background-002.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[transform-translate-background-002.html]
- expected: FAIL
diff --git a/tests/wpt/meta/css/css-values/tree-counting/sibling-function-descriptors.tentative.html.ini b/tests/wpt/meta/css/css-values/tree-counting/sibling-function-descriptors.tentative.html.ini
index 287f1c19314..3c65d9bcf19 100644
--- a/tests/wpt/meta/css/css-values/tree-counting/sibling-function-descriptors.tentative.html.ini
+++ b/tests/wpt/meta/css/css-values/tree-counting/sibling-function-descriptors.tentative.html.ini
@@ -10,3 +10,15 @@
[sibling-count() should not be allowed in @font-face descriptors]
expected: FAIL
+
+ [sibling-index() should not be allowed in @font-palette-values descriptors]
+ expected: FAIL
+
+ [sibling-count() should not be allowed in @font-palette-values descriptors]
+ expected: FAIL
+
+ [sibling-index() should not be allowed in @counter-style descriptors]
+ expected: FAIL
+
+ [sibling-count() should not be allowed in @counter-style descriptors]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/css-values/tree-counting/sibling-index-keyframe-length-value-dynamic.html.ini b/tests/wpt/meta/css/css-values/tree-counting/sibling-index-keyframe-length-value-dynamic.html.ini
new file mode 100644
index 00000000000..f61a6205280
--- /dev/null
+++ b/tests/wpt/meta/css/css-values/tree-counting/sibling-index-keyframe-length-value-dynamic.html.ini
@@ -0,0 +1,6 @@
+[sibling-index-keyframe-length-value-dynamic.html]
+ [Initially, the sibling-index() is 2 for #target]
+ expected: FAIL
+
+ [Removing a preceding sibling of #target reduces the sibling-index()]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/css-values/tree-counting/sibling-index-keyframe-value-dynamic.html.ini b/tests/wpt/meta/css/css-values/tree-counting/sibling-index-keyframe-value-dynamic.html.ini
new file mode 100644
index 00000000000..df16c83f839
--- /dev/null
+++ b/tests/wpt/meta/css/css-values/tree-counting/sibling-index-keyframe-value-dynamic.html.ini
@@ -0,0 +1,6 @@
+[sibling-index-keyframe-value-dynamic.html]
+ [Initially, the sibling-index() is 3 for #target]
+ expected: FAIL
+
+ [Removing a preceding sibling of #target reduces the sibling-index()]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/cssom-view/scrollIntoView-container.html.ini b/tests/wpt/meta/css/cssom-view/scrollIntoView-container.html.ini
new file mode 100644
index 00000000000..0e9b4a98ac4
--- /dev/null
+++ b/tests/wpt/meta/css/cssom-view/scrollIntoView-container.html.ini
@@ -0,0 +1,15 @@
+[scrollIntoView-container.html]
+ [scrollIntoView() defaults to scrolling ancestors]
+ expected: FAIL
+
+ [scrollIntoView({container: 'all'}) scrolls ancestors]
+ expected: FAIL
+
+ [scrollIntoView({container: 'nearest'}) only scrolls nearest scroll container]
+ expected: FAIL
+
+ [scrollIntoView({container: 'nearest'}) doesn't stop at itself]
+ expected: FAIL
+
+ [scrollIntoView({container: 'nearest'}) doesn't propagate to outer frames]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/filter-effects/backdrop-filter-scale-transform.html.ini b/tests/wpt/meta/css/filter-effects/backdrop-filter-scale-transform.html.ini
new file mode 100644
index 00000000000..128ff19f7aa
--- /dev/null
+++ b/tests/wpt/meta/css/filter-effects/backdrop-filter-scale-transform.html.ini
@@ -0,0 +1,2 @@
+[backdrop-filter-scale-transform.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/geometry/DOMMatrix-001.html.ini b/tests/wpt/meta/css/geometry/DOMMatrix-001.html.ini
new file mode 100644
index 00000000000..cb607530097
--- /dev/null
+++ b/tests/wpt/meta/css/geometry/DOMMatrix-001.html.ini
@@ -0,0 +1,6 @@
+[DOMMatrix-001.html]
+ [new DOMMatrix("scale(sign(1em))")]
+ expected: FAIL
+
+ [new DOMMatrixReadOnly("scale(sign(1em))")]
+ expected: FAIL
diff --git a/tests/wpt/meta/custom-elements/parser/serializing-html-fragments-customized-builtins.html.ini b/tests/wpt/meta/custom-elements/parser/serializing-html-fragments-customized-builtins.html.ini
deleted file mode 100644
index 15f02f181e4..00000000000
--- a/tests/wpt/meta/custom-elements/parser/serializing-html-fragments-customized-builtins.html.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[serializing-html-fragments-customized-builtins.html]
- ["is" value should be serialized if the custom element has no "is" content attribute]
- expected: FAIL
-
- ["is" value should be serialized even for an undefined element]
- expected: FAIL
diff --git a/tests/wpt/meta/dom/events/webkit-animation-end-event.html.ini b/tests/wpt/meta/dom/events/webkit-animation-end-event.html.ini
index 3369a3a2a49..5750c2948be 100644
--- a/tests/wpt/meta/dom/events/webkit-animation-end-event.html.ini
+++ b/tests/wpt/meta/dom/events/webkit-animation-end-event.html.ini
@@ -1,28 +1,22 @@
[webkit-animation-end-event.html]
expected: TIMEOUT
- [onanimationend and onwebkitanimationend are not aliases]
- expected: FAIL
-
[dispatchEvent of a webkitAnimationEnd event does trigger a prefixed event handler or listener]
expected: FAIL
- [dispatchEvent of an animationend event does not trigger a prefixed event handler or listener]
- expected: FAIL
-
[onwebkitanimationend event handler should trigger for an animation]
- expected: FAIL
+ expected: TIMEOUT
[onwebkitanimationend event handler should not trigger if an unprefixed event handler also exists]
- expected: FAIL
+ expected: NOTRUN
[onwebkitanimationend event handler should not trigger if an unprefixed listener also exists]
- expected: FAIL
+ expected: NOTRUN
[event types for prefixed and unprefixed animationend event handlers should be named appropriately]
- expected: FAIL
+ expected: NOTRUN
[webkitAnimationEnd event listener should trigger for an animation]
- expected: TIMEOUT
+ expected: NOTRUN
[webkitAnimationEnd event listener should not trigger if an unprefixed listener also exists]
expected: NOTRUN
diff --git a/tests/wpt/meta/dom/events/webkit-animation-iteration-event.html.ini b/tests/wpt/meta/dom/events/webkit-animation-iteration-event.html.ini
index 880ef51e59d..233a98ed69b 100644
--- a/tests/wpt/meta/dom/events/webkit-animation-iteration-event.html.ini
+++ b/tests/wpt/meta/dom/events/webkit-animation-iteration-event.html.ini
@@ -1,28 +1,22 @@
[webkit-animation-iteration-event.html]
expected: TIMEOUT
- [onanimationiteration and onwebkitanimationiteration are not aliases]
- expected: FAIL
-
[dispatchEvent of a webkitAnimationIteration event does trigger a prefixed event handler or listener]
expected: FAIL
- [dispatchEvent of an animationiteration event does not trigger a prefixed event handler or listener]
- expected: FAIL
-
[onwebkitanimationiteration event handler should trigger for an animation]
- expected: FAIL
+ expected: TIMEOUT
[onwebkitanimationiteration event handler should not trigger if an unprefixed event handler also exists]
- expected: FAIL
+ expected: NOTRUN
[onwebkitanimationiteration event handler should not trigger if an unprefixed listener also exists]
- expected: FAIL
+ expected: NOTRUN
[event types for prefixed and unprefixed animationiteration event handlers should be named appropriately]
- expected: FAIL
+ expected: NOTRUN
[webkitAnimationIteration event listener should trigger for an animation]
- expected: TIMEOUT
+ expected: NOTRUN
[webkitAnimationIteration event listener should not trigger if an unprefixed listener also exists]
expected: NOTRUN
diff --git a/tests/wpt/meta/dom/events/webkit-animation-start-event.html.ini b/tests/wpt/meta/dom/events/webkit-animation-start-event.html.ini
index ca7e2b96918..6e3e5febb82 100644
--- a/tests/wpt/meta/dom/events/webkit-animation-start-event.html.ini
+++ b/tests/wpt/meta/dom/events/webkit-animation-start-event.html.ini
@@ -1,4 +1,5 @@
[webkit-animation-start-event.html]
+ expected: TIMEOUT
[onanimationstart and onwebkitanimationstart are not aliases]
expected: FAIL
@@ -8,32 +9,29 @@
[dispatchEvent of a webkitAnimationStart event does not trigger an unprefixed event handler or listener]
expected: FAIL
- [dispatchEvent of an animationstart event does not trigger a prefixed event handler or listener]
- expected: FAIL
-
[onwebkitanimationstart event handler should trigger for an animation]
- expected: FAIL
+ expected: TIMEOUT
[onwebkitanimationstart event handler should not trigger if an unprefixed event handler also exists]
- expected: FAIL
+ expected: NOTRUN
[onwebkitanimationstart event handler should not trigger if an unprefixed listener also exists]
- expected: FAIL
+ expected: NOTRUN
[event types for prefixed and unprefixed animationstart event handlers should be named appropriately]
- expected: FAIL
+ expected: NOTRUN
[webkitAnimationStart event listener should trigger for an animation]
- expected: FAIL
+ expected: NOTRUN
[webkitAnimationStart event listener should not trigger if an unprefixed listener also exists]
- expected: FAIL
+ expected: NOTRUN
[webkitAnimationStart event listener should not trigger if an unprefixed event handler also exists]
- expected: FAIL
+ expected: NOTRUN
[event types for prefixed and unprefixed animationstart event listeners should be named appropriately]
- expected: FAIL
+ expected: NOTRUN
[webkitAnimationStart event listener is case sensitive]
- expected: FAIL
+ expected: NOTRUN
diff --git a/tests/wpt/meta/dom/events/webkit-transition-end-event.html.ini b/tests/wpt/meta/dom/events/webkit-transition-end-event.html.ini
index b7fe8688a23..d1d6814c761 100644
--- a/tests/wpt/meta/dom/events/webkit-transition-end-event.html.ini
+++ b/tests/wpt/meta/dom/events/webkit-transition-end-event.html.ini
@@ -1,27 +1,15 @@
[webkit-transition-end-event.html]
- [dispatchEvent of an transitionend event does not trigger a prefixed event handler or listener]
- expected: FAIL
-
[onwebkittransitionend event handler should trigger for an animation]
expected: FAIL
- [onwebkittransitionend event handler should not trigger if an unprefixed listener also exists]
- expected: FAIL
-
[event types for prefixed and unprefixed transitionend event handlers should be named appropriately]
expected: FAIL
[event types for prefixed and unprefixed transitionend event listeners should be named appropriately]
expected: FAIL
- [ontransitionend and onwebkittransitionend are not aliases]
- expected: FAIL
-
[webkitTransitionEnd event listener should trigger for an animation]
expected: FAIL
[dispatchEvent of a webkitTransitionEnd event does trigger a prefixed event handler or listener]
expected: FAIL
-
- [onwebkittransitionend event handler should not trigger if an unprefixed event handler also exists]
- expected: FAIL
diff --git a/tests/wpt/meta/fetch/http-cache/pragma-no-cache-with-cache-control.html.ini b/tests/wpt/meta/fetch/http-cache/pragma-no-cache-with-cache-control.html.ini
new file mode 100644
index 00000000000..5e3ad18d789
--- /dev/null
+++ b/tests/wpt/meta/fetch/http-cache/pragma-no-cache-with-cache-control.html.ini
@@ -0,0 +1,3 @@
+[pragma-no-cache-with-cache-control.html]
+ [Response with Cache-Control: max-age=2592000, public and Pragma: no-cache should be cached]
+ expected: FAIL
diff --git a/tests/wpt/meta/fetch/metadata/generated/css-font-face.https.sub.tentative.html.ini b/tests/wpt/meta/fetch/metadata/generated/css-font-face.https.sub.tentative.html.ini
index a02fcf871ab..12a2314e747 100644
--- a/tests/wpt/meta/fetch/metadata/generated/css-font-face.https.sub.tentative.html.ini
+++ b/tests/wpt/meta/fetch/metadata/generated/css-font-face.https.sub.tentative.html.ini
@@ -52,3 +52,6 @@
[sec-fetch-storage-access - Cross-site]
expected: FAIL
+
+ [sec-fetch-user]
+ expected: FAIL
diff --git a/tests/wpt/meta/fetch/metadata/generated/svg-image.https.sub.html.ini b/tests/wpt/meta/fetch/metadata/generated/svg-image.https.sub.html.ini
index 5d85add7f82..1e547ae5f21 100644
--- a/tests/wpt/meta/fetch/metadata/generated/svg-image.https.sub.html.ini
+++ b/tests/wpt/meta/fetch/metadata/generated/svg-image.https.sub.html.ini
@@ -1,70 +1,69 @@
[svg-image.https.sub.html]
- expected: TIMEOUT
[sec-fetch-site - Same origin no attributes]
- expected: TIMEOUT
+ expected: FAIL
[sec-fetch-site - Cross-site no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-site - Same site no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-site - Cross-Site -> Same Origin no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-site - Cross-Site -> Same-Site no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-site - Cross-Site -> Cross-Site no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-site - Same-Origin -> Same Origin no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-site - Same-Origin -> Same-Site no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-site - Same-Origin -> Cross-Site no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-site - Same-Site -> Same Origin no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-site - Same-Site -> Same-Site no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-site - Same-Site -> Cross-Site no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-site - HTTPS downgrade-upgrade no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-mode no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-mode attributes: crossorigin]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-mode attributes: crossorigin=anonymous]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-mode attributes: crossorigin=use-credentials]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-dest no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-user no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-storage-access - Cross-site no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-storage-access - Same site no attributes]
- expected: NOTRUN
+ expected: FAIL
diff --git a/tests/wpt/meta/fetch/metadata/generated/svg-image.sub.html.ini b/tests/wpt/meta/fetch/metadata/generated/svg-image.sub.html.ini
index dc3a74db79c..cec7a0548b1 100644
--- a/tests/wpt/meta/fetch/metadata/generated/svg-image.sub.html.ini
+++ b/tests/wpt/meta/fetch/metadata/generated/svg-image.sub.html.ini
@@ -1,55 +1,54 @@
[svg-image.sub.html]
- expected: TIMEOUT
[sec-fetch-site - Not sent to non-trustworthy same-origin destination no attributes]
- expected: TIMEOUT
+ expected: FAIL
[sec-fetch-site - Not sent to non-trustworthy same-site destination no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-site - Not sent to non-trustworthy cross-site destination no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-mode - Not sent to non-trustworthy same-origin destination no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-mode - Not sent to non-trustworthy same-site destination no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-mode - Not sent to non-trustworthy cross-site destination no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-dest - Not sent to non-trustworthy same-origin destination no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-dest - Not sent to non-trustworthy same-site destination no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-dest - Not sent to non-trustworthy cross-site destination no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-user - Not sent to non-trustworthy same-origin destination no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-user - Not sent to non-trustworthy same-site destination no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-user - Not sent to non-trustworthy cross-site destination no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-site - HTTPS downgrade (header not sent) no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-site - HTTPS upgrade no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-site - HTTPS downgrade-upgrade no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-storage-access - Not sent to non-trustworthy same-site destination no attributes]
- expected: NOTRUN
+ expected: FAIL
[sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination no attributes]
- expected: NOTRUN
+ expected: FAIL
diff --git a/tests/wpt/meta/fetch/metadata/report.https.sub.html.ini b/tests/wpt/meta/fetch/metadata/report.https.sub.html.ini
index 058151d81ce..0348be9f384 100644
--- a/tests/wpt/meta/fetch/metadata/report.https.sub.html.ini
+++ b/tests/wpt/meta/fetch/metadata/report.https.sub.html.ini
@@ -1,2 +1,10 @@
[report.https.sub.html]
- expected: TIMEOUT
+ expected: ERROR
+ [same-origin report]
+ expected: TIMEOUT
+
+ [same-site report]
+ expected: TIMEOUT
+
+ [cross-site report]
+ expected: TIMEOUT
diff --git a/tests/wpt/meta/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html.ini b/tests/wpt/meta/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html.ini
index 2241163d563..6313c3e33dd 100644
--- a/tests/wpt/meta/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html.ini
+++ b/tests/wpt/meta/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-nosrc.html.ini
@@ -2,8 +2,5 @@
[window.open]
expected: FAIL
- [form submission]
- expected: FAIL
-
[link click]
expected: FAIL
diff --git a/tests/wpt/meta/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html.ini b/tests/wpt/meta/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html.ini
index 5f9a07f92f0..277436ffad2 100644
--- a/tests/wpt/meta/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html.ini
+++ b/tests/wpt/meta/html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html.ini
@@ -1,6 +1,3 @@
[iframe-src-aboutblank-navigate-immediately.html]
[Navigating to a different document with window.open]
expected: FAIL
-
- [Navigating to a different document with form submission]
- expected: FAIL
diff --git a/tests/wpt/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html.ini b/tests/wpt/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html.ini
new file mode 100644
index 00000000000..60a4fa51f8a
--- /dev/null
+++ b/tests/wpt/meta/html/browsers/browsing-the-web/navigating-across-documents/replace-before-load/a-click.html.ini
@@ -0,0 +1,3 @@
+[a-click.html]
+ [aElement.click() before the load event must NOT replace]
+ expected: FAIL
diff --git a/tests/wpt/meta/html/browsers/browsing-the-web/scroll-to-fragid/scroll-to-top.html.ini b/tests/wpt/meta/html/browsers/browsing-the-web/scroll-to-fragid/scroll-to-top.html.ini
deleted file mode 100644
index 6e8c7d20d83..00000000000
--- a/tests/wpt/meta/html/browsers/browsing-the-web/scroll-to-fragid/scroll-to-top.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[scroll-to-top.html]
- [Fragment Navigation: When fragid is TOP scroll to the top of the document]
- expected: FAIL
diff --git a/tests/wpt/meta/html/browsers/history/the-history-interface/traverse_the_history_3.html.ini b/tests/wpt/meta/html/browsers/history/the-history-interface/traverse_the_history_5.html.ini
index a03a8322165..7a5fcb79165 100644
--- a/tests/wpt/meta/html/browsers/history/the-history-interface/traverse_the_history_3.html.ini
+++ b/tests/wpt/meta/html/browsers/history/the-history-interface/traverse_the_history_5.html.ini
@@ -1,3 +1,3 @@
-[traverse_the_history_3.html]
+[traverse_the_history_5.html]
[Multiple history traversals, last would be aborted]
expected: FAIL
diff --git a/tests/wpt/meta/html/browsers/windows/auxiliary-browsing-contexts/named-lookup-noopener.html.ini b/tests/wpt/meta/html/browsers/windows/auxiliary-browsing-contexts/named-lookup-noopener.html.ini
new file mode 100644
index 00000000000..6907354e049
--- /dev/null
+++ b/tests/wpt/meta/html/browsers/windows/auxiliary-browsing-contexts/named-lookup-noopener.html.ini
@@ -0,0 +1,7 @@
+[named-lookup-noopener.html]
+ expected: TIMEOUT
+ [Two noopener window.open() calls create separate windows]
+ expected: TIMEOUT
+
+ [Two rel=noopener <a href> clicks create separate windows]
+ expected: NOTRUN
diff --git a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html.ini b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html.ini
index c94490ae5ee..9cf5b2af86a 100644
--- a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html.ini
+++ b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html.ini
@@ -1,13 +1,13 @@
[createImageBitmap-drawImage.html]
expected: ERROR
[createImageBitmap from an OffscreenCanvas resized, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from a vector HTMLImageElement resized, and drawImage on the created ImageBitmap]
expected: FAIL
[createImageBitmap from an OffscreenCanvas, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an HTMLCanvasElement, and drawImage on the created ImageBitmap]
expected: FAIL
@@ -22,34 +22,34 @@
expected: FAIL
[createImageBitmap from an ImageData scaled down, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an OffscreenCanvas scaled down, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from a bitmap SVGImageElement, and drawImage on the created ImageBitmap]
- expected: TIMEOUT
+ expected: FAIL
[createImageBitmap from a bitmap SVGImageElement resized, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an HTMLCanvasElement scaled down, and drawImage on the created ImageBitmap]
expected: FAIL
[createImageBitmap from a vector SVGImageElement, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an ImageData scaled up, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an HTMLVideoElement with negative sw/sh, and drawImage on the created ImageBitmap]
expected: FAIL
[createImageBitmap from a bitmap SVGImageElement scaled up, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from a vector SVGImageElement resized, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from a bitmap HTMLImageElement scaled up, and drawImage on the created ImageBitmap]
expected: FAIL
@@ -58,10 +58,10 @@
expected: FAIL
[createImageBitmap from a vector SVGImageElement scaled down, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from a vector SVGImageElement with negative sw/sh, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from a bitmap HTMLImageElement scaled down, and drawImage on the created ImageBitmap]
expected: FAIL
@@ -73,16 +73,16 @@
expected: FAIL
[createImageBitmap from a Blob scaled down, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an ImageData resized, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from a vector HTMLImageElement scaled down, and drawImage on the created ImageBitmap]
expected: FAIL
[createImageBitmap from an ImageData, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an HTMLCanvasElement with negative sw/sh, and drawImage on the created ImageBitmap]
expected: FAIL
@@ -91,16 +91,16 @@
expected: FAIL
[createImageBitmap from a vector SVGImageElement scaled up, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an ImageBitmap, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from a Blob scaled up, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from a bitmap SVGImageElement scaled down, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an HTMLVideoElement scaled up, and drawImage on the created ImageBitmap]
expected: FAIL
@@ -109,7 +109,7 @@
expected: FAIL
[createImageBitmap from a Blob, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an HTMLVideoElement resized, and drawImage on the created ImageBitmap]
expected: FAIL
@@ -118,31 +118,31 @@
expected: FAIL
[createImageBitmap from an ImageBitmap scaled down, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from a Blob with negative sw/sh, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from a bitmap SVGImageElement with negative sw/sh, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an ImageData with negative sw/sh, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an ImageBitmap scaled up, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an ImageBitmap resized, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an OffscreenCanvas scaled up, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an HTMLCanvasElement scaled up, and drawImage on the created ImageBitmap]
expected: FAIL
[createImageBitmap from a Blob resized, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an HTMLVideoElement from a data URL, and drawImage on the created ImageBitmap]
expected: FAIL
@@ -154,10 +154,10 @@
expected: FAIL
[createImageBitmap from an ImageBitmap with negative sw/sh, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an OffscreenCanvas with negative sw/sh, and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from a vector HTMLImageElement with negative sw/sh, and drawImage on the created ImageBitmap]
expected: FAIL
diff --git a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html.ini b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html.ini
index f08e8a2918a..0cb93c5abc9 100644
--- a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html.ini
+++ b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html.ini
@@ -4,7 +4,7 @@
expected: NOTRUN
[createImageBitmap from a vector SVGImageElement imageOrientation: "flipY", and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an HTMLCanvasElement imageOrientation: "none", and drawImage on the created ImageBitmap]
expected: FAIL
@@ -16,7 +16,7 @@
expected: FAIL
[createImageBitmap from an OffscreenCanvas imageOrientation: "flipY", and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from a vector HTMLImageElement imageOrientation: "flipY", and drawImage on the created ImageBitmap]
expected: FAIL
@@ -25,7 +25,7 @@
expected: FAIL
[createImageBitmap from a Blob imageOrientation: "flipY", and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an HTMLCanvasElement imageOrientation: "flipY", and drawImage on the created ImageBitmap]
expected: FAIL
@@ -34,7 +34,7 @@
expected: FAIL
[createImageBitmap from an ImageData imageOrientation: "flipY", and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from a bitmap HTMLImageElement imageOrientation: "flipY", and drawImage on the created ImageBitmap]
expected: FAIL
@@ -43,7 +43,7 @@
expected: NOTRUN
[createImageBitmap from an ImageBitmap imageOrientation: "flipY", and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from a vector HTMLImageElement imageOrientation: "none", and drawImage on the created ImageBitmap]
expected: FAIL
@@ -61,7 +61,7 @@
expected: FAIL
[createImageBitmap from a bitmap SVGImageElement imageOrientation: "flipY", and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from a bitmap SVGImageElement imageOrientation: "none", and drawImage on the created ImageBitmap]
expected: TIMEOUT
@@ -82,19 +82,19 @@
expected: FAIL
[createImageBitmap from a bitmap SVGImageElement imageOrientation: "from-image", and drawImage on the created ImageBitmap]
- expected: TIMEOUT
+ expected: FAIL
[createImageBitmap from a vector SVGImageElement imageOrientation: "from-image", and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an OffscreenCanvas imageOrientation: "from-image", and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an ImageData imageOrientation: "from-image", and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from an ImageBitmap imageOrientation: "from-image", and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[createImageBitmap from a Blob imageOrientation: "from-image", and drawImage on the created ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
diff --git a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html.ini b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html.ini
index 8daf97ba765..aba246039a0 100644
--- a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html.ini
+++ b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html.ini
@@ -1,5 +1,5 @@
[createImageBitmap-invalid-args.html]
- expected: TIMEOUT
+ expected: CRASH
[createImageBitmap with a vector HTMLImageElement source and sw set to 0]
expected: FAIL
diff --git a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html.ini b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html.ini
index 210d26f7740..d7226bfed74 100644
--- a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html.ini
+++ b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html.ini
@@ -1,34 +1,34 @@
[createImageBitmap-origin.sub.html]
expected: TIMEOUT
[redirected to cross-origin HTMLVideoElement: origin unclear 2dContext.drawImage]
- expected: NOTRUN
+ expected: FAIL
[redirected to cross-origin HTMLVideoElement: origin unclear bitmaprenderer.transferFromImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[unclean HTMLCanvasElement: origin unclear bitmaprenderer.transferFromImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[unclean HTMLCanvasElement: origin unclear getImageData]
- expected: NOTRUN
+ expected: FAIL
[cross-origin HTMLVideoElement: origin unclear getImageData]
- expected: NOTRUN
+ expected: FAIL
[cross-origin SVGImageElement: origin unclear bitmaprenderer.transferFromImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[cross-origin HTMLVideoElement: origin unclear bitmaprenderer.transferFromImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[redirected to same-origin HTMLVideoElement: origin unclear getImageData]
- expected: NOTRUN
+ expected: FAIL
[cross-origin HTMLImageElement: origin unclear 2dContext.drawImage]
expected: FAIL
[cross-origin SVGImageElement: origin unclear 2dContext.drawImage]
- expected: NOTRUN
+ expected: FAIL
[cross-origin HTMLImageElement: origin unclear getImageData]
expected: FAIL
@@ -37,28 +37,28 @@
expected: FAIL
[redirected to same-origin HTMLVideoElement: origin unclear 2dContext.drawImage]
- expected: NOTRUN
+ expected: FAIL
[unclean ImageBitmap: origin unclear bitmaprenderer.transferFromImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[redirected to same-origin HTMLVideoElement: origin unclear bitmaprenderer.transferFromImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[redirected to cross-origin HTMLVideoElement: origin unclear getImageData]
- expected: NOTRUN
+ expected: FAIL
[unclean ImageBitmap: origin unclear getImageData]
- expected: NOTRUN
+ expected: FAIL
[unclean HTMLCanvasElement: origin unclear 2dContext.drawImage]
- expected: NOTRUN
+ expected: FAIL
[cross-origin HTMLVideoElement: origin unclear 2dContext.drawImage]
- expected: NOTRUN
+ expected: FAIL
[unclean ImageBitmap: origin unclear 2dContext.drawImage]
- expected: NOTRUN
+ expected: FAIL
[cross-origin SVGImageElement: origin unclear getImageData]
- expected: TIMEOUT
+ expected: FAIL
diff --git a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html.ini b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html.ini
index 7616121487c..4234bcb2133 100644
--- a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html.ini
+++ b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html.ini
@@ -1,7 +1,6 @@
[createImageBitmap-serializable.html]
- expected: TIMEOUT
[Serialize ImageBitmap created from a vector SVGImageElement]
- expected: NOTRUN
+ expected: FAIL
[Serialize ImageBitmap created from an HTMLVideoElement]
expected: FAIL
@@ -13,25 +12,25 @@
expected: FAIL
[Serialize ImageBitmap created from an OffscreenCanvas]
- expected: NOTRUN
+ expected: FAIL
[Serialize ImageBitmap created from a vector HTMLImageElement]
expected: FAIL
[Serialize ImageBitmap created from a Blob]
- expected: NOTRUN
+ expected: FAIL
[Serialize ImageBitmap created from a bitmap HTMLImageElement]
expected: FAIL
[Serializing a non-origin-clean ImageBitmap throws.]
- expected: NOTRUN
+ expected: FAIL
[Serialize ImageBitmap created from an ImageData]
- expected: NOTRUN
+ expected: FAIL
[Serialize ImageBitmap created from an ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[Serialize ImageBitmap created from a bitmap SVGImageElement]
- expected: TIMEOUT
+ expected: FAIL
diff --git a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html.ini b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html.ini
index 7e036a1c4e4..cff84fd3a26 100644
--- a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html.ini
+++ b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html.ini
@@ -1,22 +1,21 @@
[createImageBitmap-transfer.html]
- expected: TIMEOUT
[Transfer ImageBitmap created from a vector HTMLImageElement]
expected: FAIL
[Transfer ImageBitmap created from an ImageData]
- expected: NOTRUN
+ expected: FAIL
[Transfer ImageBitmap created from a vector SVGImageElement]
- expected: NOTRUN
+ expected: FAIL
[Transfer ImageBitmap created from a Blob]
- expected: NOTRUN
+ expected: FAIL
[Transfer ImageBitmap created from an HTMLCanvasElement]
expected: FAIL
[Transfer ImageBitmap created from an OffscreenCanvas]
- expected: NOTRUN
+ expected: FAIL
[Transfer ImageBitmap created from a bitmap HTMLImageElement]
expected: FAIL
@@ -25,13 +24,13 @@
expected: FAIL
[Transfer ImageBitmap created from a bitmap SVGImageElement]
- expected: TIMEOUT
+ expected: FAIL
[Transfer ImageBitmap created from an ImageBitmap]
- expected: NOTRUN
+ expected: FAIL
[Transfer ImageBitmap created from an HTMLVideoElement]
expected: FAIL
[Transferring a non-origin-clean ImageBitmap throws.]
- expected: NOTRUN
+ expected: FAIL
diff --git a/tests/wpt/meta/html/canvas/element/manual/text/canvas.2d.disconnected.html.ini b/tests/wpt/meta/html/canvas/element/manual/text/canvas.2d.disconnected.html.ini
new file mode 100644
index 00000000000..5cdcce07c65
--- /dev/null
+++ b/tests/wpt/meta/html/canvas/element/manual/text/canvas.2d.disconnected.html.ini
@@ -0,0 +1,2 @@
+[canvas.2d.disconnected.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.basics.html.ini b/tests/wpt/meta/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.basics.html.ini
deleted file mode 100644
index 1a3c292f9f2..00000000000
--- a/tests/wpt/meta/html/canvas/element/pixel-manipulation/2d.imageData.object.ctor.basics.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[2d.imageData.object.ctor.basics.html]
- [Testing different type of ImageData constructor]
- expected: FAIL
diff --git a/tests/wpt/meta/html/dom/idlharness.https.html.ini b/tests/wpt/meta/html/dom/idlharness.https.html.ini
index 25375c6ea47..981cd249aa6 100644
--- a/tests/wpt/meta/html/dom/idlharness.https.html.ini
+++ b/tests/wpt/meta/html/dom/idlharness.https.html.ini
@@ -1657,48 +1657,27 @@
[Window interface: window must inherit property "external" with the proper type]
expected: FAIL
- [Window interface: attribute onwebkitanimationstart]
- expected: FAIL
-
[Window interface: window must inherit property "statusbar" with the proper type]
expected: FAIL
- [Document interface: new Document() must inherit property "onwebkittransitionend" with the proper type]
- expected: FAIL
-
[Document interface: calling queryCommandEnabled(DOMString) on documentWithHandlers with too few arguments must throw TypeError]
expected: FAIL
- [Document interface: new Document() must inherit property "onwebkitanimationstart" with the proper type]
- expected: FAIL
-
[Document interface: calling queryCommandIndeterm(DOMString) on documentWithHandlers with too few arguments must throw TypeError]
expected: FAIL
- [Document interface: iframe.contentDocument must inherit property "onwebkitanimationend" with the proper type]
- expected: FAIL
-
[Document interface: iframe.contentDocument must inherit property "dir" with the proper type]
expected: FAIL
- [Window interface: window must inherit property "onwebkitanimationiteration" with the proper type]
- expected: FAIL
-
[Window interface: window must inherit property "applicationCache" with the proper type]
expected: FAIL
- [Document interface: attribute onauxclick]
- expected: FAIL
-
[Window interface: window must inherit property "menubar" with the proper type]
expected: FAIL
[Window interface: internal [[SetPrototypeOf\]\] method of interface prototype object - setting to a new value via Reflect.setPrototypeOf should return false]
expected: FAIL
- [Document interface: new Document() must inherit property "onwebkitanimationiteration" with the proper type]
- expected: FAIL
-
[Document interface: attribute designMode]
expected: FAIL
@@ -1714,9 +1693,6 @@
[Document interface: calling execCommand(DOMString, optional boolean, optional DOMString) on new Document() with too few arguments must throw TypeError]
expected: FAIL
- [Document interface: attribute onwebkitanimationiteration]
- expected: FAIL
-
[Document interface: operation queryCommandEnabled(DOMString)]
expected: FAIL
@@ -1741,42 +1717,24 @@
[Document interface: iframe.contentDocument must inherit property "linkColor" with the proper type]
expected: FAIL
- [Document interface: documentWithHandlers must inherit property "onslotchange" with the proper type]
- expected: FAIL
-
[Document interface: documentWithHandlers must inherit property "alinkColor" with the proper type]
expected: FAIL
[Document interface: documentWithHandlers must inherit property "dir" with the proper type]
expected: FAIL
- [Window interface: window must inherit property "onslotchange" with the proper type]
- expected: FAIL
-
[SVGAElement includes HTMLHyperlinkElementUtils: member names are unique]
expected: FAIL
[Window interface: attribute applicationCache]
expected: FAIL
- [Document interface: iframe.contentDocument must inherit property "onwebkitanimationstart" with the proper type]
- expected: FAIL
-
- [Document interface: attribute onslotchange]
- expected: FAIL
-
[Document interface: calling queryCommandIndeterm(DOMString) on iframe.contentDocument with too few arguments must throw TypeError]
expected: FAIL
[Document interface: iframe.contentDocument must inherit property "queryCommandValue(DOMString)" with the proper type]
expected: FAIL
- [Document interface: iframe.contentDocument must inherit property "onwebkitanimationiteration" with the proper type]
- expected: FAIL
-
- [Document interface: attribute onwebkittransitionend]
- expected: FAIL
-
[Document interface: iframe.contentDocument must inherit property "all" with the proper type]
expected: FAIL
@@ -1789,9 +1747,6 @@
[Document interface: attribute linkColor]
expected: FAIL
- [Window interface: attribute onwebkitanimationiteration]
- expected: FAIL
-
[Window interface: window must inherit property "scrollbars" with the proper type]
expected: FAIL
@@ -1807,18 +1762,12 @@
[Document interface: iframe.contentDocument must inherit property "designMode" with the proper type]
expected: FAIL
- [Document interface: documentWithHandlers must inherit property "onwebkitanimationiteration" with the proper type]
- expected: FAIL
-
[Window interface: attribute toolbar]
expected: FAIL
[Window interface: attribute statusbar]
expected: FAIL
- [Window interface: window must inherit property "onwebkittransitionend" with the proper type]
- expected: FAIL
-
[Document interface: new Document() must inherit property "linkColor" with the proper type]
expected: FAIL
@@ -1828,15 +1777,9 @@
[Document interface: new Document() must inherit property "queryCommandState(DOMString)" with the proper type]
expected: FAIL
- [Window interface: attribute onwebkittransitionend]
- expected: FAIL
-
[Document interface: calling execCommand(DOMString, optional boolean, optional DOMString) on iframe.contentDocument with too few arguments must throw TypeError]
expected: FAIL
- [Document interface: new Document() must inherit property "onslotchange" with the proper type]
- expected: FAIL
-
[Document interface: documentWithHandlers must inherit property "designMode" with the proper type]
expected: FAIL
@@ -1855,9 +1798,6 @@
[Window interface: window must inherit property "personalbar" with the proper type]
expected: FAIL
- [Document interface: attribute onwebkitanimationend]
- expected: FAIL
-
[Window interface: internal [[SetPrototypeOf\]\] method of interface prototype object - setting to a new value via __proto__ should throw a TypeError]
expected: FAIL
@@ -1882,24 +1822,15 @@
[Document interface: attribute vlinkColor]
expected: FAIL
- [Window interface: window must inherit property "onauxclick" with the proper type]
- expected: FAIL
-
[Document interface: iframe.contentDocument must inherit property "queryCommandEnabled(DOMString)" with the proper type]
expected: FAIL
- [Document interface: iframe.contentDocument must inherit property "onslotchange" with the proper type]
- expected: FAIL
-
[Document interface: new Document() must inherit property "alinkColor" with the proper type]
expected: FAIL
[Document interface: new Document() must inherit property "queryCommandEnabled(DOMString)" with the proper type]
expected: FAIL
- [Window interface: attribute onslotchange]
- expected: FAIL
-
[Document interface: new Document() must inherit property "all" with the proper type]
expected: FAIL
@@ -1909,24 +1840,6 @@
[Window interface: window must inherit property "print()" with the proper type]
expected: FAIL
- [Document interface: documentWithHandlers must inherit property "onauxclick" with the proper type]
- expected: FAIL
-
- [Document interface: documentWithHandlers must inherit property "onwebkitanimationend" with the proper type]
- expected: FAIL
-
- [Document interface: new Document() must inherit property "onauxclick" with the proper type]
- expected: FAIL
-
- [Document interface: documentWithHandlers must inherit property "onwebkitanimationstart" with the proper type]
- expected: FAIL
-
- [Document interface: attribute onwebkitanimationstart]
- expected: FAIL
-
- [Document interface: iframe.contentDocument must inherit property "onauxclick" with the proper type]
- expected: FAIL
-
[Document interface: calling queryCommandValue(DOMString) on documentWithHandlers with too few arguments must throw TypeError]
expected: FAIL
@@ -1939,9 +1852,6 @@
[Document interface: calling queryCommandState(DOMString) on new Document() with too few arguments must throw TypeError]
expected: FAIL
- [Window interface: attribute onauxclick]
- expected: FAIL
-
[Document interface: iframe.contentDocument must inherit property "queryCommandState(DOMString)" with the proper type]
expected: FAIL
@@ -1954,48 +1864,30 @@
[Document interface: new Document() must inherit property "queryCommandValue(DOMString)" with the proper type]
expected: FAIL
- [Window interface: window must inherit property "onwebkitanimationend" with the proper type]
- expected: FAIL
-
[Window interface: window must inherit property "toolbar" with the proper type]
expected: FAIL
[Document interface: documentWithHandlers must inherit property "linkColor" with the proper type]
expected: FAIL
- [Document interface: new Document() must inherit property "onwebkitanimationend" with the proper type]
- expected: FAIL
-
[Document interface: operation queryCommandIndeterm(DOMString)]
expected: FAIL
- [Document interface: iframe.contentDocument must inherit property "onwebkittransitionend" with the proper type]
- expected: FAIL
-
[Document interface: calling queryCommandIndeterm(DOMString) on new Document() with too few arguments must throw TypeError]
expected: FAIL
- [Window interface: attribute onwebkitanimationend]
- expected: FAIL
-
[Document interface: calling execCommand(DOMString, optional boolean, optional DOMString) on documentWithHandlers with too few arguments must throw TypeError]
expected: FAIL
[Document interface: documentWithHandlers must inherit property "queryCommandState(DOMString)" with the proper type]
expected: FAIL
- [Document interface: documentWithHandlers must inherit property "onwebkittransitionend" with the proper type]
- expected: FAIL
-
[Document interface: new Document() must inherit property "vlinkColor" with the proper type]
expected: FAIL
[Window interface: window must inherit property "locationbar" with the proper type]
expected: FAIL
- [Window interface: window must inherit property "onwebkitanimationstart" with the proper type]
- expected: FAIL
-
[Document interface: documentWithHandlers must inherit property "queryCommandIndeterm(DOMString)" with the proper type]
expected: FAIL
@@ -2023,24 +1915,12 @@
[Window interface: attribute clientInformation]
expected: FAIL
- [Window interface: attribute oncontextlost]
- expected: FAIL
-
- [Window interface: attribute oncontextrestored]
- expected: FAIL
-
[Window interface: operation reportError(any)]
expected: FAIL
[Window interface: window must inherit property "clientInformation" with the proper type]
expected: FAIL
- [Window interface: window must inherit property "oncontextlost" with the proper type]
- expected: FAIL
-
- [Window interface: window must inherit property "oncontextrestored" with the proper type]
- expected: FAIL
-
[Window interface: window must inherit property "reportError(any)" with the proper type]
expected: FAIL
@@ -2050,129 +1930,15 @@
[Document interface: attribute onvisibilitychange]
expected: FAIL
- [Document interface: attribute oncontextlost]
- expected: FAIL
-
- [Document interface: attribute oncontextrestored]
- expected: FAIL
-
[Document interface: iframe.contentDocument must inherit property "onvisibilitychange" with the proper type]
expected: FAIL
- [Document interface: iframe.contentDocument must inherit property "oncontextlost" with the proper type]
- expected: FAIL
-
- [Document interface: iframe.contentDocument must inherit property "oncontextrestored" with the proper type]
- expected: FAIL
-
[Document interface: new Document() must inherit property "onvisibilitychange" with the proper type]
expected: FAIL
- [Document interface: new Document() must inherit property "oncontextlost" with the proper type]
- expected: FAIL
-
- [Document interface: new Document() must inherit property "oncontextrestored" with the proper type]
- expected: FAIL
-
[Document interface: documentWithHandlers must inherit property "onvisibilitychange" with the proper type]
expected: FAIL
- [Document interface: documentWithHandlers must inherit property "oncontextlost" with the proper type]
- expected: FAIL
-
- [Document interface: documentWithHandlers must inherit property "oncontextrestored" with the proper type]
- expected: FAIL
-
- [Window interface: attribute onbeforeinput]
- expected: FAIL
-
- [Window interface: attribute onbeforematch]
- expected: FAIL
-
- [Window interface: attribute onscrollend]
- expected: FAIL
-
- [Window interface: window must inherit property "onbeforeinput" with the proper type]
- expected: FAIL
-
- [Window interface: window must inherit property "onbeforematch" with the proper type]
- expected: FAIL
-
- [Window interface: window must inherit property "onscrollend" with the proper type]
- expected: FAIL
-
- [Document interface: attribute onbeforeinput]
- expected: FAIL
-
- [Document interface: attribute onbeforematch]
- expected: FAIL
-
- [Document interface: attribute onscrollend]
- expected: FAIL
-
- [Document interface: iframe.contentDocument must inherit property "onbeforeinput" with the proper type]
- expected: FAIL
-
- [Document interface: iframe.contentDocument must inherit property "onbeforematch" with the proper type]
- expected: FAIL
-
- [Document interface: iframe.contentDocument must inherit property "onscrollend" with the proper type]
- expected: FAIL
-
- [Document interface: new Document() must inherit property "onbeforeinput" with the proper type]
- expected: FAIL
-
- [Document interface: new Document() must inherit property "onbeforematch" with the proper type]
- expected: FAIL
-
- [Document interface: new Document() must inherit property "onscrollend" with the proper type]
- expected: FAIL
-
- [Document interface: documentWithHandlers must inherit property "onbeforeinput" with the proper type]
- expected: FAIL
-
- [Document interface: documentWithHandlers must inherit property "onbeforematch" with the proper type]
- expected: FAIL
-
- [Document interface: documentWithHandlers must inherit property "onscrollend" with the proper type]
- expected: FAIL
-
- [Window interface: attribute oncopy]
- expected: FAIL
-
- [Window interface: attribute oncut]
- expected: FAIL
-
- [Window interface: attribute onpaste]
- expected: FAIL
-
- [Window interface: window must inherit property "oncopy" with the proper type]
- expected: FAIL
-
- [Window interface: window must inherit property "oncut" with the proper type]
- expected: FAIL
-
- [Window interface: window must inherit property "onpaste" with the proper type]
- expected: FAIL
-
- [Window interface: attribute onbeforetoggle]
- expected: FAIL
-
- [Window interface: window must inherit property "onbeforetoggle" with the proper type]
- expected: FAIL
-
- [Document interface: attribute onbeforetoggle]
- expected: FAIL
-
- [Document interface: iframe.contentDocument must inherit property "onbeforetoggle" with the proper type]
- expected: FAIL
-
- [Document interface: new Document() must inherit property "onbeforetoggle" with the proper type]
- expected: FAIL
-
- [Document interface: documentWithHandlers must inherit property "onbeforetoggle" with the proper type]
- expected: FAIL
-
[Window interface: attribute navigation]
expected: FAIL
@@ -2191,18 +1957,6 @@
[Document interface: calling parseHTMLUnsafe(DOMString) on documentWithHandlers with too few arguments must throw TypeError]
expected: FAIL
- [Window interface: attribute onpagereveal]
- expected: FAIL
-
- [Window interface: window must inherit property "onpagereveal" with the proper type]
- expected: FAIL
-
- [Window interface: attribute onpageswap]
- expected: FAIL
-
- [Window interface: window must inherit property "onpageswap" with the proper type]
- expected: FAIL
-
[Document interface: operation parseHTMLUnsafe(HTMLString)]
expected: FAIL
@@ -2227,24 +1981,6 @@
[Document interface: calling parseHTMLUnsafe((TrustedHTML or DOMString)) on documentWithHandlers with too few arguments must throw TypeError]
expected: FAIL
- [Window interface: attribute oncommand]
- expected: FAIL
-
- [Window interface: window must inherit property "oncommand" with the proper type]
- expected: FAIL
-
- [Document interface: attribute oncommand]
- expected: FAIL
-
- [Document interface: iframe.contentDocument must inherit property "oncommand" with the proper type]
- expected: FAIL
-
- [Document interface: new Document() must inherit property "oncommand" with the proper type]
- expected: FAIL
-
- [Document interface: documentWithHandlers must inherit property "oncommand" with the proper type]
- expected: FAIL
-
[idlharness.https.html?include=HTML.*]
[HTMLTableSectionElement interface: document.createElement("tfoot") must inherit property "align" with the proper type]
@@ -2610,12 +2346,6 @@
[HTMLImageElement interface: document.createElement("img") must inherit property "decoding" with the proper type]
expected: FAIL
- [HTMLElement interface: attribute onwebkitanimationiteration]
- expected: FAIL
-
- [HTMLElement interface: attribute onslotchange]
- expected: FAIL
-
[HTMLVideoElement interface: attribute width]
expected: FAIL
@@ -2727,9 +2457,6 @@
[HTMLAnchorElement interface: attribute type]
expected: FAIL
- [HTMLElement interface: attribute onwebkitanimationend]
- expected: FAIL
-
[HTMLInputElement interface: attribute height]
expected: FAIL
@@ -2949,9 +2676,6 @@
[HTMLElement interface: attribute tabIndex]
expected: FAIL
- [HTMLElement interface: attribute onwebkitanimationstart]
- expected: FAIL
-
[HTMLImageElement interface: new Image() must inherit property "loading" with the proper type]
expected: FAIL
@@ -3639,9 +3363,6 @@
[HTMLAllCollection interface: document.all must inherit property "namedItem(DOMString)" with the proper type]
expected: FAIL
- [HTMLElement interface: attribute onwebkittransitionend]
- expected: FAIL
-
[HTMLLinkElement interface: document.createElement("link") must inherit property "imageSrcset" with the proper type]
expected: FAIL
@@ -3945,12 +3666,6 @@
[HTMLSlotElement interface: calling assign((Element or Text)...) on document.createElement("slot") with too few arguments must throw TypeError]
expected: FAIL
- [HTMLElement interface: attribute oncontextlost]
- expected: FAIL
-
- [HTMLElement interface: attribute oncontextrestored]
- expected: FAIL
-
[HTMLElement interface: document.createElement("noscript") must inherit property "oncontextlost" with the proper type]
expected: FAIL
@@ -4044,15 +3759,6 @@
[HTMLElement interface: attribute inert]
expected: FAIL
- [HTMLElement interface: attribute onbeforeinput]
- expected: FAIL
-
- [HTMLElement interface: attribute onbeforematch]
- expected: FAIL
-
- [HTMLElement interface: attribute onscrollend]
- expected: FAIL
-
[HTMLElement interface: document.createElement("noscript") must inherit property "inert" with the proper type]
expected: FAIL
@@ -4317,36 +4023,12 @@
[HTMLSelectElement interface: document.createElement("select") must inherit property "showPicker()" with the proper type]
expected: FAIL
- [HTMLBodyElement interface: attribute onpagereveal]
- expected: FAIL
-
- [HTMLBodyElement interface: document.createElement("body") must inherit property "onpagereveal" with the proper type]
- expected: FAIL
-
- [HTMLFrameSetElement interface: attribute onpagereveal]
- expected: FAIL
-
- [HTMLFrameSetElement interface: document.createElement("frameset") must inherit property "onpagereveal" with the proper type]
- expected: FAIL
-
[HTMLTemplateElement interface: attribute shadowRootClonable]
expected: FAIL
[HTMLTemplateElement interface: document.createElement("template") must inherit property "shadowRootClonable" with the proper type]
expected: FAIL
- [HTMLBodyElement interface: attribute onpageswap]
- expected: FAIL
-
- [HTMLBodyElement interface: document.createElement("body") must inherit property "onpageswap" with the proper type]
- expected: FAIL
-
- [HTMLFrameSetElement interface: attribute onpageswap]
- expected: FAIL
-
- [HTMLFrameSetElement interface: document.createElement("frameset") must inherit property "onpageswap" with the proper type]
- expected: FAIL
-
[HTMLElement interface: attribute writingSuggestions]
expected: FAIL
@@ -5441,231 +5123,6 @@
[External interface: window.external must inherit property "IsSearchProviderInstalled()" with the proper type]
expected: FAIL
- [SVGElement interface: attribute onabort]
- expected: FAIL
-
- [SVGElement interface: attribute onauxclick]
- expected: FAIL
-
- [SVGElement interface: attribute onbeforeinput]
- expected: FAIL
-
- [SVGElement interface: attribute onbeforematch]
- expected: FAIL
-
- [SVGElement interface: attribute onbeforetoggle]
- expected: FAIL
-
- [SVGElement interface: attribute onblur]
- expected: FAIL
-
- [SVGElement interface: attribute oncancel]
- expected: FAIL
-
- [SVGElement interface: attribute oncanplay]
- expected: FAIL
-
- [SVGElement interface: attribute oncanplaythrough]
- expected: FAIL
-
- [SVGElement interface: attribute onchange]
- expected: FAIL
-
- [SVGElement interface: attribute onclick]
- expected: FAIL
-
- [SVGElement interface: attribute onclose]
- expected: FAIL
-
- [SVGElement interface: attribute oncontextlost]
- expected: FAIL
-
- [SVGElement interface: attribute oncontextmenu]
- expected: FAIL
-
- [SVGElement interface: attribute oncontextrestored]
- expected: FAIL
-
- [SVGElement interface: attribute oncopy]
- expected: FAIL
-
- [SVGElement interface: attribute oncuechange]
- expected: FAIL
-
- [SVGElement interface: attribute oncut]
- expected: FAIL
-
- [SVGElement interface: attribute ondblclick]
- expected: FAIL
-
- [SVGElement interface: attribute ondrag]
- expected: FAIL
-
- [SVGElement interface: attribute ondragend]
- expected: FAIL
-
- [SVGElement interface: attribute ondragenter]
- expected: FAIL
-
- [SVGElement interface: attribute ondragleave]
- expected: FAIL
-
- [SVGElement interface: attribute ondragover]
- expected: FAIL
-
- [SVGElement interface: attribute ondragstart]
- expected: FAIL
-
- [SVGElement interface: attribute ondrop]
- expected: FAIL
-
- [SVGElement interface: attribute ondurationchange]
- expected: FAIL
-
- [SVGElement interface: attribute onemptied]
- expected: FAIL
-
- [SVGElement interface: attribute onended]
- expected: FAIL
-
- [SVGElement interface: attribute onerror]
- expected: FAIL
-
- [SVGElement interface: attribute onfocus]
- expected: FAIL
-
- [SVGElement interface: attribute onformdata]
- expected: FAIL
-
- [SVGElement interface: attribute oninput]
- expected: FAIL
-
- [SVGElement interface: attribute oninvalid]
- expected: FAIL
-
- [SVGElement interface: attribute onkeydown]
- expected: FAIL
-
- [SVGElement interface: attribute onkeypress]
- expected: FAIL
-
- [SVGElement interface: attribute onkeyup]
- expected: FAIL
-
- [SVGElement interface: attribute onload]
- expected: FAIL
-
- [SVGElement interface: attribute onloadeddata]
- expected: FAIL
-
- [SVGElement interface: attribute onloadedmetadata]
- expected: FAIL
-
- [SVGElement interface: attribute onloadstart]
- expected: FAIL
-
- [SVGElement interface: attribute onmousedown]
- expected: FAIL
-
- [SVGElement interface: attribute onmouseenter]
- expected: FAIL
-
- [SVGElement interface: attribute onmouseleave]
- expected: FAIL
-
- [SVGElement interface: attribute onmousemove]
- expected: FAIL
-
- [SVGElement interface: attribute onmouseout]
- expected: FAIL
-
- [SVGElement interface: attribute onmouseover]
- expected: FAIL
-
- [SVGElement interface: attribute onmouseup]
- expected: FAIL
-
- [SVGElement interface: attribute onpaste]
- expected: FAIL
-
- [SVGElement interface: attribute onpause]
- expected: FAIL
-
- [SVGElement interface: attribute onplay]
- expected: FAIL
-
- [SVGElement interface: attribute onplaying]
- expected: FAIL
-
- [SVGElement interface: attribute onprogress]
- expected: FAIL
-
- [SVGElement interface: attribute onratechange]
- expected: FAIL
-
- [SVGElement interface: attribute onreset]
- expected: FAIL
-
- [SVGElement interface: attribute onresize]
- expected: FAIL
-
- [SVGElement interface: attribute onscroll]
- expected: FAIL
-
- [SVGElement interface: attribute onscrollend]
- expected: FAIL
-
- [SVGElement interface: attribute onsecuritypolicyviolation]
- expected: FAIL
-
- [SVGElement interface: attribute onseeked]
- expected: FAIL
-
- [SVGElement interface: attribute onseeking]
- expected: FAIL
-
- [SVGElement interface: attribute onselect]
- expected: FAIL
-
- [SVGElement interface: attribute onslotchange]
- expected: FAIL
-
- [SVGElement interface: attribute onstalled]
- expected: FAIL
-
- [SVGElement interface: attribute onsubmit]
- expected: FAIL
-
- [SVGElement interface: attribute onsuspend]
- expected: FAIL
-
- [SVGElement interface: attribute ontimeupdate]
- expected: FAIL
-
- [SVGElement interface: attribute ontoggle]
- expected: FAIL
-
- [SVGElement interface: attribute onvolumechange]
- expected: FAIL
-
- [SVGElement interface: attribute onwaiting]
- expected: FAIL
-
- [SVGElement interface: attribute onwebkitanimationend]
- expected: FAIL
-
- [SVGElement interface: attribute onwebkitanimationiteration]
- expected: FAIL
-
- [SVGElement interface: attribute onwebkitanimationstart]
- expected: FAIL
-
- [SVGElement interface: attribute onwebkittransitionend]
- expected: FAIL
-
- [SVGElement interface: attribute onwheel]
- expected: FAIL
-
[SVGElement interface: attribute dataset]
expected: FAIL
@@ -5762,9 +5219,6 @@
[CommandEvent interface: attribute command]
expected: FAIL
- [SVGElement interface: attribute oncommand]
- expected: FAIL
-
[CanvasRenderingContext2D interface: attribute lang]
expected: FAIL
@@ -5866,42 +5320,6 @@
[HTMLElement interface: attribute popover]
expected: FAIL
- [HTMLElement interface: attribute onauxclick]
- expected: FAIL
-
- [HTMLElement interface: attribute onbeforeinput]
- expected: FAIL
-
- [HTMLElement interface: attribute onbeforematch]
- expected: FAIL
-
- [HTMLElement interface: attribute onbeforetoggle]
- expected: FAIL
-
- [HTMLElement interface: attribute oncontextlost]
- expected: FAIL
-
- [HTMLElement interface: attribute oncontextrestored]
- expected: FAIL
-
- [HTMLElement interface: attribute onscrollend]
- expected: FAIL
-
- [HTMLElement interface: attribute onslotchange]
- expected: FAIL
-
- [HTMLElement interface: attribute onwebkitanimationend]
- expected: FAIL
-
- [HTMLElement interface: attribute onwebkitanimationiteration]
- expected: FAIL
-
- [HTMLElement interface: attribute onwebkitanimationstart]
- expected: FAIL
-
- [HTMLElement interface: attribute onwebkittransitionend]
- expected: FAIL
-
[HTMLElement interface: attribute enterKeyHint]
expected: FAIL
@@ -5950,42 +5368,6 @@
[HTMLElement interface: document.createElement("noscript") must inherit property "popover" with the proper type]
expected: FAIL
- [HTMLElement interface: document.createElement("noscript") must inherit property "onauxclick" with the proper type]
- expected: FAIL
-
- [HTMLElement interface: document.createElement("noscript") must inherit property "onbeforeinput" with the proper type]
- expected: FAIL
-
- [HTMLElement interface: document.createElement("noscript") must inherit property "onbeforematch" with the proper type]
- expected: FAIL
-
- [HTMLElement interface: document.createElement("noscript") must inherit property "onbeforetoggle" with the proper type]
- expected: FAIL
-
- [HTMLElement interface: document.createElement("noscript") must inherit property "oncontextlost" with the proper type]
- expected: FAIL
-
- [HTMLElement interface: document.createElement("noscript") must inherit property "oncontextrestored" with the proper type]
- expected: FAIL
-
- [HTMLElement interface: document.createElement("noscript") must inherit property "onscrollend" with the proper type]
- expected: FAIL
-
- [HTMLElement interface: document.createElement("noscript") must inherit property "onslotchange" with the proper type]
- expected: FAIL
-
- [HTMLElement interface: document.createElement("noscript") must inherit property "onwebkitanimationend" with the proper type]
- expected: FAIL
-
- [HTMLElement interface: document.createElement("noscript") must inherit property "onwebkitanimationiteration" with the proper type]
- expected: FAIL
-
- [HTMLElement interface: document.createElement("noscript") must inherit property "onwebkitanimationstart" with the proper type]
- expected: FAIL
-
- [HTMLElement interface: document.createElement("noscript") must inherit property "onwebkittransitionend" with the proper type]
- expected: FAIL
-
[HTMLElement interface: document.createElement("noscript") must inherit property "enterKeyHint" with the proper type]
expected: FAIL
@@ -6064,12 +5446,6 @@
[HTMLBodyElement interface: attribute aLink]
expected: FAIL
- [HTMLBodyElement interface: attribute onpagereveal]
- expected: FAIL
-
- [HTMLBodyElement interface: attribute onpageswap]
- expected: FAIL
-
[HTMLBodyElement interface: document.createElement("body") must inherit property "link" with the proper type]
expected: FAIL
@@ -6079,12 +5455,6 @@
[HTMLBodyElement interface: document.createElement("body") must inherit property "aLink" with the proper type]
expected: FAIL
- [HTMLBodyElement interface: document.createElement("body") must inherit property "onpagereveal" with the proper type]
- expected: FAIL
-
- [HTMLBodyElement interface: document.createElement("body") must inherit property "onpageswap" with the proper type]
- expected: FAIL
-
[HTMLHeadingElement interface: attribute align]
expected: FAIL
@@ -7681,24 +7051,12 @@
[HTMLFrameSetElement interface: attribute rows]
expected: FAIL
- [HTMLFrameSetElement interface: attribute onpagereveal]
- expected: FAIL
-
- [HTMLFrameSetElement interface: attribute onpageswap]
- expected: FAIL
-
[HTMLFrameSetElement interface: document.createElement("frameset") must inherit property "cols" with the proper type]
expected: FAIL
[HTMLFrameSetElement interface: document.createElement("frameset") must inherit property "rows" with the proper type]
expected: FAIL
- [HTMLFrameSetElement interface: document.createElement("frameset") must inherit property "onpagereveal" with the proper type]
- expected: FAIL
-
- [HTMLFrameSetElement interface: document.createElement("frameset") must inherit property "onpageswap" with the proper type]
- expected: FAIL
-
[HTMLFrameElement interface: attribute name]
expected: FAIL
@@ -7813,12 +7171,6 @@
[HTMLDialogElement interface: operation requestClose(optional DOMString)]
expected: FAIL
- [HTMLElement interface: attribute oncommand]
- expected: FAIL
-
- [HTMLElement interface: document.createElement("noscript") must inherit property "oncommand" with the proper type]
- expected: FAIL
-
[HTMLButtonElement interface: attribute command]
expected: FAIL
diff --git a/tests/wpt/meta/html/rendering/replaced-elements/the-select-element/select-1-block-size.html.ini b/tests/wpt/meta/html/rendering/replaced-elements/the-select-element/select-1-block-size.html.ini
deleted file mode 100644
index 706aff5771a..00000000000
--- a/tests/wpt/meta/html/rendering/replaced-elements/the-select-element/select-1-block-size.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[select-1-block-size.html]
- expected: FAIL
diff --git a/tests/wpt/meta/html/rendering/replaced-elements/the-select-element/select-multiple-re-add-option-via-document-fragment.html.ini b/tests/wpt/meta/html/rendering/replaced-elements/the-select-element/select-multiple-re-add-option-via-document-fragment.html.ini
deleted file mode 100644
index bacaac5e98d..00000000000
--- a/tests/wpt/meta/html/rendering/replaced-elements/the-select-element/select-multiple-re-add-option-via-document-fragment.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[select-multiple-re-add-option-via-document-fragment.html]
- expected: FAIL
diff --git a/tests/wpt/meta/html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html.ini b/tests/wpt/meta/html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html.ini
index cacfdf56967..81329bc1635 100644
--- a/tests/wpt/meta/html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html.ini
+++ b/tests/wpt/meta/html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html.ini
@@ -1,16 +1,16 @@
[security.pattern.fillStyle.sub.html]
expected: TIMEOUT
[cross-origin SVGImageElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean]
- expected: TIMEOUT
+ expected: FAIL
[cross-origin HTMLVideoElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean]
- expected: NOTRUN
+ expected: FAIL
[redirected to cross-origin HTMLVideoElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean]
- expected: NOTRUN
+ expected: FAIL
[redirected to same-origin HTMLVideoElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean]
- expected: NOTRUN
+ expected: TIMEOUT
[unclean HTMLCanvasElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean]
expected: NOTRUN
diff --git a/tests/wpt/meta/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html.ini b/tests/wpt/meta/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html.ini
index 6a420504feb..7da2bc5ac80 100644
--- a/tests/wpt/meta/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html.ini
+++ b/tests/wpt/meta/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html.ini
@@ -1,3 +1,4 @@
[iframe_sandbox_popups_escaping-3.html]
+ expected: TIMEOUT
[Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used]
- expected: FAIL
+ expected: TIMEOUT
diff --git a/tests/wpt/meta/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html.ini b/tests/wpt/meta/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html.ini
index bbc1f35d8d9..e8872b3585b 100644
--- a/tests/wpt/meta/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html.ini
+++ b/tests/wpt/meta/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html.ini
@@ -1,3 +1,4 @@
[iframe_sandbox_popups_nonescaping-1.html]
+ expected: TIMEOUT
[Check that popups from a sandboxed iframe do not escape the sandbox]
- expected: FAIL
+ expected: NOTRUN
diff --git a/tests/wpt/meta/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html.ini b/tests/wpt/meta/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html.ini
index d5fd800f09d..ff6467094b8 100644
--- a/tests/wpt/meta/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html.ini
+++ b/tests/wpt/meta/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html.ini
@@ -1,4 +1,3 @@
[iframe_sandbox_popups_nonescaping-3.html]
- expected: TIMEOUT
[Check that popups from a sandboxed iframe do not escape the sandbox]
- expected: NOTRUN
+ expected: FAIL
diff --git a/tests/wpt/meta/html/semantics/forms/form-submission-0/reparent-form-during-planned-navigation-task.html.ini b/tests/wpt/meta/html/semantics/forms/form-submission-0/reparent-form-during-planned-navigation-task.html.ini
deleted file mode 100644
index 7682a4830bf..00000000000
--- a/tests/wpt/meta/html/semantics/forms/form-submission-0/reparent-form-during-planned-navigation-task.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[reparent-form-during-planned-navigation-task.html]
- expected: TIMEOUT
- [reparent-form-during-planned-navigation-task]
- expected: TIMEOUT
diff --git a/tests/wpt/meta/html/semantics/forms/form-submission-0/text-plain.window.js.ini b/tests/wpt/meta/html/semantics/forms/form-submission-0/text-plain.window.js.ini
index 1abe0eb8b88..320f8cf856c 100644
--- a/tests/wpt/meta/html/semantics/forms/form-submission-0/text-plain.window.js.ini
+++ b/tests/wpt/meta/html/semantics/forms/form-submission-0/text-plain.window.js.ini
@@ -181,3 +181,6 @@
[text/plain: non-ASCII in filename (formdata event)]
expected: FAIL
+
+ [text/plain: 0x00 in name (normal form)]
+ expected: FAIL
diff --git a/tests/wpt/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/blob-url.any.js.ini b/tests/wpt/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/blob-url.any.js.ini
index 095b97aa5a0..8dba4e9c469 100644
--- a/tests/wpt/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/blob-url.any.js.ini
+++ b/tests/wpt/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/blob-url.any.js.ini
@@ -5,9 +5,6 @@
[Different blob URLs pointing to the same blob resolve to different modules]
expected: FAIL
- [Revoking a blob URL immediately after calling import will not fail]
- expected: FAIL
-
[blob-url.any.worker-module.html]
expected: TIMEOUT
diff --git a/tests/wpt/meta/html/semantics/the-button-element/command-and-commandfor/button-event-dispatch-content-attribute.html.ini b/tests/wpt/meta/html/semantics/the-button-element/command-and-commandfor/button-event-dispatch-content-attribute.html.ini
index bf4c7b0b9c9..3a75e5ee0ee 100644
--- a/tests/wpt/meta/html/semantics/the-button-element/command-and-commandfor/button-event-dispatch-content-attribute.html.ini
+++ b/tests/wpt/meta/html/semantics/the-button-element/command-and-commandfor/button-event-dispatch-content-attribute.html.ini
@@ -1,6 +1,3 @@
[button-event-dispatch-content-attribute.html]
[oncommand content attribute works]
expected: FAIL
-
- [oncommand content with a value of false prevents default]
- expected: FAIL
diff --git a/tests/wpt/meta/html/semantics/the-button-element/interest-target/interesttarget-outline-appearance.tentative.html.ini b/tests/wpt/meta/html/semantics/the-button-element/interest-target/interesttarget-outline-appearance.tentative.html.ini
deleted file mode 100644
index 45513bfcfe5..00000000000
--- a/tests/wpt/meta/html/semantics/the-button-element/interest-target/interesttarget-outline-appearance.tentative.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[interesttarget-outline-appearance.tentative.html]
- expected: FAIL
diff --git a/tests/wpt/meta/html/webappapis/scripting/events/event-handler-all-global-events.html.ini b/tests/wpt/meta/html/webappapis/scripting/events/event-handler-all-global-events.html.ini
index cd77f908ffb..5c286c73da4 100644
--- a/tests/wpt/meta/html/webappapis/scripting/events/event-handler-all-global-events.html.ini
+++ b/tests/wpt/meta/html/webappapis/scripting/events/event-handler-all-global-events.html.ini
@@ -1,520 +1,16 @@
[event-handler-all-global-events.html]
- [onabort: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onabort: the default value must be null]
- expected: FAIL
-
- [onauxclick: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onauxclick: the default value must be null]
- expected: FAIL
-
- [onauxclick: the content attribute must be compiled into a function as the corresponding property]
- expected: FAIL
-
- [onblur: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onblur: the default value must be null]
- expected: FAIL
-
- [oncancel: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [oncancel: the default value must be null]
- expected: FAIL
-
- [oncanplay: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [oncanplay: the default value must be null]
- expected: FAIL
-
- [oncanplaythrough: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [oncanplaythrough: the default value must be null]
- expected: FAIL
-
- [onchange: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onchange: the default value must be null]
- expected: FAIL
-
- [onclick: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onclick: the default value must be null]
- expected: FAIL
-
- [onclose: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onclose: the default value must be null]
- expected: FAIL
-
- [oncontextlost: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [oncontextlost: the default value must be null]
- expected: FAIL
-
- [oncontextlost: the content attribute must be compiled into a function as the corresponding property]
- expected: FAIL
-
- [oncontextmenu: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [oncontextmenu: the default value must be null]
- expected: FAIL
-
- [oncontextrestored: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [oncontextrestored: the default value must be null]
- expected: FAIL
-
- [oncontextrestored: the content attribute must be compiled into a function as the corresponding property]
- expected: FAIL
-
- [oncuechange: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [oncuechange: the default value must be null]
- expected: FAIL
-
- [ondblclick: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [ondblclick: the default value must be null]
- expected: FAIL
-
- [ondrag: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [ondrag: the default value must be null]
- expected: FAIL
-
- [ondragend: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [ondragend: the default value must be null]
- expected: FAIL
-
- [ondragenter: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [ondragenter: the default value must be null]
- expected: FAIL
-
- [ondragleave: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [ondragleave: the default value must be null]
- expected: FAIL
-
- [ondragover: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [ondragover: the default value must be null]
- expected: FAIL
-
- [ondragstart: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [ondragstart: the default value must be null]
- expected: FAIL
-
- [ondrop: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [ondrop: the default value must be null]
- expected: FAIL
-
- [ondurationchange: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [ondurationchange: the default value must be null]
- expected: FAIL
-
- [onemptied: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onemptied: the default value must be null]
- expected: FAIL
-
- [onended: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onended: the default value must be null]
- expected: FAIL
-
- [onfocus: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onfocus: the default value must be null]
- expected: FAIL
-
- [onformdata: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onformdata: the default value must be null]
- expected: FAIL
-
- [oninput: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [oninput: the default value must be null]
- expected: FAIL
-
- [oninvalid: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [oninvalid: the default value must be null]
- expected: FAIL
-
- [onkeydown: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onkeydown: the default value must be null]
- expected: FAIL
-
- [onkeypress: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onkeypress: the default value must be null]
- expected: FAIL
-
- [onkeyup: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onkeyup: the default value must be null]
- expected: FAIL
-
- [onload: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onload: the default value must be null]
- expected: FAIL
-
- [onloadeddata: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onloadeddata: the default value must be null]
- expected: FAIL
-
- [onloadedmetadata: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onloadedmetadata: the default value must be null]
- expected: FAIL
-
- [onloadstart: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onloadstart: the default value must be null]
- expected: FAIL
-
- [onmousedown: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onmousedown: the default value must be null]
- expected: FAIL
-
- [onmouseenter: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onmouseenter: the default value must be null]
- expected: FAIL
-
- [onmouseleave: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onmouseleave: the default value must be null]
- expected: FAIL
-
- [onmousemove: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onmousemove: the default value must be null]
- expected: FAIL
-
- [onmouseout: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onmouseout: the default value must be null]
- expected: FAIL
-
- [onmouseover: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onmouseover: the default value must be null]
- expected: FAIL
-
- [onmouseup: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onmouseup: the default value must be null]
- expected: FAIL
-
- [onpause: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onpause: the default value must be null]
- expected: FAIL
-
- [onplay: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onplay: the default value must be null]
- expected: FAIL
-
- [onplaying: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onplaying: the default value must be null]
- expected: FAIL
-
- [onprogress: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onprogress: the default value must be null]
- expected: FAIL
-
- [onratechange: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onratechange: the default value must be null]
- expected: FAIL
-
- [onreset: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onreset: the default value must be null]
- expected: FAIL
-
- [onresize: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onresize: the default value must be null]
- expected: FAIL
-
- [onscroll: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onscroll: the default value must be null]
- expected: FAIL
-
- [onsecuritypolicyviolation: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onsecuritypolicyviolation: the default value must be null]
- expected: FAIL
-
- [onseeked: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onseeked: the default value must be null]
- expected: FAIL
-
- [onseeking: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onseeking: the default value must be null]
- expected: FAIL
-
- [onselect: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onselect: the default value must be null]
- expected: FAIL
-
- [onslotchange: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onslotchange: the default value must be null]
- expected: FAIL
-
- [onslotchange: the content attribute must be compiled into a function as the corresponding property]
- expected: FAIL
-
- [onstalled: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onstalled: the default value must be null]
- expected: FAIL
-
- [onsubmit: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onsubmit: the default value must be null]
- expected: FAIL
-
- [onsuspend: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onsuspend: the default value must be null]
- expected: FAIL
-
- [ontimeupdate: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [ontimeupdate: the default value must be null]
- expected: FAIL
-
- [ontoggle: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [ontoggle: the default value must be null]
- expected: FAIL
-
- [onvolumechange: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onvolumechange: the default value must be null]
- expected: FAIL
-
- [onwaiting: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onwaiting: the default value must be null]
- expected: FAIL
-
- [onwebkitanimationend: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onwebkitanimationend: the default value must be null]
- expected: FAIL
-
- [onwebkitanimationend: the content attribute must be compiled into a function as the corresponding property]
- expected: FAIL
-
[onwebkitanimationend: the content attribute must execute when an event is dispatched]
expected: FAIL
- [onwebkitanimationiteration: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onwebkitanimationiteration: the default value must be null]
- expected: FAIL
-
- [onwebkitanimationiteration: the content attribute must be compiled into a function as the corresponding property]
- expected: FAIL
-
[onwebkitanimationiteration: the content attribute must execute when an event is dispatched]
expected: FAIL
- [onwebkitanimationstart: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onwebkitanimationstart: the default value must be null]
- expected: FAIL
-
- [onwebkitanimationstart: the content attribute must be compiled into a function as the corresponding property]
- expected: FAIL
-
[onwebkitanimationstart: the content attribute must execute when an event is dispatched]
expected: FAIL
- [onwebkittransitionend: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onwebkittransitionend: the default value must be null]
- expected: FAIL
-
- [onwebkittransitionend: the content attribute must be compiled into a function as the corresponding property]
- expected: FAIL
-
[onwebkittransitionend: the content attribute must execute when an event is dispatched]
expected: FAIL
- [onwheel: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onwheel: the default value must be null]
- expected: FAIL
-
- [onbeforeinput: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onbeforeinput: the default value must be null]
- expected: FAIL
-
- [onbeforeinput: the content attribute must be compiled into a function as the corresponding property]
- expected: FAIL
-
- [onbeforematch: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onbeforematch: the default value must be null]
- expected: FAIL
-
- [onbeforematch: the content attribute must be compiled into a function as the corresponding property]
- expected: FAIL
-
- [onscrollend: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onscrollend: the default value must be null]
- expected: FAIL
-
- [onscrollend: the content attribute must be compiled into a function as the corresponding property]
- expected: FAIL
-
- [oncopy: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [oncopy: the default value must be null]
- expected: FAIL
-
- [oncut: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [oncut: the default value must be null]
- expected: FAIL
-
- [onpaste: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onpaste: the default value must be null]
- expected: FAIL
-
- [onbeforetoggle: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [onbeforetoggle: the default value must be null]
- expected: FAIL
-
- [onbeforetoggle: the content attribute must be compiled into a function as the corresponding property]
- expected: FAIL
-
- [onauxclick: dispatching an Event at a <meta> element must trigger element.onauxclick]
- expected: FAIL
-
- [onbeforeinput: dispatching an Event at a <meta> element must trigger element.onbeforeinput]
- expected: FAIL
-
- [onbeforematch: dispatching an Event at a <meta> element must trigger element.onbeforematch]
- expected: FAIL
-
- [onbeforetoggle: dispatching an Event at a <meta> element must trigger element.onbeforetoggle]
- expected: FAIL
-
- [oncontextlost: dispatching an Event at a <meta> element must trigger element.oncontextlost]
- expected: FAIL
-
- [oncontextrestored: dispatching an Event at a <meta> element must trigger element.oncontextrestored]
- expected: FAIL
-
- [onscrollend: dispatching an Event at a <meta> element must trigger element.onscrollend]
- expected: FAIL
-
- [onslotchange: dispatching an Event at a <meta> element must trigger element.onslotchange]
- expected: FAIL
-
[onwebkitanimationend: dispatching an Event at a <meta> element must trigger element.onwebkitanimationend]
expected: FAIL
@@ -526,15 +22,3 @@
[onwebkittransitionend: dispatching an Event at a <meta> element must trigger element.onwebkittransitionend]
expected: FAIL
-
- [oncommand: must be on the appropriate locations for GlobalEventHandlers]
- expected: FAIL
-
- [oncommand: the default value must be null]
- expected: FAIL
-
- [oncommand: the content attribute must be compiled into a function as the corresponding property]
- expected: FAIL
-
- [oncommand: dispatching an Event at a <meta> element must trigger element.oncommand]
- expected: FAIL
diff --git a/tests/wpt/meta/html/webappapis/scripting/events/event-handler-attributes-body-window.html.ini b/tests/wpt/meta/html/webappapis/scripting/events/event-handler-attributes-body-window.html.ini
index deec7c5c132..3b3a37ff7c2 100644
--- a/tests/wpt/meta/html/webappapis/scripting/events/event-handler-attributes-body-window.html.ini
+++ b/tests/wpt/meta/html/webappapis/scripting/events/event-handler-attributes-body-window.html.ini
@@ -1,181 +1,19 @@
[event-handler-attributes-body-window.html]
- [not shadowed auxclick (document.body)]
- expected: FAIL
-
- [not shadowed contextlost (document.body)]
- expected: FAIL
-
- [not shadowed contextrestored (document.body)]
- expected: FAIL
-
- [not shadowed slotchange (document.body)]
- expected: FAIL
-
- [not shadowed webkitanimationend (document.body)]
- expected: FAIL
-
- [not shadowed webkitanimationiteration (document.body)]
- expected: FAIL
-
- [not shadowed webkitanimationstart (document.body)]
- expected: FAIL
-
- [not shadowed webkittransitionend (document.body)]
- expected: FAIL
-
- [not shadowed copy (document.body)]
- expected: FAIL
-
- [not shadowed cut (document.body)]
- expected: FAIL
-
- [not shadowed paste (document.body)]
- expected: FAIL
-
- [not shadowed auxclick (document.createElement("body"))]
- expected: FAIL
-
- [not shadowed contextlost (document.createElement("body"))]
- expected: FAIL
-
- [not shadowed contextrestored (document.createElement("body"))]
- expected: FAIL
-
- [not shadowed slotchange (document.createElement("body"))]
- expected: FAIL
-
- [not shadowed webkitanimationend (document.createElement("body"))]
- expected: FAIL
-
- [not shadowed webkitanimationiteration (document.createElement("body"))]
- expected: FAIL
-
- [not shadowed webkitanimationstart (document.createElement("body"))]
- expected: FAIL
-
- [not shadowed webkittransitionend (document.createElement("body"))]
- expected: FAIL
-
- [not shadowed copy (document.createElement("body"))]
- expected: FAIL
-
- [not shadowed cut (document.createElement("body"))]
- expected: FAIL
-
- [not shadowed paste (document.createElement("body"))]
- expected: FAIL
-
- [not shadowed auxclick (window)]
- expected: FAIL
-
- [not shadowed contextlost (window)]
- expected: FAIL
-
- [not shadowed contextrestored (window)]
- expected: FAIL
-
- [not shadowed slotchange (window)]
- expected: FAIL
-
- [not shadowed webkitanimationend (window)]
- expected: FAIL
-
- [not shadowed webkitanimationiteration (window)]
- expected: FAIL
-
- [not shadowed webkitanimationstart (window)]
- expected: FAIL
-
- [not shadowed webkittransitionend (window)]
- expected: FAIL
-
- [not shadowed beforeinput (document.body)]
- expected: FAIL
-
- [not shadowed beforematch (document.body)]
- expected: FAIL
-
- [not shadowed scrollend (document.body)]
- expected: FAIL
-
- [not shadowed beforeinput (document.createElement("body"))]
- expected: FAIL
-
- [not shadowed beforematch (document.createElement("body"))]
- expected: FAIL
-
- [not shadowed scrollend (document.createElement("body"))]
- expected: FAIL
-
- [not shadowed beforeinput (window)]
- expected: FAIL
-
- [not shadowed beforematch (window)]
- expected: FAIL
-
- [not shadowed scrollend (window)]
- expected: FAIL
-
- [not shadowed beforetoggle (document.body)]
- expected: FAIL
-
- [not shadowed beforetoggle (document.createElement("body"))]
- expected: FAIL
-
- [not shadowed beforetoggle (window)]
- expected: FAIL
-
- [shadowed pagereveal (document.body)]
- expected: FAIL
-
- [shadowed pagereveal removal (document.body)]
- expected: FAIL
-
- [shadowed pagereveal (document.createElement("body"))]
- expected: FAIL
-
[shadowed pagereveal removal (document.createElement("body"))]
expected: FAIL
- [shadowed pagereveal (window)]
- expected: FAIL
-
[shadowed pagereveal removal (window)]
expected: FAIL
- [shadowed pageswap (document.body)]
- expected: FAIL
-
- [shadowed pageswap removal (document.body)]
- expected: FAIL
-
- [shadowed pageswap (document.createElement("body"))]
- expected: FAIL
-
[shadowed pageswap removal (document.createElement("body"))]
expected: FAIL
- [shadowed pageswap (window)]
- expected: FAIL
-
[shadowed pageswap removal (window)]
expected: FAIL
[shadowed blur on body fires when event dispatched on window]
expected: FAIL
- [shadowed messageerror on body fires when event dispatched on window]
- expected: FAIL
-
- [shadowed pagereveal on body fires when event dispatched on window]
- expected: FAIL
-
- [shadowed pageswap on body fires when event dispatched on window]
- expected: FAIL
-
- [shadowed rejectionhandled on body fires when event dispatched on window]
- expected: FAIL
-
[shadowed focus removal (document.createElement("body"))]
expected: FAIL
@@ -206,6 +44,9 @@
[shadowed message removal (document.createElement("body"))]
expected: FAIL
+ [shadowed messageerror removal (document.createElement("body"))]
+ expected: FAIL
+
[shadowed offline removal (document.createElement("body"))]
expected: FAIL
@@ -233,6 +74,9 @@
[shadowed load removal (window)]
expected: FAIL
+ [shadowed rejectionhandled removal (document.createElement("body"))]
+ expected: FAIL
+
[shadowed resize removal (window)]
expected: FAIL
@@ -257,6 +101,9 @@
[shadowed message removal (window)]
expected: FAIL
+ [shadowed messageerror removal (window)]
+ expected: FAIL
+
[shadowed offline removal (window)]
expected: FAIL
@@ -272,17 +119,11 @@
[shadowed popstate removal (window)]
expected: FAIL
- [shadowed storage removal (window)]
+ [shadowed rejectionhandled removal (window)]
expected: FAIL
- [shadowed unload removal (window)]
- expected: FAIL
-
- [not shadowed command (document.body)]
- expected: FAIL
-
- [not shadowed command (document.createElement("body"))]
+ [shadowed storage removal (window)]
expected: FAIL
- [not shadowed command (window)]
+ [shadowed unload removal (window)]
expected: FAIL
diff --git a/tests/wpt/meta/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html.ini b/tests/wpt/meta/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html.ini
index 4484356912c..b18a8cf6d5b 100644
--- a/tests/wpt/meta/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html.ini
+++ b/tests/wpt/meta/html/webappapis/scripting/events/event-handler-attributes-frameset-window.html.ini
@@ -1,166 +1,4 @@
[event-handler-attributes-frameset-window.html]
- [not shadowed auxclick (document.body)]
- expected: FAIL
-
- [not shadowed contextlost (document.body)]
- expected: FAIL
-
- [not shadowed contextrestored (document.body)]
- expected: FAIL
-
- [not shadowed slotchange (document.body)]
- expected: FAIL
-
- [not shadowed webkitanimationend (document.body)]
- expected: FAIL
-
- [not shadowed webkitanimationiteration (document.body)]
- expected: FAIL
-
- [not shadowed webkitanimationstart (document.body)]
- expected: FAIL
-
- [not shadowed webkittransitionend (document.body)]
- expected: FAIL
-
- [not shadowed copy (document.body)]
- expected: FAIL
-
- [not shadowed cut (document.body)]
- expected: FAIL
-
- [not shadowed paste (document.body)]
- expected: FAIL
-
- [not shadowed auxclick (document.createElement("frameset"))]
- expected: FAIL
-
- [not shadowed contextlost (document.createElement("frameset"))]
- expected: FAIL
-
- [not shadowed contextrestored (document.createElement("frameset"))]
- expected: FAIL
-
- [not shadowed slotchange (document.createElement("frameset"))]
- expected: FAIL
-
- [not shadowed webkitanimationend (document.createElement("frameset"))]
- expected: FAIL
-
- [not shadowed webkitanimationiteration (document.createElement("frameset"))]
- expected: FAIL
-
- [not shadowed webkitanimationstart (document.createElement("frameset"))]
- expected: FAIL
-
- [not shadowed webkittransitionend (document.createElement("frameset"))]
- expected: FAIL
-
- [not shadowed copy (document.createElement("frameset"))]
- expected: FAIL
-
- [not shadowed cut (document.createElement("frameset"))]
- expected: FAIL
-
- [not shadowed paste (document.createElement("frameset"))]
- expected: FAIL
-
- [not shadowed auxclick (window)]
- expected: FAIL
-
- [not shadowed contextlost (window)]
- expected: FAIL
-
- [not shadowed contextrestored (window)]
- expected: FAIL
-
- [not shadowed slotchange (window)]
- expected: FAIL
-
- [not shadowed webkitanimationend (window)]
- expected: FAIL
-
- [not shadowed webkitanimationiteration (window)]
- expected: FAIL
-
- [not shadowed webkitanimationstart (window)]
- expected: FAIL
-
- [not shadowed webkittransitionend (window)]
- expected: FAIL
-
- [not shadowed beforeinput (document.body)]
- expected: FAIL
-
- [not shadowed beforematch (document.body)]
- expected: FAIL
-
- [not shadowed scrollend (document.body)]
- expected: FAIL
-
- [not shadowed beforeinput (document.createElement("frameset"))]
- expected: FAIL
-
- [not shadowed beforematch (document.createElement("frameset"))]
- expected: FAIL
-
- [not shadowed scrollend (document.createElement("frameset"))]
- expected: FAIL
-
- [not shadowed beforeinput (window)]
- expected: FAIL
-
- [not shadowed beforematch (window)]
- expected: FAIL
-
- [not shadowed scrollend (window)]
- expected: FAIL
-
- [not shadowed beforetoggle (document.body)]
- expected: FAIL
-
- [not shadowed beforetoggle (document.createElement("frameset"))]
- expected: FAIL
-
- [not shadowed beforetoggle (window)]
- expected: FAIL
-
- [shadowed pagereveal (document.body)]
- expected: FAIL
-
- [shadowed pagereveal removal (document.body)]
- expected: FAIL
-
- [shadowed pagereveal (document.createElement("frameset"))]
- expected: FAIL
-
- [shadowed pagereveal removal (document.createElement("frameset"))]
- expected: FAIL
-
- [shadowed pagereveal (window)]
- expected: FAIL
-
- [shadowed pagereveal removal (window)]
- expected: FAIL
-
- [shadowed pageswap (document.body)]
- expected: FAIL
-
- [shadowed pageswap removal (document.body)]
- expected: FAIL
-
- [shadowed pageswap (document.createElement("frameset"))]
- expected: FAIL
-
- [shadowed pageswap removal (document.createElement("frameset"))]
- expected: FAIL
-
- [shadowed pageswap (window)]
- expected: FAIL
-
- [shadowed pageswap removal (window)]
- expected: FAIL
-
[shadowed blur on body fires when event dispatched on window]
expected: FAIL
@@ -226,12 +64,3 @@
[shadowed unload on body fires when event dispatched on window]
expected: FAIL
-
- [not shadowed command (document.body)]
- expected: FAIL
-
- [not shadowed command (document.createElement("frameset"))]
- expected: FAIL
-
- [not shadowed command (window)]
- expected: FAIL
diff --git a/tests/wpt/meta/html/webappapis/scripting/events/event-handler-attributes-windowless-body.html.ini b/tests/wpt/meta/html/webappapis/scripting/events/event-handler-attributes-windowless-body.html.ini
deleted file mode 100644
index db4d6e3321d..00000000000
--- a/tests/wpt/meta/html/webappapis/scripting/events/event-handler-attributes-windowless-body.html.ini
+++ /dev/null
@@ -1,102 +0,0 @@
-[event-handler-attributes-windowless-body.html]
- [auxclick is unaffected on a windowless body]
- expected: FAIL
-
- [contextlost is unaffected on a windowless body]
- expected: FAIL
-
- [contextrestored is unaffected on a windowless body]
- expected: FAIL
-
- [slotchange is unaffected on a windowless body]
- expected: FAIL
-
- [webkitanimationend is unaffected on a windowless body]
- expected: FAIL
-
- [webkitanimationiteration is unaffected on a windowless body]
- expected: FAIL
-
- [webkitanimationstart is unaffected on a windowless body]
- expected: FAIL
-
- [webkittransitionend is unaffected on a windowless body]
- expected: FAIL
-
- [auxclick is unaffected on a windowless frameset]
- expected: FAIL
-
- [contextlost is unaffected on a windowless frameset]
- expected: FAIL
-
- [contextrestored is unaffected on a windowless frameset]
- expected: FAIL
-
- [slotchange is unaffected on a windowless frameset]
- expected: FAIL
-
- [webkitanimationend is unaffected on a windowless frameset]
- expected: FAIL
-
- [webkitanimationiteration is unaffected on a windowless frameset]
- expected: FAIL
-
- [webkitanimationstart is unaffected on a windowless frameset]
- expected: FAIL
-
- [webkittransitionend is unaffected on a windowless frameset]
- expected: FAIL
-
- [beforeinput is unaffected on a windowless body]
- expected: FAIL
-
- [beforematch is unaffected on a windowless body]
- expected: FAIL
-
- [scrollend is unaffected on a windowless body]
- expected: FAIL
-
- [beforeinput is unaffected on a windowless frameset]
- expected: FAIL
-
- [beforematch is unaffected on a windowless frameset]
- expected: FAIL
-
- [scrollend is unaffected on a windowless frameset]
- expected: FAIL
-
- [beforetoggle is unaffected on a windowless body]
- expected: FAIL
-
- [beforetoggle is unaffected on a windowless frameset]
- expected: FAIL
-
- [Return null when getting the pagereveal event handler of a windowless body]
- expected: FAIL
-
- [Ignore setting of pagereveal window event handlers on windowless body]
- expected: FAIL
-
- [Return null when getting the pagereveal event handler of a windowless frameset]
- expected: FAIL
-
- [Ignore setting of pagereveal window event handlers on windowless frameset]
- expected: FAIL
-
- [Return null when getting the pageswap event handler of a windowless body]
- expected: FAIL
-
- [Ignore setting of pageswap window event handlers on windowless body]
- expected: FAIL
-
- [Return null when getting the pageswap event handler of a windowless frameset]
- expected: FAIL
-
- [Ignore setting of pageswap window event handlers on windowless frameset]
- expected: FAIL
-
- [command is unaffected on a windowless body]
- expected: FAIL
-
- [command is unaffected on a windowless frameset]
- expected: FAIL
diff --git a/tests/wpt/meta/streams/transferable/transfer-with-messageport.window.js.ini b/tests/wpt/meta/streams/transferable/transfer-with-messageport.window.js.ini
deleted file mode 100644
index fc885c5a594..00000000000
--- a/tests/wpt/meta/streams/transferable/transfer-with-messageport.window.js.ini
+++ /dev/null
@@ -1,12 +0,0 @@
-[transfer-with-messageport.window.html]
- [Transferring a MessagePort with a TransformStream should set `.ports`]
- expected: FAIL
-
- [Transferring a MessagePort with a TransformStream should set `.ports`, advanced]
- expected: FAIL
-
- [Transferring a MessagePort with multiple streams should set `.ports`]
- expected: FAIL
-
- [TransformStream must not be serializable]
- expected: FAIL
diff --git a/tests/wpt/meta/streams/transferable/transform-stream-members.any.js.ini b/tests/wpt/meta/streams/transferable/transform-stream-members.any.js.ini
index c5d5f43d1a4..4414054a1cf 100644
--- a/tests/wpt/meta/streams/transferable/transform-stream-members.any.js.ini
+++ b/tests/wpt/meta/streams/transferable/transform-stream-members.any.js.ini
@@ -10,14 +10,10 @@
[transform-stream-members.any.shadowrealm-in-window.html]
expected: ERROR
-[transform-stream-members.any.html]
- expected: ERROR
[transform-stream-members.https.any.shadowrealm-in-serviceworker.html]
expected: ERROR
-[transform-stream-members.any.worker.html]
- expected: ERROR
[transform-stream-members.any.shadowrealm-in-dedicatedworker.html]
expected: ERROR
diff --git a/tests/wpt/meta/streams/transferable/transform-stream.html.ini b/tests/wpt/meta/streams/transferable/transform-stream.html.ini
index dc6fe8a6c75..a5097f80874 100644
--- a/tests/wpt/meta/streams/transferable/transform-stream.html.ini
+++ b/tests/wpt/meta/streams/transferable/transform-stream.html.ini
@@ -1,15 +1,3 @@
[transform-stream.html]
- [window.postMessage should be able to transfer a TransformStream]
- expected: FAIL
-
- [a TransformStream with a locked writable should not be transferable]
- expected: FAIL
-
- [a TransformStream with a locked readable should not be transferable]
- expected: FAIL
-
- [a TransformStream with both sides locked should not be transferable]
- expected: FAIL
-
[piping through transferred transforms should work]
expected: FAIL
diff --git a/tests/wpt/meta/streams/transferable/writable-stream.html.ini b/tests/wpt/meta/streams/transferable/writable-stream.html.ini
deleted file mode 100644
index 47326208f88..00000000000
--- a/tests/wpt/meta/streams/transferable/writable-stream.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[writable-stream.html]
- [window.postMessage should be able to transfer a {readable, writable} pair]
- expected: FAIL
diff --git a/tests/wpt/meta/streams/transform-streams/backpressure.any.js.ini b/tests/wpt/meta/streams/transform-streams/backpressure.any.js.ini
new file mode 100644
index 00000000000..099d3a6f2e0
--- /dev/null
+++ b/tests/wpt/meta/streams/transform-streams/backpressure.any.js.ini
@@ -0,0 +1,23 @@
+[backpressure.any.shadowrealm-in-shadowrealm.html]
+ expected: ERROR
+
+[backpressure.any.shadowrealm-in-window.html]
+ expected: ERROR
+
+[backpressure.any.serviceworker.html]
+ expected: ERROR
+
+[backpressure.any.shadowrealm-in-dedicatedworker.html]
+ expected: ERROR
+
+[backpressure.any.sharedworker.html]
+ expected: ERROR
+
+[backpressure.https.any.shadowrealm-in-serviceworker.html]
+ expected: ERROR
+
+[backpressure.any.shadowrealm-in-sharedworker.html]
+ expected: ERROR
+
+[backpressure.https.any.shadowrealm-in-audioworklet.html]
+ expected: ERROR \ No newline at end of file
diff --git a/tests/wpt/meta/streams/transform-streams/cancel.any.js.ini b/tests/wpt/meta/streams/transform-streams/cancel.any.js.ini
new file mode 100644
index 00000000000..7e5a1b9af50
--- /dev/null
+++ b/tests/wpt/meta/streams/transform-streams/cancel.any.js.ini
@@ -0,0 +1,32 @@
+[cancel.any.shadowrealm-in-dedicatedworker.html]
+ expected: ERROR
+
+[cancel.any.serviceworker.html]
+ expected: ERROR
+
+[cancel.https.any.shadowrealm-in-audioworklet.html]
+ expected: ERROR
+
+[cancel.any.shadowrealm-in-sharedworker.html]
+ expected: ERROR
+
+[cancel.any.sharedworker.html]
+ expected: ERROR
+
+[cancel.any.shadowrealm-in-window.html]
+ expected: ERROR
+
+[cancel.any.shadowrealm-in-shadowrealm.html]
+ expected: ERROR
+
+[cancel.https.any.shadowrealm-in-serviceworker.html]
+ expected: ERROR
+
+[cancel.any.worker.html]
+ [readable.cancel() and a parallel writable.close() should reject if a transformer.cancel() calls controller.error()]
+ expected: FAIL
+
+
+[cancel.any.html]
+ [readable.cancel() and a parallel writable.close() should reject if a transformer.cancel() calls controller.error()]
+ expected: FAIL
diff --git a/tests/wpt/meta/streams/transform-streams/errors.any.js.ini b/tests/wpt/meta/streams/transform-streams/errors.any.js.ini
new file mode 100644
index 00000000000..02eaa76ca8e
--- /dev/null
+++ b/tests/wpt/meta/streams/transform-streams/errors.any.js.ini
@@ -0,0 +1,32 @@
+[errors.any.sharedworker.html]
+ expected: ERROR
+
+[errors.https.any.shadowrealm-in-serviceworker.html]
+ expected: ERROR
+
+[errors.any.shadowrealm-in-sharedworker.html]
+ expected: ERROR
+
+[errors.any.shadowrealm-in-shadowrealm.html]
+ expected: ERROR
+
+[errors.any.serviceworker.html]
+ expected: ERROR
+
+[errors.https.any.shadowrealm-in-audioworklet.html]
+ expected: ERROR
+
+[errors.any.shadowrealm-in-dedicatedworker.html]
+ expected: ERROR
+
+[errors.any.shadowrealm-in-window.html]
+ expected: ERROR
+
+[errors.any.html]
+ [abort should set the close reason for the writable when it happens before cancel during start, and cancel should reject]
+ expected: FAIL
+
+
+[errors.any.worker.html]
+ [abort should set the close reason for the writable when it happens before cancel during start, and cancel should reject]
+ expected: FAIL
diff --git a/tests/wpt/meta/streams/transform-streams/flush.any.js.ini b/tests/wpt/meta/streams/transform-streams/flush.any.js.ini
new file mode 100644
index 00000000000..a2ee0101993
--- /dev/null
+++ b/tests/wpt/meta/streams/transform-streams/flush.any.js.ini
@@ -0,0 +1,23 @@
+[flush.any.shadowrealm-in-dedicatedworker.html]
+ expected: ERROR
+
+[flush.any.shadowrealm-in-window.html]
+ expected: ERROR
+
+[flush.https.any.shadowrealm-in-audioworklet.html]
+ expected: ERROR
+
+[flush.any.shadowrealm-in-sharedworker.html]
+ expected: ERROR
+
+[flush.any.shadowrealm-in-shadowrealm.html]
+ expected: ERROR
+
+[flush.any.sharedworker.html]
+ expected: ERROR
+
+[flush.https.any.shadowrealm-in-serviceworker.html]
+ expected: ERROR
+
+[flush.any.serviceworker.html]
+ expected: ERROR \ No newline at end of file
diff --git a/tests/wpt/meta/streams/transform-streams/general.any.js.ini b/tests/wpt/meta/streams/transform-streams/general.any.js.ini
new file mode 100644
index 00000000000..11dfc7d4ece
--- /dev/null
+++ b/tests/wpt/meta/streams/transform-streams/general.any.js.ini
@@ -0,0 +1,23 @@
+[general.any.shadowrealm-in-sharedworker.html]
+ expected: ERROR
+
+[general.https.any.shadowrealm-in-serviceworker.html]
+ expected: ERROR
+
+[general.any.shadowrealm-in-shadowrealm.html]
+ expected: ERROR
+
+[general.any.shadowrealm-in-dedicatedworker.html]
+ expected: ERROR
+
+[general.https.any.shadowrealm-in-audioworklet.html]
+ expected: ERROR
+
+[general.any.serviceworker.html]
+ expected: ERROR
+
+[general.any.shadowrealm-in-window.html]
+ expected: ERROR
+
+[general.any.sharedworker.html]
+ expected: ERROR
diff --git a/tests/wpt/meta/streams/transform-streams/lipfuzz.any.js.ini b/tests/wpt/meta/streams/transform-streams/lipfuzz.any.js.ini
new file mode 100644
index 00000000000..5af2be4a1ae
--- /dev/null
+++ b/tests/wpt/meta/streams/transform-streams/lipfuzz.any.js.ini
@@ -0,0 +1,23 @@
+[lipfuzz.any.shadowrealm-in-window.html]
+ expected: ERROR
+
+[lipfuzz.https.any.shadowrealm-in-serviceworker.html]
+ expected: ERROR
+
+[lipfuzz.any.serviceworker.html]
+ expected: ERROR
+
+[lipfuzz.any.shadowrealm-in-shadowrealm.html]
+ expected: ERROR
+
+[lipfuzz.any.shadowrealm-in-dedicatedworker.html]
+ expected: ERROR
+
+[lipfuzz.https.any.shadowrealm-in-audioworklet.html]
+ expected: ERROR
+
+[lipfuzz.any.sharedworker.html]
+ expected: ERROR
+
+[lipfuzz.any.shadowrealm-in-sharedworker.html]
+ expected: ERROR
diff --git a/tests/wpt/meta/streams/transform-streams/patched-global.any.js.ini b/tests/wpt/meta/streams/transform-streams/patched-global.any.js.ini
new file mode 100644
index 00000000000..06a324cb060
--- /dev/null
+++ b/tests/wpt/meta/streams/transform-streams/patched-global.any.js.ini
@@ -0,0 +1,23 @@
+[patched-global.any.shadowrealm-in-dedicatedworker.html]
+ expected: ERROR
+
+[patched-global.any.shadowrealm-in-window.html]
+ expected: ERROR
+
+[patched-global.any.shadowrealm-in-sharedworker.html]
+ expected: ERROR
+
+[patched-global.https.any.shadowrealm-in-serviceworker.html]
+ expected: ERROR
+
+[patched-global.any.shadowrealm-in-shadowrealm.html]
+ expected: ERROR
+
+[patched-global.https.any.shadowrealm-in-audioworklet.html]
+ expected: ERROR
+
+[patched-global.any.sharedworker.html]
+ expected: ERROR
+
+[patched-global.any.serviceworker.html]
+ expected: ERROR
diff --git a/tests/wpt/meta/streams/transform-streams/properties.any.js.ini b/tests/wpt/meta/streams/transform-streams/properties.any.js.ini
new file mode 100644
index 00000000000..f5573ee57e4
--- /dev/null
+++ b/tests/wpt/meta/streams/transform-streams/properties.any.js.ini
@@ -0,0 +1,23 @@
+[properties.any.shadowrealm-in-shadowrealm.html]
+ expected: ERROR
+
+[properties.https.any.shadowrealm-in-serviceworker.html]
+ expected: ERROR
+
+[properties.any.shadowrealm-in-window.html]
+ expected: ERROR
+
+[properties.any.shadowrealm-in-dedicatedworker.html]
+ expected: ERROR
+
+[properties.any.sharedworker.html]
+ expected: ERROR
+
+[properties.https.any.shadowrealm-in-audioworklet.html]
+ expected: ERROR
+
+[properties.any.serviceworker.html]
+ expected: ERROR
+
+[properties.any.shadowrealm-in-sharedworker.html]
+ expected: ERROR
diff --git a/tests/wpt/meta/streams/transform-streams/reentrant-strategies.any.js.ini b/tests/wpt/meta/streams/transform-streams/reentrant-strategies.any.js.ini
new file mode 100644
index 00000000000..1c6b9fd51da
--- /dev/null
+++ b/tests/wpt/meta/streams/transform-streams/reentrant-strategies.any.js.ini
@@ -0,0 +1,23 @@
+[reentrant-strategies.any.shadowrealm-in-window.html]
+ expected: ERROR
+
+[reentrant-strategies.https.any.shadowrealm-in-serviceworker.html]
+ expected: ERROR
+
+[reentrant-strategies.any.sharedworker.html]
+ expected: ERROR
+
+[reentrant-strategies.https.any.shadowrealm-in-audioworklet.html]
+ expected: ERROR
+
+[reentrant-strategies.any.shadowrealm-in-dedicatedworker.html]
+ expected: ERROR
+
+[reentrant-strategies.any.serviceworker.html]
+ expected: ERROR
+
+[reentrant-strategies.any.shadowrealm-in-sharedworker.html]
+ expected: ERROR
+
+[reentrant-strategies.any.shadowrealm-in-shadowrealm.html]
+ expected: ERROR
diff --git a/tests/wpt/meta/streams/transform-streams/strategies.any.js.ini b/tests/wpt/meta/streams/transform-streams/strategies.any.js.ini
new file mode 100644
index 00000000000..52c18b67fd4
--- /dev/null
+++ b/tests/wpt/meta/streams/transform-streams/strategies.any.js.ini
@@ -0,0 +1,24 @@
+[strategies.any.shadowrealm-in-shadowrealm.html]
+ expected: ERROR
+
+[strategies.any.serviceworker.html]
+ expected: ERROR
+
+[strategies.https.any.shadowrealm-in-serviceworker.html]
+ expected: ERROR
+
+[strategies.any.shadowrealm-in-window.html]
+ expected: ERROR
+
+[strategies.any.shadowrealm-in-sharedworker.html]
+ expected: ERROR
+
+[strategies.any.shadowrealm-in-dedicatedworker.html]
+ expected: ERROR
+
+[strategies.any.sharedworker.html]
+ expected: ERROR
+
+[strategies.https.any.shadowrealm-in-audioworklet.html]
+ expected: ERROR
+
diff --git a/tests/wpt/meta/streams/transform-streams/terminate.any.js.ini b/tests/wpt/meta/streams/transform-streams/terminate.any.js.ini
new file mode 100644
index 00000000000..c81b1e40d1e
--- /dev/null
+++ b/tests/wpt/meta/streams/transform-streams/terminate.any.js.ini
@@ -0,0 +1,23 @@
+[terminate.any.serviceworker.html]
+ expected: ERROR
+
+[terminate.any.shadowrealm-in-sharedworker.html]
+ expected: ERROR
+
+[terminate.https.any.shadowrealm-in-serviceworker.html]
+ expected: ERROR
+
+[terminate.any.shadowrealm-in-window.html]
+ expected: ERROR
+
+[terminate.any.shadowrealm-in-shadowrealm.html]
+ expected: ERROR
+
+[terminate.https.any.shadowrealm-in-audioworklet.html]
+ expected: ERROR
+
+[terminate.any.shadowrealm-in-dedicatedworker.html]
+ expected: ERROR
+
+[terminate.any.sharedworker.html]
+ expected: ERROR
diff --git a/tests/wpt/meta/streams/writable-streams/crashtests/garbage-collection.any.js.ini b/tests/wpt/meta/streams/writable-streams/crashtests/garbage-collection.any.js.ini
new file mode 100644
index 00000000000..6204bdc21da
--- /dev/null
+++ b/tests/wpt/meta/streams/writable-streams/crashtests/garbage-collection.any.js.ini
@@ -0,0 +1,9 @@
+[garbage-collection.any.html]
+
+[garbage-collection.any.sharedworker.html]
+ expected: ERROR
+
+[garbage-collection.any.serviceworker.html]
+ expected: ERROR
+
+[garbage-collection.any.worker.html]
diff --git a/tests/wpt/meta/subresource-integrity/tentative/integrity-policy/parsing.https.html.ini b/tests/wpt/meta/subresource-integrity/tentative/integrity-policy/parsing.https.html.ini
new file mode 100644
index 00000000000..8634900f9c2
--- /dev/null
+++ b/tests/wpt/meta/subresource-integrity/tentative/integrity-policy/parsing.https.html.ini
@@ -0,0 +1,9 @@
+[parsing.https.html?type=enforce]
+ [Ensure that test is working with a valid destination]
+ expected: FAIL
+
+ [Ensure that test is working with a valid destination and source]
+ expected: FAIL
+
+
+[parsing.https.html?type=report]
diff --git a/tests/wpt/meta/subresource-integrity/tentative/integrity-policy/script.https.html.ini b/tests/wpt/meta/subresource-integrity/tentative/integrity-policy/script.https.html.ini
new file mode 100644
index 00000000000..3563393c660
--- /dev/null
+++ b/tests/wpt/meta/subresource-integrity/tentative/integrity-policy/script.https.html.ini
@@ -0,0 +1,15 @@
+[script.https.html]
+ [Ensure that a script without integrity did not run]
+ expected: FAIL
+
+ [Ensure that a script with unknown integrity algorithm did not run]
+ expected: FAIL
+
+ [Ensure that a script without integrity algorithm runs and gets reported in report-only mode]
+ expected: FAIL
+
+ [Ensure that a no-cors script gets blocked]
+ expected: FAIL
+
+ [Ensure that ReportingObserver gets called without endpoints]
+ expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/HTMLElement-generic.html.ini b/tests/wpt/meta/trusted-types/HTMLElement-generic.html.ini
deleted file mode 100644
index 8ed1a875db3..00000000000
--- a/tests/wpt/meta/trusted-types/HTMLElement-generic.html.ini
+++ /dev/null
@@ -1,24 +0,0 @@
-[HTMLElement-generic.html]
- [TT enabled: div.innerHTML\n = String on a\n connected element\n ]
- expected: FAIL
-
- [TT enabled: div.innerHTML\n = String on a\n non-connected element\n ]
- expected: FAIL
-
- [TT enabled: iframe.srcdoc\n = String on a\n connected element\n ]
- expected: FAIL
-
- [TT enabled: iframe.srcdoc\n = String on a\n non-connected element\n ]
- expected: FAIL
-
- [TT enabled: div.innerHTML\n = String on a\n connected element\n after removing the "require-trusted-types-for 'script' directive]
- expected: FAIL
-
- [TT enabled: div.innerHTML\n = String on a\n non-connected element\n after removing the "require-trusted-types-for 'script' directive]
- expected: FAIL
-
- [TT enabled: iframe.srcdoc\n = String on a\n connected element\n after removing the "require-trusted-types-for 'script' directive]
- expected: FAIL
-
- [TT enabled: iframe.srcdoc\n = String on a\n non-connected element\n after removing the "require-trusted-types-for 'script' directive]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-insertAdjacentHTML.html.ini b/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-insertAdjacentHTML.html.ini
deleted file mode 100644
index 1306015ff46..00000000000
--- a/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-insertAdjacentHTML.html.ini
+++ /dev/null
@@ -1,12 +0,0 @@
-[block-string-assignment-to-Element-insertAdjacentHTML.html]
- [`insertAdjacentHTML(string)` throws.]
- expected: FAIL
-
- [`insertAdjacentHTML(string)` still throws TypeError when position invalid.]
- expected: FAIL
-
- [`insertAdjacentHTML(null)` throws.]
- expected: FAIL
-
- [`insertAdjacentHTML(string)` assigned via default policy (successful HTML transformation).]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-outerHTML.html.ini b/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-outerHTML.html.ini
deleted file mode 100644
index e5abefcc766..00000000000
--- a/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-outerHTML.html.ini
+++ /dev/null
@@ -1,15 +0,0 @@
-[block-string-assignment-to-Element-outerHTML.html]
- [`outerHTML = string` throws.]
- expected: FAIL
-
- [`outerHTML = string` throws TypeError even when parent is a document.]
- expected: FAIL
-
- [`outerHTML = null` throws.]
- expected: FAIL
-
- [`outerHTML = string` assigned via default policy (successful HTML transformation).]
- expected: FAIL
-
- [`outerHTML = null` assigned via default policy does not throw]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-setAttribute.html.ini b/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-setAttribute.html.ini
index 7ad472ef325..0df917b86be 100644
--- a/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-setAttribute.html.ini
+++ b/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-setAttribute.html.ini
@@ -20,9 +20,6 @@
[div.onclick's mutationobservers receive the default policy's value.]
expected: FAIL
- [iframe.srcdoc accepts string and null after default policy was created.]
- expected: FAIL
-
[div.onclick accepts string and null after default policy was created.]
expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-setHTMLUnsafe.html.ini b/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-setHTMLUnsafe.html.ini
deleted file mode 100644
index e6b59ba6714..00000000000
--- a/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-setHTMLUnsafe.html.ini
+++ /dev/null
@@ -1,12 +0,0 @@
-[block-string-assignment-to-Element-setHTMLUnsafe.html]
- [`element.setHTMLUnsafe(string)` throws.]
- expected: FAIL
-
- [`element.setHTMLUnsafe(null)` throws.]
- expected: FAIL
-
- [`element.setHTMLUnsafe(string)` assigned via default policy (successful HTML transformation).]
- expected: FAIL
-
- [`element.setHTMLUnsafe(string)` assigned via default policy does not throw]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/block-string-assignment-to-HTMLElement-generic.html.ini b/tests/wpt/meta/trusted-types/block-string-assignment-to-HTMLElement-generic.html.ini
deleted file mode 100644
index ed3a70b31ab..00000000000
--- a/tests/wpt/meta/trusted-types/block-string-assignment-to-HTMLElement-generic.html.ini
+++ /dev/null
@@ -1,12 +0,0 @@
-[block-string-assignment-to-HTMLElement-generic.html]
- [div.innerHTML accepts only TrustedHTML]
- expected: FAIL
-
- [iframe.srcdoc accepts only TrustedHTML]
- expected: FAIL
-
- [div.innerHTML accepts string and null after default policy was created]
- expected: FAIL
-
- [iframe.srcdoc accepts string and null after default policy was created]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/block-string-assignment-to-HTMLIFrameElement-srcdoc.html.ini b/tests/wpt/meta/trusted-types/block-string-assignment-to-HTMLIFrameElement-srcdoc.html.ini
deleted file mode 100644
index e7747a96001..00000000000
--- a/tests/wpt/meta/trusted-types/block-string-assignment-to-HTMLIFrameElement-srcdoc.html.ini
+++ /dev/null
@@ -1,9 +0,0 @@
-[block-string-assignment-to-HTMLIFrameElement-srcdoc.html]
- [`iframe.srcdoc = string` throws.]
- expected: FAIL
-
- [`iframe.srcdoc = null` throws.]
- expected: FAIL
-
- [`iframe.srcdoc = string` assigned via default policy (successful HTML transformation).]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/block-string-assignment-to-text-and-url-sinks.html.ini b/tests/wpt/meta/trusted-types/block-string-assignment-to-text-and-url-sinks.html.ini
deleted file mode 100644
index 1e9f6e44f44..00000000000
--- a/tests/wpt/meta/trusted-types/block-string-assignment-to-text-and-url-sinks.html.ini
+++ /dev/null
@@ -1,9 +0,0 @@
-[block-string-assignment-to-text-and-url-sinks.html]
- [Setting HTMLDivElement.innerHTML to a plain string]
- expected: FAIL
-
- [Setting HTMLScriptElement.innerHTML to a plain string]
- expected: FAIL
-
- [Setting SVGScriptElement.innerHTML to a plain string]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/default-policy.html.ini b/tests/wpt/meta/trusted-types/default-policy.html.ini
deleted file mode 100644
index 15588646951..00000000000
--- a/tests/wpt/meta/trusted-types/default-policy.html.ini
+++ /dev/null
@@ -1,22 +0,0 @@
-[default-policy.html]
- expected: TIMEOUT
- [Count SecurityPolicyViolation events.]
- expected: TIMEOUT
-
- [div.innerHTML no default policy]
- expected: FAIL
-
- [div.innerHTML default]
- expected: FAIL
-
- [div.innerHTML null]
- expected: FAIL
-
- [div.innerHTML throw]
- expected: FAIL
-
- [div.innerHTML undefined]
- expected: FAIL
-
- [div.innerHTML typeerror]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/empty-default-policy.html.ini b/tests/wpt/meta/trusted-types/empty-default-policy.html.ini
deleted file mode 100644
index 4f06e4c971f..00000000000
--- a/tests/wpt/meta/trusted-types/empty-default-policy.html.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[empty-default-policy.html]
- expected: TIMEOUT
- [Count SecurityPolicyViolation events.]
- expected: TIMEOUT
-
- [div.innerHTML default]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/require-trusted-types-for-TypeError-belongs-to-the-global-object-realm.html.ini b/tests/wpt/meta/trusted-types/require-trusted-types-for-TypeError-belongs-to-the-global-object-realm.html.ini
index df0dfe485ed..63099aaaaff 100644
--- a/tests/wpt/meta/trusted-types/require-trusted-types-for-TypeError-belongs-to-the-global-object-realm.html.ini
+++ b/tests/wpt/meta/trusted-types/require-trusted-types-for-TypeError-belongs-to-the-global-object-realm.html.ini
@@ -1,6 +1,3 @@
[require-trusted-types-for-TypeError-belongs-to-the-global-object-realm.html]
- [Setting innerHTML on a node inserted by the parser.]
- expected: FAIL
-
[Setting innerHTML on a node adopted from a subframe.]
expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/require-trusted-types-for-report-only.html.ini b/tests/wpt/meta/trusted-types/require-trusted-types-for-report-only.html.ini
deleted file mode 100644
index 39ec281d5f2..00000000000
--- a/tests/wpt/meta/trusted-types/require-trusted-types-for-report-only.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[require-trusted-types-for-report-only.html]
- [Require trusted types for 'script' block create HTML.]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/require-trusted-types-for.html.ini b/tests/wpt/meta/trusted-types/require-trusted-types-for.html.ini
deleted file mode 100644
index 38d5f9eb35a..00000000000
--- a/tests/wpt/meta/trusted-types/require-trusted-types-for.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[require-trusted-types-for.html]
- [Require trusted types for 'script' block create HTML.]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/should-sink-type-mismatch-violation-be-blocked-by-csp-001.html.ini b/tests/wpt/meta/trusted-types/should-sink-type-mismatch-violation-be-blocked-by-csp-001.html.ini
index 57cd759f8bf..d3218647e91 100644
--- a/tests/wpt/meta/trusted-types/should-sink-type-mismatch-violation-be-blocked-by-csp-001.html.ini
+++ b/tests/wpt/meta/trusted-types/should-sink-type-mismatch-violation-be-blocked-by-csp-001.html.ini
@@ -1,20 +1,5 @@
[should-sink-type-mismatch-violation-be-blocked-by-csp-001.html]
expected: TIMEOUT
- [Multiple enforce require-trusted-types-for directives.]
- expected: FAIL
-
- [Multiple report-only require-trusted-types-for directives.]
- expected: FAIL
-
- [One violated report-only require-trusted-types-for directive followed by multiple enforce directives]
- expected: FAIL
-
- [One violated enforce require-trusted-types-for directive followed by multiple report-only directives]
- expected: FAIL
-
- [Mixing enforce and report-only require-trusted-types-for directives.]
- expected: FAIL
-
[directive "require-trusted-types-for 'script'%09'script'%0A'script'%0C'script'%0D'script'%20'script'" (required-ascii-whitespace)]
expected: TIMEOUT
@@ -33,9 +18,6 @@
[directive "require-trusted-types-for unquoted-invalid 'script' also-unquoted-invalid (unknown sink group)]
expected: NOTRUN
- [directive "require-trusted-types-for 'invalid'%09'script'" (required-ascii-whitespace)]
- expected: FAIL
-
[directive "require-trusted-types-for 'invalid'%0A%20'script'" (required-ascii-whitespace)]
expected: TIMEOUT
diff --git a/tests/wpt/meta/trusted-types/trusted-types-createHTMLDocument.html.ini b/tests/wpt/meta/trusted-types/trusted-types-createHTMLDocument.html.ini
deleted file mode 100644
index cca7dc42f2b..00000000000
--- a/tests/wpt/meta/trusted-types/trusted-types-createHTMLDocument.html.ini
+++ /dev/null
@@ -1,24 +0,0 @@
-[trusted-types-createHTMLDocument.html]
- [Trusted Type instances created in the main doc can be used. (document)]
- expected: FAIL
-
- [Trusted Type instances created in the main doc can be used. (createHTMLDocument)]
- expected: FAIL
-
- [Trusted Type instances created in the main doc can be used. (DOMParser)]
- expected: FAIL
-
- [Trusted Type instances created in the main doc can be used. (XHR)]
- expected: FAIL
-
- [Default policy applies. (document)]
- expected: FAIL
-
- [Default policy applies. (createHTMLDocument)]
- expected: FAIL
-
- [Default policy applies. (DOMParser)]
- expected: FAIL
-
- [Default policy applies. (XHR)]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/trusted-types-event-handlers.html.ini b/tests/wpt/meta/trusted-types/trusted-types-event-handlers.html.ini
index 225c4d84121..205dd5886a8 100644
--- a/tests/wpt/meta/trusted-types/trusted-types-event-handlers.html.ini
+++ b/tests/wpt/meta/trusted-types/trusted-types-event-handlers.html.ini
@@ -23,9 +23,21 @@
[Event handler div.onpaste should be blocked.]
expected: FAIL
+ [Event handler div.onauxclick should be blocked.]
+ expected: FAIL
+
[Event handler div.onabort should be blocked.]
expected: FAIL
+ [Event handler div.onbeforeinput should be blocked.]
+ expected: FAIL
+
+ [Event handler div.onbeforematch should be blocked.]
+ expected: FAIL
+
+ [Event handler div.onbeforetoggle should be blocked.]
+ expected: FAIL
+
[Event handler div.onblur should be blocked.]
expected: FAIL
@@ -47,9 +59,18 @@
[Event handler div.onclose should be blocked.]
expected: FAIL
+ [Event handler div.oncommand should be blocked.]
+ expected: FAIL
+
[Event handler div.oncontextmenu should be blocked.]
expected: FAIL
+ [Event handler div.oncontextlost should be blocked.]
+ expected: FAIL
+
+ [Event handler div.oncontextrestored should be blocked.]
+ expected: FAIL
+
[Event handler div.oncuechange should be blocked.]
expected: FAIL
@@ -173,6 +194,9 @@
[Event handler div.onscroll should be blocked.]
expected: FAIL
+ [Event handler div.onscrollend should be blocked.]
+ expected: FAIL
+
[Event handler div.onsecuritypolicyviolation should be blocked.]
expected: FAIL
@@ -188,6 +212,9 @@
[Event handler div.onshow should be blocked.]
expected: FAIL
+ [Event handler div.onslotchange should be blocked.]
+ expected: FAIL
+
[Event handler div.onstalled should be blocked.]
expected: FAIL
@@ -229,3 +256,15 @@
[Event handler div.onselectionchange should be blocked.]
expected: FAIL
+
+ [Event handler div.onwebkitanimationend should be blocked.]
+ expected: FAIL
+
+ [Event handler div.onwebkitanimationiteration should be blocked.]
+ expected: FAIL
+
+ [Event handler div.onwebkitanimationstart should be blocked.]
+ expected: FAIL
+
+ [Event handler div.onwebkittransitionend should be blocked.]
+ expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/trusted-types-report-only.html.ini b/tests/wpt/meta/trusted-types/trusted-types-report-only.html.ini
deleted file mode 100644
index 253b126c18f..00000000000
--- a/tests/wpt/meta/trusted-types/trusted-types-report-only.html.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[trusted-types-report-only.html]
- [Trusted Type violation report-only: assign string to html]
- expected: FAIL
-
- [Trusted Type violation report-only: assign string to script content]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-innerHTML.html.ini b/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-innerHTML.html.ini
deleted file mode 100644
index ae31cf41b62..00000000000
--- a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-innerHTML.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[trusted-types-reporting-for-Element-innerHTML.html]
- [Violation report for plain string.]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-insertAdjacentHTML.html.ini b/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-insertAdjacentHTML.html.ini
deleted file mode 100644
index ea9822131e6..00000000000
--- a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-insertAdjacentHTML.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[trusted-types-reporting-for-Element-insertAdjacentHTML.html]
- [Violation report for plain string.]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-outerHTML.html.ini b/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-outerHTML.html.ini
deleted file mode 100644
index ccf4d385ddc..00000000000
--- a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-outerHTML.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[trusted-types-reporting-for-Element-outerHTML.html]
- [Violation report for plain string.]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-setHTMLUnsafe.html.ini b/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-setHTMLUnsafe.html.ini
deleted file mode 100644
index a288f0fc52b..00000000000
--- a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-setHTMLUnsafe.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[trusted-types-reporting-for-Element-setHTMLUnsafe.html]
- [Violation report for plain string.]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-HTMLIFrameElement-srcdoc.html.ini b/tests/wpt/meta/trusted-types/trusted-types-reporting-for-HTMLIFrameElement-srcdoc.html.ini
deleted file mode 100644
index 9f2a73c0900..00000000000
--- a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-HTMLIFrameElement-srcdoc.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[trusted-types-reporting-for-HTMLIFrameElement-srcdoc.html]
- [Violation report for plain string.]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-HTMLScriptElement-innerHTML.html.ini b/tests/wpt/meta/trusted-types/trusted-types-reporting-for-HTMLScriptElement-innerHTML.html.ini
deleted file mode 100644
index 74b6b2803cd..00000000000
--- a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-HTMLScriptElement-innerHTML.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[trusted-types-reporting-for-HTMLScriptElement-innerHTML.html]
- [Violation report for plain string.]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-SVGScriptElement-innerHTML.html.ini b/tests/wpt/meta/trusted-types/trusted-types-reporting-for-SVGScriptElement-innerHTML.html.ini
deleted file mode 100644
index 55b972f6001..00000000000
--- a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-SVGScriptElement-innerHTML.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[trusted-types-reporting-for-SVGScriptElement-innerHTML.html]
- [Violation report for plain string.]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/trusted-types-sandbox-allow-scripts.html.ini b/tests/wpt/meta/trusted-types/trusted-types-sandbox-allow-scripts.html.ini
deleted file mode 100644
index 7053da4f6d0..00000000000
--- a/tests/wpt/meta/trusted-types/trusted-types-sandbox-allow-scripts.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[trusted-types-sandbox-allow-scripts.html]
- [Default Trusted Types policy in a sandboxed page with allow-scripts.]
- expected: FAIL
diff --git a/tests/wpt/meta/trusted-types/trusted-types-source-file-path.html.ini b/tests/wpt/meta/trusted-types/trusted-types-source-file-path.html.ini
deleted file mode 100644
index a05cfef0db5..00000000000
--- a/tests/wpt/meta/trusted-types/trusted-types-source-file-path.html.ini
+++ /dev/null
@@ -1,9 +0,0 @@
-[trusted-types-source-file-path.html]
- [same-document script]
- expected: FAIL
-
- [same-origin script]
- expected: FAIL
-
- [cross-origin script]
- expected: FAIL
diff --git a/tests/wpt/meta/urlpattern/urlpattern.any.js.ini b/tests/wpt/meta/urlpattern/urlpattern.any.js.ini
index 74d44f4fdb7..7248fced522 100644
--- a/tests/wpt/meta/urlpattern/urlpattern.any.js.ini
+++ b/tests/wpt/meta/urlpattern/urlpattern.any.js.ini
@@ -581,9 +581,6 @@
[Pattern: [{"pathname":"/foo/bar"}\] Inputs: ["./foo/bar","https://example.com"\]]
expected: FAIL
- [Pattern: [{"pathname":"/foo/bar"}\] Inputs: [{"pathname":"/foo/bar"},"https://example.com"\]]
- expected: FAIL
-
[Pattern: ["https://example.com:8080/foo?bar#baz"\] Inputs: [{"pathname":"/foo","search":"bar","hash":"baz","baseURL":"https://example.com:8080"}\]]
expected: FAIL
@@ -932,6 +929,12 @@
[Pattern: ["https://{sub.}?example{.com/}foo"\] Inputs: ["https://example.com/foo"\]]
expected: FAIL
+ [Pattern: [{"hostname":"bad\\\\:hostname"}\] Inputs: undefined]
+ expected: FAIL
+
+ [Pattern: [{"pathname":"/foo","baseURL":""}\] Inputs: undefined]
+ expected: FAIL
+
[urlpattern.any.sharedworker.html]
expected: ERROR
@@ -1519,9 +1522,6 @@
[Pattern: [{"pathname":"/foo/bar"}\] Inputs: ["./foo/bar","https://example.com"\]]
expected: FAIL
- [Pattern: [{"pathname":"/foo/bar"}\] Inputs: [{"pathname":"/foo/bar"},"https://example.com"\]]
- expected: FAIL
-
[Pattern: ["https://example.com:8080/foo?bar#baz"\] Inputs: [{"pathname":"/foo","search":"bar","hash":"baz","baseURL":"https://example.com:8080"}\]]
expected: FAIL
@@ -1870,6 +1870,12 @@
[Pattern: ["https://{sub.}?example{.com/}foo"\] Inputs: ["https://example.com/foo"\]]
expected: FAIL
+ [Pattern: [{"hostname":"bad\\\\:hostname"}\] Inputs: undefined]
+ expected: FAIL
+
+ [Pattern: [{"pathname":"/foo","baseURL":""}\] Inputs: undefined]
+ expected: FAIL
+
[urlpattern.any.serviceworker.html]
expected: ERROR
diff --git a/tests/wpt/meta/urlpattern/urlpattern.https.any.js.ini b/tests/wpt/meta/urlpattern/urlpattern.https.any.js.ini
index 1b230cf4b42..f1b0add7805 100644
--- a/tests/wpt/meta/urlpattern/urlpattern.https.any.js.ini
+++ b/tests/wpt/meta/urlpattern/urlpattern.https.any.js.ini
@@ -584,9 +584,6 @@
[Pattern: [{"pathname":"/foo/bar"}\] Inputs: ["./foo/bar","https://example.com"\]]
expected: FAIL
- [Pattern: [{"pathname":"/foo/bar"}\] Inputs: [{"pathname":"/foo/bar"},"https://example.com"\]]
- expected: FAIL
-
[Pattern: ["https://example.com:8080/foo?bar#baz"\] Inputs: [{"pathname":"/foo","search":"bar","hash":"baz","baseURL":"https://example.com:8080"}\]]
expected: FAIL
@@ -935,6 +932,12 @@
[Pattern: ["https://{sub.}?example{.com/}foo"\] Inputs: ["https://example.com/foo"\]]
expected: FAIL
+ [Pattern: [{"hostname":"bad\\\\:hostname"}\] Inputs: undefined]
+ expected: FAIL
+
+ [Pattern: [{"pathname":"/foo","baseURL":""}\] Inputs: undefined]
+ expected: FAIL
+
[urlpattern.https.any.worker.html]
[Pattern: [{"pathname":"/foo/bar"}\] Inputs: [{"pathname":"/foo/bar"}\]]
@@ -1519,9 +1522,6 @@
[Pattern: [{"pathname":"/foo/bar"}\] Inputs: ["./foo/bar","https://example.com"\]]
expected: FAIL
- [Pattern: [{"pathname":"/foo/bar"}\] Inputs: [{"pathname":"/foo/bar"},"https://example.com"\]]
- expected: FAIL
-
[Pattern: ["https://example.com:8080/foo?bar#baz"\] Inputs: [{"pathname":"/foo","search":"bar","hash":"baz","baseURL":"https://example.com:8080"}\]]
expected: FAIL
@@ -1870,6 +1870,12 @@
[Pattern: ["https://{sub.}?example{.com/}foo"\] Inputs: ["https://example.com/foo"\]]
expected: FAIL
+ [Pattern: [{"hostname":"bad\\\\:hostname"}\] Inputs: undefined]
+ expected: FAIL
+
+ [Pattern: [{"pathname":"/foo","baseURL":""}\] Inputs: undefined]
+ expected: FAIL
+
[urlpattern.https.any.serviceworker.html]
expected: ERROR
diff --git a/tests/wpt/meta/wasm/webapi/esm-integration/script-src-blocks-wasm.tentative.sub.html.ini b/tests/wpt/meta/wasm/webapi/esm-integration/script-src-blocks-wasm.tentative.sub.html.ini
deleted file mode 100644
index c804530024c..00000000000
--- a/tests/wpt/meta/wasm/webapi/esm-integration/script-src-blocks-wasm.tentative.sub.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[script-src-blocks-wasm.tentative.sub.html]
- [Importing a WebAssembly module should be guarded by script-src CSP.]
- expected: FAIL
diff --git a/tests/wpt/meta/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html.ini b/tests/wpt/meta/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html.ini
index 590f4ee6a07..c9d88fc368b 100644
--- a/tests/wpt/meta/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html.ini
+++ b/tests/wpt/meta/webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html.ini
@@ -784,3 +784,15 @@
[X SNR (42.96525288004425 dB) is not greater than or equal to 65.737. Got 42.96525288004425.]
expected: FAIL
+
+ [X Stitched sine-wave buffers at sample rate 43800 does not equal [0,0.06264832615852356,0.12505052983760834,0.18696144223213196,0.24813786149024963,0.308339387178421,0.36732959747314453,0.4248766601085663,0.480754554271698,0.5347436666488647,0.5866320133209229,0.6362156271934509,0.6832997798919678,0.7276994585990906,0.7692402601242065,0.8077589869499207...\] with an element-wise tolerance of {"absoluteThreshold":0.0038986,"relativeThreshold":0}.\n\tIndex\tActual\t\t\tExpected\t\tAbsError\t\tRelError\t\tTest threshold\n\t[14650\]\t-2.7243524527875707e-5\t8.6956524848937988e-1\t8.6959249201390776e-1\t1.0000313300520867e+0\t3.8985999999999999e-3\n\t[14651\]\t3.0547976493835449e-1\t8.9879405498504639e-1\t5.9331429004669189e-1\t6.6012262403823208e-1\t3.8985999999999999e-3\n\tMax AbsError of 8.6959249201390776e-1 at index of 14650.\n\tMax RelError of 1.0000313300520867e+0 at index of 14650.\n]
+ expected: FAIL
+
+ [X SNR (42.96506816850161 dB) is not greater than or equal to 65.737. Got 42.96506816850161.]
+ expected: FAIL
+
+ [X Stitched sine-wave buffers at sample rate 43800 does not equal [0,0.06264832615852356,0.12505052983760834,0.18696144223213196,0.24813786149024963,0.308339387178421,0.36732959747314453,0.4248766601085663,0.480754554271698,0.5347436666488647,0.5866320133209229,0.6362156271934509,0.6832997798919678,0.7276994585990906,0.7692402601242065,0.8077589869499207...\] with an element-wise tolerance of {"absoluteThreshold":0.0038986,"relativeThreshold":0}.\n\tIndex\tActual\t\t\tExpected\t\tAbsError\t\tRelError\t\tTest threshold\n\t[14650\]\t-1.7532469034194946e+0\t8.6956524848937988e-1\t2.6228121519088745e+0\t3.0162338668262767e+0\t3.8985999999999999e-3\n\t[14651\]\t3.0547976493835449e-1\t8.9879405498504639e-1\t5.9331429004669189e-1\t6.6012262403823208e-1\t3.8985999999999999e-3\n\tMax AbsError of 2.6228121519088745e+0 at index of 14650.\n\tMax RelError of 3.0162338668262767e+0 at index of 14650.\n]
+ expected: FAIL
+
+ [X SNR (34.8385032008375 dB) is not greater than or equal to 65.737. Got 34.8385032008375.]
+ expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/element_click/click.py.ini b/tests/wpt/meta/webdriver/tests/classic/element_click/click.py.ini
index ad0f9714ad1..9cdf1e0d0da 100644
--- a/tests/wpt/meta/webdriver/tests/classic/element_click/click.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/element_click/click.py.ini
@@ -13,9 +13,3 @@
[test_no_such_element_from_other_frame[closed\]]
expected: FAIL
-
- [test_stale_element_reference[top_context\]]
- expected: FAIL
-
- [test_stale_element_reference[child_context\]]
- expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/element_click/scroll_into_view.py.ini b/tests/wpt/meta/webdriver/tests/classic/element_click/scroll_into_view.py.ini
index 87c9c813881..2627072cf91 100644
--- a/tests/wpt/meta/webdriver/tests/classic/element_click/scroll_into_view.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/element_click/scroll_into_view.py.ini
@@ -1,30 +1,30 @@
[scroll_into_view.py]
[test_scroll_into_view]
- expected: ERROR
+ expected: FAIL
[test_partially_visible_does_not_scroll[9\]]
- expected: ERROR
+ expected: FAIL
[test_partially_visible_does_not_scroll[8\]]
- expected: ERROR
+ expected: FAIL
[test_partially_visible_does_not_scroll[7\]]
- expected: ERROR
+ expected: FAIL
[test_partially_visible_does_not_scroll[6\]]
- expected: ERROR
+ expected: FAIL
[test_partially_visible_does_not_scroll[5\]]
- expected: ERROR
+ expected: FAIL
[test_partially_visible_does_not_scroll[4\]]
- expected: ERROR
+ expected: FAIL
[test_partially_visible_does_not_scroll[3\]]
- expected: ERROR
+ expected: FAIL
[test_partially_visible_does_not_scroll[2\]]
- expected: ERROR
+ expected: FAIL
[test_partially_visible_does_not_scroll[1\]]
- expected: ERROR
+ expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/element_send_keys/form_controls.py.ini b/tests/wpt/meta/webdriver/tests/classic/element_send_keys/form_controls.py.ini
index 5d4a3bd4de5..1dea194f9b2 100644
--- a/tests/wpt/meta/webdriver/tests/classic/element_send_keys/form_controls.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/element_send_keys/form_controls.py.ini
@@ -4,3 +4,6 @@
[test_textarea_append]
expected: FAIL
+
+ [test_date]
+ expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/element_send_keys/scroll_into_view.py.ini b/tests/wpt/meta/webdriver/tests/classic/element_send_keys/scroll_into_view.py.ini
index 3e260cade03..d141c6022db 100644
--- a/tests/wpt/meta/webdriver/tests/classic/element_send_keys/scroll_into_view.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/element_send_keys/scroll_into_view.py.ini
@@ -1,7 +1,4 @@
[scroll_into_view.py]
- [test_element_outside_of_not_scrollable_viewport]
- expected: FAIL
-
[test_element_outside_of_scrollable_viewport]
expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/element_send_keys/send_keys.py.ini b/tests/wpt/meta/webdriver/tests/classic/element_send_keys/send_keys.py.ini
index b2b09490191..10a5a86e3d2 100644
--- a/tests/wpt/meta/webdriver/tests/classic/element_send_keys/send_keys.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/element_send_keys/send_keys.py.ini
@@ -13,9 +13,3 @@
[test_no_such_element_from_other_frame[closed\]]
expected: FAIL
-
- [test_stale_element_reference[top_context\]]
- expected: FAIL
-
- [test_stale_element_reference[child_context\]]
- expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/execute_async_script/collections.py.ini b/tests/wpt/meta/webdriver/tests/classic/execute_async_script/collections.py.ini
index 5d0711fe4ad..710ae93d053 100644
--- a/tests/wpt/meta/webdriver/tests/classic/execute_async_script/collections.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/execute_async_script/collections.py.ini
@@ -1,10 +1,4 @@
[collections.py]
- [test_array_in_array]
- expected: FAIL
-
- [test_dom_token_list]
- expected: FAIL
-
[test_file_list]
expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/execute_script/collections.py.ini b/tests/wpt/meta/webdriver/tests/classic/execute_script/collections.py.ini
index 68e5ec4b830..710ae93d053 100644
--- a/tests/wpt/meta/webdriver/tests/classic/execute_script/collections.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/execute_script/collections.py.ini
@@ -1,12 +1,6 @@
[collections.py]
- [test_dom_token_list]
- expected: FAIL
-
[test_file_list]
expected: FAIL
[test_html_all_collection]
expected: FAIL
-
- [test_array_in_array]
- expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/get_computed_role/get.py.ini b/tests/wpt/meta/webdriver/tests/classic/get_computed_role/get.py.ini
index abd4a7750b7..f00172fdc5a 100644
--- a/tests/wpt/meta/webdriver/tests/classic/get_computed_role/get.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/get_computed_role/get.py.ini
@@ -14,11 +14,5 @@
[test_no_such_element_from_other_frame[closed\]]
expected: FAIL
- [test_stale_element_reference[top_context\]]
- expected: FAIL
-
- [test_stale_element_reference[child_context\]]
- expected: FAIL
-
[test_computed_roles[<article>foo</article>-article-article\]]
expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/get_element_css_value/get.py.ini b/tests/wpt/meta/webdriver/tests/classic/get_element_css_value/get.py.ini
index bcd25112776..d55c5312a47 100644
--- a/tests/wpt/meta/webdriver/tests/classic/get_element_css_value/get.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/get_element_css_value/get.py.ini
@@ -13,9 +13,3 @@
[test_no_such_element_from_other_frame[closed\]]
expected: FAIL
-
- [test_stale_element_reference[top_context\]]
- expected: FAIL
-
- [test_stale_element_reference[child_context\]]
- expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/get_element_rect/get.py.ini b/tests/wpt/meta/webdriver/tests/classic/get_element_rect/get.py.ini
index 065e9fbc4ce..67875a58cd9 100644
--- a/tests/wpt/meta/webdriver/tests/classic/get_element_rect/get.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/get_element_rect/get.py.ini
@@ -14,11 +14,5 @@
[test_no_such_element_from_other_frame[closed\]]
expected: FAIL
- [test_stale_element_reference[top_context\]]
- expected: FAIL
-
- [test_stale_element_reference[child_context\]]
- expected: FAIL
-
[test_basic]
expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/get_element_tag_name/get.py.ini b/tests/wpt/meta/webdriver/tests/classic/get_element_tag_name/get.py.ini
index b810c389100..0ac8ff98d59 100644
--- a/tests/wpt/meta/webdriver/tests/classic/get_element_tag_name/get.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/get_element_tag_name/get.py.ini
@@ -14,11 +14,5 @@
[test_no_such_element_from_other_frame[closed\]]
expected: FAIL
- [test_stale_element_reference[top_context\]]
- expected: FAIL
-
- [test_stale_element_reference[child_context\]]
- expected: FAIL
-
[test_get_element_tag_name]
expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/get_element_text/get.py.ini b/tests/wpt/meta/webdriver/tests/classic/get_element_text/get.py.ini
index 5f04f967054..ad870f8f49b 100644
--- a/tests/wpt/meta/webdriver/tests/classic/get_element_text/get.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/get_element_text/get.py.ini
@@ -14,12 +14,6 @@
[test_no_such_element_from_other_frame[closed\]]
expected: FAIL
- [test_stale_element_reference[top_context\]]
- expected: FAIL
-
- [test_stale_element_reference[child_context\]]
- expected: FAIL
-
[test_transform_capitalize[space\]]
expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/is_element_selected/selected.py.ini b/tests/wpt/meta/webdriver/tests/classic/is_element_selected/selected.py.ini
index f75724979c5..eb4c0299197 100644
--- a/tests/wpt/meta/webdriver/tests/classic/is_element_selected/selected.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/is_element_selected/selected.py.ini
@@ -13,9 +13,3 @@
[test_no_such_element_from_other_frame[closed\]]
expected: FAIL
-
- [test_stale_element_reference[top_context\]]
- expected: FAIL
-
- [test_stale_element_reference[child_context\]]
- expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/new_window/new_tab.py.ini b/tests/wpt/meta/webdriver/tests/classic/new_window/new_tab.py.ini
index 77be6174789..db51a3496ae 100644
--- a/tests/wpt/meta/webdriver/tests/classic/new_window/new_tab.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/new_window/new_tab.py.ini
@@ -1,15 +1,9 @@
[new_tab.py]
[test_keeps_current_window_handle]
- expected: ERROR
+ expected: FAIL
[test_opens_about_blank_in_new_tab]
- expected: ERROR
+ expected: FAIL
[test_initial_selection_for_contenteditable]
- expected: ERROR
-
- [test_sets_no_window_name]
- expected: ERROR
-
- [test_sets_no_opener]
- expected: ERROR
+ expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/perform_actions/perform.py.ini b/tests/wpt/meta/webdriver/tests/classic/perform_actions/perform.py.ini
deleted file mode 100644
index b4a8841b9ae..00000000000
--- a/tests/wpt/meta/webdriver/tests/classic/perform_actions/perform.py.ini
+++ /dev/null
@@ -1,9 +0,0 @@
-[perform.py]
- [test_input_source_action_sequence_actions_pause_duration_valid[wheel\]]
- expected: FAIL
-
- [test_input_source_action_sequence_actions_pause_duration_missing[wheel\]]
- expected: FAIL
-
- [test_input_source_action_sequence_pointer_parameters_not_processed[wheel\]]
- expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_mouse.py.ini b/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_mouse.py.ini
index 4a163fe2fac..4222966b349 100644
--- a/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_mouse.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_mouse.py.ini
@@ -1,18 +1,11 @@
[pointer_mouse.py]
- expected: TIMEOUT
[test_no_top_browsing_context]
expected: FAIL
[test_no_browsing_context]
- expected: ERROR
-
- [test_pointer_down_closes_browsing_context]
- expected: FAIL
-
- [test_stale_element_reference[top_context\]]
expected: FAIL
- [test_stale_element_reference[child_context\]]
+ [test_pointer_down_closes_browsing_context]
expected: FAIL
[test_click_at_coordinates]
@@ -39,9 +32,6 @@
[test_click_element_in_shadow_tree[inner-closed\]]
expected: FAIL
- [test_click_navigation]
- expected: FAIL
-
[test_move_to_position_in_viewport[x\]]
expected: FAIL
@@ -60,5 +50,5 @@
[test_move_to_origin_position_within_frame[element\]]
expected: FAIL
- [test_invalid_element_origin]
+ [test_params_actions_origin_outside_viewport[element\]]
expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_mouse_drag.py.ini b/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_mouse_drag.py.ini
index 8657edd79e8..3fa735ac7ea 100644
--- a/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_mouse_drag.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_mouse_drag.py.ini
@@ -1,63 +1,63 @@
[pointer_mouse_drag.py]
[test_drag_and_drop[20-0-0\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop[20-0-300\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop[20-0-800\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop[0-15-0\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop[0-15-300\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop[0-15-800\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop[10-15-0\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop[10-15-300\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop[10-15-800\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop[-20-0-0\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop[-20-0-300\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop[-20-0-800\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop[10--15-0\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop[10--15-300\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop[10--15-800\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop[-10--15-0\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop[-10--15-300\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop[-10--15-800\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop_with_draggable_element[0\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop_with_draggable_element[300\]]
- expected: ERROR
+ expected: FAIL
[test_drag_and_drop_with_draggable_element[800\]]
- expected: ERROR
+ expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_origin.py.ini b/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_origin.py.ini
index 112a4f9ddf4..8102334d66b 100644
--- a/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_origin.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_origin.py.ini
@@ -1,27 +1,18 @@
[pointer_origin.py]
[test_viewport_inside]
- expected: ERROR
+ expected: FAIL
[test_pointer_inside]
- expected: ERROR
+ expected: FAIL
[test_element_center_point]
- expected: ERROR
+ expected: FAIL
[test_element_center_point_with_offset]
- expected: ERROR
+ expected: FAIL
[test_element_in_view_center_point_partly_visible]
- expected: ERROR
+ expected: FAIL
[test_element_larger_than_viewport]
- expected: ERROR
-
- [test_element_outside_of_view_port]
- expected: ERROR
-
- [test_viewport_outside]
- expected: ERROR
-
- [test_pointer_outside]
- expected: ERROR
+ expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_pen.py.ini b/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_pen.py.ini
index a3d52579f7d..5c08076b7b2 100644
--- a/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_pen.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_pen.py.ini
@@ -8,12 +8,6 @@
[test_pointer_down_closes_browsing_context]
expected: FAIL
- [test_stale_element_reference[top_context\]]
- expected: FAIL
-
- [test_stale_element_reference[child_context\]]
- expected: FAIL
-
[test_pen_pointer_in_shadow_tree[outer-open\]]
expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_touch.py.ini b/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_touch.py.ini
index 65101d138aa..2dd2ee19891 100644
--- a/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_touch.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/perform_actions/pointer_touch.py.ini
@@ -8,12 +8,6 @@
[test_pointer_down_closes_browsing_context]
expected: FAIL
- [test_stale_element_reference[top_context\]]
- expected: FAIL
-
- [test_stale_element_reference[child_context\]]
- expected: FAIL
-
[test_touch_pointer_in_shadow_tree[outer-open\]]
expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/perform_actions/sequence.py.ini b/tests/wpt/meta/webdriver/tests/classic/perform_actions/sequence.py.ini
deleted file mode 100644
index 54ee376545d..00000000000
--- a/tests/wpt/meta/webdriver/tests/classic/perform_actions/sequence.py.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[sequence.py]
- [test_perform_no_actions_send_no_events]
- expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/perform_actions/wheel.py.ini b/tests/wpt/meta/webdriver/tests/classic/perform_actions/wheel.py.ini
index 3f6abc70f3e..c8a0364b783 100644
--- a/tests/wpt/meta/webdriver/tests/classic/perform_actions/wheel.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/perform_actions/wheel.py.ini
@@ -1,19 +1,10 @@
[wheel.py]
- [test_null_response_value]
- expected: FAIL
-
[test_no_top_browsing_context]
expected: FAIL
[test_no_browsing_context]
expected: FAIL
- [test_params_actions_origin_outside_viewport[element\]]
- expected: FAIL
-
- [test_params_actions_origin_outside_viewport[viewport\]]
- expected: FAIL
-
[test_scroll_not_scrollable]
expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/switch_to_frame/switch_webelement.py.ini b/tests/wpt/meta/webdriver/tests/classic/switch_to_frame/switch_webelement.py.ini
index 9932ab9a5d5..1f469cd85c3 100644
--- a/tests/wpt/meta/webdriver/tests/classic/switch_to_frame/switch_webelement.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/switch_to_frame/switch_webelement.py.ini
@@ -1,10 +1,4 @@
[switch_webelement.py]
- [test_frame_id_webelement_stale_element_reference[top_context\]]
- expected: FAIL
-
- [test_frame_id_webelement_stale_element_reference[child_context\]]
- expected: FAIL
-
[test_frame_id_webelement_frame[0-foo\]]
expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/take_element_screenshot/screenshot.py.ini b/tests/wpt/meta/webdriver/tests/classic/take_element_screenshot/screenshot.py.ini
index 319e4bf848f..981c68641a8 100644
--- a/tests/wpt/meta/webdriver/tests/classic/take_element_screenshot/screenshot.py.ini
+++ b/tests/wpt/meta/webdriver/tests/classic/take_element_screenshot/screenshot.py.ini
@@ -19,12 +19,3 @@
[test_no_such_element_from_other_frame[closed\]]
expected: FAIL
-
- [test_stale_element_reference[top_context\]]
- expected: FAIL
-
- [test_stale_element_reference[child_context\]]
- expected: FAIL
-
- [test_format_and_dimensions]
- expected: FAIL
diff --git a/tests/wpt/meta/webdriver/tests/classic/take_screenshot/iframe.py.ini b/tests/wpt/meta/webdriver/tests/classic/take_screenshot/iframe.py.ini
deleted file mode 100644
index 62f7ab5e4da..00000000000
--- a/tests/wpt/meta/webdriver/tests/classic/take_screenshot/iframe.py.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[iframe.py]
- [test_always_captures_top_browsing_context]
- expected: FAIL
diff --git a/tests/wpt/meta/webmessaging/messageerror.html.ini b/tests/wpt/meta/webmessaging/messageerror.html.ini
deleted file mode 100644
index 9b875551f90..00000000000
--- a/tests/wpt/meta/webmessaging/messageerror.html.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[messageerror.html]
- [The onmessageerror content attribute must be compiled into the onmessageerror property]
- expected: FAIL
-
- [The onmessageerror content attribute must execute when an event is dispatched on the window]
- expected: FAIL
diff --git a/tests/wpt/meta/webxr/render_state_update.https.html.ini b/tests/wpt/meta/webxr/render_state_update.https.html.ini
new file mode 100644
index 00000000000..0e57356683e
--- /dev/null
+++ b/tests/wpt/meta/webxr/render_state_update.https.html.ini
@@ -0,0 +1,2 @@
+[render_state_update.https.html]
+ expected: ERROR
diff --git a/tests/wpt/meta/workers/WorkerGlobalScope-close.html.ini b/tests/wpt/meta/workers/WorkerGlobalScope-close.html.ini
new file mode 100644
index 00000000000..24daae4c2e7
--- /dev/null
+++ b/tests/wpt/meta/workers/WorkerGlobalScope-close.html.ini
@@ -0,0 +1,3 @@
+[WorkerGlobalScope-close.html]
+ [Test sending a message after closing.]
+ expected: FAIL
diff --git a/tests/wpt/meta/xhr/formdata/append.any.js.ini b/tests/wpt/meta/xhr/formdata/append.any.js.ini
new file mode 100644
index 00000000000..e727a77e38d
--- /dev/null
+++ b/tests/wpt/meta/xhr/formdata/append.any.js.ini
@@ -0,0 +1,8 @@
+[append.any.worker.html]
+ [testFormDataAppendEmptyBlob]
+ expected: FAIL
+
+
+[append.any.html]
+ [testFormDataAppendEmptyBlob]
+ expected: FAIL
diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json
index 7035ae424dc..2797b63f2ae 100644
--- a/tests/wpt/mozilla/meta/MANIFEST.json
+++ b/tests/wpt/mozilla/meta/MANIFEST.json
@@ -16,6 +16,13 @@
null,
{}
]
+ ],
+ "transfer-control-to-offscreencanvas-then-change-dimensions-crash.html": [
+ "bc0c7afd6ab2e1c313bb61ea4a5c68b402105f5d",
+ [
+ null,
+ {}
+ ]
]
},
"datatransferitem-crash.html": [
@@ -13575,14 +13582,14 @@
]
],
"interfaces.https.html": [
- "76d746b0663ed73865816e678c2536eceff31f2d",
+ "eee8c799727b91e00b512795756b693a5f121f86",
[
null,
{}
]
],
"interfaces.worker.js": [
- "8d109502622fac7266a4564de09684a3ab94118c",
+ "e86f34f261442aeaa7074c525fb4b1206219769d",
[
"mozilla/interfaces.worker.html",
{}
diff --git a/tests/wpt/mozilla/meta/__dir__.ini b/tests/wpt/mozilla/meta/__dir__.ini
index 925f07e1c50..e7a12669952 100644
--- a/tests/wpt/mozilla/meta/__dir__.ini
+++ b/tests/wpt/mozilla/meta/__dir__.ini
@@ -1,4 +1,5 @@
prefs: [
+ "dom_serviceworker_enabled:true",
"dom_urlpattern_enabled:true",
"media_testing_enabled:true",
]
diff --git a/tests/wpt/mozilla/tests/mozilla/canvas/transfer-control-to-offscreencanvas-then-change-dimensions-crash.html b/tests/wpt/mozilla/tests/mozilla/canvas/transfer-control-to-offscreencanvas-then-change-dimensions-crash.html
new file mode 100644
index 00000000000..bc0c7afd6ab
--- /dev/null
+++ b/tests/wpt/mozilla/tests/mozilla/canvas/transfer-control-to-offscreencanvas-then-change-dimensions-crash.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<script>
+ let canvas = document.createElement("canvas");
+ let offscreenCanvas = canvas.transferControlToOffscreen();
+ offscreenCanvas.height = 0;
+</script>
+
diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html
index 76d746b0663..eee8c799727 100644
--- a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html
+++ b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html
@@ -315,6 +315,7 @@ test_interfaces([
"SubtleCrypto",
"SVGElement",
"SVGGraphicsElement",
+ "SVGImageElement",
"SVGRect",
"SVGSVGElement",
"Text",
@@ -371,6 +372,8 @@ test_interfaces([
"WritableStream",
"WritableStreamDefaultController",
"WritableStreamDefaultWriter",
+ "TransformStream",
+ "TransformStreamDefaultController",
"WGSLLanguageFeatures",
"XMLDocument",
"XMLHttpRequest",
diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js
index 8d109502622..e86f34f2614 100644
--- a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js
+++ b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js
@@ -137,6 +137,8 @@ test_interfaces([
"WritableStream",
"WritableStreamDefaultController",
"WritableStreamDefaultWriter",
+ "TransformStream",
+ "TransformStreamDefaultController",
"WGSLLanguageFeatures",
"XMLHttpRequest",
"XMLHttpRequestEventTarget",
diff --git a/tests/wpt/tests/IndexedDB/idbcursor-request-source.any.js b/tests/wpt/tests/IndexedDB/idbcursor-request-source.any.js
index 2fe8c66f2e5..8e1b34ee798 100644
--- a/tests/wpt/tests/IndexedDB/idbcursor-request-source.any.js
+++ b/tests/wpt/tests/IndexedDB/idbcursor-request-source.any.js
@@ -6,21 +6,111 @@
'use strict';
-[cursor => cursor.update(0), cursor => cursor.delete()].forEach(
- func => indexeddb_test(
- (t, db) => {
- db.createObjectStore('store', {autoIncrement: true});
- },
- (t, db) => {
- const tx = db.transaction('store', 'readwrite');
- const store = tx.objectStore('store');
- store.put('value');
- store.openCursor().onsuccess = t.step_func(e => {
- const cursor = e.target.result;
- assert_equals(
- func(cursor).source, cursor,
- `${func}.source should be the cursor itself`);
+// Setup each test by populating an object store with an index for the cursor to
+// iterate and manipulate.
+function initializeDatabase(db) {
+ const store = db.createObjectStore('store', {autoIncrement: true});
+ store.createIndex('index', /*keypath=*/ 'value');
+ store.put({value: 'z'});
+ store.put({value: 'y'});
+ store.put({value: 'x'});
+ store.put({value: 'w'});
+}
+
+function isIndex(cursorSourceType) {
+ return cursorSourceType === 'IDBIndex';
+}
+
+// Return the object store or index, depending on the test's `cursorSourceType`.
+function getCursorSource(transaction, cursorSourceType) {
+ let cursorSource = transaction.objectStore('store');
+ if (isIndex(cursorSourceType)) {
+ cursorSource = cursorSource.index('index');
+ }
+ return cursorSource;
+}
+
+// Verify the request source after calling delete() or update() on the cursor.
+function cursor_request_source_test(
+ cursorSourceType, createRequestFunctionName, createRequestFunctionArgs) {
+ indexeddb_test(
+ (t, db) => initializeDatabase(db),
+ (t, db) => {
+ const tx = db.transaction('store', 'readwrite');
+ const cursorSource = getCursorSource(tx, cursorSourceType);
+
+ // Open the cursor.
+ const openCursorRequest = cursorSource.openCursor();
+ openCursorRequest.onerror =
+ t.unreached_func('The cursor must not fail to open.');
+
+ openCursorRequest.onsuccess = t.step_func(e => {
+ // Use the cursor to create a new request.
+ const cursor = e.target.result;
+ const request =
+ cursor[createRequestFunctionName](...createRequestFunctionArgs);
+ assert_equals(
+ request.source, cursor,
+ `The request's source must be the cursor itself.`);
+ t.done();
+ });
+ },
+ `The source of the request from ${cursorSourceType}::${
+ createRequestFunctionName}() is the cursor itself`);
+}
+
+// Verify the request source after calling openCursor() or openKeyCursor() and
+// then using the cursor to iterate.
+function open_cursor_request_source_test(
+ cursorSourceType, openCursorFunctionName) {
+ indexeddb_test(
+ (t, db) => initializeDatabase(db),
+ (t, db) => {
+ const tx = db.transaction('store', 'readonly');
+ const cursorSource = getCursorSource(tx, cursorSourceType);
+
+ // Open the cursor.
+ const openCursorRequest = cursorSource[openCursorFunctionName]();
+ openCursorRequest.onerror =
+ t.unreached_func('The cursor must not fail to open or iterate.');
+
+ assert_equals(
+ openCursorRequest.source, cursorSource,
+ 'The request source must be the opener of the cursor.');
+
+ // Verify the cursor's `request.source` after iterating with
+ // `advance()`, `continue()`, and `continuePrimaryKey()`.
+ let iterationCount = 0;
+ openCursorRequest.onsuccess = t.step_func(e => {
+ assert_equals(
+ openCursorRequest.source, cursorSource,
+ 'The request source must be the opener of the cursor after iterating.');
+
+ const cursor = e.target.result;
+ ++iterationCount;
+
+ if (iterationCount == 1) {
+ cursor.advance(1);
+ } else if (iterationCount == 2) {
+ cursor.continue();
+ } else if (iterationCount == 3 && isIndex(cursorSourceType)) {
+ cursor.continuePrimaryKey('z', 0);
+ } else {
t.done();
- });
- },
- `The source of the request from ${func} is the cursor itself`));
+ }
+ });
+ },
+ `${cursorSourceType}::${
+ openCursorFunctionName}'s request source must be the ${
+ cursorSourceType} instance that opened the cursor`);
+}
+
+open_cursor_request_source_test('IDBObjectStore', 'openCursor');
+open_cursor_request_source_test('IDBObjectStore', 'openKeyCursor');
+open_cursor_request_source_test('IDBIndex', 'openCursor');
+open_cursor_request_source_test('IDBIndex', 'openKeyCursor');
+
+cursor_request_source_test('IDBObjectStore', 'update', /*args=*/[0]);
+cursor_request_source_test('IDBObjectStore', 'delete', /*args=*/[]);
+cursor_request_source_test('IDBIndex', 'update', /*args=*/[0]);
+cursor_request_source_test('IDBIndex', 'delete', /*args=*/[]);
diff --git a/tests/wpt/tests/WebCryptoAPI/idlharness.https.any.js b/tests/wpt/tests/WebCryptoAPI/idlharness.https.any.js
index ae65eb49f21..5ddf7eab6db 100644
--- a/tests/wpt/tests/WebCryptoAPI/idlharness.https.any.js
+++ b/tests/wpt/tests/WebCryptoAPI/idlharness.https.any.js
@@ -5,7 +5,7 @@
// https://w3c.github.io/webcrypto/Overview.html
idl_test(
- ['WebCryptoAPI'],
+ ['webcrypto'],
['html', 'dom'],
idl_array => {
idl_array.add_objects({
diff --git a/tests/wpt/tests/ai/translator/resources/util.js b/tests/wpt/tests/ai/translator/resources/util.js
new file mode 100644
index 00000000000..ad06086a123
--- /dev/null
+++ b/tests/wpt/tests/ai/translator/resources/util.js
@@ -0,0 +1,4 @@
+async function createTranslator(options) {
+ await test_driver.bless();
+ return await Translator.create(options);
+}
diff --git a/tests/wpt/tests/ai/translator/translator-bad-input.tentative.https.window.js b/tests/wpt/tests/ai/translator/translator-bad-input.https.window.js
index 53a184bfd19..db8905a61f6 100644
--- a/tests/wpt/tests/ai/translator/translator-bad-input.tentative.https.window.js
+++ b/tests/wpt/tests/ai/translator/translator-bad-input.https.window.js
@@ -8,8 +8,7 @@
'use strict';
promise_test(async t => {
- await promise_rejects_js(
- t, TypeError, Translator.create(/*empty options*/));
+ await promise_rejects_js(t, TypeError, Translator.create(/*empty options*/));
}, 'Translator.create rejects with TypeError if no options are passed.');
promise_test(async t => {
diff --git a/tests/wpt/tests/ai/translator/translator-translate.tentative.https.window.js b/tests/wpt/tests/ai/translator/translator.optional.https.window.js
index a8aad5e03e1..96eca09d28b 100644
--- a/tests/wpt/tests/ai/translator/translator-translate.tentative.https.window.js
+++ b/tests/wpt/tests/ai/translator/translator.optional.https.window.js
@@ -1,21 +1,16 @@
-// META: title=Translate from English to Japanese
+// META: title=Translator Translate
// META: global=window
// META: timeout=long
// META: script=../resources/util.js
// META: script=../resources/language_codes.js
// META: script=/resources/testdriver.js
+// META: script=resources/util.js
//
// Setting `timeout=long` as this test may require downloading the translation
// library and the language models.
'use strict';
-async function createTranslator(options) {
- return await test_driver.bless('Create translator', async () => {
- return await Translator.create(options);
- });
-}
-
promise_test(async t => {
const languagePair = {sourceLanguage: 'en', targetLanguage: 'ja'};
@@ -141,7 +136,7 @@ promise_test(async t => {
for (let i = 0; i < translatableStrings.length; i++) {
assert_not_equals(translatedTranslatableString[i], translatableStrings[i]);
}
-}, 'Translator.translate() echos non-translatable content');
+}, 'Translator.translate() echoes non-translatable content');
promise_test(async t => {
const translator =
diff --git a/tests/wpt/tests/badging/WEB_FEATURES.yml b/tests/wpt/tests/badging/WEB_FEATURES.yml
new file mode 100644
index 00000000000..3c4f69200f6
--- /dev/null
+++ b/tests/wpt/tests/badging/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: badging
+ files: "**"
diff --git a/tests/wpt/tests/clear-site-data/WEB_FEATURES.yml b/tests/wpt/tests/clear-site-data/WEB_FEATURES.yml
new file mode 100644
index 00000000000..d55b5faaf11
--- /dev/null
+++ b/tests/wpt/tests/clear-site-data/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: clear-site-data
+ files: "**"
diff --git a/tests/wpt/tests/compat/webkit-box-ignores-flex-wrap.tentative.html b/tests/wpt/tests/compat/webkit-box-ignores-flex-wrap.tentative.html
new file mode 100644
index 00000000000..98086f83570
--- /dev/null
+++ b/tests/wpt/tests/compat/webkit-box-ignores-flex-wrap.tentative.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<link rel="help" href="https://compat.spec.whatwg.org/#valdef-flex--webkit-box">
+<link rel="match" href="green-ref.html">
+<div style="display: -webkit-box; flex-wrap: wrap; width: 50px;">
+ <div style="width: 50px; height: 100px; background: green;"></div>
+ <div style="width: 50px; height: 100px; background: green;"></div>
+</div>
diff --git a/tests/wpt/tests/content-security-policy/img-src/icon-blocked.sub.html b/tests/wpt/tests/content-security-policy/img-src/icon-blocked.sub.html
index cc882347a1a..4c39e5dec73 100644
--- a/tests/wpt/tests/content-security-policy/img-src/icon-blocked.sub.html
+++ b/tests/wpt/tests/content-security-policy/img-src/icon-blocked.sub.html
@@ -12,6 +12,7 @@
var t_spv = async_test("Test that spv event is fired");
window.addEventListener("securitypolicyviolation", t_spv.step_func_done(function(e) {
assert_equals(e.violatedDirective, 'img-src');
+ assert_equals(e.target, document);
assert_true(e.blockedURI.endsWith('/support/fail.png'));
}));
diff --git a/tests/wpt/tests/content-security-policy/img-src/img-src-targeting.html b/tests/wpt/tests/content-security-policy/img-src/img-src-targeting.html
new file mode 100644
index 00000000000..3b4fe7c690b
--- /dev/null
+++ b/tests/wpt/tests/content-security-policy/img-src/img-src-targeting.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta http-equiv="Content-Security-Policy" content="img-src 'none';">
+ <script src='/resources/testharness.js'></script>
+ <script src='/resources/testharnessreport.js'></script>
+</head>
+<body>
+<p>Check that img-src sets correct target</p>
+ <script>
+ var t = async_test("Test that image does not load");
+ var t_spv = async_test("Test that spv event is fired");
+ window.addEventListener("securitypolicyviolation", t_spv.step_func_done(function(e) {
+ assert_equals(e.violatedDirective, 'img-src');
+ assert_equals(e.target, document);
+ assert_true(e.blockedURI.endsWith('/support/fail.png'));
+ }));
+ </script>
+ <img src='/content-security-policy/support/fail.png'
+ onload='t.step(function() { assert_unreached("Image should not have loaded"); t.done(); });'
+ onerror='t.done();'>
+</body>
+
+</html>
diff --git a/tests/wpt/tests/content-security-policy/style-src/style-src-inline-style-with-csstext.html b/tests/wpt/tests/content-security-policy/style-src/style-src-inline-style-with-csstext.html
new file mode 100644
index 00000000000..5e812b4aee9
--- /dev/null
+++ b/tests/wpt/tests/content-security-policy/style-src/style-src-inline-style-with-csstext.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html>
+<head>
+ <meta http-equiv="Content-Security-Policy" content="style-src 'self';">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+
+ <script>
+ var t = async_test("Manipulating cssText should be allowed with 'self'");
+ document.addEventListener("securitypolicyviolation", t.unreached_func("Should not trigger a security policy violation"));
+ </script>
+</head>
+<body>
+ <div id='log'></div>
+
+ <div id="content">Lorem ipsum</div>
+
+ <script>
+ t.step(function() {
+ var contentEl = document.getElementById("content");
+ contentEl.style.cssText = 'margin-left: 2px;';
+ var marginLeftVal = getComputedStyle(contentEl).getPropertyValue('margin-left');
+ assert_equals(marginLeftVal, "2px");
+ t.done();
+ });
+ </script>
+
+</body>
+</html>
diff --git a/tests/wpt/tests/cookie-store/cookieListItem_attributes.https.any.js b/tests/wpt/tests/cookie-store/cookieListItem_attributes.https.any.js
index 6716d91788d..542bd6c5387 100644
--- a/tests/wpt/tests/cookie-store/cookieListItem_attributes.https.any.js
+++ b/tests/wpt/tests/cookie-store/cookieListItem_attributes.https.any.js
@@ -207,3 +207,20 @@ promise_test(async testCase => {
const cookie = await cookieStore.get('cookie-name');
assert_equals(cookie.secure, true);
}, 'CookieListItem - secure defaults to true');
+
+promise_test(async testCase => {
+ await cookieStore.delete('cookie-name');
+ testCase.add_cleanup(async () => {
+ await cookieStore.delete('cookie-name');
+ });
+
+ let encodedCookie = encodeURIComponent(JSON.stringify("cookie-name=1; max-age=99999999999999999999999999999; path=/"));
+ await fetch(`/cookies/resources/cookie.py?set=${encodedCookie}`);
+
+ assert_equals(document.cookie, "cookie-name=1", 'The cookie was set as expected.');
+
+ const cookie = await cookieStore.get('cookie-name');
+ assert_equals(cookie.name, 'cookie-name');
+ assert_equals(cookie.value, '1');
+ assert_approx_equals(cookie.expires, kFourHundredDaysFromNow, kOneDay);
+}, "Test max-age attribute over the 400 days");
diff --git a/tests/wpt/tests/cookie-store/httponly_cookies.https.window.js b/tests/wpt/tests/cookie-store/httponly_cookies.https.window.js
index 8a10e358ef6..605e94e6744 100644
--- a/tests/wpt/tests/cookie-store/httponly_cookies.https.window.js
+++ b/tests/wpt/tests/cookie-store/httponly_cookies.https.window.js
@@ -67,3 +67,33 @@ cookie_test(async t => {
'cookie1=value1; cookie2=value2; cookie3=value3',
'httpOnly is not an option for CookieStore.set()');
}, 'HttpOnly cookies can not be set by CookieStore');
+
+cookie_test(async t => {
+ await setCookieStringHttp('HTTPONLY-cookie=value; path=/; httponly');
+ assert_equals(
+ await getCookieString(),
+ undefined,
+ 'HttpOnly cookie we wrote using HTTP in cookie jar' +
+ ' is invisible to script');
+ assert_equals(
+ await getCookieStringHttp(),
+ 'HTTPONLY-cookie=value',
+ 'HttpOnly cookie we wrote using HTTP in HTTP cookie jar');
+
+ try {
+ await cookieStore.set('HTTPONLY-cookie', 'dummy');
+ } catch(e) {}
+
+ assert_equals(
+ await getCookieString(),
+ undefined,
+ 'HttpOnly cookie is not overwritten');
+
+ try {
+ await cookieStore.delete('HTTPONLY-cookie');
+ } catch(e) {}
+
+ assert_equals(await getCookieString(), undefined, 'HttpOnly cookie is not overwritten');
+
+ assert_equals(await getCookieStringHttp(), 'HTTPONLY-cookie=value', 'HttpOnly cookie is not deleted');
+}, 'HttpOnly cookies are not deleted/overwritten');
diff --git a/tests/wpt/tests/css/compositing/root-element-background-image-opaque-crash.html b/tests/wpt/tests/css/compositing/root-element-background-image-opaque-crash.html
new file mode 100644
index 00000000000..d380310d096
--- /dev/null
+++ b/tests/wpt/tests/css/compositing/root-element-background-image-opaque-crash.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<style>
+ :root {
+ background-color: rgba(255, 255, 255, 0.98);
+ background-image: url(/images/computer.jpg); /* opaque */
+ background-position: top left;
+ background-repeat: repeat;
+ }
+</style>
diff --git a/tests/wpt/tests/css/css-align/blocks/justify-items-anonymous.tentative.html b/tests/wpt/tests/css/css-align/blocks/justify-items-anonymous.tentative.html
new file mode 100644
index 00000000000..641dea1f54b
--- /dev/null
+++ b/tests/wpt/tests/css/css-align/blocks/justify-items-anonymous.tentative.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/11461">
+<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
+<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
+<div style="width: 100px; height: 100px; background: red;">
+ <div style="width: 200px; justify-items: right;">
+ <span style="display: inline-block; width: 100px; height: 100px; background: green;"></span>
+ <div></div> <!-- Forces an anonymous block around <span> in most browsers. -->
+ </div>
+</div>
diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-position-grid-001.html b/tests/wpt/tests/css/css-anchor-position/anchor-position-grid-001.html
index 92fb4d275b8..7bd389b39d8 100644
--- a/tests/wpt/tests/css/css-anchor-position/anchor-position-grid-001.html
+++ b/tests/wpt/tests/css/css-anchor-position/anchor-position-grid-001.html
@@ -75,8 +75,8 @@
<div class="target target1-l" data-offset-x=100 data-expected-height=100></div>
<div class="target target1-r" data-offset-x=404 data-expected-height=100></div>
- <div class="target target1-t" data-offset-y=0 data-expected-width=310></div>
- <div class="target target1-b" data-offset-y=95 data-expected-width=310></div>
+ <div class="target target1-t" data-offset-y=-10 data-expected-width=310></div>
+ <div class="target target1-b" data-offset-y=85 data-expected-width=310></div>
</div>
</div>
</div>
diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-002.html b/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-002.html
index 7b2691a2b90..1e1f0a5c95b 100644
--- a/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-002.html
+++ b/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-002.html
@@ -71,15 +71,15 @@
<!-- The containing block of querying elements is block-fragmented. -->
<div class="target target1"
- data-offset-x=18 data-offset-y=65
+ data-offset-x=128 data-offset-y=-35
data-expected-width=160 data-expected-height=100></div>
<div class="target target1-rb"
- data-offset-x=168 data-offset-y=155></div>
+ data-offset-x=278 data-offset-y=55></div>
<div class="target fixed target1"
- data-offset-x=26 data-offset-y=70
+ data-offset-x=136 data-offset-y=-30
data-expected-width=160 data-expected-height=100></div>
<div class="target fixed target1-rb"
- data-offset-x=176 data-offset-y=160></div>
+ data-offset-x=286 data-offset-y=60></div>
</div>
<!-- The containing block of querying elements is a multi-column. -->
diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-004.html b/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-004.html
index 399494120ea..8f7a3dad129 100644
--- a/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-004.html
+++ b/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-004.html
@@ -69,16 +69,16 @@
<div class="anchor1" style="width: 20px"></div>
<div class="target target1"
- data-offset-x=18 data-offset-y=65
+ data-offset-x=128 data-offset-y=-35
data-expected-width=130 data-expected-height=100></div>
<div class="target target1-rb"
- data-offset-x=138 data-offset-y=155></div>
+ data-offset-x=248 data-offset-y=55></div>
</div>
<div class="target target1"
- data-offset-x=34 data-offset-y=225
+ data-offset-x=364 data-offset-y=-75
data-expected-width=130 data-expected-height=100></div>
<div class="target target1-rb"
- data-offset-x=154 data-offset-y=315></div>
+ data-offset-x=484 data-offset-y=15></div>
</div>
</body>
diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-nested-001.html b/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-nested-001.html
index 35ab2cfc15c..c5ce41299af 100644
--- a/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-nested-001.html
+++ b/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-nested-001.html
@@ -68,8 +68,8 @@
data-expected-width=180 data-expected-height=100></div>
</div>
</div>
- <div class="target"
- data-offset-x=13 data-offset-y=97
+ <div class="target" style="background:hotpink;"
+ data-offset-x=173 data-offset-y=-3
data-expected-width=180 data-expected-height=100></div>
</div>
</div>
diff --git a/tests/wpt/tests/css/css-animations/crashtests/chrome-bug-415627003.html b/tests/wpt/tests/css/css-animations/crashtests/chrome-bug-415627003.html
new file mode 100644
index 00000000000..af942fbcc97
--- /dev/null
+++ b/tests/wpt/tests/css/css-animations/crashtests/chrome-bug-415627003.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<link rel="help" href="https://crbug.com/415627003">
+<style>
+ @keyframes --anim {
+ from {
+ text-size-adjust: calc(50% * sibling-index());
+ }
+ to {
+ text-size-adjust: calc(50%);
+ }
+ }
+ #target {
+ animation: --anim 2s;
+ }
+</style>
+<p>Pass if no crash</p>
+<div id="target"></div>
diff --git a/tests/wpt/tests/css/css-break/root-margin-001-print-ref.html b/tests/wpt/tests/css/css-break/root-margin-001-print-ref.html
new file mode 100644
index 00000000000..320da2e0f55
--- /dev/null
+++ b/tests/wpt/tests/css/css-break/root-margin-001-print-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<div style="height:1px;"></div>
+<div style="break-before:page; margin-top:8px;"></div>
+This text should be on the second page.
+The first page should be blank.
diff --git a/tests/wpt/tests/css/css-break/root-margin-001-print.html b/tests/wpt/tests/css/css-break/root-margin-001-print.html
new file mode 100644
index 00000000000..b2ec34fe079
--- /dev/null
+++ b/tests/wpt/tests/css/css-break/root-margin-001-print.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<title>Root block-start margin taller than the page</title>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-break/#break-margins">
+<link rel="match" href="root-margin-001-print-ref.html">
+<style>
+ html { margin-top: 10in; }
+</style>
+This text should be on the second page.
+The first page should be blank.
diff --git a/tests/wpt/tests/css/css-break/table/border-spacing.html b/tests/wpt/tests/css/css-break/table/border-spacing.html
index fc5e87e35dd..9fd94760681 100644
--- a/tests/wpt/tests/css/css-break/table/border-spacing.html
+++ b/tests/wpt/tests/css/css-break/table/border-spacing.html
@@ -5,7 +5,7 @@
.cell > div { background:white; }
</style>
<p>No red should be seen below.</p>
-<div id="multicol" style="columns:2; column-fill:auto; height:70px; width:200px; line-height:20px;">
+<div id="multicol" style="columns:2; column-fill:auto; gap:0; height:70px; width:200px; line-height:20px;">
<div style="position:relative; display:table; border-spacing:10px;">
<div class="cell" style="display:table-cell; width:30px; background:red;">
<div id="child1">1<br></div>
@@ -20,15 +20,20 @@
<script src="/resources/testharnessreport.js"></script>
<script>
test(() => {
+ assert_equals(document.getElementById("child1").offsetLeft, 10);
assert_equals(document.getElementById("child1").offsetTop, 10);
assert_equals(document.getElementById("child1").offsetHeight, 20);
+ assert_equals(document.getElementById("child2").offsetLeft, 10);
assert_equals(document.getElementById("child2").offsetTop, 30);
assert_equals(document.getElementById("child2").offsetHeight, 20);
+ assert_equals(document.getElementById("child3").offsetLeft, 10);
assert_equals(document.getElementById("child3").offsetTop, 50);
assert_equals(document.getElementById("child3").offsetHeight, 20);
- assert_equals(document.getElementById("child4").offsetTop, 70);
+ assert_equals(document.getElementById("child4").offsetLeft, 110);
+ assert_equals(document.getElementById("child4").offsetTop, 0);
assert_equals(document.getElementById("child4").offsetHeight, 20);
- assert_equals(document.getElementById("child5").offsetTop, 90);
+ assert_equals(document.getElementById("child5").offsetLeft, 110);
+ assert_equals(document.getElementById("child5").offsetTop, 20);
assert_equals(document.getElementById("child5").offsetHeight, 20);
}, "Table with border spacing");
</script>
diff --git a/tests/wpt/tests/css/css-break/table/table-parts-offsets-vertical-rl.tentative.html b/tests/wpt/tests/css/css-break/table/table-parts-offsets-vertical-rl.tentative.html
index 9d4a472d438..1eb751032ca 100644
--- a/tests/wpt/tests/css/css-break/table/table-parts-offsets-vertical-rl.tentative.html
+++ b/tests/wpt/tests/css/css-break/table/table-parts-offsets-vertical-rl.tentative.html
@@ -37,104 +37,104 @@
<script src="/resources/testharnessreport.js"></script>
<script>
test(() => {
- assert_equals(table.offsetTop, 408, "offsetTop");
- assert_equals(table.offsetLeft, 41, "offsetLeft");
+ assert_equals(table.offsetTop, 8, "offsetTop");
+ assert_equals(table.offsetLeft, 8, "offsetLeft");
assert_equals(table.offsetWidth, 177, "offsetWidth");
assert_equals(table.offsetHeight, 184, "offsetHeight");
}, "table");
test(() => {
- assert_equals(colgroup.offsetTop, 418, "offsetTop");
- assert_equals(colgroup.offsetLeft, 51, "offsetLeft");
+ assert_equals(colgroup.offsetTop, 18, "offsetTop");
+ assert_equals(colgroup.offsetLeft, 8, "offsetLeft");
assert_equals(colgroup.offsetWidth, 157, "offsetWidth");
assert_equals(colgroup.offsetHeight, 107, "offsetHeight");
}, "colgroup");
test(() => {
- assert_equals(col.offsetTop, 418, "offsetTop");
- assert_equals(col.offsetLeft, 51, "offsetLeft");
+ assert_equals(col.offsetTop, 18, "offsetTop");
+ assert_equals(col.offsetLeft, 8, "offsetLeft");
assert_equals(col.offsetWidth, 157, "offsetWidth");
assert_equals(col.offsetHeight, 50, "offsetHeight");
}, "col");
test(() => {
- assert_equals(col2.offsetTop, 475, "offsetTop");
- assert_equals(col2.offsetLeft, 51, "offsetLeft");
+ assert_equals(col2.offsetTop, 75, "offsetTop");
+ assert_equals(col2.offsetLeft, 8, "offsetLeft");
assert_equals(col2.offsetWidth, 157, "offsetWidth");
assert_equals(col2.offsetHeight, 50, "offsetHeight");
}, "col2");
test(() => {
- assert_equals(colgroup2.offsetTop, 532, "offsetTop");
- assert_equals(colgroup2.offsetLeft, 51, "offsetLeft");
+ assert_equals(colgroup2.offsetTop, 132, "offsetTop");
+ assert_equals(colgroup2.offsetLeft, 8, "offsetLeft");
assert_equals(colgroup2.offsetWidth, 157, "offsetWidth");
assert_equals(colgroup2.offsetHeight, 50, "offsetHeight");
}, "colgroup2");
test(() => {
- assert_equals(col3.offsetTop, 532, "offsetTop");
- assert_equals(col3.offsetLeft, 51, "offsetLeft");
+ assert_equals(col3.offsetTop, 132, "offsetTop");
+ assert_equals(col3.offsetLeft, 8, "offsetLeft");
assert_equals(col3.offsetWidth, 157, "offsetWidth");
assert_equals(col3.offsetHeight, 50, "offsetHeight");
}, "col3");
test(() => {
- assert_equals(rowgroup.offsetTop, 418, "offsetTop");
- assert_equals(rowgroup.offsetLeft, 51, "offsetLeft");
+ assert_equals(rowgroup.offsetTop, 18, "offsetTop");
+ assert_equals(rowgroup.offsetLeft, 8, "offsetLeft");
assert_equals(rowgroup.offsetWidth, 157, "offsetWidth");
assert_equals(rowgroup.offsetHeight, 164, "offsetHeight");
}, "rowgroup");
test(() => {
- assert_equals(row.offsetTop, 218, "offsetTop");
- assert_equals(row.offsetLeft, 38, "offsetLeft");
+ assert_equals(row.offsetTop, 18, "offsetTop");
+ assert_equals(row.offsetLeft, 8, "offsetLeft");
assert_equals(row.offsetWidth, 100, "offsetWidth");
assert_equals(row.offsetHeight, 164, "offsetHeight");
}, "row");
test(() => {
- assert_equals(cell.offsetTop, 218, "offsetTop");
- assert_equals(cell.offsetLeft, 38, "offsetLeft");
+ assert_equals(cell.offsetTop, 18, "offsetTop");
+ assert_equals(cell.offsetLeft, 8, "offsetLeft");
assert_equals(cell.offsetWidth, 100, "offsetWidth");
assert_equals(cell.offsetHeight, 50, "offsetHeight");
}, "cell");
test(() => {
- assert_equals(content.offsetTop, 218, "offsetTop");
- assert_equals(content.offsetLeft, 38, "offsetLeft");
+ assert_equals(content.offsetTop, 18, "offsetTop");
+ assert_equals(content.offsetLeft, 8, "offsetLeft");
assert_equals(content.offsetWidth, 100, "offsetWidth");
assert_equals(content.offsetHeight, 50, "offsetHeight");
}, "content");
test(() => {
- assert_equals(cell2.offsetTop, 275, "offsetTop");
- assert_equals(cell2.offsetLeft, 38, "offsetLeft");
+ assert_equals(cell2.offsetTop, 75, "offsetTop");
+ assert_equals(cell2.offsetLeft, 8, "offsetLeft");
assert_equals(cell2.offsetWidth, 100, "offsetWidth");
assert_equals(cell2.offsetHeight, 50, "offsetHeight");
}, "cell2");
test(() => {
- assert_equals(content2.offsetTop, 275, "offsetTop");
- assert_equals(content2.offsetLeft, 38, "offsetLeft");
+ assert_equals(content2.offsetTop, 75, "offsetTop");
+ assert_equals(content2.offsetLeft, 8, "offsetLeft");
assert_equals(content2.offsetWidth, 100, "offsetWidth");
assert_equals(content2.offsetHeight, 50, "offsetHeight");
}, "content2");
test(() => {
- assert_equals(cell3.offsetTop, 332, "offsetTop");
- assert_equals(cell3.offsetLeft, 38, "offsetLeft");
+ assert_equals(cell3.offsetTop, 132, "offsetTop");
+ assert_equals(cell3.offsetLeft, 8, "offsetLeft");
assert_equals(cell3.offsetWidth, 100, "offsetWidth");
assert_equals(cell3.offsetHeight, 50, "offsetHeight");
}, "cell3");
test(() => {
- assert_equals(content3.offsetTop, 332, "offsetTop");
- assert_equals(content3.offsetLeft, 38, "offsetLeft");
+ assert_equals(content3.offsetTop, 132, "offsetTop");
+ assert_equals(content3.offsetLeft, 8, "offsetLeft");
assert_equals(content3.offsetWidth, 100, "offsetWidth");
assert_equals(content3.offsetHeight, 50, "offsetHeight");
}, "content3");
test(() => {
- assert_equals(row2.offsetTop, 418, "offsetTop");
- assert_equals(row2.offsetLeft, 51, "offsetLeft");
+ assert_equals(row2.offsetTop, 218, "offsetTop");
+ assert_equals(row2.offsetLeft, 8, "offsetLeft");
assert_equals(row2.offsetWidth, 50, "offsetWidth");
assert_equals(row2.offsetHeight, 164, "offsetHeight");
}, "row2");
test(() => {
- assert_equals(cell4.offsetTop, 418, "offsetTop");
- assert_equals(cell4.offsetLeft, 51, "offsetLeft");
+ assert_equals(cell4.offsetTop, 218, "offsetTop");
+ assert_equals(cell4.offsetLeft, 8, "offsetLeft");
assert_equals(cell4.offsetWidth, 50, "offsetWidth");
assert_equals(cell4.offsetHeight, 50, "offsetHeight");
}, "cell4");
test(() => {
- assert_equals(content4.offsetTop, 418, "offsetTop");
- assert_equals(content4.offsetLeft, 51, "offsetLeft");
+ assert_equals(content4.offsetTop, 218, "offsetTop");
+ assert_equals(content4.offsetLeft, 8, "offsetLeft");
assert_equals(content4.offsetWidth, 50, "offsetWidth");
assert_equals(content4.offsetHeight, 50, "offsetHeight");
}, "content4");
diff --git a/tests/wpt/tests/css/css-cascade/inline-style-background.html b/tests/wpt/tests/css/css-cascade/inline-style-background.html
new file mode 100644
index 00000000000..11451f8cefd
--- /dev/null
+++ b/tests/wpt/tests/css/css-cascade/inline-style-background.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<head>
+ <link rel="help" href="https://crbug.com/40934009">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="d" style="background:revert">Test passes if it does not crash.</div>
+ <script>
+ test(() => {
+ d.offsetTop;
+ d.style.background = "url(dummy.png)";
+ d.offsetLeft;
+ });
+ </script>
+</body>
diff --git a/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001-ref.html b/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001-ref.html
index c7553716ab6..e2516f3d378 100644
--- a/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001-ref.html
+++ b/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001-ref.html
@@ -1,62 +1,13 @@
<!DOCTYPE HTML>
-<html>
-<head>
- <meta charset="utf-8">
- <title>CSS Reftest Reference</title>
- <link rel="author" title="Yusuf Sermet" href="mailto:ysermet@mozilla.com">
- <style>
- div {
- position: relative;
- width: 100px;
- }
- #div1,
- #div3 {
- background-color: #cfc;
- }
- #div1 {
- z-index: 5;
- }
- #div2 {
- z-index: 1;
- background-color: #fdd;
- height: 100px;
- top: -20px;
- }
- #div2_1 {
- background-color: #ffc;
- z-index: 6;
- top: -10px;
- }
- #div2_2 {
- z-index: 3;
- position: absolute;
- top: -15px;
- width: 40px;
- height: 100px;
- background-color: #ddf;
- }
- #div3 {
- z-index: 2;
- top: -50px;
- }
- </style>
-</head>
-<body>
- <div id="div1">
- <br/><br/>
- </div>
-
- <div id="div2">
- <div id="div2_1">
- <br/><br/>
- </div>
-
- <div id="div2_2">
- </div>
- </div>
-
- <div id="div3">
- <br/><br/>
- </div>
-</body>
-</html>
+<meta charset="utf-8">
+<title>Paint Containment Stacking Context Reference</title>
+<link rel="author" title="Psychpsyo" href="mailto:psychpsyo@gmail.com">
+<style>
+ div {
+ width: 100px;
+ height: 100px;
+ background-color: green;
+ }
+</style>
+<div></div>
+Test succeeds if there is no red.
diff --git a/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001a.html b/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001a.html
index 71102b6c73a..5e588cb6174 100644
--- a/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001a.html
+++ b/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001a.html
@@ -1,66 +1,32 @@
<!DOCTYPE HTML>
-<html>
-<head>
- <meta charset="utf-8">
- <title>CSS Test: 'contain: paint' with stacking contents. Z-index is defined only for siblings and children.</title>
- <link rel="author" title="Yusuf Sermet" href="mailto:ysermet@mozilla.com">
-
- <link rel="help" href="https://drafts.csswg.org/css2/visuren.html#x43">
- <link rel="help" href="https://drafts.csswg.org/css-contain/#containment-paint">
- <link rel="match" href="contain-paint-stacking-context-001-ref.html">
- <style>
- div {
- position: relative;
- width: 100px;
- }
- #div1,
- #div3 {
- background-color: #cfc;
- }
- #div1 {
- z-index: 5;
- }
- #div2 {
- contain: paint;
- background-color: #fdd;
- height: 100px;
- top: -20px;
- }
- #div2_1 {
- background-color: #ffc;
- z-index: 6;
- top: -10px;
- }
- #div2_2 {
- z-index: 3;
- position: absolute;
- top: -15px;
- width: 40px;
- height: 100px;
- background-color: #ddf;
- }
- #div3 {
- z-index: 2;
- top: -50px;
- }
- </style>
-</head>
-<body>
- <div id="div1">
- <br/><br/>
- </div>
-
- <div id="div2">
- <div id="div2_1">
- <br/><br/>
- </div>
-
- <div id="div2_2">
- </div>
- </div>
-
- <div id="div3">
- <br/><br/>
- </div>
-</body>
-</html>
+<title>'contain: paint' establishes stacking context.</title>
+<link rel="author" title="Psychpsyo" href="mailto:psychpsyo@gmail.com">
+<link rel="help" href="https://drafts.csswg.org/css2/visuren.html#x43">
+<link rel="help" href="https://drafts.csswg.org/css-contain/#containment-paint">
+<link rel="match" href="contain-paint-stacking-context-001-ref.html">
+<style>
+ div {
+ width: 100px;
+ height: 100px;
+ }
+ #front {
+ background-color: green;
+ /* makes a stacking context and puts this on top */
+ position: absolute;
+ z-index: 10;
+ }
+ #back {
+ contain: paint;
+ }
+ #notOnTop {
+ background-color: red;
+ /* z-index is higher than on #front, but this should still be covered up because it is inside #back, which has 'contain: paint' */
+ position: absolute;
+ z-index: 1000;
+ }
+</style>
+<div id="front"></div>
+<div id="back">
+ <div id="notOnTop"></div>
+</div>
+Test succeeds if there is no red.
diff --git a/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001b.html b/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001b.html
index 0c4d3323bf7..e03323782a7 100644
--- a/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001b.html
+++ b/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001b.html
@@ -1,66 +1,32 @@
<!DOCTYPE HTML>
-<html>
-<head>
- <meta charset="utf-8">
- <title>CSS Test: 'will-change: contain' with stacking contents. Z-index is defined only for siblings and children.</title>
- <link rel="author" title="Yusuf Sermet" href="mailto:ysermet@mozilla.com">
-
- <link rel="help" href="https://drafts.csswg.org/css2/visuren.html#x43">
- <link rel="help" href="https://drafts.csswg.org/css-contain/#containment-paint">
- <link rel="match" href="contain-paint-stacking-context-001-ref.html">
- <style>
- div {
- position: relative;
- width: 100px;
- }
- #div1,
- #div3 {
- background-color: #cfc;
- }
- #div1 {
- z-index: 5;
- }
- #div2 {
- will-change: contain;
- background-color: #fdd;
- height: 100px;
- top: -20px;
- }
- #div2_1 {
- background-color: #ffc;
- z-index: 6;
- top: -10px;
- }
- #div2_2 {
- z-index: 3;
- position: absolute;
- top: -15px;
- width: 40px;
- height: 100px;
- background-color: #ddf;
- }
- #div3 {
- z-index: 2;
- top: -50px;
- }
- </style>
-</head>
-<body>
- <div id="div1">
- <br/><br/>
- </div>
-
- <div id="div2">
- <div id="div2_1">
- <br/><br/>
- </div>
-
- <div id="div2_2">
- </div>
- </div>
-
- <div id="div3">
- <br/><br/>
- </div>
-</body>
-</html>
+<title>'will-change: contain' establishes stacking context.</title>
+<link rel="author" title="Psychpsyo" href="mailto:psychpsyo@gmail.com">
+<link rel="help" href="https://drafts.csswg.org/css2/visuren.html#x43">
+<link rel="help" href="https://drafts.csswg.org/css-contain/#containment-paint">
+<link rel="match" href="contain-paint-stacking-context-001-ref.html">
+<style>
+ div {
+ width: 100px;
+ height: 100px;
+ }
+ #front {
+ background-color: green;
+ /* makes a stacking context and puts this on top */
+ position: absolute;
+ z-index: 10;
+ }
+ #back {
+ will-change: contain;
+ }
+ #notOnTop {
+ background-color: red;
+ /* z-index is higher than on #front, but this should still be covered up because it is inside #back, which has 'will-change: contain' */
+ position: absolute;
+ z-index: 1000;
+ }
+</style>
+<div id="front"></div>
+<div id="back">
+ <div id="notOnTop"></div>
+</div>
+Test succeeds if there is no red.
diff --git a/tests/wpt/tests/css/css-display/display-contents-inline-002.html b/tests/wpt/tests/css/css-display/display-contents-inline-002.html
new file mode 100644
index 00000000000..f40a34129f3
--- /dev/null
+++ b/tests/wpt/tests/css/css-display/display-contents-inline-002.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Display: display:contents in inline layout should affect style of descendants</title>
+<link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com">
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-display-3/#valdef-display-contents">
+<link rel="match" href="display-contents-pass-no-red-ref.html">
+<style>
+ #contents {
+ display: contents;
+ color: black;
+ }
+ .red { color: red; }
+</style>
+<p>You should see the word PASS and no red below.</p>
+<span>
+ P<span class="red"><div id="contents">AS</div></span>S
+</span>
diff --git a/tests/wpt/tests/css/css-flexbox/flex-container-max-content-002.tentative.html b/tests/wpt/tests/css/css-flexbox/flex-container-max-content-002.tentative.html
new file mode 100644
index 00000000000..77a074b153a
--- /dev/null
+++ b/tests/wpt/tests/css/css-flexbox/flex-container-max-content-002.tentative.html
@@ -0,0 +1,169 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Flex Container Max-Content Sizes</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#intrinsic-main-sizes"
+ title="9.9.1. Flex Container Intrinsic Main Sizes">
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#intrinsic-cross-sizes"
+ title="9.9.2. Flex Container Intrinsic Cross Sizes">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/12123">
+
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+<style>
+.flex {
+ display: inline-flex;
+ vertical-align: top;
+ border: 5px solid magenta;
+ width: max-content;
+ height: max-content;
+}
+.flex.min {
+ width: 0;
+ height: 0;
+ min-width: max-content;
+ min-height: max-content;
+}
+.flex.max {
+ width: 200px;
+ height: 200px;
+ max-width: max-content;
+ max-height: max-content;
+}
+.flex > div {
+ font: 25px/1 Ahem;
+ border: 5px solid cyan;
+}
+</style>
+
+<!-- Single-line row flex container -->
+<div class="flex" style="flex-flow: row nowrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+<div class="flex min" style="flex-flow: row nowrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+<div class="flex max" style="flex-flow: row nowrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+
+<div class="flex" style="flex-flow: row nowrap"
+ data-expected-width="180" data-expected-height="45">
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+</div>
+<div class="flex min" style="flex-flow: row nowrap"
+ data-expected-width="180" data-expected-height="45">
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+</div>
+<div class="flex max" style="flex-flow: row nowrap"
+ data-expected-width="180" data-expected-height="45">
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+</div>
+
+<hr>
+
+<!-- Single-line column flex container -->
+<div class="flex" style="flex-flow: column nowrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+<div class="flex min" style="flex-flow: column nowrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+<div class="flex max" style="flex-flow: column nowrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+
+<div class="flex" style="flex-flow: column nowrap"
+ data-expected-width="95" data-expected-height="80">
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+</div>
+<div class="flex min" style="flex-flow: column nowrap"
+ data-expected-width="95" data-expected-height="80">
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+</div>
+<div class="flex max" style="flex-flow: column nowrap"
+ data-expected-width="95" data-expected-height="80">
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+</div>
+
+<hr>
+
+<!-- Multi-line row flex container -->
+<div class="flex" style="flex-flow: row wrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+<div class="flex min" style="flex-flow: row wrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+<div class="flex max" style="flex-flow: row wrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+
+<div class="flex" style="flex-flow: row wrap"
+ data-expected-width="180" data-expected-height="45">
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+</div>
+<div class="flex min" style="flex-flow: row wrap"
+ data-expected-width="180" data-expected-height="45">
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+</div>
+<div class="flex max" style="flex-flow: row wrap"
+ data-expected-width="180" data-expected-height="45">
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+</div>
+
+<hr>
+
+<!-- Multi-line column flex container -->
+<div class="flex" style="flex-flow: column wrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+<div class="flex min" style="flex-flow: column wrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+<div class="flex max" style="flex-flow: column wrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+
+<div class="flex" style="flex-flow: column wrap"
+ data-expected-width="95" data-expected-height="80">
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+</div>
+<div class="flex min" style="flex-flow: column wrap"
+ data-expected-width="95" data-expected-height="80">
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+</div>
+<div class="flex max" style="flex-flow: column wrap"
+ data-expected-width="95" data-expected-height="80">
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+ <div data-expected-width="85" data-expected-height="35">X X</div>
+</div>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+<script>
+checkLayout(".flex");
+</script>
diff --git a/tests/wpt/tests/css/css-flexbox/flex-container-min-content-002.tentative.html b/tests/wpt/tests/css/css-flexbox/flex-container-min-content-002.tentative.html
new file mode 100644
index 00000000000..92d37e3b9d8
--- /dev/null
+++ b/tests/wpt/tests/css/css-flexbox/flex-container-min-content-002.tentative.html
@@ -0,0 +1,169 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Flex Container Min-Content Sizes</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#intrinsic-main-sizes"
+ title="9.9.1. Flex Container Intrinsic Main Sizes">
+<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#intrinsic-cross-sizes"
+ title="9.9.2. Flex Container Intrinsic Cross Sizes">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/12123">
+
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+<style>
+.flex {
+ display: inline-flex;
+ vertical-align: top;
+ border: 5px solid magenta;
+ width: min-content;
+ height: min-content;
+}
+.flex.min {
+ width: 0;
+ height: 0;
+ min-width: min-content;
+ min-height: min-content;
+}
+.flex.max {
+ width: 200px;
+ height: 200px;
+ max-width: min-content;
+ max-height: min-content;
+}
+.flex > div {
+ font: 25px/1 Ahem;
+ border: 5px solid cyan;
+}
+</style>
+
+<!-- Single-line row flex container -->
+<div class="flex" style="flex-flow: row nowrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+<div class="flex min" style="flex-flow: row nowrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+<div class="flex max" style="flex-flow: row nowrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+
+<div class="flex" style="flex-flow: row nowrap"
+ data-expected-width="80" data-expected-height="70">
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+</div>
+<div class="flex min" style="flex-flow: row nowrap"
+ data-expected-width="80" data-expected-height="70">
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+</div>
+<div class="flex max" style="flex-flow: row nowrap"
+ data-expected-width="80" data-expected-height="70">
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+</div>
+
+<hr>
+
+<!-- Single-line column flex container -->
+<div class="flex" style="flex-flow: column nowrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+<div class="flex min" style="flex-flow: column nowrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+<div class="flex max" style="flex-flow: column nowrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+
+<div class="flex" style="flex-flow: column nowrap"
+ data-expected-width="45" data-expected-height="130">
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+</div>
+<div class="flex min" style="flex-flow: column nowrap"
+ data-expected-width="45" data-expected-height="130">
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+</div>
+<div class="flex max" style="flex-flow: column nowrap"
+ data-expected-width="45" data-expected-height="130">
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+</div>
+
+<hr>
+
+<!-- Multi-line row flex container -->
+<div class="flex" style="flex-flow: row wrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+<div class="flex min" style="flex-flow: row wrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+<div class="flex max" style="flex-flow: row wrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+
+<div class="flex" style="flex-flow: row wrap"
+ data-expected-width="45" data-expected-height="130">
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+</div>
+<div class="flex min" style="flex-flow: row wrap"
+ data-expected-width="45" data-expected-height="130">
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+</div>
+<div class="flex max" style="flex-flow: row wrap"
+ data-expected-width="45" data-expected-height="130">
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+</div>
+
+<hr>
+
+<!-- Multi-line column flex container -->
+<div class="flex" style="flex-flow: column wrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+<div class="flex min" style="flex-flow: column wrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+<div class="flex max" style="flex-flow: column wrap"
+ data-expected-width="45" data-expected-height="45">
+ <div style="width: 25px; height: 25px">X</div>
+</div>
+
+<div class="flex" style="flex-flow: column wrap"
+ data-expected-width="45" data-expected-height="130">
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+</div>
+<div class="flex min" style="flex-flow: column wrap"
+ data-expected-width="45" data-expected-height="130">
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+</div>
+<div class="flex max" style="flex-flow: column wrap"
+ data-expected-width="45" data-expected-height="130">
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+ <div data-expected-width="35" data-expected-height="60">X X</div>
+</div>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+<script>
+checkLayout(".flex");
+</script>
diff --git a/tests/wpt/tests/css/css-fonts/font-variant-emoji-005-ref.html b/tests/wpt/tests/css/css-fonts/font-variant-emoji-005-ref.html
new file mode 100644
index 00000000000..d4275b5c951
--- /dev/null
+++ b/tests/wpt/tests/css/css-fonts/font-variant-emoji-005-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<meta charset="UTF-8" />
+<title>CSS Fonts reference</title>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+
+<style>
+body { background: white; }
+p { font: 16px/3 serif; }
+span { color: white; font: 25px Ahem; }
+span span { color: initial; font-variant-emoji: emoji; }
+</style>
+
+<h4>Only lines 3 and 4 should show a keycap numeral:</h4>
+
+<p>1. text: <span>X</span></p>
+
+<p>2. unicode: <span>X</span></p>
+
+<p>3. emoji: <span>X<span>3&#xfe0f;&#x20e3;</span>X</span></p>
+
+<p>4. with VS16: <span>X<span>4&#xfe0f;&#x20e3;</span>X</span></p>
+
+<p>5. emoji, with VS15: <span>X</span></p>
diff --git a/tests/wpt/tests/css/css-fonts/font-variant-emoji-005.html b/tests/wpt/tests/css/css-fonts/font-variant-emoji-005.html
new file mode 100644
index 00000000000..0f5c2d7a98a
--- /dev/null
+++ b/tests/wpt/tests/css/css-fonts/font-variant-emoji-005.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="UTF-8" />
+<title>CSS Fonts: font-variant-emoji web font test</title>
+<link rel="help" href="https://www.w3.org/TR/css-fonts-4/#font-variant-emoji-prop" />
+<link rel="help" href="https://www.unicode.org/reports/tr51/tr51-25.html#Emoji_Properties_and_Data_Files" />
+<link rel="match" href="font-variant-emoji-005-ref.html"/>
+<meta name="assert" content="Digit-keycap sequences render as emoji if required, in preference to using the named font"/>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+
+<style>
+body { background: white; }
+p { font: 16px/3 serif; }
+/* A color-emoji glyph in the span will be visible despite the white color;
+ * if color-emoji presentation is not used, the span will be invisible.
+ */
+span { color: white; font: 25px Ahem; }
+</style>
+
+<h4>Only lines 3 and 4 should show a keycap numeral:</h4>
+
+<p>1. text: <span style="font-variant-emoji: text">X1&#x20e3;X</span></p>
+
+<p>2. unicode: <span style="font-variant-emoji: unicode">X2&#x20e3;X</span></p>
+
+<p>3. emoji: <span style="font-variant-emoji: emoji">X3&#x20e3;X</span></p>
+
+<p>4. with VS16: <span style="font-variant-emoji: normal">X4&#xfe0f;&#x20e3;X</span></p>
+
+<p>5. emoji, with VS15: <span style="font-variant-emoji: emoji">X5&#xfe0e;&#x20e3;X</span></p>
diff --git a/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-002-ref.html b/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-002-ref.html
new file mode 100644
index 00000000000..42c5ef128b4
--- /dev/null
+++ b/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-002-ref.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1/">
+<link rel="author" title="Javier Contreras" href="mailto:javiercon@microsoft.com">
+<style>
+ body {
+ margin: 0px;
+ }
+
+ .container {
+ display: flex;
+ width: 120px;
+ height: 110px;
+ column-gap: 20px;
+ row-gap: 10px;
+ flex-wrap: wrap;
+ }
+
+ .item {
+ background: skyblue;
+ height: 50px;
+ width: 50px;
+ margin: 0;
+ }
+
+ .row-gap {
+ position: absolute;
+ top: 50px;
+ background: gold;
+ width: 120px;
+ height: 10px;
+ }
+
+ .column-gap {
+ position: absolute;
+ top: 0px;
+ left: 50px;
+ background: blue;
+ height: 110px;
+ width: 20px;
+ }
+
+</style>
+<div class="container">
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+</div>
+
+<div class="row-gap"></div>
+<div class="column-gap"></div>
diff --git a/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-022.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-022.html
new file mode 100644
index 00000000000..5f3b512ef76
--- /dev/null
+++ b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-022.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<title>
+ CSS Gap Decorations: flex column gaps are painted with different sized gaps and row-rule-outset 0.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1/">
+<link rel="match" href="../agnostic/gap-decorations-002-ref.html">
+<link rel="author" title="Javier Contreras" href="mailto:javiercon@microsoft.com">
+<style>
+ body {
+ margin: 0px;
+ }
+
+ .flex-container {
+ height: 110px;
+ width: 120px;
+
+ display: flex;
+
+ column-gap: 20px;
+ row-gap: 10px;
+
+ column-rule-color: blue;
+ column-rule-style: solid;
+ column-rule-width: 20px;
+
+ row-rule-color: gold;
+ row-rule-style: solid;
+ row-rule-width: 10px;
+
+ row-rule-break: intersection;
+ row-rule-outset: 0;
+
+ flex-wrap: wrap;
+ }
+
+ .flex-item {
+ background: skyblue;
+ width: 50px;
+ }
+</style>
+<div class="flex-container">
+ <div class="flex-item"></div>
+ <div class="flex-item"></div>
+ <div class="flex-item"></div>
+ <div class="flex-item"></div>
+</div>
diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-029-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-029-ref.html
new file mode 100644
index 00000000000..35450e07ae5
--- /dev/null
+++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-029-ref.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1">
+<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com">
+<style>
+ body {
+ margin: 0px;
+ }
+
+ .grid-container {
+ display: grid;
+ grid-gap: 10px;
+ grid-template-columns: 100px 100px 100px;
+ grid-template-rows: 100px 100px 100px;
+ width: 120px;
+ height: 120px;
+ }
+
+ .item {
+ background: gray;
+ opacity: 0.5;
+ }
+
+ .row-gap {
+ position: absolute;
+ width: 320px;
+ height: 0px;
+ border-bottom: solid 5px red;
+ }
+
+ .row-gap1 {
+ top: 102.5px;
+ }
+
+ .row-gap2 {
+ top: 212.5px;
+ }
+
+ .col-gap {
+ position: absolute;
+ top: 0px;
+ width: 0px;
+ height: 320px;
+ border-left: solid 5px blue;
+ }
+
+ .col-gap1 {
+ left: 102.5px;
+ }
+
+ .col-gap2 {
+ left: 212.5px;
+ }
+</style>
+<div class="grid-container">
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+</div>
+
+<div class="col-gap col-gap1"> </div>
+<div class="col-gap col-gap2"> </div>
+
+<div class="row-gap row-gap1"> </div>
+<div class="row-gap row-gap2"> </div>
diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-029.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-029.html
new file mode 100644
index 00000000000..6da75548d94
--- /dev/null
+++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-029.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<title>
+ CSS Gap Decorations: Gaps are painted when items overflow container.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1/">
+<link rel="match" href="grid-gap-decorations-029-ref.html">
+<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com">
+<style>
+ body {
+ margin: 0px;
+ }
+
+ .grid-container {
+ display: grid;
+ grid-gap: 10px;
+ grid-template-columns: 100px 100px 100px;
+ grid-template-rows: 100px 100px 100px;
+ width: 120px;
+ height: 120px;
+
+ column-rule-color: blue;
+ column-rule-style: solid;
+ column-rule-width: 5px;
+
+ row-rule-color: red;
+ row-rule-style: solid;
+ row-rule-width: 5px;
+ }
+
+ .item {
+ background: gray;
+ opacity: 0.5;
+ }
+</style>
+
+<body>
+ <div class="grid-container">
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ </div>
+</body>
diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-030-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-030-ref.html
new file mode 100644
index 00000000000..d4953ae2ddd
--- /dev/null
+++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-030-ref.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1">
+<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com">
+<style>
+ body {
+ margin: 0px;
+ }
+
+ .grid-container {
+ display: grid;
+ grid-gap: 10px;
+ grid-template-columns: 100px 100px 100px;
+ width: 120px;
+ height: 120px;
+ }
+
+ .item {
+ background: gray;
+ opacity: 0.5;
+ }
+
+ .col-gap {
+ position: absolute;
+ top: 0px;
+ width: 0px;
+ height: 120px;
+ border-left: solid 5px blue;
+ }
+
+ .col-gap1 {
+ left: 102.5px;
+ }
+
+ .col-gap2 {
+ left: 212.5px;
+ }
+</style>
+<div class="grid-container">
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+</div>
+
+<div class="col-gap col-gap1"> </div>
+<div class="col-gap col-gap2"> </div>
diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-030.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-030.html
new file mode 100644
index 00000000000..ef4507ef926
--- /dev/null
+++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-030.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>
+ CSS Gap Decorations: Gaps are painted when items overflow container - no row gaps.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1/">
+<link rel="match" href="grid-gap-decorations-030-ref.html">
+<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com">
+<style>
+ body {
+ margin: 0px;
+ }
+
+ .grid-container {
+ display: grid;
+ grid-gap: 10px;
+ grid-template-columns: 100px 100px 100px;
+ width: 120px;
+ height: 120px;
+
+ column-rule-color: blue;
+ column-rule-style: solid;
+ column-rule-width: 5px;
+ }
+
+ .item {
+ background: gray;
+ opacity: 0.5;
+ }
+</style>
+
+<body>
+ <div class="grid-container">
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ </div>
+</body>
diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-031-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-031-ref.html
new file mode 100644
index 00000000000..0615305bc9f
--- /dev/null
+++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-031-ref.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1">
+<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com">
+<style>
+ body {
+ margin: 0px;
+ }
+
+ .grid-container {
+ display: grid;
+ grid-gap: 10px;
+ grid-template-rows: 100px 100px 100px;
+ grid-auto-flow: column;
+ width: 120px;
+ height: 120px;
+ }
+
+ .item {
+ background: gray;
+ opacity: 0.5;
+ }
+
+ .row-gap {
+ position: absolute;
+ width: 120px;
+ height: 0px;
+ border-bottom: solid 5px red;
+ }
+
+ .row-gap1 {
+ top: 102.5px;
+ }
+
+ .row-gap2 {
+ top: 212.5px;
+ }
+</style>
+<div class="grid-container">
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+</div>
+
+<div class="row-gap row-gap1"> </div>
+<div class="row-gap row-gap2"> </div>
diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-031.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-031.html
new file mode 100644
index 00000000000..530b661bfe3
--- /dev/null
+++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-031.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<title>
+ CSS Gap Decorations: Gaps are painted when items overflow container - no column gaps.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1/">
+<link rel="match" href="grid-gap-decorations-031-ref.html">
+<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com">
+<style>
+ body {
+ margin: 0px;
+ }
+
+ .grid-container {
+ display: grid;
+ grid-gap: 10px;
+ grid-template-rows: 100px 100px 100px;
+ grid-auto-flow: column;
+ width: 120px;
+ height: 120px;
+
+ row-rule-color: red;
+ row-rule-style: solid;
+ row-rule-width: 5px;
+ }
+
+ .item {
+ background: gray;
+ opacity: 0.5;
+ }
+</style>
+
+<body>
+ <div class="grid-container">
+ <div class="item"></div>
+ <div class="item"></div>
+ <div class="item"></div>
+ </div>
+</body>
diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-032-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-032-ref.html
new file mode 100644
index 00000000000..f90e3dd6813
--- /dev/null
+++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-032-ref.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1">
+<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com">
+<style>
+ body {
+ margin: 0px;
+ overflow: hidden;
+ }
+
+ .grid-container {
+ display: grid;
+ grid-gap: 10px;
+ grid-template-columns: 100px 100px 100px;
+ grid-template-rows: repeat(6, 100px);
+ width: 120px;
+ height: 120px;
+ }
+
+ .row-gap {
+ position: absolute;
+ width: 320px;
+ height: 0px;
+ border-bottom: solid 5px red;
+ }
+
+ .row-gap1 {
+ top: 102.5px;
+ }
+
+ .row-gap2 {
+ top: 212.5px;
+ }
+
+ .row-gap3 {
+ top: 322.5px;
+ }
+
+ .row-gap4 {
+ top: 432.5px;
+ }
+
+ .row-gap5 {
+ top: 542.5px;
+ }
+
+ .col-gap {
+ position: absolute;
+ top: 0px;
+ width: 0px;
+ height: 650px;
+ border-left: solid 10px blue;
+ }
+
+ .col-gap1 {
+ left: 100px;
+ }
+
+ .col-gap2 {
+ left: 210px;
+ }
+</style>
+<div class="grid-container"></div>
+
+<div class="col-gap col-gap1"> </div>
+<div class="col-gap col-gap2"> </div>
+
+<div class="row-gap row-gap1"> </div>
+<div class="row-gap row-gap2"> </div>
+<div class="row-gap row-gap3"> </div>
+<div class="row-gap row-gap4"> </div>
+<div class="row-gap row-gap5"> </div>
diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-032.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-032.html
new file mode 100644
index 00000000000..ac2d38fdda0
--- /dev/null
+++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-032.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<title>
+ CSS Gap Decorations: Gaps are painted when rows are dynamically added and overflow container.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1/">
+<link rel="match" href="grid-gap-decorations-032-ref.html">
+<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com">
+<style>
+ body {
+ margin: 0px;
+ overflow: hidden;
+ }
+
+ .grid-container {
+ display: grid;
+ grid-gap: 10px;
+ grid-template-columns: 100px 100px 100px;
+ grid-template-rows: 100px;
+ width: 120px;
+ height: 120px;
+
+ column-rule-color: blue;
+ column-rule-style: solid;
+ column-rule-width: 10px;
+ }
+
+</style>
+
+<body>
+ <div class="grid-container"></div>
+<script>
+ const grid = document.querySelector('.grid-container');
+ grid.style.gridTemplateRows = 'repeat(6, 100px)';
+
+ grid.style.rowRuleColor = 'red';
+ grid.style.rowRuleStyle = 'solid';
+ grid.style.rowRuleWidth = '5px';
+</script>
+</body>
diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-033-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-033-ref.html
new file mode 100644
index 00000000000..e26143a6b93
--- /dev/null
+++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-033-ref.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1">
+<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com">
+<style>
+ body {
+ margin: 0px;
+ overflow: hidden;
+ }
+
+ .grid-container {
+ display: grid;
+ grid-gap: 10px;
+ grid-template-columns: repeat(3, 50px);
+ grid-template-rows: repeat(6, 50px);
+ width: 50px;
+ height: 50px;
+ }
+
+ .row-gap {
+ position: absolute;
+ width: 170px;
+ height: 0px;
+ border-bottom: solid 5px red;
+ }
+
+ .row-gap1 {
+ top: 52.5px;
+ }
+
+ .row-gap2 {
+ top: 112.5px;
+ }
+
+ .row-gap3 {
+ top: 172.5px;
+ }
+
+ .row-gap4 {
+ top: 232.5px;
+ }
+
+ .row-gap5 {
+ top: 195px;
+ border-bottom: solid 200px red; /*expand the last row gap's height*/
+ }
+
+ .col-gap {
+ position: absolute;
+ top: 0px;
+ width: 0px;
+ height: 350px;
+ border-left: solid 10px blue;
+ }
+
+ .col-gap1 {
+ left: 50px;
+ }
+
+ .col-gap2 {
+ left: 110px;
+ }
+</style>
+<div class="grid-container"></div>
+
+<div class="col-gap col-gap1"> </div>
+<div class="col-gap col-gap2"> </div>
+
+<div class="row-gap row-gap1"> </div>
+<div class="row-gap row-gap2"> </div>
+<div class="row-gap row-gap3"> </div>
+<div class="row-gap row-gap4"> </div>
+<div class="row-gap row-gap5"> </div>
diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-033.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-033.html
new file mode 100644
index 00000000000..47ef35cc368
--- /dev/null
+++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-033.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<title>
+ CSS Gap Decorations: Decorations are painted when rule thickness is greater than gap size.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1/">
+<link rel="match" href="grid-gap-decorations-033-ref.html">
+<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com">
+<style>
+ body {
+ margin: 0px;
+ overflow: hidden;
+ }
+
+ .grid-container {
+ display: grid;
+ gap: 10px;
+ grid-template-columns: repeat(3, 50px);
+ grid-template-rows: repeat(6, 50px);
+ width: 50px;
+ height: 50px;
+
+ column-rule-color: blue;
+ column-rule-style: solid;
+ column-rule-width: 10px;
+ }
+
+</style>
+
+<body>
+ <div class="grid-container"></div>
+<script>
+ const grid = document.querySelector('.grid-container');
+
+ grid.style.rowRuleColor = 'red';
+ grid.style.rowRuleStyle = 'solid';
+ grid.style.rowRuleWidth = 'repeat(4, 5px) 200px';
+</script>
+</body>
diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-38.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-38.html
new file mode 100644
index 00000000000..8b87bcd4860
--- /dev/null
+++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-38.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<title>
+ CSS Gap Decorations: grid column gaps are painted with different sized gaps and column-rule-outset 0.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1/">
+<link rel="match" href="../agnostic/gap-decorations-002-ref.html">
+<link rel="author" title="Javier Contreras" href="mailto:javiercon@microsoft.com">
+<style>
+ body {
+ margin: 0px;
+ }
+
+ .grid-container {
+ height: 110px;
+ width: 120px;
+
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+
+ column-gap: 20px;
+ row-gap: 10px;
+
+ column-rule-color: blue;
+ column-rule-style: solid;
+ column-rule-width: 20px;
+
+ row-rule-color: gold;
+ row-rule-style: solid;
+ row-rule-width: 10px;
+
+ row-rule-break: intersection;
+ row-rule-outset: 0;
+ }
+
+ .grid-item {
+ background: skyblue;
+ }
+</style>
+<div class="grid-container">
+ <div class="grid-item"></div>
+ <div class="grid-item"></div>
+ <div class="grid-item"></div>
+ <div class="grid-item"></div>
+</div>
diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-017-ref.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-017-ref.html
new file mode 100644
index 00000000000..23fd089ac8e
--- /dev/null
+++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-017-ref.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1/">
+<link rel="author" title="Javier Contreras" href="mailto:javiercon@microsoft.com">
+<style>
+ body {
+ margin: 0px;
+ }
+
+ .container {
+ border: 2px solid rgb(96 139 168);
+ width: 210px;
+ height: 130px;
+ column-gap: 20px;
+ display: flex;
+ }
+
+ .items {
+ background: rgb(96 139 168 / 0.2);
+ height: 130px;
+ margin: 0px;
+ width: 56.666px;
+ }
+
+ .row-gap {
+ position: absolute;
+ height: 10px;
+ width: 210px;
+ background: gold;
+ left: 2px;
+ top: 62px;
+ }
+
+ .column-gap {
+ position: absolute;
+ height: 130px;
+ width: 20px;
+ background: blue;
+ top: 2px;
+ }
+</style>
+
+<div class="container">
+ <div class="items"></div>
+ <div class="items"></div>
+ <div class="items"></div>
+</div>
+<div class="row-gap"></div>
+<div class="column-gap" style="left:58.666px;"></div>
+<div class="column-gap" style="left:135.332px;"></div>
diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-017.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-017.html
new file mode 100644
index 00000000000..c7d1fe42584
--- /dev/null
+++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-017.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<title>
+ CSS Gap Decorations: Multicolumn gap decorations painted with column-wrap and with different sized gaps and row-rule-outset 0.
+</title>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1/">
+<link rel="match" href="multicol-gap-decorations-017-ref.html">
+<link rel="author" title="Javier Contreras" href="mailto:javiercon@microsoft.com">
+<style>
+ body {
+ margin: 0px;
+ }
+
+ .container {
+ border: 2px solid rgb(96 139 168);
+ width: 210px;
+ height: 130px;
+ column-count: 3;
+ column-width: 56.666px;
+ column-height: 60px;
+ column-gap: 20px;
+ row-gap: 10px;
+ column-rule-width: 20px;
+ column-rule-style: solid;
+ column-rule-color: blue;
+ row-rule-width: 10px;
+ row-rule-style: solid;
+ row-rule-color: gold;
+ column-wrap: wrap;
+
+ row-rule-break: intersection;
+ row-rule-outset: 0;
+ }
+
+ p {
+ background: rgb(96 139 168 / 0.2);
+ height: 60px;
+ margin: 0px;
+ }
+</style>
+
+<div class="container">
+ <p></p>
+ <p></p>
+ <p></p>
+ <p></p>
+ <p></p>
+ <p></p>
+</div>
diff --git a/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-col-rule-width.html b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-col-rule-width.html
new file mode 100644
index 00000000000..db7b97e74a5
--- /dev/null
+++ b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-col-rule-width.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSS Gap Decorations: Ensure getComputedStyle for column-rule-width is as specified with multiple values</title>
+<link rel="help" href="https://drafts.csswg.org/css-gaps-1/#column-row-rule-width">
+<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com">
+<script src="/resources/testharness.js" type="text/javascript"></script>
+<script src="/resources/testharnessreport.js" type="text/javascript"></script>
+</head>
+<body>
+<div id="target1"></div>
+<div id="target2"></div>
+<div id="target3"></div>
+<style>
+ #target1 {
+ column-rule-width: thin;
+ }
+
+ #target2 {
+ column-rule-width: 5px 10px 15px;
+ }
+
+ #target3 {
+ column-rule-width: repeat(auto, 5px);
+ }
+</style>
+<script>
+ test(function() {
+ const containerStyle = window.getComputedStyle(document.querySelector('#target1'));
+ const columnRuleWidth = containerStyle.getPropertyValue('column-rule-width');
+ assert_equals(columnRuleWidth, '0px');
+
+ }, "`column-rule-width` should be `0px` when `column-rule-style` is `none` with single value");
+
+ test(function() {
+ const containerStyle = window.getComputedStyle(document.querySelector('#target2'));
+ const columnRuleWidth = containerStyle.getPropertyValue('column-rule-width');
+ assert_equals(columnRuleWidth, '5px 10px 15px');
+
+ }, "`column-rule-width` should be as specified regardless of `column-rule-style` with multiple values");
+
+ test(function() {
+ const containerStyle = window.getComputedStyle(document.querySelector('#target3'));
+ const columnRuleWidth = containerStyle.getPropertyValue('column-rule-width');
+ assert_equals(columnRuleWidth, 'repeat(auto, 5px)');
+
+ }, "`column-rule-width` should be as specified regardless of `column-rule-style` with multiple (repeat) values");
+</script>
+</body>
+</html>
diff --git a/tests/wpt/tests/css/css-images/linear-gradient-body-sibling-index-ref.html b/tests/wpt/tests/css/css-images/linear-gradient-body-sibling-index-ref.html
new file mode 100644
index 00000000000..7325685511a
--- /dev/null
+++ b/tests/wpt/tests/css/css-images/linear-gradient-body-sibling-index-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>CSS Test Reference</title>
+<style>
+ body {
+ background-position: top left;
+ background-repeat: no-repeat;
+ background-size: 100px 100px;
+ background-image: linear-gradient(blue 40px, yellow);
+ }
+</style>
+<body>
diff --git a/tests/wpt/tests/css/css-images/linear-gradient-body-sibling-index.html b/tests/wpt/tests/css/css-images/linear-gradient-body-sibling-index.html
new file mode 100644
index 00000000000..299b86d464a
--- /dev/null
+++ b/tests/wpt/tests/css/css-images/linear-gradient-body-sibling-index.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<title>CSS Images Test: Linear gradient with sibling-index()</title>
+<link rel="help" href="https://drafts.csswg.org/css-images/#linear-gradients">
+<link rel="help" href="https://drafts.csswg.org/css-values-5/#tree-counting">
+<link rel="match" href="linear-gradient-body-sibling-index-ref.html">
+<link rel="assert" content"sibling-index() is 2 for body">
+<style>
+ body {
+ background-position: top left;
+ background-repeat: no-repeat;
+ background-size: 100px 100px;
+ background-image: linear-gradient(blue calc(20px * sibling-index()), yellow);
+ }
+</style>
+<body>
diff --git a/tests/wpt/tests/css/css-images/linear-gradient-calc-em-units-ref.html b/tests/wpt/tests/css/css-images/linear-gradient-calc-em-units-ref.html
new file mode 100644
index 00000000000..be13be8e260
--- /dev/null
+++ b/tests/wpt/tests/css/css-images/linear-gradient-calc-em-units-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<title>CSS Test Reference</title>
+<style>
+ div { width: 100px; height: 100px; }
+</style>
+<div style="background: linear-gradient(blue 20px, yellow)"></div>
+<div style="background: linear-gradient(blue 60px, yellow)"></div>
diff --git a/tests/wpt/tests/css/css-images/linear-gradient-calc-em-units.html b/tests/wpt/tests/css/css-images/linear-gradient-calc-em-units.html
new file mode 100644
index 00000000000..e764e62890d
--- /dev/null
+++ b/tests/wpt/tests/css/css-images/linear-gradient-calc-em-units.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>CSS Images Test: Linear gradient with em in calc()</title>
+<link rel="help" href="https://drafts.csswg.org/css-images/#linear-gradients">
+<link rel="match" href="linear-gradient-calc-em-units-ref.html">
+<style>
+ div {
+ width: 100px;
+ height: 100px;
+ background: linear-gradient(blue calc(2em), yellow);
+ }
+ #em1 {
+ font-size: 10px;
+ }
+ #em2 {
+ font-size: 30px;
+ }
+</style>
+<div id="em1"></div>
+<div id="em2"></div>
diff --git a/tests/wpt/tests/css/css-images/linear-gradient-sibling-index-ref.html b/tests/wpt/tests/css/css-images/linear-gradient-sibling-index-ref.html
new file mode 100644
index 00000000000..eea8518d95b
--- /dev/null
+++ b/tests/wpt/tests/css/css-images/linear-gradient-sibling-index-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<title>CSS Test Reference</title>
+<style>
+ div { width: 100px; height: 100px; }
+</style>
+<div style="background: linear-gradient(blue 20px, yellow)"></div>
+<div style="background: linear-gradient(blue 50px, yellow)"></div>
diff --git a/tests/wpt/tests/css/css-images/linear-gradient-sibling-index.html b/tests/wpt/tests/css/css-images/linear-gradient-sibling-index.html
new file mode 100644
index 00000000000..b92897e0697
--- /dev/null
+++ b/tests/wpt/tests/css/css-images/linear-gradient-sibling-index.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>CSS Images Test: Linear gradient with sibling-index()</title>
+<link rel="help" href="https://drafts.csswg.org/css-images/#linear-gradients">
+<link rel="help" href="https://drafts.csswg.org/css-values-5/#tree-counting">
+<link rel="match" href="linear-gradient-sibling-index-ref.html">
+<style>
+ .grad {
+ width: 100px;
+ height: 100px;
+ background: linear-gradient(blue calc(10px * sibling-index()), yellow);
+ }
+</style>
+<div>
+ <div></div>
+ <div class="grad"></div>
+ <div></div>
+ <div></div>
+ <div class="grad"></div>
+</div>
diff --git a/tests/wpt/tests/css/css-mixins/dashed-function-cycles.tentative.html b/tests/wpt/tests/css/css-mixins/dashed-function-cycles.html
index 11e653e9b7d..15305be2b28 100644
--- a/tests/wpt/tests/css/css-mixins/dashed-function-cycles.tentative.html
+++ b/tests/wpt/tests/css/css-mixins/dashed-function-cycles.html
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<title>Custom Functions: Handling cycles</title>
-<link rel="help" href="https://drafts.csswg.org/css-mixins-1/#cycles">
-<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/11500">
+<link rel="help" href="https://drafts.csswg.org/css-mixins-1/#evaluating-custom-functions">
+<link rel="help" href="https://drafts.csswg.org/css-values-5/#cyclic-substitution-contexts">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/utils.js"></script>
diff --git a/tests/wpt/tests/css/css-multicol/getclientrects-005.html b/tests/wpt/tests/css/css-multicol/getclientrects-005.html
new file mode 100644
index 00000000000..ee60a607dcc
--- /dev/null
+++ b/tests/wpt/tests/css/css-multicol/getclientrects-005.html
@@ -0,0 +1,139 @@
+<!DOCTYPE html>
+<title>getClientRects on fragmented table-column and table-column-group</title>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-getclientrects">
+<link rel="help" href="https://drafts.csswg.org/css-multicol/">
+<style>
+ body {
+ margin: 8px;
+ }
+</style>
+<div style="columns:6; gap:20px; inline-size:580px; column-rule:solid; column-fill:auto; block-size:100px; background:yellow;">
+ <div style="block-size:110px;"></div>
+ <div style="display:table; inline-size:100%; border-spacing:5px; border:2px solid; background:pink;">
+ <div style="display:table-caption; block-size:100px;"></div>
+ <div style="display:table-caption; caption-side:bottom; block-size:100px;"></div>
+ <div id="col1" style="display:table-column; inline-size:15px; background:blue;"></div>
+ <div id="col2" style="display:table-column; background:lime;"></div>
+ <div id="group" style="display:table-column-group;">
+ <div id="col3" style="display:table-column; inline-size:20px; background:hotpink;"></div>
+ <div id="col4" style="display:table-column; inline-size:10px; background:cyan;" id="col2"></div>
+ </div>
+ <div style="display:table-row-group;">
+ <div style="display:table-cell;">
+ <div style="block-size:5px;"></div>
+ </div>
+ </div>
+ <div style="display:table-row-group;">
+ <div style="display:table-cell;">
+ <div style="block-size:240px;"></div>
+ </div>
+ <div style="display:table-cell;"></div>
+ <div style="display:table-cell;"></div>
+ <div style="display:table-cell;"></div>
+ </div>
+ </div>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=> {
+ let rects = col1.getClientRects();
+ assert_equals(rects.length, 3);
+
+ assert_equals(rects[0].left, 215);
+ assert_equals(rects[0].top, 25);
+ assert_equals(rects[0].width, 15);
+ assert_equals(rects[0].height, 83);
+
+ assert_equals(rects[1].left, 315);
+ assert_equals(rects[1].top, 8);
+ assert_equals(rects[1].width, 15);
+ assert_equals(rects[1].height, 100);
+
+ assert_equals(rects[2].left, 415);
+ assert_equals(rects[2].top, 8);
+ assert_equals(rects[2].width, 15);
+ assert_equals(rects[2].height, 67);
+ }, "#col1");
+
+ test(()=> {
+ let rects = col2.getClientRects();
+ assert_equals(rects.length, 3);
+
+ assert_equals(rects[0].left, 235);
+ assert_equals(rects[0].top, 25);
+ assert_equals(rects[0].width, 10);
+ assert_equals(rects[0].height, 83);
+
+ assert_equals(rects[1].left, 335);
+ assert_equals(rects[1].top, 8);
+ assert_equals(rects[1].width, 10);
+ assert_equals(rects[1].height, 100);
+
+ assert_equals(rects[2].left, 435);
+ assert_equals(rects[2].top, 8);
+ assert_equals(rects[2].width, 10);
+ assert_equals(rects[2].height, 67);
+ }, "#col2");
+
+ test(()=> {
+ let rects = col3.getClientRects();
+ assert_equals(rects.length, 3);
+
+ assert_equals(rects[0].left, 250);
+ assert_equals(rects[0].top, 25);
+ assert_equals(rects[0].width, 20);
+ assert_equals(rects[0].height, 83);
+
+ assert_equals(rects[1].left, 350);
+ assert_equals(rects[1].top, 8);
+ assert_equals(rects[1].width, 20);
+ assert_equals(rects[1].height, 100);
+
+ assert_equals(rects[2].left, 450);
+ assert_equals(rects[2].top, 8);
+ assert_equals(rects[2].width, 20);
+ assert_equals(rects[2].height, 67);
+ }, "#col3");
+
+ test(()=> {
+ let rects = col4.getClientRects();
+ assert_equals(rects.length, 3);
+
+ assert_equals(rects[0].left, 275);
+ assert_equals(rects[0].top, 25);
+ assert_equals(rects[0].width, 10);
+ assert_equals(rects[0].height, 83);
+
+ assert_equals(rects[1].left, 375);
+ assert_equals(rects[1].top, 8);
+ assert_equals(rects[1].width, 10);
+ assert_equals(rects[1].height, 100);
+
+ assert_equals(rects[2].left, 475);
+ assert_equals(rects[2].top, 8);
+ assert_equals(rects[2].width, 10);
+ assert_equals(rects[2].height, 67);
+ }, "#col4");
+
+ test(()=> {
+ let rects = group.getClientRects();
+ assert_equals(rects.length, 3);
+
+ assert_equals(rects[0].left, 250);
+ assert_equals(rects[0].top, 25);
+ assert_equals(rects[0].width, 35);
+ assert_equals(rects[0].height, 83);
+
+ assert_equals(rects[1].left, 350);
+ assert_equals(rects[1].top, 8);
+ assert_equals(rects[1].width, 35);
+ assert_equals(rects[1].height, 100);
+
+ assert_equals(rects[2].left, 450);
+ assert_equals(rects[2].top, 8);
+ assert_equals(rects[2].width, 35);
+ assert_equals(rects[2].height, 67);
+ }, "#group");
+</script>
diff --git a/tests/wpt/tests/css/css-multicol/getclientrects-006.html b/tests/wpt/tests/css/css-multicol/getclientrects-006.html
new file mode 100644
index 00000000000..9f07a7de744
--- /dev/null
+++ b/tests/wpt/tests/css/css-multicol/getclientrects-006.html
@@ -0,0 +1,139 @@
+<!DOCTYPE html>
+<title>getClientRects on fragmented table-column and table-column-group, vertical-rl</title>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-getclientrects">
+<link rel="help" href="https://drafts.csswg.org/css-multicol/">
+<style>
+ body {
+ margin: 8px;
+ }
+</style>
+<div style="writing-mode:vertical-rl; columns:6; gap:20px; inline-size:580px; column-rule:solid; column-fill:auto; block-size:100px; background:yellow;">
+ <div style="block-size:110px;"></div>
+ <div style="display:table; inline-size:100%; border-spacing:5px; border:2px solid; background:pink;">
+ <div style="display:table-caption; block-size:100px;"></div>
+ <div style="display:table-caption; caption-side:bottom; block-size:100px;"></div>
+ <div id="col1" style="display:table-column; inline-size:15px; background:blue;"></div>
+ <div id="col2" style="display:table-column; background:lime;"></div>
+ <div id="group" style="display:table-column-group;">
+ <div id="col3" style="display:table-column; inline-size:20px; background:hotpink;"></div>
+ <div id="col4" style="display:table-column; inline-size:10px; background:cyan;" id="col2"></div>
+ </div>
+ <div style="display:table-row-group;">
+ <div style="display:table-cell;">
+ <div style="block-size:5px;"></div>
+ </div>
+ </div>
+ <div style="display:table-row-group;">
+ <div style="display:table-cell;">
+ <div style="block-size:240px;"></div>
+ </div>
+ <div style="display:table-cell;"></div>
+ <div style="display:table-cell;"></div>
+ <div style="display:table-cell;"></div>
+ </div>
+ </div>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=> {
+ let rects = col1.getClientRects();
+ assert_equals(rects.length, 3);
+
+ assert_equals(rects[0].left, 8);
+ assert_equals(rects[0].top, 215);
+ assert_equals(rects[0].width, 83);
+ assert_equals(rects[0].height, 15);
+
+ assert_equals(rects[1].left, 8);
+ assert_equals(rects[1].top, 315);
+ assert_equals(rects[1].width, 100);
+ assert_equals(rects[1].height, 15);
+
+ assert_equals(rects[2].left, 41);
+ assert_equals(rects[2].top, 415);
+ assert_equals(rects[2].width, 67);
+ assert_equals(rects[2].height, 15);
+ }, "#col1");
+
+ test(()=> {
+ let rects = col2.getClientRects();
+ assert_equals(rects.length, 3);
+
+ assert_equals(rects[0].left, 8);
+ assert_equals(rects[0].top, 235);
+ assert_equals(rects[0].width, 83);
+ assert_equals(rects[0].height, 10);
+
+ assert_equals(rects[1].left, 8);
+ assert_equals(rects[1].top, 335);
+ assert_equals(rects[1].width, 100);
+ assert_equals(rects[1].height, 10);
+
+ assert_equals(rects[2].left, 41);
+ assert_equals(rects[2].top, 435);
+ assert_equals(rects[2].width, 67);
+ assert_equals(rects[2].height, 10);
+ }, "#col2");
+
+ test(()=> {
+ let rects = col3.getClientRects();
+ assert_equals(rects.length, 3);
+
+ assert_equals(rects[0].left, 8);
+ assert_equals(rects[0].top, 250);
+ assert_equals(rects[0].width, 83);
+ assert_equals(rects[0].height, 20);
+
+ assert_equals(rects[1].left, 8);
+ assert_equals(rects[1].top, 350);
+ assert_equals(rects[1].width, 100);
+ assert_equals(rects[1].height, 20);
+
+ assert_equals(rects[2].left, 41);
+ assert_equals(rects[2].top, 450);
+ assert_equals(rects[2].width, 67);
+ assert_equals(rects[2].height, 20);
+ }, "#col3");
+
+ test(()=> {
+ let rects = col4.getClientRects();
+ assert_equals(rects.length, 3);
+
+ assert_equals(rects[0].left, 8);
+ assert_equals(rects[0].top, 275);
+ assert_equals(rects[0].width, 83);
+ assert_equals(rects[0].height, 10);
+
+ assert_equals(rects[1].left, 8);
+ assert_equals(rects[1].top, 375);
+ assert_equals(rects[1].width, 100);
+ assert_equals(rects[1].height, 10);
+
+ assert_equals(rects[2].left, 41);
+ assert_equals(rects[2].top, 475);
+ assert_equals(rects[2].width, 67);
+ assert_equals(rects[2].height, 10);
+ }, "#col4");
+
+ test(()=> {
+ let rects = group.getClientRects();
+ assert_equals(rects.length, 3);
+
+ assert_equals(rects[0].left, 8);
+ assert_equals(rects[0].top, 250);
+ assert_equals(rects[0].width, 83);
+ assert_equals(rects[0].height, 35);
+
+ assert_equals(rects[1].left, 8);
+ assert_equals(rects[1].top, 350);
+ assert_equals(rects[1].width, 100);
+ assert_equals(rects[1].height, 35);
+
+ assert_equals(rects[2].left, 41);
+ assert_equals(rects[2].top, 450);
+ assert_equals(rects[2].width, 67);
+ assert_equals(rects[2].height, 35);
+ }, "#group");
+</script>
diff --git a/tests/wpt/tests/css/css-multicol/getclientrects-007.html b/tests/wpt/tests/css/css-multicol/getclientrects-007.html
new file mode 100644
index 00000000000..0791c5b931f
--- /dev/null
+++ b/tests/wpt/tests/css/css-multicol/getclientrects-007.html
@@ -0,0 +1,139 @@
+<!DOCTYPE html>
+<title>getClientRects on fragmented table-column and table-column-group, vertical-lr</title>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-getclientrects">
+<link rel="help" href="https://drafts.csswg.org/css-multicol/">
+<style>
+ body {
+ margin: 8px;
+ }
+</style>
+<div style="float:left; writing-mode:vertical-lr; columns:6; gap:20px; inline-size:580px; column-rule:solid; column-fill:auto; block-size:100px; background:yellow;">
+ <div style="block-size:110px;"></div>
+ <div style="display:table; inline-size:100%; border-spacing:5px; border:2px solid; background:pink;">
+ <div style="display:table-caption; block-size:100px;"></div>
+ <div style="display:table-caption; caption-side:bottom; block-size:100px;"></div>
+ <div id="col1" style="display:table-column; inline-size:15px; background:blue;"></div>
+ <div id="col2" style="display:table-column; background:lime;"></div>
+ <div id="group" style="display:table-column-group;">
+ <div id="col3" style="display:table-column; inline-size:20px; background:hotpink;"></div>
+ <div id="col4" style="display:table-column; inline-size:10px; background:cyan;" id="col2"></div>
+ </div>
+ <div style="display:table-row-group;">
+ <div style="display:table-cell;">
+ <div style="block-size:5px;"></div>
+ </div>
+ </div>
+ <div style="display:table-row-group;">
+ <div style="display:table-cell;">
+ <div style="block-size:240px;"></div>
+ </div>
+ <div style="display:table-cell;"></div>
+ <div style="display:table-cell;"></div>
+ <div style="display:table-cell;"></div>
+ </div>
+ </div>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=> {
+ let rects = col1.getClientRects();
+ assert_equals(rects.length, 3);
+
+ assert_equals(rects[0].top, 215);
+ assert_equals(rects[0].left, 25);
+ assert_equals(rects[0].height, 15);
+ assert_equals(rects[0].width, 83);
+
+ assert_equals(rects[1].top, 315);
+ assert_equals(rects[1].left, 8);
+ assert_equals(rects[1].height, 15);
+ assert_equals(rects[1].width, 100);
+
+ assert_equals(rects[2].top, 415);
+ assert_equals(rects[2].left, 8);
+ assert_equals(rects[2].height, 15);
+ assert_equals(rects[2].width, 67);
+ }, "#col1");
+
+ test(()=> {
+ let rects = col2.getClientRects();
+ assert_equals(rects.length, 3);
+
+ assert_equals(rects[0].top, 235);
+ assert_equals(rects[0].left, 25);
+ assert_equals(rects[0].height, 10);
+ assert_equals(rects[0].width, 83);
+
+ assert_equals(rects[1].top, 335);
+ assert_equals(rects[1].left, 8);
+ assert_equals(rects[1].height, 10);
+ assert_equals(rects[1].width, 100);
+
+ assert_equals(rects[2].top, 435);
+ assert_equals(rects[2].left, 8);
+ assert_equals(rects[2].height, 10);
+ assert_equals(rects[2].width, 67);
+ }, "#col2");
+
+ test(()=> {
+ let rects = col3.getClientRects();
+ assert_equals(rects.length, 3);
+
+ assert_equals(rects[0].top, 250);
+ assert_equals(rects[0].left, 25);
+ assert_equals(rects[0].height, 20);
+ assert_equals(rects[0].width, 83);
+
+ assert_equals(rects[1].top, 350);
+ assert_equals(rects[1].left, 8);
+ assert_equals(rects[1].height, 20);
+ assert_equals(rects[1].width, 100);
+
+ assert_equals(rects[2].top, 450);
+ assert_equals(rects[2].left, 8);
+ assert_equals(rects[2].height, 20);
+ assert_equals(rects[2].width, 67);
+ }, "#col3");
+
+ test(()=> {
+ let rects = col4.getClientRects();
+ assert_equals(rects.length, 3);
+
+ assert_equals(rects[0].top, 275);
+ assert_equals(rects[0].left, 25);
+ assert_equals(rects[0].height, 10);
+ assert_equals(rects[0].width, 83);
+
+ assert_equals(rects[1].top, 375);
+ assert_equals(rects[1].left, 8);
+ assert_equals(rects[1].height, 10);
+ assert_equals(rects[1].width, 100);
+
+ assert_equals(rects[2].top, 475);
+ assert_equals(rects[2].left, 8);
+ assert_equals(rects[2].height, 10);
+ assert_equals(rects[2].width, 67);
+ }, "#col4");
+
+ test(()=> {
+ let rects = group.getClientRects();
+ assert_equals(rects.length, 3);
+
+ assert_equals(rects[0].top, 250);
+ assert_equals(rects[0].left, 25);
+ assert_equals(rects[0].height, 35);
+ assert_equals(rects[0].width, 83);
+
+ assert_equals(rects[1].top, 350);
+ assert_equals(rects[1].left, 8);
+ assert_equals(rects[1].height, 35);
+ assert_equals(rects[1].width, 100);
+
+ assert_equals(rects[2].top, 450);
+ assert_equals(rects[2].left, 8);
+ assert_equals(rects[2].height, 35);
+ assert_equals(rects[2].width, 67);
+ }, "#group");
+</script>
diff --git a/tests/wpt/tests/css/css-multicol/getclientrects-008.html b/tests/wpt/tests/css/css-multicol/getclientrects-008.html
new file mode 100644
index 00000000000..21505a7e48a
--- /dev/null
+++ b/tests/wpt/tests/css/css-multicol/getclientrects-008.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<title>getClientRects on monolithic elements and their container</title>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-getclientrects">
+<link rel="help" href="https://drafts.csswg.org/css-multicol/">
+<style>
+ body {
+ margin: 8px;
+ }
+</style>
+<div style="columns:3; column-fill:auto; gap:10px; width:320px; height:100px; background:yellow;">
+ <div id="container" style="background:gray;">
+ <div id="monolith1" style="contain:size; width:50%; height:250px; background:cyan;"></div>
+ <div id="monolith2" style="contain:size; width:50%; height:50px; background:black;"></div>
+ <div id="monolith3" style="contain:size; width:50%; height:250px; background:hotpink;"></div>
+ </div>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=> {
+ let rects = monolith1.getClientRects();
+ assert_equals(rects.length, 1);
+ assert_equals(rects[0].left, 8);
+ assert_equals(rects[0].top, 8);
+ assert_equals(rects[0].width, 50);
+ assert_equals(rects[0].height, 250);
+ }, "#monolith1");
+
+ test(()=> {
+ let rects = monolith2.getClientRects();
+ assert_equals(rects.length, 1);
+ assert_equals(rects[0].left, 118);
+ assert_equals(rects[0].top, 8);
+ assert_equals(rects[0].width, 50);
+ assert_equals(rects[0].height, 50);
+ }, "#monolith2");
+
+ test(()=> {
+ let rects = monolith3.getClientRects();
+ assert_equals(rects.length, 1);
+ assert_equals(rects[0].left, 228);
+ assert_equals(rects[0].top, 8);
+ assert_equals(rects[0].width, 50);
+ assert_equals(rects[0].height, 250);
+ }, "#monolith3");
+
+ test(()=> {
+ let rects = container.getClientRects();
+ assert_equals(rects.length, 3);
+
+ assert_equals(rects[0].left, 8);
+ assert_equals(rects[0].top, 8);
+ assert_equals(rects[0].width, 100);
+ assert_equals(rects[0].height, 250);
+
+ assert_equals(rects[1].left, 118);
+ assert_equals(rects[1].top, 8);
+ assert_equals(rects[1].width, 100);
+ assert_equals(rects[1].height, 100);
+
+ assert_equals(rects[2].left, 228);
+ assert_equals(rects[2].top, 8);
+ assert_equals(rects[2].width, 100);
+ assert_equals(rects[2].height, 250);
+ }, "#container");
+</script>
diff --git a/tests/wpt/tests/css/css-overflow/scroll-button-activation-without-scroller.html b/tests/wpt/tests/css/css-overflow/scroll-button-activation-without-scroller.html
new file mode 100644
index 00000000000..f5951efc419
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/scroll-button-activation-without-scroller.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Test: ::scroll-button() activation without scroller shouldn't crash</title>
+<link rel="help" href="https://drafts.csswg.org/css-overflow-5/#scroll-buttons">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<style>
+div::scroll-button(inline-start) {
+ content: "";
+ height: 100px;
+ width: 100px;
+}
+</style>
+<div></div>
+<script>
+ promise_test(async t => {
+ await new test_driver.Actions()
+ .pointerMove(50, 50)
+ .pointerDown()
+ .pointerUp()
+ .pointerDown()
+ .pointerUp()
+ .send();
+ const kTab = '\uE004';
+ const kEnter = '\uE007';
+ await new test_driver.Actions()
+ .keyDown(kTab)
+ .keyUp(kTab)
+ .keyDown(kEnter)
+ .keyUp(kEnter)
+ .send();
+ });
+</script>
diff --git a/tests/wpt/tests/css/css-overflow/scroll-button-disabled-no-focus.html b/tests/wpt/tests/css/css-overflow/scroll-button-disabled-no-focus.html
new file mode 100644
index 00000000000..a8897706874
--- /dev/null
+++ b/tests/wpt/tests/css/css-overflow/scroll-button-disabled-no-focus.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Test: disabled ::scroll-button() shouldn't be focusable</title>
+<link rel="help" href="https://drafts.csswg.org/css-overflow-5/#scroll-buttons">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<style>
+div::scroll-button(inline-start) {
+ content: "";
+ background-color: green;
+}
+
+div::scroll-button(inline-start):focus {
+ background-color: red;
+}
+</style>
+<div id="target"></div>
+<script>
+ promise_test(async t => {
+ const kTab = '\uE004';
+ await new test_driver.Actions()
+ .keyDown(kTab)
+ .keyUp(kTab)
+ .send();
+ assert_equals(getComputedStyle(target, "::scroll-button(inline-start)").backgroundColor, "rgb(0, 128, 0)");
+ });
+</script>
diff --git a/tests/wpt/tests/css/css-overflow/scroll-buttons-appearance-ref.html b/tests/wpt/tests/css/css-overflow/scroll-buttons-appearance-ref.html
index 462186407d5..c382f738a6f 100644
--- a/tests/wpt/tests/css/css-overflow/scroll-buttons-appearance-ref.html
+++ b/tests/wpt/tests/css/css-overflow/scroll-buttons-appearance-ref.html
@@ -7,4 +7,4 @@
<p>Test passes if there are two buttons, the first one using appearance auto
and the second using appearance: none.</p>
<div></div>
-<button>appearance: auto</button><button class="none">appearance: none</button>
+<button disabled="true">appearance: auto</button><button disabled="true" class="none">appearance: none</button>
diff --git a/tests/wpt/tests/css/css-scroll-anchoring/table-col-and-dead-row-group-crash.html b/tests/wpt/tests/css/css-scroll-anchoring/table-col-and-dead-row-group-crash.html
new file mode 100644
index 00000000000..e6e90a63a34
--- /dev/null
+++ b/tests/wpt/tests/css/css-scroll-anchoring/table-col-and-dead-row-group-crash.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org">
+<link rel="help" href="https://issues.chromium.org/issues/416061199">
+<div style="display:table;">
+ <div style="display:table-column-group;"></div>
+ <div id="e13" style="display:table-row-group;">
+ <div style="height:200vh;"></div>
+ x
+ </div>
+</div>
+<script>
+ window.scroll(0, 100);
+ e13.style.display = "none";
+</script>
diff --git a/tests/wpt/tests/css/css-text/letter-spacing/letter-spacing-cursive-001.html b/tests/wpt/tests/css/css-text/letter-spacing/letter-spacing-cursive-001.html
new file mode 100644
index 00000000000..5700c600981
--- /dev/null
+++ b/tests/wpt/tests/css/css-text/letter-spacing/letter-spacing-cursive-001.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+<title>
+CSS Text Test - Letter-spacing should not be applied for Cursive Scripts.
+</title>
+<link rel="author" title="Sejal Anand" href="mailto:sejalanand@microsoft.com">
+<link rel="help" href="https://www.w3.org/TR/css-text-3/#cursive-tracking">
+<link rel="match" href="reference/letter-spacing-cursive-001-ref.html">
+<meta name="assert" content="Letter-spacing must not be applied to Arabic text,
+ preserving cursive connections.">
+<style>
+div {
+ margin: 1em;
+ font: 24px serif;
+ white-space: nowrap;
+ display: inline-block;
+ outline: 1px solid gray;
+}
+.letterspacing {
+ letter-spacing: 10px; /* Should NOT affect Arabic text */
+}
+</style>
+</head>
+<body>
+<p>
+The following test will pass if no spacing appears between letters within
+Arabic words. Both Arabic texts should display identically.
+</p>
+<div class="letterspacing">
+<span>مرحباً</span>
+</div>
+<br>
+<div>
+<span>مرحباً</span>
+</div>
+<br>
+</body>
+</html>
diff --git a/tests/wpt/tests/css/css-text/letter-spacing/reference/letter-spacing-cursive-001-ref.html b/tests/wpt/tests/css/css-text/letter-spacing/reference/letter-spacing-cursive-001-ref.html
new file mode 100644
index 00000000000..720c9491b4d
--- /dev/null
+++ b/tests/wpt/tests/css/css-text/letter-spacing/reference/letter-spacing-cursive-001-ref.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<head>
+<meta charset="utf-8">
+<title>
+CSS Text Test - Letter-spacing should not be applied for Cursive Scripts.
+</title>
+<link rel="author" title="Sejal Anand" href="mailto:sejalanand@microsoft.com">
+<style>
+div {
+ margin: 1em;
+ font: 24px serif;
+ white-space: nowrap;
+ display: inline-block;
+ outline: 1px solid gray;
+}
+</style>
+</head>
+<body>
+<p>
+The following test will pass if no spacing appears between letters within
+Arabic words. Both Arabic texts should display identically.
+</p>
+<div>
+<span>مرحباً</span>
+</div>
+<br>
+<div>
+<span>مرحباً</span>
+</div>
+<br>
+</body>
+</html>
diff --git a/tests/wpt/tests/css/css-transforms/css-scale-of-clip-path-ref.html b/tests/wpt/tests/css/css-transforms/css-scale-of-clip-path-ref.html
new file mode 100644
index 00000000000..d6fa480b040
--- /dev/null
+++ b/tests/wpt/tests/css/css-transforms/css-scale-of-clip-path-ref.html
@@ -0,0 +1,20 @@
+<!doctype HTML>
+<style>
+.tile {
+ width: 620px;
+ height: 671px;
+ position: absolute;
+ clip-path: circle(50%);
+ background-color: lightblue;
+}
+#container {
+ transform: scale(0.25);
+ position: relative;
+ left: -200px;
+ will-change: transform;
+}
+</style>
+<div id=container>
+ <div class="tile" style="top: 520px;"></div>
+ <div class="tile" style="top: 688px; left: 390px;"></div>
+</div>
diff --git a/tests/wpt/tests/css/css-transforms/css-scale-of-clip-path.html b/tests/wpt/tests/css/css-transforms/css-scale-of-clip-path.html
new file mode 100644
index 00000000000..dda72c0d139
--- /dev/null
+++ b/tests/wpt/tests/css/css-transforms/css-scale-of-clip-path.html
@@ -0,0 +1,23 @@
+<!doctype HTML>
+<link rel="author" title="Chris Harrelson">
+<link rel="help" href="https://drafts.csswg.org/css-transforms-2/">
+<link rel="match" href="css-scale-of-clip-path-ref.html">
+<meta name=fuzzy content="maxDifference=0-40;totalPixels=0-3000">
+<style>
+.tile {
+ width: 620px;
+ height: 671px;
+ position: absolute;
+ clip-path: circle(50%);
+ background-color: lightblue;
+}
+#container {
+ transform: scale(0.25);
+ position: relative;
+ left: -200px;
+}
+</style>
+<div id=container>
+ <div class="tile" style="top: 520px;"></div>
+ <div class="tile" style="top: 688px; left: 390px;"></div>
+</div>
diff --git a/tests/wpt/tests/css/css-transforms/scale-transform-filtered-text-ref.html b/tests/wpt/tests/css/css-transforms/scale-transform-filtered-text-ref.html
new file mode 100644
index 00000000000..e8b564ce3dd
--- /dev/null
+++ b/tests/wpt/tests/css/css-transforms/scale-transform-filtered-text-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<style>
+ #container {
+ transform: scale(0.5);
+ background-color: lightblue;
+ font-size: 80px;
+ }
+ #animated, #content {
+ will-change: transform;
+ }
+</style>
+<div id=container>
+ <div id=content>
+ <div id=animated>Text</div>
+ </div>
+</div>
diff --git a/tests/wpt/tests/css/css-transforms/scale-transform-filtered-text.html b/tests/wpt/tests/css/css-transforms/scale-transform-filtered-text.html
new file mode 100644
index 00000000000..a479b12528a
--- /dev/null
+++ b/tests/wpt/tests/css/css-transforms/scale-transform-filtered-text.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<link rel="author" href="mailto:chrishtr@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-transforms-2/">
+<meta name="assert" content="Text is not blurry under w/filter and animation.">
+<link rel="match" href="scale-transform-filtered-text-ref.html">
+<meta name=fuzzy content="maxDifference=0-25;totalPixels=0-500">
+<style>
+ #container {
+ transform: scale(0.5);
+ background-color: lightblue;
+ font-size: 80px;
+ }
+ #content {
+ filter: blur(0px);
+ }
+ #animated {
+ will-change: transform;
+ }
+</style>
+<div id=container>
+ <div id=content>
+ <div id=animated>Text</div>
+ </div>
+</div>
diff --git a/tests/wpt/tests/css/css-typed-om/the-stylepropertymap/properties/transform.html b/tests/wpt/tests/css/css-typed-om/the-stylepropertymap/properties/transform.html
index 7a852545a74..d12714a499c 100644
--- a/tests/wpt/tests/css/css-typed-om/the-stylepropertymap/properties/transform.html
+++ b/tests/wpt/tests/css/css-typed-om/the-stylepropertymap/properties/transform.html
@@ -18,4 +18,8 @@ runPropertyTests('transform', [
{ syntax: '<transform>' },
]);
+runUnsupportedPropertyTests('transform', [
+ 'matrix(sibling-index(), 2, 3, 4, 5 ,6)'
+]);
+
</script>
diff --git a/tests/wpt/tests/css/css-values/tree-counting/sibling-function-descriptors.tentative.html b/tests/wpt/tests/css/css-values/tree-counting/sibling-function-descriptors.tentative.html
index d31d4acf499..76d2ff8ee4d 100644
--- a/tests/wpt/tests/css/css-values/tree-counting/sibling-function-descriptors.tentative.html
+++ b/tests/wpt/tests/css/css-values/tree-counting/sibling-function-descriptors.tentative.html
@@ -4,7 +4,7 @@
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/10982">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
-<style id="test_sheet">
+<style id="page_sheet">
@page {
margin: 100px;
margin: calc(0px * sibling-index());
@@ -13,33 +13,131 @@
margin: 100px;
margin: calc(0px * sibling-count());
}
+</style>
+<style id="font_face_sheet">
@font-face {
font-family: my-font;
font-weight: 300;
font-weight: calc(max(0 * sibling-index(), 400));
+ font-feature-settings: "vert" 2;
+ font-feature-settings: "vert" calc(max(sibling-index(), 1));
}
@font-face {
font-family: my-font;
font-weight: 300;
font-weight: calc(max(0 * sibling-count(), 400));
+ font-feature-settings: "vert" 2;
+ font-feature-settings: "vert" calc(max(sibling-count(), 1));
+ }
+</style>
+<style id="font_palette_sheet">
+ @font-palette-values --foo {
+ font-family: my-font;
+ base-palette: 1;
+ base-palette: calc(max(sibling-index(), 2));
+ override-colors: 1 green;
+ override-colors: sibling-index() red;
+ }
+ @font-palette-values --foo {
+ font-family: my-font;
+ base-palette: 1;
+ base-palette: calc(max(sibling-count(), 2));
+ override-colors: 1 green;
+ override-colors: sibling-count() red;
+ }
+</style>
+<style id="counter_style_sheet">
+ @counter-style --foo {
+ system: fixed 1;
+ system: fixed calc(max(sibling-index(), 2));
+ negative: --pass;
+ negative: linear-gradient(red calc(20px * sibling-index()), pink);
+ prefix: --pass;
+ prefix: linear-gradient(red calc(20px * sibling-index()), pink);
+ suffix: --pass;
+ suffix: linear-gradient(red calc(20px * sibling-index()), pink);
+ range: 1 infinite;
+ range: calc(max(sibling-index(), 2)) infinite;
+ pad: 1 --pass;
+ pad: 1 linear-gradient(red calc(20px * sibling-index()), pink);
+ pad: calc(max(sibling-index(), 2)) --fail;
+ symbols: --pass;
+ symbols: linear-gradient(red calc(20px * sibling-index()), pink);
+ }
+ @counter-style --foo {
+ system: fixed 1;
+ system: fixed calc(max(sibling-count(), 2));
+ negative: --pass;
+ negative: linear-gradient(green, green);
+ negative: linear-gradient(red calc(20px * sibling-count()), pink);
+ prefix: --pass;
+ prefix: linear-gradient(red calc(20px * sibling-count()), pink);
+ suffix: --pass;
+ suffix: linear-gradient(red calc(20px * sibling-count()), pink);
+ range: 1 infinite;
+ range: calc(max(sibling-count(), 2)) infinite;
+ pad: 1 --pass;
+ pad: 1 linear-gradient(red calc(20px * sibling-count()), pink);
+ pad: calc(max(sibling-count(), 2)) --fail;
+ symbols: --pass;
+ symbols: linear-gradient(red calc(20px * sibling-count()), pink);
}
</style>
<script>
- const rules = test_sheet.sheet.cssRules;
+ const page_rules = page_sheet.sheet.cssRules;
test(() => {
- assert_equals(rules[0].style.margin, "100px");
+ assert_equals(page_rules[0].style.margin, "100px");
}, "sibling-index() should not be allowed in @page properties");
test(() => {
- assert_equals(rules[1].style.margin, "100px");
+ assert_equals(page_rules[1].style.margin, "100px");
}, "sibling-count() should not be allowed in @page properties");
+ const font_face_rules = font_face_sheet.sheet.cssRules;
+
test(() => {
- assert_equals(rules[2].style.fontWeight, "300");
+ assert_equals(font_face_rules[0].style.fontWeight, "300");
+ assert_equals(font_face_rules[0].style.fontFeatureSettings, "\"vert\" 2");
}, "sibling-index() should not be allowed in @font-face descriptors");
test(() => {
- assert_equals(rules[3].style.fontWeight, "300");
+ assert_equals(font_face_rules[1].style.fontWeight, "300");
+ assert_equals(font_face_rules[1].style.fontFeatureSettings, "\"vert\" 2");
}, "sibling-count() should not be allowed in @font-face descriptors");
+
+ const font_palette_rules = font_palette_sheet.sheet.cssRules;
+
+ test(() => {
+ assert_equals(font_palette_rules[0].basePalette, "1");
+ assert_equals(font_palette_rules[0].overrideColors, "1 green");
+ }, "sibling-index() should not be allowed in @font-palette-values descriptors");
+
+ test(() => {
+ assert_equals(font_palette_rules[1].basePalette, "1");
+ assert_equals(font_palette_rules[1].overrideColors, "1 green");
+ }, "sibling-count() should not be allowed in @font-palette-values descriptors");
+
+ const counter_style_rules = counter_style_sheet.sheet.cssRules;
+
+ test(() => {
+ assert_equals(counter_style_rules[0].system, "fixed 1");
+ assert_equals(counter_style_rules[0].negative, "--pass");
+ assert_equals(counter_style_rules[0].prefix, "--pass");
+ assert_equals(counter_style_rules[0].suffix, "--pass");
+ assert_equals(counter_style_rules[0].range, "1 infinite");
+ assert_equals(counter_style_rules[0].pad, "1 --pass");
+ assert_equals(counter_style_rules[0].symbols, "--pass");
+ }, "sibling-index() should not be allowed in @counter-style descriptors");
+
+ test(() => {
+ assert_equals(counter_style_rules[1].system, "fixed 1");
+ assert_equals(counter_style_rules[1].negative, "--pass");
+ assert_equals(counter_style_rules[1].prefix, "--pass");
+ assert_equals(counter_style_rules[1].suffix, "--pass");
+ assert_equals(counter_style_rules[1].range, "1 infinite");
+ assert_equals(counter_style_rules[1].pad, "1 --pass");
+ assert_equals(counter_style_rules[1].symbols, "--pass");
+ }, "sibling-count() should not be allowed in @counter-style descriptors");
+
</script>
diff --git a/tests/wpt/tests/css/css-values/tree-counting/sibling-index-keyframe-length-value-dynamic.html b/tests/wpt/tests/css/css-values/tree-counting/sibling-index-keyframe-length-value-dynamic.html
new file mode 100644
index 00000000000..cbd34602fb3
--- /dev/null
+++ b/tests/wpt/tests/css/css-values/tree-counting/sibling-index-keyframe-length-value-dynamic.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>CSS Values and Units Test: sibling-index() changing length value during @keyframes animation</title>
+<link rel="help" href="https://drafts.csswg.org/css-values-5/#tree-counting">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ @keyframes --anim {
+ from {
+ top: calc(100px * sibling-index());
+ }
+ to {
+ top: 0px;
+ }
+ }
+ #target {
+ animation: --anim 1000s step-end;
+ position: absolute;
+ top: 13px;
+ width: 100px;
+ height: 100px;
+ }
+</style>
+<div>
+ <div id="rm"></div>
+ <div id="target"></div>
+</div>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(target).top, "200px");
+ }, "Initially, the sibling-index() is 2 for #target");
+
+ test(() => {
+ rm.remove();
+ assert_equals(getComputedStyle(target).top, "100px");
+ }, "Removing a preceding sibling of #target reduces the sibling-index()");
+
+</script>
diff --git a/tests/wpt/tests/css/css-values/tree-counting/sibling-index-keyframe-value-dynamic.html b/tests/wpt/tests/css/css-values/tree-counting/sibling-index-keyframe-value-dynamic.html
new file mode 100644
index 00000000000..286e0d3d3e2
--- /dev/null
+++ b/tests/wpt/tests/css/css-values/tree-counting/sibling-index-keyframe-value-dynamic.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<title>CSS Values and Units Test: sibling-index() changing value during @keyframes animation</title>
+<link rel="help" href="https://drafts.csswg.org/css-values-5/#tree-counting">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ @keyframes --anim {
+ from {
+ z-index: sibling-index();
+ }
+ to {
+ z-index: 1;
+ }
+ }
+ #target {
+ animation: --anim 1000s step-end;
+ position: relative;
+ width: 100px;
+ height: 100px;
+ background: red;
+ }
+ #abs {
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ z-index: 3;
+ background: green;
+ }
+</style>
+<p>You should see a green square below.</p>
+<div>
+ <div id="rm"></div>
+ <div id="abs"></div>
+ <div id="target"></div>
+</div>
+<script>
+ test(() => {
+ assert_equals(getComputedStyle(target).zIndex, "3");
+ }, "Initially, the sibling-index() is 3 for #target");
+
+ test(() => {
+ rm.remove();
+ assert_equals(getComputedStyle(target).zIndex, "2");
+ }, "Removing a preceding sibling of #target reduces the sibling-index()");
+
+</script>
diff --git a/tests/wpt/tests/css/css-view-transitions/root-preserve3d-crash.html b/tests/wpt/tests/css/css-view-transitions/root-preserve3d-crash.html
new file mode 100644
index 00000000000..11232801875
--- /dev/null
+++ b/tests/wpt/tests/css/css-view-transitions/root-preserve3d-crash.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1963368">
+<style>
+ * {
+ border-style: double !important;
+ -webkit-transform-style: preserve-3d !important;
+ }
+</style>
+<script>
+ document.addEventListener("DOMContentLoaded", async () => {
+ document.startViewTransition(undefined)
+ })
+</script>
diff --git a/tests/wpt/tests/css/css-view-transitions/root-replace-crash.html b/tests/wpt/tests/css/css-view-transitions/root-replace-crash.html
new file mode 100644
index 00000000000..165eec41b6b
--- /dev/null
+++ b/tests/wpt/tests/css/css-view-transitions/root-replace-crash.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+ <script>
+ document.addEventListener("DOMContentLoaded", async () => {
+ const tt = document.createElementNS("http://www.w3.org/1999/xhtml", "tt")
+ tt.popover = "manual"
+ const viewTransition = document.startViewTransition(undefined)
+ await viewTransition.updateCallbackDone
+ for (let i = 0; i < 2; i++) {
+ document.replaceChild(tt, document.childNodes[(2324524876 % document.childNodes.length)])
+ }
+ })
+ </script>
+ <head>
+</html>
diff --git a/tests/wpt/tests/css/css-view-transitions/scoped/crashtests/shadow-dom.html b/tests/wpt/tests/css/css-view-transitions/scoped/crashtests/shadow-dom.html
new file mode 100644
index 00000000000..a2faafc1694
--- /dev/null
+++ b/tests/wpt/tests/css/css-view-transitions/scoped/crashtests/shadow-dom.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html class="test-wait">
+<body>
+<meter><div id="node1"></div></meter>
+<div>
+ <template shadowrootmode="open"></template>
+ <div id="node2"></div>
+</div>
+<div style="display:none">
+ <div id="node3"></div>
+</div>
+<script>
+ const tr1 = node1.startViewTransition();
+ const tr2 = node2.startViewTransition();
+ const tr3 = node3.startViewTransition();
+ onload = async () => {
+ await tr1.finished;
+ await tr2.finished;
+ await tr3.finished;
+ document.documentElement.classList.remove('test-wait');
+ }
+</script>
+</body>
+</html>
diff --git a/tests/wpt/tests/css/css-view-transitions/start-skip-start.html b/tests/wpt/tests/css/css-view-transitions/start-skip-start.html
new file mode 100644
index 00000000000..e04979472f1
--- /dev/null
+++ b/tests/wpt/tests/css/css-view-transitions/start-skip-start.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <title>Test start skip and then start on a view transition</title>
+ <link rel="help" href="https://www.w3.org/TR/css-view-transitions-1/">
+</head>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+ :root {
+ view-transition-name: none;
+ }
+ #target {
+ width: 100px;
+ height: 100px;
+ background: blue;
+ contain: paint;
+ view-transition-name: target;
+ }
+ #target.update-1 {
+ height: 150px;
+ }
+ #target.update-2 {
+ height: 200px;
+ }
+</style>
+
+<body>
+ <div id="target"></div>
+</body>
+<script>
+ promise_test(async t => {
+ let rejected_promise_tally = 0;
+ const target = document.getElementById("target");
+ assert_implements(document.startViewTransition,
+ "Missing document.startViewTransition");
+
+ const verifyAbortedTransition = (promise) => {
+ return promise.then(
+ () => { assert_not_reached('transition aborted') },
+ (reason) => {
+ assert_true(reason instanceof DOMException);
+ assert_equals(reason.code, DOMException.ABORT_ERR);
+ rejected_promise_tally++;
+ });
+ };
+
+ const vt1 = document.startViewTransition(() => {
+ target.className = 'update-1';
+ });
+
+ vt1.skipTransition();
+ const vt2 = document.startViewTransition(() => {
+ assert_equals(target.className, 'update-1');
+ target.className = 'update-2';
+ });
+
+ vt2.skipTransition();
+ const vt3 = document.startViewTransition(() => {
+ assert_equals(target.className, 'update-2');
+ });
+
+ await verifyAbortedTransition(vt1.ready);
+ await verifyAbortedTransition(vt2.ready);
+ await vt3.ready;
+
+ assert_equals(rejected_promise_tally, 2,
+ 'first 2 transitions were skipped');
+ const sizeTransformAnimations = document.getAnimations().filter(a => {
+ return 'height' in a.effect.getKeyframes()[0];
+ });
+ assert_equals(sizeTransformAnimations.length, 1);
+ const startingHeight =
+ sizeTransformAnimations[0].effect.getKeyframes()[0].height;
+
+ assert_equals(startingHeight, '200px',
+ 'Height change applied before capture');
+
+ }, 'Synchronously starting a view transition blocks on DOM callback for ' +
+ 'previously skipped transitions');
+
+</script>
+</html>
diff --git a/tests/wpt/tests/css/css-view-transitions/view-transition-types-mutable-no-document-element-crashtest.html b/tests/wpt/tests/css/css-view-transitions/view-transition-types-mutable-no-document-element-crashtest.html
index d5c0abd652c..3ac946b273b 100644
--- a/tests/wpt/tests/css/css-view-transitions/view-transition-types-mutable-no-document-element-crashtest.html
+++ b/tests/wpt/tests/css/css-view-transitions/view-transition-types-mutable-no-document-element-crashtest.html
@@ -3,6 +3,7 @@
<link rel="help" href="https://www.w3.org/TR/css-transitions-2/">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
+<script src="/dom/events/scrolling/scroll_support.js"></script>
<style>
::view-transition-group(*),
@@ -14,14 +15,18 @@
</style>
<script>
- test(() => {
- assert_implements(document.startViewTransition);
+promise_test(async () => {
+ assert_implements(document.startViewTransition);
- const { types } = document.startViewTransition();
- document.documentElement.remove();
- types.add("a");
- assert_array_equals([...types], ["a"]);
- }, "ViewTransitionTypeSet should not crash when documentElement is null");
+ await new Promise((r) => requestAnimationFrame(() => {
+ waitForCompositorReady().then(r);
+ }));
+
+ const { types } = document.startViewTransition();
+ document.documentElement.remove();
+ types.add("a");
+ assert_array_equals([...types], ["a"]);
+}, "ViewTransitionTypeSet should not crash when documentElement is null");
</script>
</html>
diff --git a/tests/wpt/tests/css/cssom-view/getBoundingClientRect-newline.html b/tests/wpt/tests/css/cssom-view/getBoundingClientRect-newline.html
index 40e29181d6f..ce82b7237d0 100644
--- a/tests/wpt/tests/css/cssom-view/getBoundingClientRect-newline.html
+++ b/tests/wpt/tests/css/cssom-view/getBoundingClientRect-newline.html
@@ -5,17 +5,14 @@
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
-div {
+div[contenteditable] {
white-space: pre;
- font-family: Ahem;
- font-size: 10px;
- line-height: 1;
+ font: 10px/1 Ahem;
width: 10ch;
}
</style>
<body>
<div contenteditable></div>
-</body>
<script>
function getBoundingClientRect(node, offset) {
const range = document.createRange();
@@ -37,3 +34,4 @@ test(function() {
assert_less_than(rect6.y, rect7.y);
}, 'Range.getBoundingClientRect() should return the first position of the next line when the collapsed range is a newline character');
</script>
+</body>
diff --git a/tests/wpt/tests/css/cssom-view/resources/scrollIntoView-frame.html b/tests/wpt/tests/css/cssom-view/resources/scrollIntoView-frame.html
new file mode 100644
index 00000000000..ee2be2fd3ab
--- /dev/null
+++ b/tests/wpt/tests/css/cssom-view/resources/scrollIntoView-frame.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="viewport" content="initial-scale=1">
+<style>
+body {
+ height: 600px;
+}
+#target {
+ position: absolute;
+ top: 400px;
+ height: 200px;
+}
+</style>
+<body>
+<div id="target"></div>
+</body>
diff --git a/tests/wpt/tests/css/cssom-view/scrollIntoView-container.html b/tests/wpt/tests/css/cssom-view/scrollIntoView-container.html
new file mode 100644
index 00000000000..3b241ea2251
--- /dev/null
+++ b/tests/wpt/tests/css/cssom-view/scrollIntoView-container.html
@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<title>CSSOM View - scrollIntoView container option</title>
+<meta charset="utf-8">
+<meta name="viewport" content="initial-scale=1">
+<link rel="author" title="Rob Flack" href="mailto:flackr@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-scrollintoview">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+.scroller {
+ overflow: auto;
+ height: 200px;
+}
+.spacer {
+ height: 400px;
+}
+#target {
+ height: 200px;
+}
+</style>
+<script>
+let setFrameLoaded = null;
+let frameLoaded = new Promise(resolve => {
+ setFrameLoaded = resolve;
+});
+</script>
+<div id="outer" class="scroller">
+ <div class="spacer"></div>
+ <div id="inner" class="scroller">
+ <div class="spacer"></div>
+ <div id="target"></div>
+ <iframe id="frame" height="200" src="resources/scrollIntoView-frame.html" onload="setFrameLoaded()"></iframe>
+ </div>
+</div>
+<script>
+const outer = document.getElementById('outer');
+const inner = document.getElementById('inner');
+const target = document.getElementById('target');
+
+function reset() {
+ outer.scrollTop = 0;
+ inner.scrollTop = 0;
+}
+
+test(() => {
+ reset();
+ target.scrollIntoView();
+ assert_equals(inner.scrollTop, 400, '#inner scrollTop');
+ assert_equals(outer.scrollTop, 400, '#outer scrollTop');
+}, `scrollIntoView() defaults to scrolling ancestors`);
+
+test(() => {
+ reset();
+ target.scrollIntoView({container: 'all'});
+ assert_equals(inner.scrollTop, 400, '#inner scrollTop');
+ assert_equals(outer.scrollTop, 400, '#outer scrollTop');
+}, `scrollIntoView({container: 'all'}) scrolls ancestors`);
+
+test(() => {
+ reset();
+ target.scrollIntoView({container: 'nearest'});
+ assert_equals(inner.scrollTop, 400, '#inner scrollTop');
+ assert_equals(outer.scrollTop, 0, '#outer scrollTop');
+}, `scrollIntoView({container: 'nearest'}) only scrolls nearest scroll container`);
+
+test(() => {
+ reset();
+ inner.scrollIntoView({container: 'nearest'});
+ assert_equals(outer.scrollTop, 400, '#outer scrollTop');
+ assert_equals(inner.scrollTop, 0, '#inner scrollTop');
+}, `scrollIntoView({container: 'nearest'}) doesn't stop at itself`);
+
+promise_test(async () => {
+ reset();
+ await frameLoaded;
+ const frameDoc = document.getElementById("frame").contentDocument;
+ const frameTarget = frameDoc.getElementById("target");
+ frameTarget.scrollIntoView({container: 'nearest'});
+ assert_equals(frameDoc.scrollingElement.scrollTop, 400, 'frame scrollingElement scrollTop');
+ assert_equals(inner.scrollTop, 0, '#inner scrollTop');
+ assert_equals(outer.scrollTop, 0, '#outer scrollTop');
+}, `scrollIntoView({container: 'nearest'}) doesn't propagate to outer frames`);
+
+</script>
diff --git a/tests/wpt/tests/css/cssom-view/table-client-props.html b/tests/wpt/tests/css/cssom-view/table-client-props.html
index 4af06d6bf71..2895bebebbf 100644
--- a/tests/wpt/tests/css/cssom-view/table-client-props.html
+++ b/tests/wpt/tests/css/cssom-view/table-client-props.html
@@ -43,6 +43,19 @@
<tr><td>`,
26, 34,
"Table with collapsed border" ],
+ [ `<div style="display: flex">
+ <table style="width: 20px; height: 30px;
+ border-width: 1px 2px 3px 4px; border-style: solid;
+ border-collapse: separate; box-sizing: content-box">`,
+ 26, 34,
+ "Flex-level table with separated border" ],
+ [ `<div style="display: flex">
+ <table style="width: 20px; height: 30px;
+ border-width: 2px 4px 6px 8px; border-style: solid;
+ border-collapse: collapse; box-sizing: content-box">
+ <tr><td>`,
+ 26, 34,
+ "Flex-level table with collapsed border" ],
[ `<table>
<caption style="width: 40px; height: 50px; padding: 1px 2px 3px 4px">`,
46, 54,
diff --git a/tests/wpt/tests/css/filter-effects/backdrop-filter-scale-transform-ref.html b/tests/wpt/tests/css/filter-effects/backdrop-filter-scale-transform-ref.html
new file mode 100644
index 00000000000..d7faa0e97a1
--- /dev/null
+++ b/tests/wpt/tests/css/filter-effects/backdrop-filter-scale-transform-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<style>
+ #container {
+ position: relative;
+ width: 200px;
+ height: 200px;
+ transform: scale(1.1);
+ }
+
+ #backdropfilter {
+ background: black;
+ position: relative;
+ width: 100%;
+ height: 100%;
+ }
+</style>
+<div id=container>
+ <div id=backdropfilter></div>
+</div>
+
diff --git a/tests/wpt/tests/css/filter-effects/backdrop-filter-scale-transform.html b/tests/wpt/tests/css/filter-effects/backdrop-filter-scale-transform.html
new file mode 100644
index 00000000000..813fb95d5ad
--- /dev/null
+++ b/tests/wpt/tests/css/filter-effects/backdrop-filter-scale-transform.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<link rel="author" href="mailto:chrishtr@chromium.org">
+<link rel="help" href="https://drafts.fxtf.org/filter-effects-2/#BackdropFilterProperty">
+<link rel="match" href="backdrop-filter-scale-transform-ref.html">
+<style>
+ #container {
+ position: relative;
+ width: 200px;
+ height: 200px;
+ transform: scale(1.1);
+ }
+
+ #backdropfilter {
+ backdrop-filter: invert(100%);
+ position: relative;
+ width: 100%;
+ height: 100%;
+ }
+</style>
+<div id=container>
+ <div id=backdropfilter></div>
+</div>
+
diff --git a/tests/wpt/tests/css/filter-effects/backdrop-filter-transform-popover-crash.html b/tests/wpt/tests/css/filter-effects/backdrop-filter-transform-popover-crash.html
new file mode 100644
index 00000000000..582f0c11965
--- /dev/null
+++ b/tests/wpt/tests/css/filter-effects/backdrop-filter-transform-popover-crash.html
@@ -0,0 +1,17 @@
+<lijnk rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1845184">
+<style>
+* {
+ min-width: 1952643579% !important;
+ transform: skew(0, 1.8turn) scale(1.5794431017420837e+38) translate(215mm, 62%) scaleY(55498.5);
+ border-top-left-radius: 98779902.05ch;
+ backdrop-filter: sepia() invert(870236770%) drop-shadow(16Q -79.35pc 125vw hsla(-1.93deg, 66%, 84%));
+}
+</style>
+<script>
+window.addEventListener("load", () => {
+ let a = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas")
+ document.documentElement.appendChild(a)
+ a.popover = "a"
+ a.togglePopover(true)
+})
+</script>
diff --git a/tests/wpt/tests/css/filter-effects/css-filters-opacity-hit-testing.html b/tests/wpt/tests/css/filter-effects/css-filters-opacity-hit-testing.html
new file mode 100644
index 00000000000..3b288fca4df
--- /dev/null
+++ b/tests/wpt/tests/css/filter-effects/css-filters-opacity-hit-testing.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Elements with 'opacity: 0' should respond to hit testing.</title>
+<link rel="author" title="Psychpsyo" href="psychpsyo@gmail.com">
+<link rel="help" href="https://drafts.fxtf.org/filter-effects/#FilterProperty">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ div {
+ width: 100px;
+ height: 100px;
+ opacity: 0;
+ }
+</style>
+<div></div>
+<script>
+ test(() => {
+ assert_equals(document.elementFromPoint(50, 50).tagName, "DIV", "element with 'opacity: 0' doesn't respond to hit testing");
+ });
+</script>
diff --git a/tests/wpt/tests/css/geometry/DOMMatrix-001.html b/tests/wpt/tests/css/geometry/DOMMatrix-001.html
index f578da076cb..3436e17ced0 100644
--- a/tests/wpt/tests/css/geometry/DOMMatrix-001.html
+++ b/tests/wpt/tests/css/geometry/DOMMatrix-001.html
@@ -113,6 +113,8 @@
['translateX (5px)',
'scale(2 2) translateX(5) translateY(5)',
'scale(2, 2), translateX(5) ,translateY(5)',
+ 'scale(sign(1em))',
+ 'scale(sibling-index())',
'translateX(5em)',
'translateX(5ex)',
'translateX(5ch)',
@@ -130,10 +132,14 @@
'translateX(5vmin)',
'translateX(5vmax)',
'translateX(5%)',
+ 'translateX(calc(10px * sign(1em - 10px)))',
+ 'translateX(calc(10px * sibling-index()))',
'rotate(5)',
'rotate(5, 5, 5)',
'rotate(5, 5px, 5px)',
'rotate(5deg, 5px, 5px)',
+ 'rotate(calc(5deg * sign(1em - 10px)))',
+ 'rotate(calc(5deg * sibling-index()))',
' ',
'/**/',
'\0',
diff --git a/tests/wpt/tests/css/mediaqueries/WEB_FEATURES.yml b/tests/wpt/tests/css/mediaqueries/WEB_FEATURES.yml
index 24027360aab..7f15b7fa475 100644
--- a/tests/wpt/tests/css/mediaqueries/WEB_FEATURES.yml
+++ b/tests/wpt/tests/css/mediaqueries/WEB_FEATURES.yml
@@ -25,3 +25,7 @@ features:
- name: update
files:
- update-media-feature.html
+- name: scripting
+ files:
+ - scripting.html
+ - scripting-*
diff --git a/tests/wpt/tests/docs/writing-tests/testdriver.md b/tests/wpt/tests/docs/writing-tests/testdriver.md
index 67757af63f2..e0fae529875 100644
--- a/tests/wpt/tests/docs/writing-tests/testdriver.md
+++ b/tests/wpt/tests/docs/writing-tests/testdriver.md
@@ -313,3 +313,11 @@ The module provides access to [Web Bluetooth](https://webbluetoothcg.github.io/w
.. js:autofunction:: test_driver.bidi.bluetooth.simulate_preconnected_peripheral
.. js:autofunction:: test_driver.bidi.bluetooth.request_device_prompt_updated
```
+
+### Emulation ###
+
+Emulation of browser APIs via [WebDriver BiDi Emulation](https://www.w3.org/TR/webdriver-bidi/#module-emulation).
+
+```eval_rst
+.. js:autofunction:: test_driver.bidi.emulation.set_geolocation_override
+```
diff --git a/tests/wpt/tests/dom/events/scrolling/overscroll-deltas.html b/tests/wpt/tests/dom/events/scrolling/overscroll-deltas.tentative.html
index e13e9f1cce5..e13e9f1cce5 100644
--- a/tests/wpt/tests/dom/events/scrolling/overscroll-deltas.html
+++ b/tests/wpt/tests/dom/events/scrolling/overscroll-deltas.tentative.html
diff --git a/tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-document.html b/tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-document.tentative.html
index c054ffca9c4..c054ffca9c4 100644
--- a/tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-document.html
+++ b/tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-document.tentative.html
diff --git a/tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-element-with-overscroll-behavior.html b/tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-element-with-overscroll-behavior.tentative.html
index 750080e6568..750080e6568 100644
--- a/tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-element-with-overscroll-behavior.html
+++ b/tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-element-with-overscroll-behavior.tentative.html
diff --git a/tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-scrolled-element.html b/tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-scrolled-element.tentative.html
index be4176df59d..be4176df59d 100644
--- a/tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-scrolled-element.html
+++ b/tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-scrolled-element.tentative.html
diff --git a/tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-window.html b/tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-window.tentative.html
index ef5ae3daef8..ef5ae3daef8 100644
--- a/tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-window.html
+++ b/tests/wpt/tests/dom/events/scrolling/overscroll-event-fired-to-window.tentative.html
diff --git a/tests/wpt/tests/dom/observable/WEB_FEATURES.yml b/tests/wpt/tests/dom/observable/WEB_FEATURES.yml
new file mode 100644
index 00000000000..3e872791165
--- /dev/null
+++ b/tests/wpt/tests/dom/observable/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: observable
+ files: "**"
diff --git a/tests/wpt/tests/domparsing/XMLSerializer-serializeToString.html b/tests/wpt/tests/domparsing/XMLSerializer-serializeToString.html
index 6c294e464a5..352a62c7d5d 100644
--- a/tests/wpt/tests/domparsing/XMLSerializer-serializeToString.html
+++ b/tests/wpt/tests/domparsing/XMLSerializer-serializeToString.html
@@ -256,6 +256,10 @@ test(function () {
root.setAttributeNS(XMLNS_URI, 'xmlns:foo', '');
assert_equals(serialize(root), '<root xmlns="" xmlns:foo=""/>');
}, 'Check if a prefix bound to an empty namespace URI ("no namespace") serialize');
+
+test(function() {
+ assert_equals(serialize(document.createAttribute("foobar")), "")
+}, 'Attribute nodes are serialized as the empty string')
</script>
</body>
</html>
diff --git a/tests/wpt/tests/element-timing/resources/element-timing-helpers.js b/tests/wpt/tests/element-timing/resources/element-timing-helpers.js
index 2c1bd195ea7..5ff01bfa120 100644
--- a/tests/wpt/tests/element-timing/resources/element-timing-helpers.js
+++ b/tests/wpt/tests/element-timing/resources/element-timing-helpers.js
@@ -19,7 +19,7 @@ function checkElementInternal(entry, expectedUrl, expectedIdentifier, expectedID
assert_greater_than_equal(entry.paintTime, beforeRender, 'paintTime should represent the time when the UA started painting');
// PaintTimingMixin
- if ("presentationTime" in entry) {
+ if ("presentationTime" in entry && entry.presentationTime !== null) {
assert_greater_than(entry.presentationTime, entry.paintTime);
assert_equals(entry.presentationTime, entry.renderTime);
} else {
diff --git a/tests/wpt/tests/event-timing/orphan-keydown.html b/tests/wpt/tests/event-timing/orphan-keydown.html
new file mode 100644
index 00000000000..0ab105d4d72
--- /dev/null
+++ b/tests/wpt/tests/event-timing/orphan-keydown.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<meta charset=utf-8 />
+<title>Event Timing: orphan keydown.</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/resources/testdriver.js></script>
+<script src=/resources/testdriver-actions.js></script>
+<script src=/resources/testdriver-vendor.js></script>
+<script src=resources/event-timing-test-utils.js></script>
+
+<body>
+ <button id='target'>Click me</button>
+
+ <script>
+ let observedEntries = [];
+ const map = new Map();
+ const events = ['keydown'];
+
+ promise_test(async t => {
+ assert_implements(window.PerformanceEventTiming, 'Event Timing is not supported.');
+
+ const callback = (entryList) => {observedEntries = observedEntries.concat(entryList.getEntries().filter(filterAndAddToMap(events, map))); };
+ const readyToResolve = () => { return observedEntries.length >= 1; };
+ const observerPromise = createPerformanceObserverPromise(['event'], callback, readyToResolve);
+
+ await interactAndObserve('orphan-keydown', document.getElementById('target'), observerPromise);
+
+ assert_equals(observedEntries.length, 1, "Keydown without a keyup should be fired properly.");
+ assert_greater_than(map.get('keydown'), 0, "Should have a non-trivial interactionId.")
+ }, "Event Timing: Orphan keydown should be measured as an interaction.");
+ </script>
+</body>
+</html>
diff --git a/tests/wpt/tests/event-timing/resources/event-timing-test-utils.js b/tests/wpt/tests/event-timing/resources/event-timing-test-utils.js
index 59c78ebf39c..afad67bb6fc 100644
--- a/tests/wpt/tests/event-timing/resources/event-timing-test-utils.js
+++ b/tests/wpt/tests/event-timing/resources/event-timing-test-utils.js
@@ -554,6 +554,17 @@ async function interactAndObserve(interactionType, target, observerPromise, key
interactionPromise = textSelectionAndBlockMain(target, 30);
break;
}
+ case 'orphan-keydown': {
+ addListeners(target, ['keydown']);
+ interactionPromise = new test_driver.Actions()
+ .pointerMove(0, 0, {origin: target})
+ .pointerDown()
+ .pointerUp()
+ .addTick()
+ .keyDown('a')
+ .send();
+ break;
+ }
}
return Promise.all([interactionPromise, observerPromise]);
}
diff --git a/tests/wpt/tests/fedcm/fedcm-disconnect.sub.https.html b/tests/wpt/tests/fedcm/fedcm-disconnect.sub.https.html
index 2ea2d4a2599..04fcd272dcf 100644
--- a/tests/wpt/tests/fedcm/fedcm-disconnect.sub.https.html
+++ b/tests/wpt/tests/fedcm/fedcm-disconnect.sub.https.html
@@ -13,6 +13,7 @@ import {fedcm_test,
mark_signed_in,
disconnect_options,
fedcm_get_and_select_first_account,
+ fedcm_select_account_promise,
request_options_with_mediation_required,
alt_manifest_origin,
alt_request_options_with_mediation_required,
@@ -82,4 +83,20 @@ fedcm_test(async t => {
await IdentityCredential.disconnect(disconnect_options("1"));
return IdentityCredential.disconnect(alt_disconnect_options("2"));
}, 'Disconnect is bound to each IDP');
+
+fedcm_test(async t => {
+ await mark_signed_in(alt_manifest_origin);
+ // Get at least one connected account that can be disconnected.
+ await fedcm_get_and_select_first_account(t, alt_request_options_with_mediation_required());
+
+ // Pending get request.
+ const credentialPromise = navigator.credentials.get(alt_request_options_with_mediation_required());
+
+ // Disconnect the one connected account.
+ await IdentityCredential.disconnect(alt_disconnect_options("1234"));
+
+ // Select an account to resolve the pending get request.
+ fedcm_select_account_promise(t, 0);
+ return credentialPromise;
+}, 'Test that disconnect succeeds when there is a pending get request and the get request succeeds after the disconnect');
</script>
diff --git a/tests/wpt/tests/fetch/api/request/WEB_FEATURES.yml b/tests/wpt/tests/fetch/api/request/WEB_FEATURES.yml
new file mode 100644
index 00000000000..69b2ea582a2
--- /dev/null
+++ b/tests/wpt/tests/fetch/api/request/WEB_FEATURES.yml
@@ -0,0 +1,4 @@
+features:
+- name: fetch-priority
+ files:
+ - request-init-priority.any.js
diff --git a/tests/wpt/tests/fetch/http-cache/pragma-no-cache-with-cache-control.html b/tests/wpt/tests/fetch/http-cache/pragma-no-cache-with-cache-control.html
new file mode 100644
index 00000000000..19a80fe5edc
--- /dev/null
+++ b/tests/wpt/tests/fetch/http-cache/pragma-no-cache-with-cache-control.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>HTTP Cache: Cache-Control with Pragma: no-cache</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+promise_test(async t => {
+ // According to https://www.rfc-editor.org/rfc/rfc9111.html#name-pragma
+ // the pragma header is deprecated.
+ // When there's a mismatch between pragma and Cache-Control then the latter
+ // should be respected, and the resource should be cached.
+ const url = 'resources/cached_pragma_rand.py'
+
+ // First fetch to populate the cache
+ const response1 = await fetch(url, { cache: 'default' });
+ assert_true(response1.ok, 'First fetch should succeed');
+ const text1 = await response1.text();
+
+ // Second fetch should be served from cache
+ const response2 = await fetch(url, { cache: 'default' });
+ assert_true(response2.ok, 'Second fetch should succeed');
+ const text2 = await response2.text();
+
+ assert_equals(text1, text2, 'Responses should be identical, indicating caching');
+}, 'Response with Cache-Control: max-age=2592000, public and Pragma: no-cache should be cached');
+</script>
+</body>
diff --git a/tests/wpt/tests/fetch/http-cache/resources/cached_pragma_rand.py b/tests/wpt/tests/fetch/http-cache/resources/cached_pragma_rand.py
new file mode 100644
index 00000000000..18c7d25159f
--- /dev/null
+++ b/tests/wpt/tests/fetch/http-cache/resources/cached_pragma_rand.py
@@ -0,0 +1,14 @@
+def main(request, response):
+ # Disable non-standard XSS protection
+ response.headers.set(b"X-XSS-Protection", b"0")
+ response.headers.set(b"Content-Type", b"text/html")
+
+ # Set caching headers
+ # According to rfc9111 Pragma: no-cache is deprecated, so we expect
+ # Cache-Control to take precedence when there's a mismatch.
+ response.headers.set(b"Cache-Control", b"max-age=2592000, public")
+ response.headers.set(b"Pragma", b"no-cache")
+
+ # Include a timestamp to verify caching behavior
+ import time
+ response.content = f"Timestamp: {time.time()}".encode('utf-8')
diff --git a/tests/wpt/tests/fetch/local-network-access/META.yml b/tests/wpt/tests/fetch/local-network-access/META.yml
new file mode 100644
index 00000000000..4c5c6983ed0
--- /dev/null
+++ b/tests/wpt/tests/fetch/local-network-access/META.yml
@@ -0,0 +1,5 @@
+spec: https://wicg.github.io/local-network-access/
+suggested_reviewers:
+ - cthomp
+ - camillelamy
+ - hchao
diff --git a/tests/wpt/tests/fetch/local-network-access/README.md b/tests/wpt/tests/fetch/local-network-access/README.md
new file mode 100644
index 00000000000..95066cdcd0b
--- /dev/null
+++ b/tests/wpt/tests/fetch/local-network-access/README.md
@@ -0,0 +1,11 @@
+# Local Network Access tests
+
+This directory contains tests for Local Network Access' integration with
+the Fetch specification.
+
+See also:
+
+* [Explainer](https://github.com/explainers-by-googlers/local-network-access)
+
+Local Network Access replaced [Private Network
+Access](https://wicg.github.io/local-network-access/).
diff --git a/tests/wpt/tests/fetch/local-network-access/fetch.tentative.https.html b/tests/wpt/tests/fetch/local-network-access/fetch.tentative.https.html
new file mode 100644
index 00000000000..9c591f309b7
--- /dev/null
+++ b/tests/wpt/tests/fetch/local-network-access/fetch.tentative.https.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>LNA Fetch tests: HTTPS </title>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/support.sub.js"></script>
+<script>
+ "use strict";
+
+ promise_test(t => {
+ const sourceUrl = resolveUrl("resources/fetch-private.html",
+ sourceResolveOptions({ server: Server.HTTPS_PUBLIC }));
+
+ function checkResult(evt) {
+ checkTestResult(evt.data, FetchTestResult.SUCCESS);
+ t.done();
+ }
+
+ const promise = new Promise((resolve) => {
+ window.addEventListener('message', resolve, {once: true});
+ }).then(checkResult);
+ const popup = window.open(sourceUrl);
+ t.add_cleanup(() => popup.close());
+
+ return promise;
+ }, 'LNA Public to private with permission');
+
+ promise_test(t => {
+ // TODO(crbug.com/406991278): consider moving permission url param into
+ // options
+ const sourceUrl = resolveUrl("resources/fetch-private.html?permission=denied",
+ sourceResolveOptions({ server: Server.HTTPS_PUBLIC }));
+
+ function checkResult(evt) {
+ checkTestResult(evt.data, FetchTestResult.FAILURE);
+ t.done();
+ }
+
+ const promise = new Promise((resolve) => {
+ window.addEventListener('message', resolve, {once: true});
+ }).then(checkResult);
+ const popup = window.open(sourceUrl);
+ t.add_cleanup(() => popup.close());
+
+ return promise;
+ }, 'LNA Public to private with permission denied');
+</script>
+</body>
diff --git a/tests/wpt/tests/fetch/local-network-access/resources/fetch-private.html b/tests/wpt/tests/fetch/local-network-access/resources/fetch-private.html
new file mode 100644
index 00000000000..b96a207ec33
--- /dev/null
+++ b/tests/wpt/tests/fetch/local-network-access/resources/fetch-private.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Fetch Private resource</title>
+
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="support.sub.js"></script>
+<script>
+"use strict";
+
+// Set the 'local-network-access' permission then attempt to fetch a resource
+// in the private address space.
+//
+// By default, 'local-network-access' permission is set to 'granted'. This can
+// be changed by passing in a different value via the 'permission' URL parameter.
+// Valid values:
+//
+// * granted
+// * denied
+// * prompt
+Promise.resolve().then(async () => {
+
+ const window_url = new URL(window.location.href);
+ let permission_value = 'granted';
+ if (window_url.searchParams.has('permission')) {
+ permission_value = window_url.searchParams.get('permission');
+ }
+
+ test_driver.set_test_context(opener);
+ await test_driver.set_permission({ name: 'local-network-access' }, permission_value);
+
+ const target = {
+ server: Server.HTTPS_PRIVATE,
+ behavior: { response: ResponseBehavior.allowCrossOrigin() },
+ };
+ const targetUrl = resolveTargetUrl(target);
+
+ fetch(targetUrl)
+ .then(async function(response) {
+ const body = await response.text();
+ const message = {
+ ok: response.ok,
+ type: response.type,
+ body: body,
+ };
+ opener.postMessage(message, "*");
+ })
+ .catch(error => {
+ opener.postMessage({ error: error.toString() }, "*");
+ });
+});
+</script>
diff --git a/tests/wpt/tests/fetch/local-network-access/resources/support.sub.js b/tests/wpt/tests/fetch/local-network-access/resources/support.sub.js
new file mode 100644
index 00000000000..774e34d0a6f
--- /dev/null
+++ b/tests/wpt/tests/fetch/local-network-access/resources/support.sub.js
@@ -0,0 +1,186 @@
+// Maps protocol (without the trailing colon) and address space to port.
+const SERVER_PORTS = {
+ "http": {
+ "local": {{ports[http][0]}},
+ "private": {{ports[http-private][0]}},
+ "public": {{ports[http-public][0]}},
+ },
+ "https": {
+ "local": {{ports[https][0]}},
+ "other-local": {{ports[https][1]}},
+ "private": {{ports[https-private][0]}},
+ "public": {{ports[https-public][0]}},
+ },
+ "ws": {
+ "local": {{ports[ws][0]}},
+ },
+ "wss": {
+ "local": {{ports[wss][0]}},
+ },
+};
+
+// A `Server` is a web server accessible by tests. It has the following shape:
+//
+// {
+// addressSpace: the IP address space of the server ("local", "private" or
+// "public"),
+// name: a human-readable name for the server,
+// port: the port on which the server listens for connections,
+// protocol: the protocol (including trailing colon) spoken by the server,
+// }
+//
+// Constants below define the available servers, which can also be accessed
+// programmatically with `get()`.
+class Server {
+ // Maps the given `protocol` (without a trailing colon) and `addressSpace` to
+ // a server. Returns null if no such server exists.
+ static get(protocol, addressSpace) {
+ const ports = SERVER_PORTS[protocol];
+ if (ports === undefined) {
+ return null;
+ }
+
+ const port = ports[addressSpace];
+ if (port === undefined) {
+ return null;
+ }
+
+ return {
+ addressSpace,
+ name: `${protocol}-${addressSpace}`,
+ port,
+ protocol: protocol + ':',
+ };
+ }
+
+ static HTTP_LOCAL = Server.get("http", "local");
+ static HTTP_PRIVATE = Server.get("http", "private");
+ static HTTP_PUBLIC = Server.get("http", "public");
+ static HTTPS_LOCAL = Server.get("https", "local");
+ static OTHER_HTTPS_LOCAL = Server.get("https", "other-local");
+ static HTTPS_PRIVATE = Server.get("https", "private");
+ static HTTPS_PUBLIC = Server.get("https", "public");
+ static WS_LOCAL = Server.get("ws", "local");
+ static WSS_LOCAL = Server.get("wss", "local");
+};
+
+// Resolves a URL relative to the current location, returning an absolute URL.
+//
+// `url` specifies the relative URL, e.g. "foo.html" or "http://foo.example".
+// `options`, if defined, should have the following shape:
+//
+// {
+// // Optional. Overrides the protocol of the returned URL.
+// protocol,
+//
+// // Optional. Overrides the port of the returned URL.
+// port,
+//
+// // Extra headers.
+// headers,
+//
+// // Extra search params.
+// searchParams,
+// }
+//
+function resolveUrl(url, options) {
+ const result = new URL(url, window.location);
+ if (options === undefined) {
+ return result;
+ }
+
+ const { port, protocol, headers, searchParams } = options;
+ if (port !== undefined) {
+ result.port = port;
+ }
+ if (protocol !== undefined) {
+ result.protocol = protocol;
+ }
+ if (headers !== undefined) {
+ const pipes = [];
+ for (key in headers) {
+ pipes.push(`header(${key},${headers[key]})`);
+ }
+ result.searchParams.append("pipe", pipes.join("|"));
+ }
+ if (searchParams !== undefined) {
+ for (key in searchParams) {
+ result.searchParams.append(key, searchParams[key]);
+ }
+ }
+
+ return result;
+}
+
+// Computes options to pass to `resolveUrl()` for a source document's URL.
+//
+// `server` identifies the server from which to load the document.
+// `treatAsPublic`, if set to true, specifies that the source document should
+// be artificially placed in the `public` address space using CSP.
+function sourceResolveOptions({ server, treatAsPublic }) {
+ const options = {...server};
+ if (treatAsPublic) {
+ options.headers = { "Content-Security-Policy": "treat-as-public-address" };
+ }
+ return options;
+}
+
+// Computes the URL of a target handler configured with the given options.
+//
+// `server` identifies the server from which to load the resource.
+// `behavior` specifies the behavior of the target server. It may contain:
+// - `response`: The result of calling one of `ResponseBehavior`'s methods.
+// - `redirect`: A URL to which the target should redirect GET requests.
+function resolveTargetUrl({ server, behavior }) {
+ if (server === undefined) {
+ throw new Error("no server specified.");
+ }
+ const options = {...server};
+ if (behavior) {
+ const { response, redirect } = behavior;
+ options.searchParams = {
+ ...response,
+ };
+ if (redirect !== undefined) {
+ options.searchParams.redirect = redirect;
+ }
+ }
+
+ return resolveUrl("target.py", options);
+}
+
+// Methods generate behavior specifications for how `resources/target.py`
+// should behave upon receiving a regular (non-preflight) request.
+const ResponseBehavior = {
+ // The response should succeed without CORS headers.
+ default: () => ({}),
+
+ // The response should succeed with CORS headers.
+ allowCrossOrigin: () => ({ "final-headers": "cors" }),
+};
+
+const FetchTestResult = {
+ SUCCESS: {
+ ok: true,
+ body: "success",
+ },
+ OPAQUE: {
+ ok: false,
+ type: "opaque",
+ body: "",
+ },
+ FAILURE: {
+ error: "TypeError: Failed to fetch",
+ },
+};
+
+// Helper function for checking results from fetch tests.
+function checkTestResult(actual, expected) {
+ assert_equals(actual.error, expected.error, "error mismatch");
+ assert_equals(actual.ok, expected.ok, "response ok mismatch");
+ assert_equals(actual.body, expected.body, "response body mismatch");
+
+ if (expected.type !== undefined) {
+ assert_equals(type, expected.type, "response type mismatch");
+ }
+}
diff --git a/tests/wpt/tests/fetch/local-network-access/resources/target.py b/tests/wpt/tests/fetch/local-network-access/resources/target.py
new file mode 100644
index 00000000000..eabcdd47517
--- /dev/null
+++ b/tests/wpt/tests/fetch/local-network-access/resources/target.py
@@ -0,0 +1,105 @@
+# This endpoint responds to requests for a target of a (possible) LNA request.
+#
+# Its behavior can be configured with various search/GET parameters, all of
+# which are optional:
+#
+# - final-headers: Valid values are:
+# - cors: this endpoint responds with valid CORS headers to CORS-enabled
+# non-preflight requests. These should be sufficient for non-preflighted
+# CORS-enabled requests to succeed.
+# - sw: this endpoint responds with a valid Service-Worker header to allow
+# for the request to serve as a Service worker script resource. This is
+# only valid in conjunction with the cors value above.
+# - unspecified: this endpoint responds with no CORS headers to non-preflight
+# requests. This should fail CORS-enabled requests, but be sufficient for
+# no-CORS requests.
+#
+# The following parameters only affect non-preflight responses:
+#
+# - redirect: If set, the response code is set to 301 and the `Location`
+# response header is set to this value.
+# - mime-type: If set, the `Content-Type` response header is set to this value.
+# - file: Specifies a path (relative to this file's directory) to a file. If
+# set, the response body is copied from this file.
+# - random-js-prefix: If set to any value, the response body is prefixed with
+# a Javascript comment line containing a random value. This is useful in
+# service worker tests, since service workers are only updated if the new
+# script is not byte-for-byte identical with the old script.
+# - body: If set and `file` is not, the response body is set to this value.
+#
+
+import os
+import random
+
+from wptserve.utils import isomorphic_encode
+
+_ACAO = ("Access-Control-Allow-Origin", "*")
+_ACAH = ("Access-Control-Allow-Headers", "Service-Worker")
+
+def _get_response_headers(method, mode, origin):
+ acam = ("Access-Control-Allow-Methods", method)
+
+ if mode == b"cors":
+ return [acam, _ACAO]
+
+ if mode == b"cors+sw":
+ return [acam, _ACAO, _ACAH]
+
+ if mode == b"navigation":
+ return [
+ acam,
+ ("Access-Control-Allow-Origin", origin),
+ ("Access-Control-Allow-Credentials", "true"),
+ ]
+
+ return []
+
+
+def _is_loaded_in_fenced_frame(request):
+ return request.GET.get(b"is-loaded-in-fenced-frame")
+
+def _final_response_body(request):
+ file_name = None
+ if file_name is None:
+ file_name = request.GET.get(b"file")
+ if file_name is None:
+ return request.GET.get(b"body") or "success"
+
+ prefix = b""
+ if request.GET.get(b"random-js-prefix"):
+ value = random.randint(0, 1000000000)
+ prefix = isomorphic_encode("// Random value: {}\n\n".format(value))
+
+ path = os.path.join(os.path.dirname(isomorphic_encode(__file__)), file_name)
+ with open(path, 'rb') as f:
+ contents = f.read()
+
+ return prefix + contents
+
+def _handle_final_request(request, response):
+ mode = request.GET.get(b"final-headers")
+ origin = request.headers.get("Origin")
+ headers = _get_response_headers(request.method, mode, origin)
+
+ redirect = request.GET.get(b"redirect")
+ if redirect is not None:
+ headers.append(("Location", redirect))
+ return (301, headers, b"")
+
+ mime_type = request.GET.get(b"mime-type")
+ if mime_type is not None:
+ headers.append(("Content-Type", mime_type),)
+
+ if _is_loaded_in_fenced_frame(request):
+ headers.append(("Supports-Loading-Mode", "fenced-frame"))
+
+ body = _final_response_body(request)
+ return (headers, body)
+
+
+def main(request, response):
+ try:
+ return _handle_final_request(request, response)
+ except BaseException as e:
+ # Surface exceptions to the client, where they show up as assertion errors.
+ return (500, [("X-exception", str(e))], "exception: {}".format(e))
diff --git a/tests/wpt/tests/file-system-access/WEB_FEATURES.yml b/tests/wpt/tests/file-system-access/WEB_FEATURES.yml
index 45e40acba1d..7faf67952b5 100644
--- a/tests/wpt/tests/file-system-access/WEB_FEATURES.yml
+++ b/tests/wpt/tests/file-system-access/WEB_FEATURES.yml
@@ -1,3 +1,7 @@
features:
- name: file-system-access
files: "**"
+- name: origin-private-file-system
+ files:
+ - getDirectory.https.any.js
+ - opaque-origin.https.window.js
diff --git a/tests/wpt/tests/html/browsers/windows/auxiliary-browsing-contexts/named-lookup-noopener.html b/tests/wpt/tests/html/browsers/windows/auxiliary-browsing-contexts/named-lookup-noopener.html
new file mode 100644
index 00000000000..91cf6ae6ee2
--- /dev/null
+++ b/tests/wpt/tests/html/browsers/windows/auxiliary-browsing-contexts/named-lookup-noopener.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<title>Named lookup does not work with noopener</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"></script>
+<link rel="help" href="https://github.com/whatwg/html/issues/11291">
+
+<body>
+<script>
+
+promise_test(async t => {
+ const windowName = 'named-window';
+
+ const rcHelper = new RemoteContextHelper();
+ const rcPopup1 = await rcHelper.addWindow(undefined, { target: windowName, features: 'noopener' });
+ const rcPopup2 = await rcHelper.addWindow(undefined, { target: windowName, features: 'noopener' });
+
+ // If both scripts execute, then the windows are separate, and the test passes.
+ // If the window is reused, then rcPopup1 will not be able to execute script, and so the test will timeout.
+
+ assert_equals(await rcPopup1.executeScript(() => window.name), windowName);
+ assert_equals(await rcPopup2.executeScript(() => window.name), windowName);
+}, 'Two noopener window.open() calls create separate windows');
+
+promise_test(async t => {
+ const windowName = 'named-window-2';
+
+ function executorCreator(url) {
+ const a = document.createElement("a");
+ a.href = url;
+ a.rel = 'noopener';
+ a.target = windowName;
+ document.body.append(a);
+ a.click();
+ }
+
+ const rcHelper = new RemoteContextHelper();
+ const rcPopup1 = await rcHelper.createContext({ executorCreator });
+ const rcPopup2 = await rcHelper.createContext({ executorCreator });
+
+ // If both scripts execute, then the windows are separate, and the test passes.
+ // If the window is reused, then rcPopup1 will not be able to execute script, and so the test will timeout.
+
+ assert_equals(await rcPopup1.executeScript(() => window.name), windowName);
+ assert_equals(await rcPopup2.executeScript(() => window.name), windowName);
+}, 'Two rel=noopener <a href> clicks create separate windows');
+</script>
diff --git a/tests/wpt/tests/html/canvas/element/manual/wide-gamut-canvas/WEB_FEATURES.yml b/tests/wpt/tests/html/canvas/element/manual/wide-gamut-canvas/WEB_FEATURES.yml
new file mode 100644
index 00000000000..b4d11212a6c
--- /dev/null
+++ b/tests/wpt/tests/html/canvas/element/manual/wide-gamut-canvas/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: canvas-2d-color-management
+ files: "**"
diff --git a/tests/wpt/tests/html/canvas/element/path-objects/WEB_FEATURES.yml b/tests/wpt/tests/html/canvas/element/path-objects/WEB_FEATURES.yml
new file mode 100644
index 00000000000..1bbef5e88d6
--- /dev/null
+++ b/tests/wpt/tests/html/canvas/element/path-objects/WEB_FEATURES.yml
@@ -0,0 +1,4 @@
+features:
+- name: canvas-roundrect
+ files:
+ - 2d.path.roundrect.*
diff --git a/tests/wpt/tests/html/canvas/element/wide-gamut-canvas/WEB_FEATURES.yml b/tests/wpt/tests/html/canvas/element/wide-gamut-canvas/WEB_FEATURES.yml
new file mode 100644
index 00000000000..b4d11212a6c
--- /dev/null
+++ b/tests/wpt/tests/html/canvas/element/wide-gamut-canvas/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: canvas-2d-color-management
+ files: "**"
diff --git a/tests/wpt/tests/html/canvas/offscreen/manual/wide-gamut-canvas/WEB_FEATURES.yml b/tests/wpt/tests/html/canvas/offscreen/manual/wide-gamut-canvas/WEB_FEATURES.yml
new file mode 100644
index 00000000000..b4d11212a6c
--- /dev/null
+++ b/tests/wpt/tests/html/canvas/offscreen/manual/wide-gamut-canvas/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: canvas-2d-color-management
+ files: "**"
diff --git a/tests/wpt/tests/html/canvas/offscreen/path-objects/WEB_FEATURES.yml b/tests/wpt/tests/html/canvas/offscreen/path-objects/WEB_FEATURES.yml
new file mode 100644
index 00000000000..1bbef5e88d6
--- /dev/null
+++ b/tests/wpt/tests/html/canvas/offscreen/path-objects/WEB_FEATURES.yml
@@ -0,0 +1,4 @@
+features:
+- name: canvas-roundrect
+ files:
+ - 2d.path.roundrect.*
diff --git a/tests/wpt/tests/html/canvas/offscreen/wide-gamut-canvas/WEB_FEATURES.yml b/tests/wpt/tests/html/canvas/offscreen/wide-gamut-canvas/WEB_FEATURES.yml
new file mode 100644
index 00000000000..b4d11212a6c
--- /dev/null
+++ b/tests/wpt/tests/html/canvas/offscreen/wide-gamut-canvas/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: canvas-2d-color-management
+ files: "**"
diff --git a/tests/wpt/tests/html/dom/reflection.js b/tests/wpt/tests/html/dom/reflection.js
index b2c3b30aae3..eeecd450fca 100644
--- a/tests/wpt/tests/html/dom/reflection.js
+++ b/tests/wpt/tests/html/dom/reflection.js
@@ -967,6 +967,7 @@ ReflectionTests.reflects = function(data, idlName, idlObj, domName, domObj) {
"previous value", "getAttribute()");
ReflectionHarness.assertEquals(idlObj[idlName], previousIdl, "IDL get");
} else {
+ var previousValue = domObj.getAttribute(domName);
idlObj[idlName] = idlTests[i];
if (data.type == "boolean") {
// Special case yay
@@ -976,6 +977,11 @@ ReflectionTests.reflects = function(data, idlName, idlObj, domName, domObj) {
var expected = idlDomExpected[i] + "";
if (data.isNullable && idlDomExpected[i] === null) {
expected = null;
+ } else if (idlName == "nonce") {
+ // nonce doesn't reflect the value, as per /content-security-policy/nonce-hiding/
+ // tests that confirm that retrieving the nonce value post IDL change does not
+ // reflect back to the attribute (for security reasons)
+ expected = previousValue;
}
ReflectionHarness.assertEquals(domObj.getAttribute(domName), expected,
"getAttribute()");
diff --git a/tests/wpt/tests/html/editing/dnd/platform/pointerdown-add-display-none.html b/tests/wpt/tests/html/editing/dnd/platform/pointerdown-add-display-none.html
new file mode 100644
index 00000000000..653944e155b
--- /dev/null
+++ b/tests/wpt/tests/html/editing/dnd/platform/pointerdown-add-display-none.html
@@ -0,0 +1,65 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<head>
+<title>Test dragging still occurs when pointerdown adds display:none to the dragged element</title>
+<style>
+.dragging {
+ display: none;
+}
+
+#dragBox {
+ width: 200px;
+ height: 200px;
+ background-color: #4CAF50;
+ color: white;
+ border-radius: 8px;
+ cursor: grab;
+ user-select: none;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+.inner {
+ background: rgba(255, 255, 255, 0.2);
+ padding: 10px;
+ margin-top: 10px;
+ border-radius: 4px;
+}
+</style>
+</head>
+<body>
+<div id="dragBox" draggable="true">
+ Drag me
+ <div class="inner" id="innerButton">Click or press here</div>
+</div>
+<script>
+
+promise_test(function() {
+ return new Promise(r => {
+ innerButton.addEventListener("pointerdown", function() {
+ innerButton.classList.add("dragging");
+ });
+
+ dragBox.addEventListener("dragstart", function(e) {
+ assert_equals(e.target, dragBox);
+ r();
+ });
+
+ const buttonRect = innerButton.getBoundingClientRect();
+ new test_driver.Actions()
+ .pointerMove(0, 0, {origin: innerButton})
+ .pointerDown()
+ .pointerMove(buttonRect.left + 10, buttonRect.top + 10)
+ .pointerUp()
+ .send();
+ });
+}, "dragstart should still fire when the dragged element gets display:none in its pointerdown");
+</script>
+</body>
+</html>
diff --git a/tests/wpt/tests/html/embedded-content/the-img-element/WEB_FEATURES.yml b/tests/wpt/tests/html/embedded-content/the-img-element/WEB_FEATURES.yml
new file mode 100644
index 00000000000..5716baa5def
--- /dev/null
+++ b/tests/wpt/tests/html/embedded-content/the-img-element/WEB_FEATURES.yml
@@ -0,0 +1,4 @@
+features:
+- name: fetch-priority
+ files:
+ - attr-img-fetchpriority.html
diff --git a/tests/wpt/tests/html/scripting/the-script-element/WEB_FEATURES.yml b/tests/wpt/tests/html/scripting/the-script-element/WEB_FEATURES.yml
new file mode 100644
index 00000000000..864bd3398ee
--- /dev/null
+++ b/tests/wpt/tests/html/scripting/the-script-element/WEB_FEATURES.yml
@@ -0,0 +1,4 @@
+features:
+- name: fetch-priority
+ files:
+ - attr-script-fetchpriority.html
diff --git a/tests/wpt/tests/html/semantics/forms/the-select-element/customizable-select/select-input-keyboard-behavior.tentative.html b/tests/wpt/tests/html/semantics/forms/the-select-element/customizable-select/select-input-keyboard-behavior.tentative.html
new file mode 100644
index 00000000000..bf3fbab9d18
--- /dev/null
+++ b/tests/wpt/tests/html/semantics/forms/the-select-element/customizable-select/select-input-keyboard-behavior.tentative.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://issues.chromium.org/issues/402429384">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+
+<style>
+select, ::picker(select) {
+ appearance: base-select;
+}
+</style>
+
+<select>
+ <div id=inputwrapper></div>
+ <option class=one>one</option>
+ <option class=two>two</option>
+</select>
+
+<script>
+const select = document.querySelector('select');
+const optionOne = select.querySelector('option.one');
+const optionTwo = select.querySelector('option.two');
+const input = document.createElement('input');
+document.getElementById('inputwrapper').appendChild(input);
+
+function pressKey(key) {
+ return (new test_driver.Actions()
+ .keyDown(key)
+ .keyUp(key))
+ .send();
+}
+const spaceKey = '\uE00D';
+const arrowDown = '\uE015';
+const arrowUp = '\uE013';
+
+promise_test(async () => {
+ assert_equals(getComputedStyle(select).appearance, 'base-select',
+ 'appearance: base-select must be supported for this test to run.');
+
+ select.focus();
+ await pressKey(spaceKey);
+ assert_true(select.matches(':open'),
+ 'select should be open after pressing space.');
+ assert_equals(document.activeElement, input,
+ 'input should be focused after opening select.');
+
+ await pressKey(arrowDown);
+ assert_equals(document.activeElement, optionOne,
+ 'first option should be focused after arrow down from input.');
+
+ await pressKey(arrowUp);
+ assert_equals(document.activeElement, input,
+ 'input should be focused after arrow up from first option.');
+}, 'Keyboard behavior of <input> in <select>.');
+</script>
diff --git a/tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-keyboard-invalidation-ref.html b/tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-keyboard-invalidation-ref.html
index 0ba07b16f49..9d520943d5f 100644
--- a/tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-keyboard-invalidation-ref.html
+++ b/tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-keyboard-invalidation-ref.html
@@ -7,21 +7,24 @@
<button id=hasinterest>Button</button>
<button class=otherselector>Button</button>
<button class=otherselector>Button</button>
-<div id=target>Target</div>
+<div popover id=target>Target</div>
<style>
#hasinterest {
- background-color: red;
- outline: solid 3px -webkit-focus-ring-color;
+ background-color: purple;
}
.otherselector {
background-color: green;
}
#target {
background-color: yellow;
+ inset:auto;
+ top:50px;
+ left:0;
}
</style>
<script>
+ target.showPopover();
hasinterest.focus();
</script>
diff --git a/tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-keyboard-invalidation.tentative.html b/tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-keyboard-invalidation.tentative.html
index 8c0ebd7a2cd..b6d0ff10932 100644
--- a/tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-keyboard-invalidation.tentative.html
+++ b/tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-keyboard-invalidation.tentative.html
@@ -13,7 +13,7 @@
<button id=b3 interesttarget=target>Button</button>
<button id=b4>Button</button>
<button id=b5>Button</button>
-<div id=target>Target</div>
+<div popover id=target>Target</div>
<style>
:has-interest {
@@ -22,18 +22,24 @@
:has-interest:has-partial-interest {
background-color: red;
}
- :has-partial-interest + button {
+ /* Test complicated combinators: */
+ :has-interest + button {
background-color: green;
}
:root:has(:has-interest) #b5 {
background-color: green;
}
- :target-of-interest:target-of-partial-interest {
+ :target-of-interest {
background-color: yellow;
}
[interesttarget] {
interest-target-delay: 0s;
}
+ #target {
+ inset:auto;
+ top:50px;
+ left:0;
+ }
</style>
<script>
diff --git a/tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-outline-appearance-ref.html b/tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-outline-appearance-ref.html
deleted file mode 100644
index 33b0cf66858..00000000000
--- a/tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-outline-appearance-ref.html
+++ /dev/null
@@ -1,9 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8" />
-<link rel="author" href="mailto:masonf@chromium.org">
-
-<button id=non_interesttarget_button>Interesttarget Button</button>
-
-<script>
- document.querySelector('#non_interesttarget_button').focus();
-</script>
diff --git a/tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-outline-appearance.tentative.html b/tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-outline-appearance.tentative.html
deleted file mode 100644
index 28cb2052a8d..00000000000
--- a/tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-outline-appearance.tentative.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<!DOCTYPE html>
-<meta charset="utf-8" />
-<link rel="author" href="mailto:masonf@chromium.org">
-<link rel="help" href="https://open-ui.org/components/interest-invokers.explainer/" />
-<link rel="mismatch" href="interesttarget-outline-appearance-ref.html">
-
-<button interesttarget=target>Interesttarget Button</button>
-
-<style>
- /* Outline style should apply even when the element doesn't have interest. */
- :has-interest { background-color: red; }
-</style>
-
-<script>
- document.querySelector('[interesttarget]').focus();
-</script>
diff --git a/tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-partial-interest.tentative.html b/tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-partial-interest.tentative.html
new file mode 100644
index 00000000000..bc7184d42d2
--- /dev/null
+++ b/tests/wpt/tests/html/semantics/the-button-element/interest-target/interesttarget-partial-interest.tentative.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<meta charset="utf-8" />
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://open-ui.org/components/interest-invokers.explainer/" />
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/invoker-utils.js"></script>
+
+<button id=invoker interesttarget=target>Button</button>
+<div id=target popover>Popover with <button id=contained>button</button></div>
+
+<style>
+ [interesttarget] {
+ interest-target-delay: 0s;
+ }
+</style>
+
+<script>
+ let expectInterest;
+ let expectPartial;
+ let eventCount = 0;
+ function checkInterest(e) {
+ const notStr = expectInterest ? "" : "not ";
+ const event = e instanceof Event ? `${e.type} event` : e;
+ assert_equals(invoker.matches(':has-interest'),expectInterest,`target should ${notStr}gain interest (${event})`);
+ assert_equals(invoker.matches(':has-partial-interest'),expectInterest&&expectPartial,`Interest should ${expectPartial ? "" : "not "}be partial (${event})`);
+ assert_equals(target.matches(':popover-open'),expectInterest,`popover should ${notStr}be open (${event})`);
+ ++eventCount;
+ }
+
+ promise_test(async function (t) {
+ invoker.addEventListener('focus',checkInterest);
+ target.addEventListener('interest',(e) => {
+ checkInterest(e);
+ expectInterest = true;
+ expectPartial = true;
+ });
+
+ expectInterest = false;
+ expectPartial = false;
+ await focusOn(invoker);
+ assert_equals(eventCount,2,'focus and interest should both have fired');
+ assert_true(expectInterest,'the interest event should set this');
+ checkInterest('before hot key');
+ await sendShowInterestHotkey();
+ expectPartial = false;
+ checkInterest('after hot key');
+ },'Partial interest timing should not be observable');
+</script>
diff --git a/tests/wpt/tests/html/semantics/the-link-element/WEB_FEATURES.yml b/tests/wpt/tests/html/semantics/the-link-element/WEB_FEATURES.yml
new file mode 100644
index 00000000000..6a444ee171a
--- /dev/null
+++ b/tests/wpt/tests/html/semantics/the-link-element/WEB_FEATURES.yml
@@ -0,0 +1,4 @@
+features:
+- name: fetch-priority
+ files:
+ - attr-link-fetchpriority.html
diff --git a/tests/wpt/tests/infrastructure/testdriver/bidi/emulation/set_geolocation_override.https.html b/tests/wpt/tests/infrastructure/testdriver/bidi/emulation/set_geolocation_override.https.html
index 5cbcf546423..e339f39dcd8 100644
--- a/tests/wpt/tests/infrastructure/testdriver/bidi/emulation/set_geolocation_override.https.html
+++ b/tests/wpt/tests/infrastructure/testdriver/bidi/emulation/set_geolocation_override.https.html
@@ -13,10 +13,6 @@
descriptor: {name: "geolocation"},
state: "granted",
});
-
- // Ensure any previously set geolocation emulations are cleared.
- await test_driver.bidi.emulation.set_geolocation_override(
- {coordinates: null});
});
/** Get the current geolocation or error */
@@ -24,13 +20,13 @@
return new Promise(
resolve => window.navigator.geolocation.getCurrentPosition(
position => resolve(position.coords.toJSON()),
- error => resolve({code: error.code, message: error.message}),
+ error => resolve({code: error.code}),
// Fail fast if geolocation is not available.
{timeout: 500}
))
}
- /** Asserts that relevant coordinate properties match */
+ /** Asserts that coordinate or error matches the expected value */
function assert_coordinates_match(actual, expected) {
for (const key in expected) {
assert_equals(actual[key], expected[key],
@@ -49,6 +45,11 @@
};
promise_test(async (t) => {
+ t.add_cleanup(async () => {
+ await test_driver.bidi.emulation.set_geolocation_override(
+ {coordinates: null});
+ });
+
// Get the initial geolocation (might be error).
const initial_coords = await get_current_geolocation();
@@ -66,6 +67,28 @@
await test_driver.bidi.emulation.set_geolocation_override(
{coordinates: null});
// Assert coordinates are set to the original value.
- assert_coordinates_match(await get_current_geolocation(), initial_coords);
+ assert_coordinates_match(await get_current_geolocation(),
+ initial_coords);
}, "emulate geolocation and clear override");
+
+ promise_test(async (t) => {
+ // Emulate position unavailable.
+ await test_driver.bidi.emulation.set_geolocation_override({
+ error: {type: 'positionUnavailable'},
+ });
+
+ // Get the geolocation after setting the override.
+ const error = await get_current_geolocation();
+
+ assert_equals(error.code, 2,
+ `Geolocation should be unavailable with code POSITION_UNAVAILABLE = 2`);
+ }, "emulate geolocation position unavailable");
+
+ promise_test(async (t) => {
+ assert_throws_js(Error,
+ () => test_driver.bidi.emulation.set_geolocation_override({
+ error: {type: 'positionUnavailable'},
+ coordinates: SOME_COORDINATES
+ }), "Setting both `coordinates` and `error` should fail");
+ }, "cannot set `coordinates` and `error` simultaneously");
</script>
diff --git a/tests/wpt/tests/input-events/input-events-spin-button-click-on-number-input-delete-document.html b/tests/wpt/tests/input-events/input-events-spin-button-click-on-number-input-delete-document.html
index fb3655398db..9e5d9b9ef99 100644
--- a/tests/wpt/tests/input-events/input-events-spin-button-click-on-number-input-delete-document.html
+++ b/tests/wpt/tests/input-events/input-events-spin-button-click-on-number-input-delete-document.html
@@ -21,7 +21,7 @@
promise_test(async function () {
await loadIframe(
- "<input type='number' style='width: 100px; height: 20px'>"
+ "<input type='number' style='width: 100px; height: 20px' value='5'>"
);
const inputElement = frame.contentDocument.querySelector("input");
@@ -38,12 +38,14 @@
events.push("change");
});
+ assert_equals(inputElement.value, "5", "Original value should be 5");
+
// Roughly get the offset to the spin up arrow button's center point within
// the iframe's viewport. Note that this is fragile and might need specific
// coordinates for each browser and maybe platform.
const rect = inputElement.getBoundingClientRect();
- const x = rect.x + rect.width - 10;
- const y = rect.y + Math.round(rect.height / 4);
+ const x = Math.round(rect.x + rect.width - 5);
+ const y = Math.round(rect.y + (3 * rect.height) / 4);
const actions = new test_driver.Actions()
.setContext(frame.contentWindow)
@@ -52,6 +54,7 @@
.pointerUp();
await actions.send();
+ assert_equals(inputElement.value, "6", "Value should increase after clicking the up arrow");
assert_array_equals(events, ['beforeinput']);
assert_false(document.body.contains(frame));
}, "Number input should not crash and not fire subsequent events when event handler removes document");
diff --git a/tests/wpt/tests/interfaces/largest-contentful-paint.idl b/tests/wpt/tests/interfaces/largest-contentful-paint.idl
index 872ba552b0d..d1630cc7daa 100644
--- a/tests/wpt/tests/interfaces/largest-contentful-paint.idl
+++ b/tests/wpt/tests/interfaces/largest-contentful-paint.idl
@@ -5,7 +5,6 @@
[Exposed=Window]
interface LargestContentfulPaint : PerformanceEntry {
- readonly attribute DOMHighResTimeStamp renderTime;
readonly attribute DOMHighResTimeStamp loadTime;
readonly attribute unsigned long size;
readonly attribute DOMString id;
@@ -13,3 +12,5 @@ interface LargestContentfulPaint : PerformanceEntry {
readonly attribute Element? element;
[Default] object toJSON();
};
+
+LargestContentfulPaint includes PaintTimingMixin;
diff --git a/tests/wpt/tests/interfaces/service-workers.idl b/tests/wpt/tests/interfaces/service-workers.idl
index d9ff2f651f8..34af3372401 100644
--- a/tests/wpt/tests/interfaces/service-workers.idl
+++ b/tests/wpt/tests/interfaces/service-workers.idl
@@ -34,7 +34,7 @@ interface ServiceWorkerRegistration : EventTarget {
readonly attribute USVString scope;
readonly attribute ServiceWorkerUpdateViaCache updateViaCache;
- [NewObject] Promise<undefined> update();
+ [NewObject] Promise<ServiceWorkerRegistration> update();
[NewObject] Promise<boolean> unregister();
// event
diff --git a/tests/wpt/tests/interfaces/webauthn.idl b/tests/wpt/tests/interfaces/webauthn.idl
index a33c85e7bad..7fbe55e6765 100644
--- a/tests/wpt/tests/interfaces/webauthn.idl
+++ b/tests/wpt/tests/interfaces/webauthn.idl
@@ -311,22 +311,37 @@ enum PublicKeyCredentialHint {
partial dictionary AuthenticationExtensionsClientInputs {
DOMString appid;
};
+partial dictionary AuthenticationExtensionsClientInputsJSON {
+ DOMString appid;
+};
partial dictionary AuthenticationExtensionsClientOutputs {
boolean appid;
};
+partial dictionary AuthenticationExtensionsClientOutputsJSON {
+ boolean appid;
+};
partial dictionary AuthenticationExtensionsClientInputs {
DOMString appidExclude;
};
+partial dictionary AuthenticationExtensionsClientInputsJSON {
+ DOMString appidExclude;
+};
partial dictionary AuthenticationExtensionsClientOutputs {
boolean appidExclude;
};
+partial dictionary AuthenticationExtensionsClientOutputsJSON {
+ boolean appidExclude;
+};
partial dictionary AuthenticationExtensionsClientInputs {
boolean credProps;
};
+partial dictionary AuthenticationExtensionsClientInputsJSON {
+ boolean credProps;
+};
dictionary CredentialPropertiesOutput {
boolean rk;
@@ -335,33 +350,57 @@ dictionary CredentialPropertiesOutput {
partial dictionary AuthenticationExtensionsClientOutputs {
CredentialPropertiesOutput credProps;
};
+partial dictionary AuthenticationExtensionsClientOutputsJSON {
+ CredentialPropertiesOutput credProps;
+};
dictionary AuthenticationExtensionsPRFValues {
required BufferSource first;
BufferSource second;
};
+dictionary AuthenticationExtensionsPRFValuesJSON {
+ required Base64URLString first;
+ Base64URLString second;
+};
dictionary AuthenticationExtensionsPRFInputs {
AuthenticationExtensionsPRFValues eval;
record<DOMString, AuthenticationExtensionsPRFValues> evalByCredential;
};
+dictionary AuthenticationExtensionsPRFInputsJSON {
+ AuthenticationExtensionsPRFValuesJSON eval;
+ record<DOMString, AuthenticationExtensionsPRFValuesJSON> evalByCredential;
+};
partial dictionary AuthenticationExtensionsClientInputs {
AuthenticationExtensionsPRFInputs prf;
};
+partial dictionary AuthenticationExtensionsClientInputsJSON {
+ AuthenticationExtensionsPRFInputsJSON prf;
+};
dictionary AuthenticationExtensionsPRFOutputs {
boolean enabled;
AuthenticationExtensionsPRFValues results;
};
+dictionary AuthenticationExtensionsPRFOutputsJSON {
+ boolean enabled;
+ AuthenticationExtensionsPRFValuesJSON results;
+};
partial dictionary AuthenticationExtensionsClientOutputs {
AuthenticationExtensionsPRFOutputs prf;
};
+partial dictionary AuthenticationExtensionsClientOutputsJSON {
+ AuthenticationExtensionsPRFOutputsJSON prf;
+};
partial dictionary AuthenticationExtensionsClientInputs {
AuthenticationExtensionsLargeBlobInputs largeBlob;
};
+partial dictionary AuthenticationExtensionsClientInputsJSON {
+ AuthenticationExtensionsLargeBlobInputsJSON largeBlob;
+};
enum LargeBlobSupport {
"required",
@@ -373,13 +412,26 @@ dictionary AuthenticationExtensionsLargeBlobInputs {
boolean read;
BufferSource write;
};
+dictionary AuthenticationExtensionsLargeBlobInputsJSON {
+ DOMString support;
+ boolean read;
+ Base64URLString write;
+};
partial dictionary AuthenticationExtensionsClientOutputs {
AuthenticationExtensionsLargeBlobOutputs largeBlob;
};
+partial dictionary AuthenticationExtensionsClientOutputsJSON {
+ AuthenticationExtensionsLargeBlobOutputsJSON largeBlob;
+};
dictionary AuthenticationExtensionsLargeBlobOutputs {
boolean supported;
ArrayBuffer blob;
boolean written;
};
+dictionary AuthenticationExtensionsLargeBlobOutputsJSON {
+ boolean supported;
+ Base64URLString blob;
+ boolean written;
+};
diff --git a/tests/wpt/tests/largest-contentful-paint/idlharness.html b/tests/wpt/tests/largest-contentful-paint/idlharness.html
index 5f5d286b356..0dd006e711c 100644
--- a/tests/wpt/tests/largest-contentful-paint/idlharness.html
+++ b/tests/wpt/tests/largest-contentful-paint/idlharness.html
@@ -10,7 +10,7 @@
idl_test(
['largest-contentful-paint'],
- ['performance-timeline', 'dom', 'hr-time'],
+ ['performance-timeline', 'dom', 'hr-time', 'paint-timing'],
async (idl_array, t) => {
idl_array.add_objects({
LargestContentfulPaint: ['lcp']
diff --git a/tests/wpt/tests/largest-contentful-paint/observe-text.html b/tests/wpt/tests/largest-contentful-paint/observe-text.html
index 0724a0bb92b..d029d500aa1 100644
--- a/tests/wpt/tests/largest-contentful-paint/observe-text.html
+++ b/tests/wpt/tests/largest-contentful-paint/observe-text.html
@@ -26,7 +26,7 @@ p {
// PaintTimingMixin
assert_greater_than_equal(entry.paintTime, beforeRender, 'paintTime should represent the time when the UA started painting');
- if ("presentationTime" in entry) {
+ if ("presentationTime" in entry && entry.presentationTime !== null) {
assert_greater_than(entry.presentationTime, entry.paintTime);
assert_equals(entry.presentationTime, entry.renderTime);
} else {
diff --git a/tests/wpt/tests/largest-contentful-paint/resources/largest-contentful-paint-helpers.js b/tests/wpt/tests/largest-contentful-paint/resources/largest-contentful-paint-helpers.js
index a01080c0088..99916f5c5d3 100644
--- a/tests/wpt/tests/largest-contentful-paint/resources/largest-contentful-paint-helpers.js
+++ b/tests/wpt/tests/largest-contentful-paint/resources/largest-contentful-paint-helpers.js
@@ -55,7 +55,7 @@ function checkImage(entry, expectedUrl, expectedID, expectedSize, timeLowerBound
assert_greater_than_equal(entry.paintTime, timeLowerBound, 'paintTime should represent the time when the UA started painting');
// PaintTimingMixin
- if ("presentationTime" in entry) {
+ if ("presentationTime" in entry && entry.presentationTime !== null) {
assert_greater_than(entry.presentationTime, entry.paintTime);
assert_equals(entry.presentationTime, entry.renderTime);
} else {
diff --git a/tests/wpt/tests/paint-timing/paint-timing-mixin.html b/tests/wpt/tests/paint-timing/paint-timing-mixin.html
index c43b7668b5c..048c985c7c7 100644
--- a/tests/wpt/tests/paint-timing/paint-timing-mixin.html
+++ b/tests/wpt/tests/paint-timing/paint-timing-mixin.html
@@ -26,7 +26,7 @@
});
const entry = await performance_entry_promise;
assert_greater_than(entry.paintTime, reference_time);
- if ("presentationTime" in entry) {
+ if ("presentationTime" in entry && entry.presentationTime !== null) {
assert_greater_than(entry.presentationTime, entry.paintTime);
assert_equals(entry.presentationTime, entry.startTime);
} else {
diff --git a/tests/wpt/tests/paint-timing/resources/utils.js b/tests/wpt/tests/paint-timing/resources/utils.js
index ed58c957c97..41c76d68266 100644
--- a/tests/wpt/tests/paint-timing/resources/utils.js
+++ b/tests/wpt/tests/paint-timing/resources/utils.js
@@ -51,7 +51,7 @@ async function test_fcp(label, before_assert_fcp_func) {
main.className = 'contentful';
const entry = await assertFirstContentfulPaint(t);
if ("paintTime" in entry) {
- if ("presentationTime" in entry) {
+ if ("presentationTime" in entry && entry.presentationTime !== null) {
assert_greater_than(entry.presentationTime, entry.paintTime);
assert_equals(entry.startTime, entry.presentationTime);
} else {
diff --git a/tests/wpt/tests/remote-playback/WEB_FEATURES.yml b/tests/wpt/tests/remote-playback/WEB_FEATURES.yml
new file mode 100644
index 00000000000..f59321971a7
--- /dev/null
+++ b/tests/wpt/tests/remote-playback/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: remote-playback
+ files: "**"
diff --git a/tests/wpt/tests/reporting/resources/report-helper.js b/tests/wpt/tests/reporting/resources/report-helper.js
index 5b5438903de..216da22eae8 100644
--- a/tests/wpt/tests/reporting/resources/report-helper.js
+++ b/tests/wpt/tests/reporting/resources/report-helper.js
@@ -40,7 +40,9 @@ function checkReportExists(reports, type, url) {
function getReport(reports, type, document_url, subresource_url) {
for (const report of reports) {
if (report.type !== type) continue;
- if (report.body.documentURL === document_url && report.body.subresourceURL == subresource_url) return report;
+ if (report.body.documentURL === document_url
+ && (report.body.subresourceURL == subresource_url
+ || report.body.blockedURL == subresource_url)) return report;
}
return null;
}
diff --git a/tests/wpt/tests/resources/test/tests/functional/api-tests-1.html b/tests/wpt/tests/resources/test/tests/functional/api-tests-1.html
index 9de875b0f12..6ec396c221b 100644
--- a/tests/wpt/tests/resources/test/tests/functional/api-tests-1.html
+++ b/tests/wpt/tests/resources/test/tests/functional/api-tests-1.html
@@ -132,7 +132,13 @@
{
assert_less_than(10, 11, "10 is less than 11")
}
- test(basicAssertApproxEquals, "basic assert_less_than test");
+ test(basicAssertLessThan, "basic assert_less_than test");
+
+ function bigintAssertLessThan()
+ {
+ assert_less_than(10n, 11n, "10n is less than 11n")
+ }
+ test(bigintAssertLessThan, "bigint assert_less_than test");
function basicAssertGreaterThan()
{
@@ -140,12 +146,24 @@
}
test(basicAssertGreaterThan, "assert_greater_than expected to fail");
+ function bigintAssertGreaterThan()
+ {
+ assert_greater_than(10n, 11n, "10n is not greater than 11n");
+ }
+ test(bigintAssertGreaterThan, "bigint assert_greater_than expected to fail");
+
function basicAssertGreaterThanEqual()
{
assert_greater_than_equal(10, 10, "10 is greater than or equal to 10")
}
test(basicAssertGreaterThanEqual, "basic assert_greater_than_equal test");
+ function bigintAssertGreaterThanEqual()
+ {
+ assert_greater_than_equal(10n, 10n, "10n is greater than or equal to 10n")
+ }
+ test(bigintAssertGreaterThanEqual, "bigint assert_greater_than_equal test");
+
function basicAssertLessThanEqual()
{
assert_greater_than_equal('10', 10, "'10' is not a number")
@@ -464,6 +482,12 @@
},
{
"status_string": "FAIL",
+ "name": "bigint assert_greater_than expected to fail",
+ "message": "assert_greater_than: 10n is not greater than 11n expected a number greater than 11n but got 10n",
+ "properties": {}
+ },
+ {
+ "status_string": "FAIL",
"name": "assert_less_than_equal expected to fail",
"message": "assert_greater_than_equal: '10' is not a number expected a number but got a \"string\"",
"properties": {}
@@ -518,12 +542,24 @@
},
{
"status_string": "PASS",
+ "name": "bigint assert_greater_than_equal test",
+ "message": null,
+ "properties": {}
+ },
+ {
+ "status_string": "PASS",
"name": "basic assert_less_than test",
"message": null,
"properties": {}
},
{
"status_string": "PASS",
+ "name": "bigint assert_less_than test",
+ "message": null,
+ "properties": {}
+ },
+ {
+ "status_string": "PASS",
"name": "body element fires the onload event set from the attribute",
"message": null,
"properties": {}
@@ -745,13 +781,22 @@
"status": 0
},
{
- "assert_name": "assert_approx_equals",
+ "assert_name": "assert_less_than",
"test": "basic assert_less_than test",
"args": [
"10",
"11",
- "1",
- "\"10 is approximately (+/- 1) 11\""
+ "\"10 is less than 11\""
+ ],
+ "status": 0
+ },
+ {
+ "assert_name": "assert_less_than",
+ "test": "bigint assert_less_than test",
+ "args": [
+ "10n",
+ "11n",
+ "\"10n is less than 11n\""
],
"status": 0
},
@@ -766,6 +811,16 @@
"status": 1
},
{
+ "assert_name": "assert_greater_than",
+ "test": "bigint assert_greater_than expected to fail",
+ "args": [
+ "10n",
+ "11n",
+ "\"10n is not greater than 11n\""
+ ],
+ "status": 1
+ },
+ {
"assert_name": "assert_greater_than_equal",
"test": "basic assert_greater_than_equal test",
"args": [
@@ -777,6 +832,16 @@
},
{
"assert_name": "assert_greater_than_equal",
+ "test": "bigint assert_greater_than_equal test",
+ "args": [
+ "10n",
+ "10n",
+ "\"10n is greater than or equal to 10n\""
+ ],
+ "status": 0
+ },
+ {
+ "assert_name": "assert_greater_than_equal",
"test": "assert_less_than_equal expected to fail",
"args": [
"\"10\"",
diff --git a/tests/wpt/tests/resources/testdriver.js b/tests/wpt/tests/resources/testdriver.js
index 6e8410b7ea4..992b9e3ab2c 100644
--- a/tests/wpt/tests/resources/testdriver.js
+++ b/tests/wpt/tests/resources/testdriver.js
@@ -280,8 +280,14 @@
* @param {object} params - Parameters for the command.
* @param {null|object} params.coordinates - The optional
* geolocation coordinates to set. Matches the
- * `emulation.GeolocationCoordinates <https://w3c.github.io/webdriver-bidi/#type-emulation-GeolocationCoordinates>`_
- * value. If null or omitted, the emulation will be removed.
+ * `emulation.GeolocationCoordinates <https://w3c.github.io/webdriver-bidi/#commands-emulationsetgeolocationoverride>`_
+ * value. If null or omitted and the `params.error` is set, the
+ * emulation will be removed. Mutually exclusive with
+ * `params.error`.
+ * @param {object} params.error - The optional
+ * geolocation error to emulate. Matches the
+ * `emulation.GeolocationPositionError <https://w3c.github.io/webdriver-bidi/#commands-emulationsetgeolocationoverride>`_
+ * value. Mutually exclusive with `params.coordinates`.
* @param {null|Array.<(Context)>} [params.contexts] The
* optional contexts parameter specifies which browsing contexts
* to set the geolocation override on. It should be either an
diff --git a/tests/wpt/tests/resources/testharness.js b/tests/wpt/tests/resources/testharness.js
index d1d9d61eab8..6ccede34483 100644
--- a/tests/wpt/tests/resources/testharness.js
+++ b/tests/wpt/tests/resources/testharness.js
@@ -1400,6 +1400,8 @@
return "-0";
}
return String(val);
+ case "bigint":
+ return String(val) + 'n';
case "object":
if (val === null) {
return "null";
@@ -1771,20 +1773,25 @@
/**
* Assert that ``actual`` is a number less than ``expected``.
*
- * @param {number} actual - Test value.
- * @param {number} expected - Number that ``actual`` must be less than.
+ * @param {number|bigint} actual - Test value.
+ * @param {number|bigint} expected - Value that ``actual`` must be less than.
* @param {string} [description] - Description of the condition being tested.
*/
function assert_less_than(actual, expected, description)
{
/*
- * Test if a primitive number is less than another
+ * Test if a primitive number (or bigint) is less than another
*/
- assert(typeof actual === "number",
+ assert(typeof actual === "number" || typeof actual === "bigint",
"assert_less_than", description,
"expected a number but got a ${type_actual}",
{type_actual:typeof actual});
+ assert(typeof actual === typeof expected,
+ "assert_less_than", description,
+ "expected a ${type_expected} but got a ${type_actual}",
+ {type_expected:typeof expected, type_actual:typeof actual});
+
assert(actual < expected,
"assert_less_than", description,
"expected a number less than ${expected} but got ${actual}",
@@ -1795,20 +1802,25 @@
/**
* Assert that ``actual`` is a number greater than ``expected``.
*
- * @param {number} actual - Test value.
- * @param {number} expected - Number that ``actual`` must be greater than.
+ * @param {number|bigint} actual - Test value.
+ * @param {number|bigint} expected - Value that ``actual`` must be greater than.
* @param {string} [description] - Description of the condition being tested.
*/
function assert_greater_than(actual, expected, description)
{
/*
- * Test if a primitive number is greater than another
+ * Test if a primitive number (or bigint) is greater than another
*/
- assert(typeof actual === "number",
+ assert(typeof actual === "number" || typeof actual === "bigint",
"assert_greater_than", description,
"expected a number but got a ${type_actual}",
{type_actual:typeof actual});
+ assert(typeof actual === typeof expected,
+ "assert_greater_than", description,
+ "expected a ${type_expected} but got a ${type_actual}",
+ {type_expected:typeof expected, type_actual:typeof actual});
+
assert(actual > expected,
"assert_greater_than", description,
"expected a number greater than ${expected} but got ${actual}",
@@ -1820,21 +1832,31 @@
* Assert that ``actual`` is a number greater than ``lower`` and less
* than ``upper`` but not equal to either.
*
- * @param {number} actual - Test value.
- * @param {number} lower - Number that ``actual`` must be greater than.
- * @param {number} upper - Number that ``actual`` must be less than.
+ * @param {number|bigint} actual - Test value.
+ * @param {number|bigint} lower - Value that ``actual`` must be greater than.
+ * @param {number|bigint} upper - Value that ``actual`` must be less than.
* @param {string} [description] - Description of the condition being tested.
*/
function assert_between_exclusive(actual, lower, upper, description)
{
/*
- * Test if a primitive number is between two others
+ * Test if a primitive number (or bigint) is between two others
*/
- assert(typeof actual === "number",
+ assert(typeof lower === typeof upper,
+ "assert_between_exclusive", description,
+ "expected lower (${type_lower}) and upper (${type_upper}) types to match (test error)",
+ {type_lower:typeof lower, type_upper:typeof upper});
+
+ assert(typeof actual === "number" || typeof actual === "bigint",
"assert_between_exclusive", description,
"expected a number but got a ${type_actual}",
{type_actual:typeof actual});
+ assert(typeof actual === typeof lower,
+ "assert_between_exclusive", description,
+ "expected a ${type_lower} but got a ${type_actual}",
+ {type_lower:typeof lower, type_actual:typeof actual});
+
assert(actual > lower && actual < upper,
"assert_between_exclusive", description,
"expected a number greater than ${lower} " +
@@ -1846,21 +1868,26 @@
/**
* Assert that ``actual`` is a number less than or equal to ``expected``.
*
- * @param {number} actual - Test value.
- * @param {number} expected - Number that ``actual`` must be less
+ * @param {number|bigint} actual - Test value.
+ * @param {number|bigint} expected - Value that ``actual`` must be less
* than or equal to.
* @param {string} [description] - Description of the condition being tested.
*/
function assert_less_than_equal(actual, expected, description)
{
/*
- * Test if a primitive number is less than or equal to another
+ * Test if a primitive number (or bigint) is less than or equal to another
*/
- assert(typeof actual === "number",
+ assert(typeof actual === "number" || typeof actual === "bigint",
"assert_less_than_equal", description,
"expected a number but got a ${type_actual}",
{type_actual:typeof actual});
+ assert(typeof actual === typeof expected,
+ "assert_less_than_equal", description,
+ "expected a ${type_expected} but got a ${type_actual}",
+ {type_expected:typeof expected, type_actual:typeof actual});
+
assert(actual <= expected,
"assert_less_than_equal", description,
"expected a number less than or equal to ${expected} but got ${actual}",
@@ -1871,21 +1898,26 @@
/**
* Assert that ``actual`` is a number greater than or equal to ``expected``.
*
- * @param {number} actual - Test value.
- * @param {number} expected - Number that ``actual`` must be greater
+ * @param {number|bigint} actual - Test value.
+ * @param {number|bigint} expected - Value that ``actual`` must be greater
* than or equal to.
* @param {string} [description] - Description of the condition being tested.
*/
function assert_greater_than_equal(actual, expected, description)
{
/*
- * Test if a primitive number is greater than or equal to another
+ * Test if a primitive number (or bigint) is greater than or equal to another
*/
- assert(typeof actual === "number",
+ assert(typeof actual === "number" || typeof actual === "bigint",
"assert_greater_than_equal", description,
"expected a number but got a ${type_actual}",
{type_actual:typeof actual});
+ assert(typeof actual === typeof expected,
+ "assert_greater_than_equal", description,
+ "expected a ${type_expected} but got a ${type_actual}",
+ {type_expected:typeof expected, type_actual:typeof actual});
+
assert(actual >= expected,
"assert_greater_than_equal", description,
"expected a number greater than or equal to ${expected} but got ${actual}",
@@ -1897,21 +1929,31 @@
* Assert that ``actual`` is a number greater than or equal to ``lower`` and less
* than or equal to ``upper``.
*
- * @param {number} actual - Test value.
- * @param {number} lower - Number that ``actual`` must be greater than or equal to.
- * @param {number} upper - Number that ``actual`` must be less than or equal to.
+ * @param {number|bigint} actual - Test value.
+ * @param {number|bigint} lower - Value that ``actual`` must be greater than or equal to.
+ * @param {number|bigint} upper - Value that ``actual`` must be less than or equal to.
* @param {string} [description] - Description of the condition being tested.
*/
function assert_between_inclusive(actual, lower, upper, description)
{
/*
- * Test if a primitive number is between to two others or equal to either of them
+ * Test if a primitive number (or bigint) is between to two others or equal to either of them
*/
- assert(typeof actual === "number",
+ assert(typeof lower === typeof upper,
+ "assert_between_inclusive", description,
+ "expected lower (${type_lower}) and upper (${type_upper}) types to match (test error)",
+ {type_lower:typeof lower, type_upper:typeof upper});
+
+ assert(typeof actual === "number" || typeof actual === "bigint",
"assert_between_inclusive", description,
"expected a number but got a ${type_actual}",
{type_actual:typeof actual});
+ assert(typeof actual === typeof lower,
+ "assert_between_inclusive", description,
+ "expected a ${type_lower} but got a ${type_actual}",
+ {type_lower:typeof lower, type_actual:typeof actual});
+
assert(actual >= lower && actual <= upper,
"assert_between_inclusive", description,
"expected a number greater than or equal to ${lower} " +
diff --git a/tests/wpt/tests/sanitizer-api/sanitizer-basic-filtering.tentative.html b/tests/wpt/tests/sanitizer-api/sanitizer-basic-filtering.tentative.html
index 0b6924b0980..38c764ae181 100644
--- a/tests/wpt/tests/sanitizer-api/sanitizer-basic-filtering.tentative.html
+++ b/tests/wpt/tests/sanitizer-api/sanitizer-basic-filtering.tentative.html
@@ -273,6 +273,15 @@ for(const group of
div.setHTMLUnsafe(testcase.data, config);
assert_testcase(div, testcase);
}, `setHTMLUnsafe testcase ${group.id}/${index}, "${testcase.data}"`);
+
+ // parseHTML and parseHTMLUnsafe need to allow "html" and "body" for these
+ // tests to be useful. Update the config, if necessary.
+ if (config && config["sanitizer"] && config["sanitizer"]["elements"]) {
+ config["sanitizer"] = new Sanitizer(config["sanitizer"]);
+ config["sanitizer"].allowElement("body");
+ config["sanitizer"].allowElement("html");
+ }
+
test(_ => {
assert_testcase(
Document.parseHTML(testcase.data, config).body, testcase);
diff --git a/tests/wpt/tests/sanitizer-api/sanitizer-parseHTML.tentative.html b/tests/wpt/tests/sanitizer-api/sanitizer-parseHTML.tentative.html
new file mode 100644
index 00000000000..c4a31e2eb03
--- /dev/null
+++ b/tests/wpt/tests/sanitizer-api/sanitizer-parseHTML.tentative.html
@@ -0,0 +1,145 @@
+<!DOCTYPE html>
+<head>
+<title>Testcases for parseHTML and parseHTMLUnsafe</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/html5lib-testcase-support.js"></script>
+<!--
+ This is a set of basic Sanitizer test cases using the parseHTML and
+ parseHTMLUnsafe methods.
+-->
+<script id="all" type="html5lib-testcases">
+#data
+text
+#document
+| <html>
+| <head>
+| <body>
+| "text"
+
+#data
+<div>text
+#config
+{ "elements": ["html", "body", "div"] }
+#document
+| <html>
+| <body>
+| <div>
+| "text"
+
+#data
+<div>text
+#config
+{ "elements": ["body", "div"] }
+#document
+
+#data
+<div>text
+#config
+{ "elements": ["html", "div"] }
+#document
+| <html>
+
+</script>
+<script id="safe" type="html5lib-testcases">
+#data
+<script>hello
+#document
+| <html>
+| <head>
+| <body>
+
+#data
+<html onload="2+2"><body onload="3+3"><div>hello
+#document
+| <html>
+| <head>
+| <body>
+| <div>
+| "hello"
+
+</script>
+<script id="unsafe" type="html5lib-testcases">
+#data
+<script>hello
+#document
+| <html>
+| <head>
+| <script>
+| "hello"
+| <body>
+
+#data
+<html onload="2+2"><body onload="3+3"><div>hello
+#document
+| <html>
+| onload="2+2"
+| <head>
+| <body>
+| onload="3+3"
+| <div>
+| "hello"
+
+</script>
+<script id="document" type="html5lib-testcases">
+#data
+<!DOCTYPE html>
+text
+#document
+| <!DOCTYPE html "" "">
+| <html>
+| <head>
+| <body>
+| "text"
+
+</script>
+<script>
+function test_safe(testcase, index) {
+ let config = undefined;
+ try {
+ config = { sanitizer: JSON.parse(testcase.config) };
+ } catch { /* config remains undefined */ }
+ test(_ => {
+ assert_testcase(Document.parseHTML(testcase.data, config), testcase);
+ }, `parseHTML testcase ${index}, "${testcase.data}"`);
+}
+function test_unsafe(testcase, index) {
+ let config = undefined;
+ try {
+ config = { sanitizer: JSON.parse(testcase.config) };
+ } catch { /* config remains undefined */ }
+ test(_ => {
+ assert_testcase(Document.parseHTMLUnsafe(testcase.data, config), testcase);
+ }, `parseHTMLUnsafe testcase ${index}, "${testcase.data}"`);
+}
+
+const all = parse_html5lib_testcases(
+ document.getElementById("all").textContent);
+const safe = parse_html5lib_testcases(
+ document.getElementById("safe").textContent);
+const unsafe = parse_html5lib_testcases(
+ document.getElementById("unsafe").textContent);
+all.forEach(test_safe);
+all.forEach(test_unsafe);
+safe.forEach(test_safe);
+unsafe.forEach(test_unsafe);
+
+
+// DOM only supports Document Type Declarations as children of documents. This
+// trips up the assert_testcase implementation, so we'll handle that seperately.
+parse_html5lib_testcases(
+ document.getElementById("document").textContent).
+ forEach((testcase, index) => {
+ test(_ => {
+ const tree = build_node_tree(new Document(), testcase.document);
+ assert_subtree_equals(Document.parseHTMLUnsafe(testcase.data, {}), tree);
+ }, `parseHTMLUnsafe full document testcase ${index}, "${testcase.data}"`);
+ test(_ => {
+ const tree = build_node_tree(new Document(), testcase.document);
+ assert_subtree_equals(Document.parseHTML(testcase.data, {}), tree);
+ }, `parseHTML full document testcase ${index}, "${testcase.data}"`);
+});
+</script>
+</head>
+<body>
+</body>
diff --git a/tests/wpt/tests/soft-navigation-heuristics/innertext.tentative.html b/tests/wpt/tests/soft-navigation-heuristics/innertext.tentative.html
index d40b6047924..7716488f25a 100644
--- a/tests/wpt/tests/soft-navigation-heuristics/innertext.tentative.html
+++ b/tests/wpt/tests/soft-navigation-heuristics/innertext.tentative.html
@@ -20,13 +20,18 @@
const link = document.getElementById("link");
testSoftNavigation({
addContent: async () => {
- document.getElementById("softnav-content").innerText =
- "Lorem Ipsum dolor sit amet";
+ document.getElementById("softnav-content").innerText =
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, ' +
+ 'sed do eiusmod tempor incididunt ut labore et dolore magna ' +
+ 'aliqua. Ut enim ad minim veniam, quis nostrud exercitation ' +
+ 'ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis ' +
+ 'aute irure dolor in reprehenderit in voluptate velit esse ' +
+ 'cillum dolore eu fugiat nulla pariatur. Excepteur sint ' +
+ 'occaecat cupidatat non proident, sunt in culpa qui officia ' +
+ 'deserunt mollit anim id est laborum.';
},
link: link,
test: "Soft navigation when only innerText was modified"});
</script>
</body>
</html>
-
-
diff --git a/tests/wpt/tests/soft-navigation-heuristics/interaction-with-paint-before-back.tentative.html b/tests/wpt/tests/soft-navigation-heuristics/interaction-with-paint-before-back.tentative.html
index 7b884f2bdb3..5961a6ebcdf 100644
--- a/tests/wpt/tests/soft-navigation-heuristics/interaction-with-paint-before-back.tentative.html
+++ b/tests/wpt/tests/soft-navigation-heuristics/interaction-with-paint-before-back.tentative.html
@@ -1,73 +1,93 @@
-<!DOCTYPE HTML>
+<!doctype html>
<html>
-<head>
-<meta charset="utf-8">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-<script src="resources/soft-navigation-helper.js"></script>
-</head>
-<body>
- <main id=main>
- <div>
- <a id=link><img src="/images/lcp-256x256.png" id="img"></a>
- <a id=not_nav><img src="/images/lcp-16x16.png"></a>
+ <head>
+ <meta charset="utf-8" />
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ </head>
+ <body>
+ <div id="main">
+ <div id="almost_soft_nav_div">
+ A click will almost initiate a soft nav, by (1) mutating the DOM by adding an image; (2)
+ causing the image to be painted.
+ </div>
+ <div id="soft_nav_div">
+ A click will initiate a soft nav, by (1) mutating the DOM by adding an image; (2) causing
+ the image to be painted; (3) updating the URL via history.back(), but without causing a hard
+ navigation.
+ </div>
</div>
- </main>
- <script>
- // Push state a couple of times
- history.pushState({}, "", "foobar.html");
- history.pushState({}, "", "anotherOne.html");
+ <script>
+ promise_test(
+ async (t) => {
+ // Setup a click handler for 'almost_soft_nav_div', which adds a
+ // specific, identifiable image 'almost_soft_nav_img' to the document.
+ document.getElementById("almost_soft_nav_div").addEventListener("click", () => {
+ const img = new Image();
+ img.src = "/images/lcp-133x106.png";
+ img.elementTiming = "almost_soft_nav_img";
+ document.getElementById("main").appendChild(img);
+ });
- (async () => {
- const link = document.getElementById("link");
- // Trigger a user interaction that doesn't result in a soft navigation, but
- // does paint.
- await (async () => {
- const not_nav = document.getElementById("not_nav");
- let non_soft_nav_click;
- const non_soft_nav_click_promise =
- new Promise(r => { non_soft_nav_click = r; });
- not_nav.addEventListener("click", () => {
- addImageToMain("lcp-133x106.png", "not_soft_nav_image");
- (new PerformanceObserver(non_soft_nav_click)).observe({type: "element"});
- });
- if (test_driver) {
- test_driver.click(not_nav);
- }
- await non_soft_nav_click_promise;
- })();
- const url = URL + "?" + counter;
- link.addEventListener("click", () => {
- // Add an LCP element.
- const img = new Image();
- img.src = '/images/lcp-100x500.png' + "?" + Math.random();
- document.getElementById("main").appendChild(img);
- history.back();
- });
- promise_test(async t => {
- const soft_nav_promise = waitOnSoftNav();
- if (test_driver) {
- test_driver.click(link);
- }
- await soft_nav_promise;
- assert_equals(
- document.softNavigations, 1,
- 'Single Soft Navigation detected');
- const [entries, options] = await new Promise(resolve => {
- (new PerformanceObserver((list, obs, options) => resolve(
- [list.getEntries(), options]))).observe(
- {type: 'soft-navigation', buffered: true});
+ // Also set up a click handler for 'soft_nav_div', for adding
+ // another image, but in addition to what we do for the almost soft nav
+ // above, also let the handler change the URL, by invoking
+ // history.back(). We prepare with pushState for the history.back()
+ // invocation, to avoid triggering a hard navigation.
+ const test_origin = new URL(location.href).origin;
+ history.pushState({}, "", "/foo.html"); // We will observe this below.
+ history.pushState({}, "", "/bar.html"); // Prep for history.back().
+ document.getElementById("soft_nav_div").addEventListener("click", () => {
+ const img = new Image();
+ img.src = "/images/lcp-133x106.png";
+ document.getElementById("main").appendChild(img);
+ history.back(); // URL change triggering the soft nav
});
- assert_equals(entries.length, 1,
- "Performance observer got an entry");
- }, "Ensure that soft navigation entry emitted through a synchronous " +
- "event that modified DOM and committed a same document navigation, " +
- "and that was preceded by a user intreaction that resulted in a " +
- "contentful paint is properly detected.");
- })();
- </script>
-</body>
+ // Now, click almost_soft_nav_div, and observe that time image
+ // was rendered but no soft nav was triggered.
+ {
+ const element_timing_promise = new Promise((resolve) => {
+ new PerformanceObserver(resolve).observe({ type: "element", buffered: true });
+ });
+ if (test_driver) {
+ test_driver.click(almost_soft_nav_div);
+ }
+ // Returns entries of type PerformanceElementTiming, see
+ // https://developer.mozilla.org/en-US/docs/Web/API/PerformanceElementTiming.
+ const entries = (await element_timing_promise).getEntries();
+ assert_equals(entries.length, 1);
+ assert_equals(
+ entries[0].identifier,
+ "almost_soft_nav_img",
+ "Image based on the user interaction was painted.",
+ );
+ assert_equals(document.softNavigations, 0, "No soft navigation detected.");
+ }
+
+ // Now, click soft_nav_div, and observe the detected soft navigation.
+ {
+ const soft_nav_promise = new Promise((resolve) => {
+ new PerformanceObserver(resolve).observe({ type: "soft-navigation", buffered: true });
+ });
+ if (test_driver) {
+ test_driver.click(soft_nav_div);
+ }
+ // Returns entries of type SoftNavigationEntry, see
+ // https://github.com/WICG/soft-navigations/
+ const entries = (await soft_nav_promise).getEntries();
+ assert_equals(document.softNavigations, 1, "Single soft navigation detected");
+ assert_equals(entries.length, 1, "Performance observer got an entry");
+ assert_equals(entries[0].name, test_origin + "/foo.html");
+ }
+ },
+ "Ensure that soft navigation entry emitted through a synchronous " +
+ "event that modified DOM and committed a same document navigation, " +
+ "and that was preceded by a user interaction that resulted in a " +
+ "contentful paint is properly detected.",
+ );
+ </script>
+ </body>
</html>
diff --git a/tests/wpt/tests/soft-navigation-heuristics/smoke/tentative/basic.html b/tests/wpt/tests/soft-navigation-heuristics/smoke/tentative/basic.html
new file mode 100644
index 00000000000..9bfedf09b19
--- /dev/null
+++ b/tests/wpt/tests/soft-navigation-heuristics/smoke/tentative/basic.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>Soft Navigation Detection: The Basics.</title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/resources/testdriver.js"></script>
+ <script src="/resources/testdriver-vendor.js"></script>
+ <script>
+ // The click handler is triggered by user interaction; it modifies
+ // the DOM, causing a paint, and also changes the URL.
+ // This constitutes a soft navigation.
+ function clickHandler() {
+ const greeting = document.createElement("div");
+ greeting.textContent = "Hello, World.";
+ document.body.appendChild(greeting);
+ history.pushState({}, "", "/greeting.html");
+ }
+ </script>
+ </head>
+ <body>
+ <div id="click-target" onclick="clickHandler()">Click here!</div>
+
+ <script>
+ test(() => {
+ const observer = new PerformanceObserver(() => {});
+ observer.observe({ type: "soft-navigation", buffered: true });
+ const records = observer.takeRecords();
+ observer.disconnect();
+ assert_equals(records.length, 0, "Expecting empty list.");
+ }, "No soft navigation detection without user interaction.");
+
+ promise_test(async (t) => {
+ const test_origin = new URL(location.href).origin;
+
+ let entries;
+ new PerformanceObserver((list, observer) => {
+ entries = list.getEntries();
+ observer.disconnect();
+ }).observe({ type: "soft-navigation" });
+
+ // Initiate the user interaction to trigger the soft navigation.
+ if (test_driver) {
+ test_driver.click(document.getElementById("click-target"));
+ }
+
+ await t.step_wait(
+ () => entries !== undefined,
+ "Waiting for entries from PerformanceObserver.",
+ );
+
+ // Now check there's one entry, and it's fields.
+ // The SoftNavigationEntry instance is spec'd in
+ // https://github.com/WICG/soft-navigations/
+ assert_equals(entries.length, 1, "Expecting one soft navigation entry.");
+
+ const expected_url = new URL("/greeting.html", test_origin);
+ assert_equals(
+ entries[0].name,
+ expected_url.toString(),
+ "Soft navigation should record destination URL as its name.",
+ );
+ }, "Detect soft navigation after a click.");
+ </script>
+ </body>
+</html>
diff --git a/tests/wpt/tests/soft-navigation-heuristics/text-lcp-followed-by-anim-image-softnav-lcp.tentative.html b/tests/wpt/tests/soft-navigation-heuristics/text-lcp-followed-by-anim-image-softnav-lcp.tentative.html
index 0615b513e61..b34a6e81a58 100644
--- a/tests/wpt/tests/soft-navigation-heuristics/text-lcp-followed-by-anim-image-softnav-lcp.tentative.html
+++ b/tests/wpt/tests/soft-navigation-heuristics/text-lcp-followed-by-anim-image-softnav-lcp.tentative.html
@@ -21,12 +21,15 @@
addContent: async () => {
const main = document.getElementById("main");
main.removeChild(document.getElementsByTagName("div")[0]);
- await addImageToMain("anim-gr.png");
+ const img = new Image(500, 500);
+ img.src = "/images/anim-gr.png?" + Math.random();
+ img.id = "imagelcp";
+ img.setAttribute("elementtiming", "imagelcp");
+ main.appendChild(img);
},
link: link,
- test: "Test that a text LCP followup by a smaller soft navigation image"
- + " LCP properly queues an LCP entry"});
+ test: "Test that a text LCP followup by an animaged image "
+ + " properly queues a soft navigation entry"});
</script>
</body>
</html>
-
diff --git a/tests/wpt/tests/soft-navigation-heuristics/visited-link.tentative.html b/tests/wpt/tests/soft-navigation-heuristics/visited-link.tentative.html
index 0bb149f00e9..0bb31aaac15 100644
--- a/tests/wpt/tests/soft-navigation-heuristics/visited-link.tentative.html
+++ b/tests/wpt/tests/soft-navigation-heuristics/visited-link.tentative.html
@@ -24,7 +24,15 @@
}));
const main = document.getElementById("main");
const div = document.createElement("div");
- const text = document.createTextNode("Lorem Ipsum");
+ const text = document.createTextNode(
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, ' +
+ 'sed do eiusmod tempor incididunt ut labore et dolore magna ' +
+ 'aliqua. Ut enim ad minim veniam, quis nostrud exercitation ' +
+ 'ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis ' +
+ 'aute irure dolor in reprehenderit in voluptate velit esse ' +
+ 'cillum dolore eu fugiat nulla pariatur. Excepteur sint ' +
+ 'occaecat cupidatat non proident, sunt in culpa qui officia ' +
+ 'deserunt mollit anim id est laborum.');
div.appendChild(text);
main.appendChild(div);
}
diff --git a/tests/wpt/tests/speculation-rules/prefetch/resources/basic-service-worker.js b/tests/wpt/tests/speculation-rules/prefetch/resources/basic-service-worker.js
index 17fccd448d2..59d7d8bae9e 100644
--- a/tests/wpt/tests/speculation-rules/prefetch/resources/basic-service-worker.js
+++ b/tests/wpt/tests/speculation-rules/prefetch/resources/basic-service-worker.js
@@ -32,6 +32,13 @@ if (swOption !== 'no-fetch-handler') {
if (swOption === 'fetch-handler') {
event.respondWith(fetch(event.request));
+ } else if (swOption === 'fetch-handler-synthetic') {
+ const finalUrl = new URL(event.request.url).searchParams.get('location');
+ if (finalUrl) {
+ event.respondWith(Response.redirect(finalUrl));
+ } else {
+ // Fallback to the network.
+ }
} else if (swOption === 'fetch-handler-modify-url') {
// The "Sec-Purpose: prefetch" header is dropped in fetch-handler-modify-*
// cases in Step 33 of // https://fetch.spec.whatwg.org/#dom-request
diff --git a/tests/wpt/tests/speculation-rules/prefetch/resources/counting-executor.py b/tests/wpt/tests/speculation-rules/prefetch/resources/counting-executor.py
index cbcbc8eccb9..3511fe4905e 100644
--- a/tests/wpt/tests/speculation-rules/prefetch/resources/counting-executor.py
+++ b/tests/wpt/tests/speculation-rules/prefetch/resources/counting-executor.py
@@ -21,6 +21,11 @@ def main(request, response):
request_count["prefetch" if prefetch else "nonPrefetch"] += 1
request.server.stash.put(uuid, request_count)
+ if b"location" in request.GET:
+ response.status = 302
+ response.headers.set(b"Location", request.GET[b"location"])
+ return
+
response.content = template(
request,
open(os.path.join(os.path.dirname(__file__), "executor.sub.html"), "rb").read())
diff --git a/tests/wpt/tests/speculation-rules/prefetch/resources/executor.sub.html b/tests/wpt/tests/speculation-rules/prefetch/resources/executor.sub.html
index d27acfe100a..975a3e5092e 100644
--- a/tests/wpt/tests/speculation-rules/prefetch/resources/executor.sub.html
+++ b/tests/wpt/tests/speculation-rules/prefetch/resources/executor.sub.html
@@ -1,5 +1,8 @@
<!DOCTYPE html>
<script src="/common/dispatcher/dispatcher.js" nonce="abc"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
<script src="utils.sub.js" nonce="abc"></script>
<script nonce="abc">
// For a given string `str` that is escaped by WPT's `.sub.html` or
@@ -45,6 +48,39 @@ Object.keys(requestHeaders).forEach(key => {
}
});
+// Add a link to the page in order to use during the test
+function add_link(id, url) {
+ const link_element = document.createElement("a");
+ const link_text = document.createTextNode(url);
+ link_element.setAttribute("href", url);
+ link_element.setAttribute("id", id);
+ link_element.appendChild(link_text);
+ document.body.appendChild(link_element);
+}
+
+// "id" is the id of the link that we need to hover on in order
+// to start the prefetch
+async function start_non_eager_prefetch_on_hover(id) {
+ let target = document.getElementById(id);
+
+ test_driver.set_test_context(window.opener);
+ // Inject the inputs to run this test.
+ await new test_driver.Actions().addPointer("mouse").pointerMove(0, 0, {origin: target}).send();
+}
+
+// "id" is the id of the link that we need to press on in order
+// to start the prefetch
+async function start_non_eager_prefetch_on_pointerdown(id) {
+ let target = document.getElementById(id);
+
+ test_driver.set_test_context(window.opener);
+ // Inject the inputs to run this test.
+ // Move mouse pointer outside of the anchor so that we don't start the
+ // navigation before making sure the prefetch request started server-side.
+ await new test_driver.Actions().addPointer("mouse").pointerMove(0, 0, {origin: target}).pointerDown().pointerMove(0, 0).pointerUp().send();
+}
+
+
// The fetch request's URL sent to the server.
window.requestUrl = reverse_html_escape(
"{{location[server]}}{{location[path]}}{{location[query]}}");
diff --git a/tests/wpt/tests/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https.html b/tests/wpt/tests/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https.html
new file mode 100644
index 00000000000..f56f6bf09b9
--- /dev/null
+++ b/tests/wpt/tests/speculation-rules/prefetch/tentative/service-worker/redirect.sub.https.html
@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="../../../resources/utils.js"></script>
+<script src="../../resources/utils.sub.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+
+<meta name="variant" content="?origin=same-site&sc=in-in&sw=fetch-handler">
+<meta name="variant" content="?origin=same-site&sc=in-in&sw=fetch-handler-to-fallback">
+<meta name="variant" content="?origin=same-site&sc=in-in&sw=fetch-handler-synthetic">
+<meta name="variant" content="?origin=same-site&sc=in-in&sw=no-fetch-handler">
+
+<meta name="variant" content="?origin=same-site&sc=in-out&sw=fetch-handler">
+<meta name="variant" content="?origin=same-site&sc=in-out&sw=fetch-handler-to-fallback">
+<meta name="variant" content="?origin=same-site&sc=in-out&sw=fetch-handler-synthetic">
+<meta name="variant" content="?origin=same-site&sc=in-out&sw=no-fetch-handler">
+
+<meta name="variant" content="?origin=same-site&sc=out-in&sw=fetch-handler">
+<meta name="variant" content="?origin=same-site&sc=out-in&sw=fetch-handler-to-fallback">
+<meta name="variant" content="?origin=same-site&sc=out-in&sw=no-fetch-handler">
+
+<script>
+setup(() => assertSpeculationRulesIsSupported());
+
+const originOption = new URL(location.href).searchParams.get('origin');
+const swOption = new URL(location.href).searchParams.get('sw');
+
+// We use a short name `sc` to avoid long file names https://crbug.com/40469687
+const scopeOption = new URL(location.href).searchParams.get('sc');
+
+promise_test(async t => {
+ const hostname = originOption === 'cross-site' ? '{{hosts[alt][www]}}'
+ : undefined;
+ const win = await spawnWindow(t, { protocol: 'https', hostname: hostname });
+
+ const finalUrl = win.getExecutorURL({ executor: 'counting-executor.py', protocol: 'https', page: 2 });
+ const initialUrl = new URL('../../resources/counting-executor.py', location.href);
+ // Assign a different UUID to track the number of requests to server
+ // separately for the initialUrl and finalUrl.
+ initialUrl.searchParams.set('uuid', token());
+ initialUrl.searchParams.set('location', finalUrl);
+
+ const swUrl = new URL('../../resources/basic-service-worker.js?sw=' + swOption, location.href).href;
+
+ let sw1;
+ if (scopeOption === 'in-in' || scopeOption === 'in-out') {
+ // Register a SW for `initialUrl`.
+ const reg = await service_worker_unregister_and_register(
+ t, swUrl + '&sw1', initialUrl);
+ sw1 = reg.installing;
+ await wait_for_state(t, reg.installing, 'activated');
+ }
+
+ let sw2;
+ if (scopeOption === 'in-in' || scopeOption === 'out-in') {
+ // Register a SW for `finalUrl`.
+ const reg = await service_worker_unregister_and_register(
+ t, swUrl + '&sw2', finalUrl);
+ sw2 = reg.installing;
+ await wait_for_state(t, reg.installing, 'activated');
+ }
+
+ // Start speculation rules prefetch and navigate to the URL.
+ await win.forceSinglePrefetch(initialUrl);
+
+ await win.navigate(initialUrl, {expectedDestinationUrl: finalUrl});
+
+ const initialRequestCount = await (await fetch(initialUrl + '&check')).json();
+ const finalRequestCount = await (await fetch(finalUrl + '&check')).json();
+
+ const headers = await win.execute_script(() => {
+ return requestHeaders;
+ }, []);
+
+ const controllerUrl = await win.execute_script(() => {
+ return navigator.serviceWorker.controller ?
+ navigator.serviceWorker.controller.scriptURL :
+ undefined;
+ }, []);
+
+ if (sw2) {
+ // The navigated page should be controlled by the ServiceWorker,
+ // regardless of whether prefetch is performed/used.
+ assert_equals(controllerUrl, swUrl + '&sw2');
+ } else {
+ assert_equals(controllerUrl, undefined);
+ }
+
+ // In any cases prefetch + redirects + ServiceWorker fails and prefetched
+ // results aren't served.
+ assert_not_prefetched(headers, "Prefetched result should not be served.");
+
+ if (swOption === 'fetch-handler-synthetic') {
+ // Synthetic redirect response from ServiceWorker prevents the initial
+ // prefetch/non-prefetch requests to the server.
+ assert_equals(initialRequestCount.prefetch, 0,
+ 'prefetch requests to initial URL should not be sent to the server.');
+ assert_equals(initialRequestCount.nonPrefetch, 0,
+ 'a non-prefetch requests to initial URL should not be sent to the server.');
+ } else {
+ // Otherwise, the initial prefetch request (either with or without
+ // ServiceWorker interception) anyway reaches the server, and then
+ // re-requested as a non-prefetch request on navigation.
+ assert_equals(initialRequestCount.prefetch, 1,
+ 'prefetch requests to initial URL should be sent to the server.');
+ assert_equals(initialRequestCount.nonPrefetch, 1,
+ 'a non-prefetch requests to initial URL should be sent to the server.');
+ }
+
+ // Prefetch requests to the URL after redirects shouldn't be sent to server
+ // because prefetch should fail immediately on receiving redirects.
+ assert_equals(finalRequestCount.prefetch, 0,
+ 'prefetch requests should not be sent to the server.');
+ // Instead, a single non-prefetch request should be sent.
+ assert_equals(finalRequestCount.nonPrefetch, 1,
+ 'a non-prefetch request should be sent to the server.');
+
+}, "Prefetch with ServiceWorker (" + swOption + ")");
+</script>
diff --git a/tests/wpt/tests/speculation-rules/speculation-tags/prefetch-eagerness-pointer-down.https.html b/tests/wpt/tests/speculation-rules/speculation-tags/prefetch-eagerness-pointer-down.https.html
new file mode 100644
index 00000000000..cbf69cefba6
--- /dev/null
+++ b/tests/wpt/tests/speculation-rules/speculation-tags/prefetch-eagerness-pointer-down.https.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<title>Sec-Speculation-Tags request headers</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"></script>
+<script src="/speculation-rules/prerender/resources/utils.js"></script>
+<script src="/speculation-rules/resources/utils.js"></script>
+<script src="/speculation-rules/prefetch/resources/utils.sub.js"></script>
+<script>
+"use strict";
+
+setup(() => assertSpeculationRulesIsSupported());
+
+promise_test(async t => {
+ const agent = await spawnWindow(t);
+ const nextUrl = agent.getExecutorURL({ page: 2 });
+ await agent.forceSpeculationRules({
+ prefetch: [
+ {source: "list", "tag": "conservative",
+ "eagerness": "conservative", urls: [nextUrl]},
+ {source: "list", "tag": "moderate",
+ "eagerness": "moderate", urls: [nextUrl]}
+ ]
+ });
+
+ const linkId = 'target';
+ await agent.execute_script(async (linkId, nextUrl) => {
+ add_link(linkId, nextUrl);
+ }, [linkId, nextUrl]);
+
+ await agent.execute_script(async (linkId) => {
+ await start_non_eager_prefetch_on_pointerdown(linkId);
+ }, [linkId]);
+ await agent.navigate(nextUrl);
+
+ const headers = await agent.getRequestHeaders();
+ assert_prefetched(headers, "must be prefetched");
+ assert_equals(headers.sec_speculation_tags, "\"conservative\", \"moderate\"", "Sec-Speculation-Tags");
+}, "Sec-Speculation-Tags prefetch with eagerness triggered by point down");
+
+</script>
diff --git a/tests/wpt/tests/speculation-rules/speculation-tags/prefetch-eagerness-pointer-hover.https.html b/tests/wpt/tests/speculation-rules/speculation-tags/prefetch-eagerness-pointer-hover.https.html
new file mode 100644
index 00000000000..f5e749b9c6f
--- /dev/null
+++ b/tests/wpt/tests/speculation-rules/speculation-tags/prefetch-eagerness-pointer-hover.https.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<title>Sec-Speculation-Tags request headers</title>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"></script>
+<script src="/speculation-rules/prerender/resources/utils.js"></script>
+<script src="/speculation-rules/resources/utils.js"></script>
+<script src="/speculation-rules/prefetch/resources/utils.sub.js"></script>
+<script>
+"use strict";
+
+setup(() => assertSpeculationRulesIsSupported());
+
+promise_test(async t => {
+ const agent = await spawnWindow(t);
+ const nextUrl = agent.getExecutorURL({ page: 2 });
+ await agent.forceSpeculationRules({
+ prefetch: [
+ {source: "list", "tag": "conservative",
+ "eagerness": "conservative", urls: [nextUrl]},
+ {source: "list", "tag": "moderate",
+ "eagerness": "moderate", urls: [nextUrl]}
+ ]
+ });
+
+ const linkId = 'target';
+ await agent.execute_script(async (linkId, nextUrl) => {
+ add_link(linkId, nextUrl);
+ }, [linkId, nextUrl]);
+
+ await agent.execute_script(async (linkId) => {
+ await start_non_eager_prefetch_on_hover(linkId);
+ }, [linkId]);
+
+ // TODO(crbug.com/381687257): Remove this when 0ms hover trigger is supported.
+ // Wait for longer than 200 ms for the onhover trigger to fire.
+ await new Promise(resolve => t.step_timeout(resolve, 500));
+ await agent.navigate(nextUrl);
+
+ const headers = await agent.getRequestHeaders();
+ assert_prefetched(headers, "must be prefetched");
+ assert_equals(headers.sec_speculation_tags, "\"moderate\"", "Sec-Speculation-Tags");
+}, "Sec-Speculation-Tags prefetch with eagerness triggered by point hover");
+
+</script>
diff --git a/tests/wpt/tests/speech-api/SpeechRecognition-installOnDevice.https.html b/tests/wpt/tests/speech-api/SpeechRecognition-installOnDevice.https.html
index 1d1dd35edc2..2f5b359c571 100644
--- a/tests/wpt/tests/speech-api/SpeechRecognition-installOnDevice.https.html
+++ b/tests/wpt/tests/speech-api/SpeechRecognition-installOnDevice.https.html
@@ -2,17 +2,39 @@
<title>SpeechRecognition installOnDevice</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
<script>
promise_test(async (t) => {
const validLang = "en-US";
const invalidLang = "invalid language code";
- window.SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
+ const validDownloadableLang = "fr-FR";
+ window.SpeechRecognition =
+ window.SpeechRecognition || window.webkitSpeechRecognition;
- // Test that it returns a promise.
- const validResultPromise = SpeechRecognition.installOnDevice(validLang);
+ // Attempt to call installOnDevice directly, without a user gesture with a
+ // language that is downloadable but not installed.
+ const installWithoutUserGesturePromise =
+ SpeechRecognition.installOnDevice(validDownloadableLang);
+
+ // Assert that the promise rejects with NotAllowedError.
+ await promise_rejects_dom(
+ t,
+ "NotAllowedError",
+ window.DOMException,
+ installWithoutUserGesturePromise,
+ "SpeechRecognition.installOnDevice() must reject with NotAllowedError if " +
+ "called without a user gesture."
+ );
+
+ // Test that it returns a promise when called with a valid language.
+ const validResultPromise = test_driver.bless(
+ "Call SpeechRecognition.installOnDevice with a valid language",
+ () => SpeechRecognition.installOnDevice(validLang)
+ );
assert_true(
validResultPromise instanceof Promise,
- "installOnDevice should return a Promise."
+ "installOnDevice (with gesture) should return a Promise."
);
// Verify the resolved value is a boolean.
@@ -22,15 +44,17 @@ promise_test(async (t) => {
"The resolved value of the installOnDevice promise should be a boolean."
);
- // Verify that the method returns true when called with a supported language code.
+ // Verify that the method returns true when called with a supported language.
assert_equals(
validResult,
true,
- "installOnDevice should resolve with `true` when called with a supported language code."
+ "installOnDevice should resolve with `true` when called with a " +
+ "supported language code."
);
// Verify that the newly installed language pack is available.
- const availableOnDeviceResultPromise = SpeechRecognition.availableOnDevice(validLang);
+ const availableOnDeviceResultPromise =
+ SpeechRecognition.availableOnDevice(validLang);
assert_true(
availableOnDeviceResultPromise instanceof Promise,
"availableOnDevice should return a Promise."
@@ -42,28 +66,58 @@ promise_test(async (t) => {
"The resolved value of the availableOnDevice promise should be a string."
);
- assert_true(availableOnDeviceResult === "available",
- "The resolved value of the availableOnDevice promise should be available."
+ assert_true(
+ availableOnDeviceResult === "available",
+ "The resolved value of the availableOnDevice promise should be 'available'."
);
- // Verify that installing an already installed language pack resolves to true.
- const secondResultPromise = SpeechRecognition.installOnDevice(validLang);
+ // Verify that installing an already installed language resolves to true.
+ const secondResultPromise = test_driver.bless(
+ "Call SpeechRecognition.installOnDevice for an already installed language",
+ () => SpeechRecognition.installOnDevice(validLang)
+ );
+ assert_true(
+ secondResultPromise instanceof Promise,
+ "installOnDevice (with gesture, for already installed language) should " +
+ "return a Promise."
+ );
const secondResult = await secondResultPromise;
+ assert_true(
+ typeof secondResult === "boolean",
+ "The resolved value of the second installOnDevice promise should be a " +
+ "boolean."
+ );
assert_equals(
secondResult,
true,
- "installOnDevice should resolve with `true` if the language is already installed."
+ "installOnDevice should resolve with `true` if the language is already " +
+ "installed."
);
- // Test that it returns a promise.
- const invalidResultPromise = SpeechRecognition.installOnDevice(invalidLang);
+ // Test that it returns a promise and resolves to false for unsupported lang.
+ const invalidResultPromise = test_driver.bless(
+ "Call SpeechRecognition.installOnDevice with an unsupported language code",
+ () => SpeechRecognition.installOnDevice(invalidLang)
+ );
+ assert_true(
+ invalidResultPromise instanceof Promise,
+ "installOnDevice (with gesture, for unsupported language) should return " +
+ "a Promise."
+ );
const invalidResult = await invalidResultPromise;
+ assert_true(
+ typeof invalidResult === "boolean",
+ "The resolved value of the installOnDevice promise (unsupported language) " +
+ "should be a boolean."
+ );
assert_equals(
invalidResult,
false,
- "installOnDevice should resolve with `false` when called with an unsupported language code."
+ "installOnDevice should resolve with `false` when called with an " +
+ "unsupported language code."
);
-}, "SpeechRecognition.installOnDevice resolves with a boolean value.");
+}, "SpeechRecognition.installOnDevice resolves with a boolean value " +
+ "(with user gesture).");
promise_test(async (t) => {
const iframe = document.createElement("iframe");
@@ -78,7 +132,13 @@ promise_test(async (t) => {
t,
"InvalidStateError",
frameDOMException,
- frameSpeechRecognition.installOnDevice("en-US"),
+ test_driver.bless(
+ "Call SpeechRecognition.installOnDevice in a detached frame context",
+ () => {
+ return frameSpeechRecognition.installOnDevice("en-US");
+ }
+ )
);
-}, "SpeechRecognition.installOnDevice rejects in a detached context.");
+}, "SpeechRecognition.installOnDevice rejects in a detached context " +
+ "(with user gesture).");
</script>
diff --git a/tests/wpt/tests/streams/readable-streams/crashtests/garbage-collection.any.js b/tests/wpt/tests/streams/readable-streams/crashtests/garbage-collection.any.js
index cf10f7f2e83..6e9d80c4142 100644
--- a/tests/wpt/tests/streams/readable-streams/crashtests/garbage-collection.any.js
+++ b/tests/wpt/tests/streams/readable-streams/crashtests/garbage-collection.any.js
@@ -14,35 +14,6 @@ promise_test(async () => {
await garbageCollect();
}, 'Garbage-collecting a stream along with its reader should not crash');
-
-// See https://crbug.com/390646657 for details.
-promise_test(async () => {
- const written = new WritableStream({
- write(chunk) {
- return new Promise(resolve => {});
- }
- }).getWriter().write('just nod if you can hear me');
- for (let i = 0; i < 5; ++i)
- await garbageCollect();
-}, 'Garbage-collecting a stream writer with a pending write should not crash');
-
-
-promise_test(async () => {
- const closed = new WritableStream({
- write(chunk) { }
- }).getWriter().closed;
- for (let i = 0; i < 5; ++i)
- await garbageCollect();
-}, 'Garbage-collecting a stream writer should not crash with closed promise is retained');
-
-promise_test(async () => {
- const ready = new WritableStream({
- write(chunk) { }
- }, {highWaterMark: 0}).getWriter().ready;
- for (let i = 0; i < 5; ++i)
- await garbageCollect();
-}, 'Garbage-collecting a stream writer should not crash when backpressure is being applied');
-
promise_test(async () => {
let reader = new ReadableStream({
pull() { }
diff --git a/tests/wpt/tests/streams/writable-streams/crashtests/garbage-collection.any.js b/tests/wpt/tests/streams/writable-streams/crashtests/garbage-collection.any.js
new file mode 100644
index 00000000000..a3796881c9f
--- /dev/null
+++ b/tests/wpt/tests/streams/writable-streams/crashtests/garbage-collection.any.js
@@ -0,0 +1,43 @@
+// META: global=window,worker
+// META: script=/common/gc.js
+'use strict';
+
+// See https://crbug.com/390646657 for details.
+promise_test(async () => {
+ const written = new WritableStream({
+ write(chunk) {
+ return new Promise(resolve => {});
+ }
+ }).getWriter().write('just nod if you can hear me');
+ for (let i = 0; i < 5; ++i)
+ await garbageCollect();
+}, 'Garbage-collecting a stream writer with a pending write should not crash');
+
+promise_test(async () => {
+ const closed = new WritableStream({
+ write(chunk) { }
+ }).getWriter().closed;
+ for (let i = 0; i < 5; ++i)
+ await garbageCollect();
+}, 'Garbage-collecting a stream writer should not crash with closed promise is retained');
+
+promise_test(async () => {
+ let writer = new WritableStream({
+ write(chunk) { return new Promise(resolve => {}); },
+ close() { return new Promise(resolve => {}); }
+ }).getWriter();
+ writer.write('is there anyone home?');
+ writer.close();
+ writer = null;
+ for (let i = 0; i < 5; ++i)
+ await garbageCollect();
+}, 'Garbage-collecting a stream writer should not crash with close promise pending');
+
+promise_test(async () => {
+ const ready = new WritableStream({
+ write(chunk) { }
+ }, {highWaterMark: 0}).getWriter().ready;
+ for (let i = 0; i < 5; ++i)
+ await garbageCollect();
+}, 'Garbage-collecting a stream writer should not crash when backpressure is being applied');
+
diff --git a/tests/wpt/tests/subresource-integrity/tentative/integrity-policy/parsing.https.html b/tests/wpt/tests/subresource-integrity/tentative/integrity-policy/parsing.https.html
new file mode 100644
index 00000000000..205854419a7
--- /dev/null
+++ b/tests/wpt/tests/subresource-integrity/tentative/integrity-policy/parsing.https.html
@@ -0,0 +1,119 @@
+<!doctype html>
+<head>
+ <meta name="timeout" content="long">
+ <meta name="variant" content="?type=enforce">
+ <meta name="variant" content="?type=report">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/common/dispatcher/dispatcher.js"></script>
+ <script src="/common/utils.js"></script>
+ <script src="/reporting/resources/report-helper.js"></script>
+</head>
+<body>
+<script>
+
+ const run_test = (test_case) => {
+ promise_test(async () => {
+ const REMOTE_EXECUTOR =
+ `/common/dispatcher/remote-executor.html?pipe=`;
+
+ let header_name = "Integrity-Policy";
+ const params = new URLSearchParams(location.search);
+ if (params.get('type') === "report") {
+ if (test_case.expected.blocked) {
+ return;
+ }
+ header_name += "-Report-Only";
+ }
+ const iframe_uuid = token();
+ const header =
+ `header(${header_name},${test_case.header_value})`;
+ const iframe_url =
+ `${REMOTE_EXECUTOR}${encodeURIComponent(header)}&uuid=${iframe_uuid}`;
+
+ const iframe = document.createElement('iframe');
+ iframe.src = iframe_url;
+ document.body.appendChild(iframe);
+
+ // Execute code directly from the iframe.
+ const ctx = new RemoteContext(iframe_uuid);
+ const result = await ctx.execute_script(async (test_case) => {
+ const resource_url = "/content-security-policy/resources/ran.js";
+ let report_observed_promise;
+
+ // Load a script with no integrity. If there's a policy in place, it
+ // would be blocked.
+ const loaded = await new Promise(resolve => {
+ const script = document.createElement('script');
+ script.onload = () => { resolve(true); };
+ script.onerror = () => { resolve(false); };
+ script.src = resource_url;
+ document.body.appendChild(script);
+ });
+ return { blocked: !loaded, ran: window.ran };
+ }, [test_case]);
+ assert_equals(!result.blocked, !!result.ran);
+ assert_equals(result.blocked, test_case.expected.blocked);
+ }, test_case.description);
+ };
+
+ const test_cases = [
+ {
+ description: "Ensure that test is working with a valid destination",
+ header_value: "blocked-destinations=\\(script\\)",
+ expected: {blocked: true},
+ },
+ {
+ description: "Ensure that test is working with a valid destination and source",
+ header_value: "blocked-destinations=\\(script\\)\\, sources=\\(inline\\)",
+ expected: {blocked: true},
+ },
+ {
+ description: "Ensure that an empty header does not block",
+ header_value: "",
+ expected: {blocked: false},
+ },
+ {
+ description: "Ensure that a destination header with a token value does not parse",
+ header_value: "blocked-destinations=script",
+ expected: {blocked: false},
+ },
+ {
+ description: "Ensure that a destination header with an inner list of strings does not parse",
+ header_value: 'blocked-destinations=\\("script"\\)',
+ expected: {blocked: false},
+ },
+ {
+ description: "Ensure that a destination header with an inner list of single-quote strings does not parse",
+ header_value: "blocked-destinations=\\('script'\\)",
+ expected: {blocked: false},
+ },
+ {
+ description: "Ensure that a destination header with an unclosed inner list does not parse",
+ header_value: "blocked-destinations=\\(script",
+ expected: {blocked: false},
+ },
+ {
+ description: "Ensure that a destination header with a malformed inner list does not parse",
+ header_value: "blocked-destinations=\\(script\\,style\\)",
+ expected: {blocked: false},
+ },
+ {
+ description: "Ensure that an unknown destination does not enforce a policy",
+ header_value: "blocked-destinations=\\(style\\)",
+ expected: {blocked: false},
+ },
+ {
+ description: "Ensure that an unknown source causes the policy to not be enforced",
+ header_value: "blocked-destinations=\\(script\\)\\, sources=\\(telepathy\\)",
+ expected: {blocked: false},
+ },
+ {
+ description: "Ensure that an invalid source causes the policy to not be enforced",
+ header_value: "blocked-destinations=\\(script\\)\\, sources=\\(invalid",
+ expected: {blocked: false},
+ },
+ ];
+ test_cases.map(run_test);
+</script>
+
diff --git a/tests/wpt/tests/subresource-integrity/tentative/integrity-policy/script.https.html b/tests/wpt/tests/subresource-integrity/tentative/integrity-policy/script.https.html
new file mode 100644
index 00000000000..783374db920
--- /dev/null
+++ b/tests/wpt/tests/subresource-integrity/tentative/integrity-policy/script.https.html
@@ -0,0 +1,239 @@
+<!doctype html>
+<meta name="timeout" content="long">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/dispatcher/dispatcher.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/reporting/resources/report-helper.js"></script>
+
+<body>
+<script>
+ const {ORIGIN} = get_host_info();
+ const getAbsoluteUrl = url => {
+ return new URL(url, window.location.href).href;
+ }
+
+ const check_report = async (reporting_endpoint, reporting_uuid, iframe_url, url, report_only) => {
+ const reports = await pollReports(reporting_endpoint, reporting_uuid);
+ const abs_iframe_url = getAbsoluteUrl(iframe_url);
+ checkReportExists(reports, 'integrity-violation', abs_iframe_url);
+ const abs_blocked_url = getAbsoluteUrl(url);
+ const report = getReport(reports, 'integrity-violation', abs_iframe_url, abs_blocked_url);
+ assert_not_equals(report, null);
+ assert_equals(report.body.documentURL, abs_iframe_url);
+ assert_equals(report.body.blockedURL, abs_blocked_url);
+ assert_equals(report.body.destination, "script");
+ assert_equals(report.body.reportOnly, report_only);
+ };
+ const blob = new Blob([`window.ran=true;`],
+ { type: 'application/javascript' });
+
+ const blob_url = URL.createObjectURL(blob);
+
+ // Generated using https://sha2.it/ed25519.html (In Chrome Canary, with Experimental Web Platform Features enabled)
+ const signature = encodeURIComponent(
+ 'header(Unencoded-Digest, sha-384=:tqyFpeo21WFM8HDeUtLqH20GUq\/q3D1R6mqTzW3RtyTZ3dAYZJhC1wUcnkgOE2ak:)' +
+ '|header(Signature-Input, signature=\\("unencoded-digest";sf\\); keyid="JrQLj5P\/89iXES9+vFgrIy29clF9CC\/oPPsw3c5D0bs="; tag="sri")' +
+ '|header(Signature, signature=:qM19uLskHm2TQG5LJcH/hY0n0BWWzYOJztVWYlwk0cZb3u0JdgUMre1J4Jn8Tma0x2u5/kPBfbXRMbB+X+vTBw==:)');
+
+ const test_cases = [
+ {
+ description: "Ensure that a script without integrity did not run",
+ url: "/content-security-policy/resources/ran.js",
+ cross_origin: true,
+ integrity: "",
+ policy_violation: true,
+ block: true,
+ endpoints: true,
+ expected: {blocked: ORIGIN + "/content-security-policy/resources/ran.js", ran: false },
+ },
+ {
+ description: "Ensure that a script with unknown integrity algorithm did not run",
+ url: "/content-security-policy/resources/ran.js",
+ cross_origin: true,
+ integrity: "foobar-AAAAAAAAAAAAAAAAAAAa",
+ policy_violation: true,
+ block: true,
+ endpoints: true,
+ expected: {blocked: ORIGIN + "/content-security-policy/resources/ran.js", ran: false },
+ },
+ {
+ description: "Ensure that a script without integrity algorithm runs and gets reported in report-only mode",
+ url: "/content-security-policy/resources/ran.js",
+ cross_origin: true,
+ integrity: "",
+ policy_violation: true,
+ block: false,
+ endpoints: true,
+ expected: {blocked: ORIGIN + "/content-security-policy/resources/ran.js", ran: true },
+ },
+ {
+ description: "Ensure that a no-cors script gets blocked",
+ url: "/content-security-policy/resources/ran.js",
+ cross_origin: false,
+ integrity: "sha384-tqyFpeo21WFM8HDeUtLqH20GUq/q3D1R6mqTzW3RtyTZ3dAYZJhC1wUcnkgOE2ak",
+ policy_violation: true,
+ block: true,
+ endpoints: true,
+ expected: {blocked: ORIGIN + "/content-security-policy/resources/ran.js", ran: false },
+ },
+ {
+ description: "Ensure that ReportingObserver gets called without endpoints",
+ url: "/content-security-policy/resources/ran.js",
+ cross_origin: false,
+ integrity: "sha384-tqyFpeo21WFM8HDeUtLqH20GUq/q3D1R6mqTzW3RtyTZ3dAYZJhC1wUcnkgOE2ak",
+ policy_violation: true,
+ block: true,
+ endpoints: false,
+ expected: {blocked: ORIGIN + "/content-security-policy/resources/ran.js", ran: false },
+ },
+ {
+ description: "Ensure that a script with integrity runs",
+ url: "/content-security-policy/resources/ran.js",
+ cross_origin: true,
+ integrity: "sha384-tqyFpeo21WFM8HDeUtLqH20GUq/q3D1R6mqTzW3RtyTZ3dAYZJhC1wUcnkgOE2ak",
+ policy_violation: false,
+ block: true,
+ endpoints: true,
+ expected: {blocked: "", ran: true },
+ },
+ {
+ description: "Ensure that a script with signature integrity runs",
+ url: "/content-security-policy/resources/ran.js?pipe=" + signature,
+ cross_origin: true,
+ integrity: "ed25519-JrQLj5P/89iXES9+vFgrIy29clF9CC/oPPsw3c5D0bs=",
+ policy_violation: false,
+ block: true,
+ endpoints: true,
+ expected: {blocked: "", ran: true },
+ },
+ {
+ description: "Ensure that a data URI script with no integrity runs",
+ url: "data:application/javascript,window.ran=true",
+ cross_origin: true,
+ integrity: "",
+ policy_violation: false,
+ block: true,
+ endpoints: true,
+ expected: {blocked: "", ran: true },
+ },
+ {
+ description: "Ensure that a no-CORS data URI script with no integrity runs",
+ url: "data:application/javascript,window.ran=true",
+ cross_origin: false,
+ integrity: "",
+ policy_violation: false,
+ block: true,
+ endpoints: true,
+ expected: {blocked: "", ran: true },
+ },
+ {
+ description: "Ensure that a blob URL script with no integrity runs",
+ url: blob_url,
+ cross_origin: true,
+ integrity: "",
+ policy_violation: false,
+ block: true,
+ endpoints: true,
+ expected: {blocked: "", ran: true },
+ },
+ {
+ description: "Ensure that a no-CORS blob URL script with no integrity runs",
+ url: blob_url,
+ cross_origin: false,
+ integrity: "",
+ policy_violation: false,
+ block: true,
+ endpoints: true,
+ expected: {blocked: "", ran: true },
+ }
+ ];
+ test_cases.map(test_case => {
+ promise_test(async () => {
+ const REMOTE_EXECUTOR =
+ `/common/dispatcher/remote-executor.html?pipe=`;
+ const iframe_uuid = token();
+
+ const params = new URLSearchParams(location.search);
+ if (params.get('type') === "report") {
+ if (test_case.expected.blocked) {
+ return;
+ }
+ header_name += "-Report-Only";
+ }
+ const reporting_uuid_1 = token();
+ const reporting_uuid_2 = token();
+ const reporting_uuid_3 = token();
+ const reporting_endpoint = `${ORIGIN}/reporting/resources/report.py`;
+ let header = "";
+ if (test_case.block) {
+ header +=
+ `header(Integrity-Policy,blocked-destinations=\\(script\\)\\, endpoints=\\(integrity-endpoint-1 integrity-endpoint-2\\))`;
+ }
+ header +=
+ `|header(Integrity-Policy-Report-Only,blocked-destinations=\\(script\\)\\, endpoints=\\(integrity-endpoint-3\\))`;
+ if (test_case.endpoints) {
+ header +=
+ `|header(Reporting-Endpoints, integrity-endpoint-1=\"${reporting_endpoint}?reportID=${reporting_uuid_1}\"\\, ` +
+ `integrity-endpoint-2=\"${reporting_endpoint}?reportID=${reporting_uuid_2}\"\\, ` +
+ `integrity-endpoint-3=\"${reporting_endpoint}?reportID=${reporting_uuid_3}\")`;
+ }
+ const iframe_url = `${REMOTE_EXECUTOR}${encodeURIComponent(header)}&uuid=${iframe_uuid}`;
+
+ const iframe = document.createElement('iframe');
+ iframe.src = iframe_url;
+ document.body.appendChild(iframe);
+
+ // Execute code directly from the iframe.
+ const ctx = new RemoteContext(iframe_uuid);
+ const result = await ctx.execute_script(async (test_case) => {
+ window.ran = false;
+ let report_observed_promise;
+ if (test_case.policy_violation) {
+ report_observed_promise = new Promise(r => {
+ (new ReportingObserver((reports, observer) => {
+ reports.forEach(report => {
+ if (report.body.blockedURL.endsWith(test_case.url)) {
+ r(report.body);
+ observer.disconnect();
+ }
+ });
+ })).observe('integrity-violation');
+ });
+ }
+
+ // Load the script
+ await new Promise(resolve => {
+ const script = document.createElement('script');
+ if (test_case.cross_origin) {
+ script.crossOrigin="anonymous";
+ }
+ if (test_case.integrity) {
+ script.integrity = test_case.integrity;
+ }
+ script.onload = resolve;
+ script.onerror = resolve;
+ script.src = test_case.url;
+ document.body.appendChild(script);
+ });
+ const report_body = await report_observed_promise;
+ return { body: report_body, ran: window.ran };
+ }, [test_case]);
+ assert_equals(result.ran, test_case.expected.ran);
+ if (test_case.policy_violation) {
+ assert_equals(result.body.blockedURL, test_case.expected.blocked);
+ assert_true(result.body.documentURL.endsWith(iframe_url));
+ assert_equals(result.body.destination, "script");
+ assert_equals(result.body.reportOnly, !test_case.block);
+ }
+ if (test_case.endpoints && test_case.policy_violation) {
+ if (test_case.block) {
+ await check_report(reporting_endpoint, reporting_uuid_1, iframe_url, test_case.url, !test_case.block);
+ await check_report(reporting_endpoint, reporting_uuid_2, iframe_url, test_case.url, !test_case.block);
+ }
+ await check_report(reporting_endpoint, reporting_uuid_3, iframe_url, test_case.url, true);
+ }
+ }, test_case.description);
+ });
+</script>
diff --git a/tests/wpt/tests/svg/animations/discard-check-removal-order.html b/tests/wpt/tests/svg/animations/discard-check-removal-order.html
deleted file mode 100644
index 2935a69adda..00000000000
--- a/tests/wpt/tests/svg/animations/discard-check-removal-order.html
+++ /dev/null
@@ -1,52 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<title>This tests for the remove order of discard elements.</title>
-<link rel="help" href="https://svgwg.org/specs/animations/#DiscardElement">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/SVGAnimationTestCase-testharness.js"></script>
-
-<body>
- <div id="removeOrder"></div>
- <svg id="svg" width="400" height="400">
- <rect id="rect" width="100" height="100" fill="green" />
- <animate id="animate" href="#rect" attributeName="x" from="0" to="300" dur="10s" />
- </svg>
- <script>
- const rootSVGElement = document.querySelector('svg');
-
- let removedNodes = new Array();
- let callback = function(mutations) {
- mutations.forEach(function(mutation) {
- let nodeList = mutation.removedNodes;
- for (let i = 0; i < nodeList.length; ++i) {
- removedNodes.push(nodeList[i].nodeName);
- }
- });
- };
- let observer = new MutationObserver(callback);
- observer.observe(rootSVGElement, { 'childList': true, 'subtree': true });
-
- function checkRemovedNodes(array) {
- assert_array_equals(removedNodes, array, 'removed nodes');
- }
-
- function discardElement(id) {
- let discard = createSVGElement("discard");
- discard.setAttribute("href", "#" + id);
- rootSVGElement.appendChild(discard);
- }
-
- discardElement("animate");
- discardElement("rect");
-
- smil_async_test(t => {
- runAnimationTest(t, [
- // [animationId, time, sampleCallback]
- ['anim', 1, checkRemovedNodes.bind(this, ['animate', 'discard', 'rect', 'discard'])],
- ]);
- });
- window.animationStartsImmediately = true;
- </script>
-</body>
-</html>
diff --git a/tests/wpt/tests/svg/animations/reftests/reference/green-100x100.svg b/tests/wpt/tests/svg/animations/reftests/reference/green-100x100.svg
deleted file mode 100644
index 120941444a4..00000000000
--- a/tests/wpt/tests/svg/animations/reftests/reference/green-100x100.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg">
- <rect width="100" height="100" fill="green"/>
-</svg>
diff --git a/tests/wpt/tests/svg/embedded/WEB_FEATURES.yml b/tests/wpt/tests/svg/embedded/WEB_FEATURES.yml
new file mode 100644
index 00000000000..8482e555584
--- /dev/null
+++ b/tests/wpt/tests/svg/embedded/WEB_FEATURES.yml
@@ -0,0 +1,4 @@
+features:
+- name: fetch-priority
+ files:
+ - attr-image-fetchpriority.html
diff --git a/tests/wpt/tests/svg/idlharness.window.js b/tests/wpt/tests/svg/idlharness.window.js
index b363128c25a..57beca77fd4 100644
--- a/tests/wpt/tests/svg/idlharness.window.js
+++ b/tests/wpt/tests/svg/idlharness.window.js
@@ -42,7 +42,6 @@ const elements = [
'script',
'animate',
'set',
- 'discard',
'animateMotion',
'mpath',
'animateTransform',
@@ -111,7 +110,6 @@ idl_test(
SVGGElement: ['objects.g'],
SVGDefsElement: ['objects.defs'],
SVGDescElement: ['objects.desc'],
- SVGDiscardElement: ['objects.discard'],
SVGTitleElement: ['objects.title'],
SVGSymbolElement: ['objects.symbol'],
SVGUseElement: ['objects.use'],
@@ -168,7 +166,6 @@ idl_test(
SVGViewElement: ['objects.view'],
SVGScriptElement: ['objects.script'],
SVGAnimateElement: ['objects.animate'],
- SVGDiscardElement: ['objects.discard'],
SVGSetElement: ['objects.set'],
SVGAnimateMotionElement: ['objects.animateMotion'],
SVGMPathElement: ['objects.mpath'],
diff --git a/tests/wpt/tests/svg/scripted/WEB_FEATURES.yml b/tests/wpt/tests/svg/scripted/WEB_FEATURES.yml
new file mode 100644
index 00000000000..864bd3398ee
--- /dev/null
+++ b/tests/wpt/tests/svg/scripted/WEB_FEATURES.yml
@@ -0,0 +1,4 @@
+features:
+- name: fetch-priority
+ files:
+ - attr-script-fetchpriority.html
diff --git a/tests/wpt/tests/svg/styling/presentation-attributes-special-cases.html b/tests/wpt/tests/svg/styling/presentation-attributes-special-cases.html
index 7bfa001030a..9a5da751ce1 100644
--- a/tests/wpt/tests/svg/styling/presentation-attributes-special-cases.html
+++ b/tests/wpt/tests/svg/styling/presentation-attributes-special-cases.html
@@ -98,7 +98,7 @@ if (CSS.supports("d", "initial")) {
// animation elements.
if (CSS.supports("fill", "initial")) {
- for (let e of ["animate", "animateMotion", "animateTransform", "discard", "set"]) {
+ for (let e of ["animate", "animateMotion", "animateTransform", "set"]) {
test(function() {
assertPresentationAttributeIsNotSupported(e, "fill", "blue", "fill");
}, `fill presentation attribute not supported on ${e}`);
diff --git a/tests/wpt/tests/tools/webdriver/webdriver/bidi/modules/emulation.py b/tests/wpt/tests/tools/webdriver/webdriver/bidi/modules/emulation.py
index e587a082c03..fd5cf733ab7 100644
--- a/tests/wpt/tests/tools/webdriver/webdriver/bidi/modules/emulation.py
+++ b/tests/wpt/tests/tools/webdriver/webdriver/bidi/modules/emulation.py
@@ -35,11 +35,16 @@ class Emulation(BidiModule):
def set_geolocation_override(
self,
coordinates: Union[CoordinatesOptions, Undefined] = UNDEFINED,
+ error: Optional[Dict[str, Any]] = None,
contexts: Optional[List[str]] = None,
user_contexts: Optional[List[str]] = None,
) -> Mapping[str, Any]:
- params: MutableMapping[str, Any] = {"coordinates": coordinates}
+ params: MutableMapping[str, Any] = {}
+ if coordinates is not UNDEFINED:
+ params["coordinates"] = coordinates
+ if error is not None:
+ params["error"] = error
if contexts is not None:
params["contexts"] = contexts
if user_contexts is not None:
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/android_webview.py b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/android_webview.py
index 0e02d3c02cd..c796be18a73 100644
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/android_webview.py
+++ b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/android_webview.py
@@ -88,7 +88,6 @@ class SystemWebViewShell(ChromeAndroidBrowserBase):
def __init__(self, logger, binary, webdriver_binary="chromedriver",
adb_binary=None,
- remote_queue=None,
device_serial=None,
webdriver_args=None,
stackwalk_binary=None,
@@ -96,7 +95,7 @@ class SystemWebViewShell(ChromeAndroidBrowserBase):
"""Creates a new representation of Chrome. The `binary` argument gives
the browser binary to use for testing."""
super().__init__(logger,
- webdriver_binary, adb_binary, remote_queue,
+ webdriver_binary, adb_binary,
device_serial, webdriver_args, stackwalk_binary,
symbols_path)
self.binary = binary
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/chrome_android.py b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/chrome_android.py
index 820323e615a..4ed7707c3be 100644
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/chrome_android.py
+++ b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/chrome_android.py
@@ -3,7 +3,7 @@
import mozprocess
import subprocess
-from .base import cmd_arg, require_arg
+from .base import OutputHandler, cmd_arg, require_arg
from .base import get_timeout_multiplier # noqa: F401
from .base import WebDriverBrowser # noqa: F401
from .chrome import executor_kwargs as chrome_executor_kwargs
@@ -79,10 +79,9 @@ def env_options():
class LogcatRunner:
- def __init__(self, logger, browser, remote_queue):
+ def __init__(self, logger, browser):
self.logger = logger
self.browser = browser
- self.remote_queue = remote_queue
def start(self):
try:
@@ -99,43 +98,31 @@ class LogcatRunner:
self.logger.error("Failed to clear logcat buffer")
self._cmd = self.browser.logcat_cmd()
+ self._output_handler = OutputHandler(self.logger, self._cmd)
self._proc = mozprocess.ProcessHandler(
self._cmd,
- processOutputLine=self.on_output,
+ processOutputLine=self._output_handler,
storeOutput=False)
self._proc.run()
-
- def _send_message(self, command, *args):
- try:
- self.remote_queue.put((command, args))
- except AssertionError:
- self.logger.warning("Error when send to remote queue")
+ self._output_handler.after_process_start(self._proc.pid)
+ self._output_handler.start()
def stop(self, force=False):
if self.is_alive():
kill_result = self._proc.kill()
if force and kill_result != 0:
self._proc.kill(9)
+ self._output_handler.after_process_stop()
def is_alive(self):
return hasattr(self._proc, "proc") and self._proc.poll() is None
- def on_output(self, line):
- data = {
- "action": "process_output",
- "process": "LOGCAT",
- "command": "logcat",
- "data": line
- }
- self._send_message("log", data)
-
class ChromeAndroidBrowserBase(WebDriverBrowser):
def __init__(self,
logger,
webdriver_binary="chromedriver",
adb_binary=None,
- remote_queue=None,
device_serial=None,
webdriver_args=None,
stackwalk_binary=None,
@@ -148,15 +135,11 @@ class ChromeAndroidBrowserBase(WebDriverBrowser):
self.device_serial = device_serial
self.stackwalk_binary = stackwalk_binary
self.symbols_path = symbols_path
- self.remote_queue = remote_queue
-
- if self.remote_queue is not None:
- self.logcat_runner = LogcatRunner(self.logger, self, self.remote_queue)
+ self.logcat_runner = LogcatRunner(self.logger, self)
def setup(self):
self.setup_adb_reverse()
- if self.remote_queue is not None:
- self.logcat_runner.start()
+ self.logcat_runner.start()
def _adb_run(self, args):
cmd = [self.adb_binary]
@@ -176,8 +159,7 @@ class ChromeAndroidBrowserBase(WebDriverBrowser):
super().cleanup()
self._adb_run(['forward', '--remove-all'])
self._adb_run(['reverse', '--remove-all'])
- if self.remote_queue is not None:
- self.logcat_runner.stop(force=True)
+ self.logcat_runner.stop(force=True)
def executor_browser(self):
cls, kwargs = super().executor_browser()
@@ -231,13 +213,12 @@ class ChromeAndroidBrowser(ChromeAndroidBrowserBase):
def __init__(self, logger, package_name,
webdriver_binary="chromedriver",
adb_binary=None,
- remote_queue = None,
device_serial=None,
webdriver_args=None,
stackwalk_binary=None,
symbols_path=None):
super().__init__(logger,
- webdriver_binary, adb_binary, remote_queue,
+ webdriver_binary, adb_binary,
device_serial, webdriver_args, stackwalk_binary,
symbols_path)
self.package_name = package_name
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/firefox.py b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/firefox.py
index 494a7514efd..c63bfa2ceeb 100644
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/firefox.py
+++ b/tests/wpt/tests/tools/wptrunner/wptrunner/browsers/firefox.py
@@ -220,6 +220,7 @@ def run_info_extras(logger, default_prefs=None, **kwargs):
not bool_pref("fission.disableSessionHistoryInParent")),
"swgl": bool_pref("gfx.webrender.software"),
"privateBrowsing": bool_pref("browser.privatebrowsing.autostart"),
+ "remoteAsyncEvents": bool_pref("remote.events.async.wheel.enabled"),
"incOriginInit": os.environ.get("MOZ_ENABLE_INC_ORIGIN_INIT") == "1",
}
rv.update(run_info_browser_version(**kwargs))
@@ -250,6 +251,7 @@ def update_properties():
"swgl",
"asan",
"tsan",
+ "remoteAsyncEvents",
"sessionHistoryInParent",
"subsuite"], {
"os": ["version"],
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/executors/asyncactions.py b/tests/wpt/tests/tools/wptrunner/wptrunner/executors/asyncactions.py
index 9925a4b511d..8397d7838a3 100644
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/executors/asyncactions.py
+++ b/tests/wpt/tests/tools/wptrunner/wptrunner/executors/asyncactions.py
@@ -1,4 +1,5 @@
# mypy: allow-untyped-defs
+from webdriver.bidi.undefined import UNDEFINED
webdriver = None
@@ -7,6 +8,22 @@ def do_delayed_imports():
global webdriver
import webdriver
+
+def get_browsing_context_id(context):
+ """
+ :param context: Either a string representing the browsing context id, or a
+ BiDi serialized window proxy object. In the latter case, the value is
+ extracted from the serialized object.
+ :return: The browsing context id.
+ """
+ if isinstance(context, str):
+ return context
+ elif isinstance(context, webdriver.bidi.protocol.BidiWindow):
+ # Context can be a serialized WindowProxy.
+ return context.browsing_context
+ raise ValueError("Unexpected context type: %s" % context)
+
+
class BidiBluetoothHandleRequestDevicePrompt:
name = "bidi.bluetooth.handle_request_device_prompt"
@@ -16,18 +33,10 @@ class BidiBluetoothHandleRequestDevicePrompt:
self.protocol = protocol
async def __call__(self, payload):
- if payload["context"] is None:
+ if "context" not in payload:
raise ValueError("Missing required parameter: context")
- context = payload["context"]
- if isinstance(context, str):
- pass
- elif isinstance(context, webdriver.bidi.protocol.BidiWindow):
- # Context can be a serialized WindowProxy.
- context = context.browsing_context
- else:
- raise ValueError("Unexpected context type: %s" % context)
-
+ context = get_browsing_context_id(payload["context"])
prompt = payload["prompt"]
accept = payload["accept"]
device = payload["device"]
@@ -42,17 +51,10 @@ class BidiBluetoothSimulateAdapterAction:
self.protocol = protocol
async def __call__(self, payload):
- if payload["context"] is None:
+ if "context" not in payload:
raise ValueError("Missing required parameter: context")
- context = payload["context"]
- if isinstance(context, str):
- pass
- elif isinstance(context, webdriver.bidi.protocol.BidiWindow):
- # Context can be a serialized WindowProxy.
- context = context.browsing_context
- else:
- raise ValueError("Unexpected context type: %s" % context)
+ context = get_browsing_context_id(payload["context"])
state = payload["state"]
return await self.protocol.bidi_bluetooth.simulate_adapter(context,
@@ -68,17 +70,9 @@ class BidiBluetoothSimulatePreconnectedPeripheralAction:
self.protocol = protocol
async def __call__(self, payload):
- if payload["context"] is None:
+ if "context" not in payload:
raise ValueError("Missing required parameter: context")
-
- context = payload["context"]
- if isinstance(context, str):
- pass
- elif isinstance(context, webdriver.bidi.protocol.BidiWindow):
- # Context can be a serialized WindowProxy.
- context = context.browsing_context
- else:
- raise ValueError("Unexpected context type: %s" % context)
+ context = get_browsing_context_id(payload["context"])
address = payload["address"]
name = payload["name"]
@@ -97,22 +91,29 @@ class BidiEmulationSetGeolocationOverrideAction:
self.protocol = protocol
async def __call__(self, payload):
- coordinates = payload['coordinates']
+ if "error" in payload and "coordinates" in payload:
+ raise ValueError(
+ "Params `error` and `coordinates` are mutually exclusive")
+
+ # If `error` is present, set it. Otherwise, do not pass it (error: None).
+ # Note, unlike `coordinates`, `error` cannot be `UNDEFINED`. It's either
+ # `None` and it's not passed, or some dict value which is passed.
+ error = payload['error'] if 'error' in payload else None
+ # If `error` is present, do not pass `coordinates` (coordinates: UNDEFINED).
+ # Otherwise, remove emulation (coordinates: None).
+ coordinates = payload['coordinates'] if 'coordinates' in payload else (
+ None if error is None else UNDEFINED)
+
+ if "contexts" not in payload:
+ raise ValueError("Missing required parameter: contexts")
contexts = []
for context in payload["contexts"]:
- # Context can be either a browsing context id, or a BiDi serialized window. In the latter case, the
- # value is extracted from the serialized object.
- if isinstance(context, str):
- contexts.append(context)
- elif isinstance(context, webdriver.bidi.protocol.BidiWindow):
- contexts.append(context.browsing_context)
- else:
- raise ValueError("Unexpected context type: %s" % context)
+ contexts.append(get_browsing_context_id(context))
if len(contexts) == 0:
raise ValueError("At least one context must be provided")
return await self.protocol.bidi_emulation.set_geolocation_override(
- coordinates, contexts)
+ coordinates, error, contexts)
class BidiSessionSubscribeAction:
@@ -126,17 +127,10 @@ class BidiSessionSubscribeAction:
async def __call__(self, payload):
events = payload["events"]
contexts = None
- if payload["contexts"] is not None:
+ if "contexts" in payload and payload["contexts"] is not None:
contexts = []
for context in payload["contexts"]:
- # Context can be either a browsing context id, or a BiDi serialized window. In the latter case, the
- # value is extracted from the serialized object.
- if isinstance(context, str):
- contexts.append(context)
- elif isinstance(context, webdriver.bidi.protocol.BidiWindow):
- contexts.append(context.browsing_context)
- else:
- raise ValueError("Unexpected context type: %s" % context)
+ contexts.append(get_browsing_context_id(context))
return await self.protocol.bidi_events.subscribe(events, contexts)
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/executors/executorwebdriver.py b/tests/wpt/tests/tools/wptrunner/wptrunner/executors/executorwebdriver.py
index 87403c2944d..7ca46a05a7b 100644
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/executors/executorwebdriver.py
+++ b/tests/wpt/tests/tools/wptrunner/wptrunner/executors/executorwebdriver.py
@@ -272,9 +272,9 @@ class WebDriverBidiEmulationProtocolPart(BidiEmulationProtocolPart):
def setup(self):
self.webdriver = self.parent.webdriver
- async def set_geolocation_override(self, coordinates, contexts):
+ async def set_geolocation_override(self, coordinates, error, contexts):
return await self.webdriver.bidi_session.emulation.set_geolocation_override(
- coordinates=coordinates, contexts=contexts)
+ coordinates=coordinates, error=error, contexts=contexts)
class WebDriverBidiPermissionsProtocolPart(BidiPermissionsProtocolPart):
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/executors/protocol.py b/tests/wpt/tests/tools/wptrunner/wptrunner/executors/protocol.py
index 833dff45636..16eb3cbb4a5 100644
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/executors/protocol.py
+++ b/tests/wpt/tests/tools/wptrunner/wptrunner/executors/protocol.py
@@ -5,7 +5,10 @@ import traceback
from http.client import HTTPConnection
from abc import ABCMeta, abstractmethod
-from typing import Any, Awaitable, Callable, ClassVar, List, Mapping, Optional, Tuple, Type
+from typing import Any, Awaitable, Callable, ClassVar, List, Mapping, Optional, \
+ Tuple, Type, Union
+
+from webdriver.bidi.undefined import Undefined
def merge_dicts(target, source):
@@ -452,7 +455,8 @@ class BidiEmulationProtocolPart(ProtocolPart):
@abstractmethod
async def set_geolocation_override(self,
- coordinates: Optional[Mapping[str, Any]],
+ coordinates: Optional[Union[Mapping[str, Any], Undefined]],
+ error: Optional[Mapping[str, Any]],
contexts: List[str]) -> None:
pass
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/formatters/chromium.py b/tests/wpt/tests/tools/wptrunner/wptrunner/formatters/chromium.py
deleted file mode 100644
index 95f53011bfc..00000000000
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/formatters/chromium.py
+++ /dev/null
@@ -1,338 +0,0 @@
-# mypy: allow-untyped-defs
-
-import functools
-import json
-import time
-
-from collections import defaultdict
-from mozlog.formatters import base
-
-from wptrunner.wptmanifest import serializer
-
-_escape_heading = functools.partial(serializer.escape, extras="]")
-
-
-class ChromiumFormatter(base.BaseFormatter): # type: ignore
- """Formatter to produce results matching the Chromium JSON Test Results format.
- https://chromium.googlesource.com/chromium/src/+/master/docs/testing/json_test_results_format.md
-
- Notably, each test has an "artifacts" field that is a dict consisting of
- "log": a list of strings (one per subtest + one for harness status, see
- _append_test_message for the format)
- "screenshots": a list of strings in the format of "url: base64"
-
- """
-
- def __init__(self):
- # Whether the run was interrupted, either by the test runner or user.
- self.interrupted = False
-
- # A map of test status to the number of tests that had that status.
- self.num_failures_by_status = defaultdict(int)
-
- # Start time, expressed as offset since UNIX epoch in seconds. Measured
- # from the first `suite_start` event.
- self.start_timestamp_seconds = None
-
- # A map of test names to test start timestamps, expressed in seconds
- # since UNIX epoch. Only contains tests that are currently running
- # (i.e., have not received the `test_end` event).
- self.test_starts = {}
-
- # Trie of test results. Each directory in the test name is a node in
- # the trie and the leaf contains the dict of per-test data.
- self.tests = {}
-
- # Two dictionaries keyed by test name. Values are lists of strings:
- # actual metadata content and other messages, respectively.
- # See _append_test_message for examples.
- self.actual_metadata = defaultdict(list)
- self.messages = defaultdict(list)
-
- # List of tests that have failing subtests.
- self.tests_with_subtest_fails = set()
-
- # Browser log for the current test under execution.
- # These logs are from ChromeDriver's stdout/err, so we cannot say for
- # sure which test a message is from, but instead we correlate them based
- # on timing.
- self.browser_log = []
-
- def _append_test_message(self, test, subtest, wpt_actual_status, message):
- r"""
- Appends the message data for a test or subtest.
-
- :param str test: the name of the test
- :param str subtest: the name of the subtest with the message. Will be
- None if this is called for a test.
- :param str wpt_actual_status: the test status as reported by WPT
- :param str message: the string to append to the message for this test
-
- Example actual_metadata of a test with a subtest:
- "[test_name]\n expected: OK\n"
- " [subtest_name]\n expected: FAIL\n"
-
- NOTE: throughout this function we output a key called "expected" but
- fill it in with the actual status. This is by design. The goal of this
- output is to look exactly like WPT's expectation metadata so that it
- can be easily diff-ed.
-
- Messages are appended verbatim to self.messages[test].
- """
- if subtest:
- result = " [%s]\n expected: %s\n" % (_escape_heading(subtest),
- wpt_actual_status)
- self.actual_metadata[test].append(result)
- if message:
- self.messages[test].append("%s: %s\n" % (subtest, message))
- else:
- # No subtest, so this is the top-level test. The result must be
- # prepended to the list, so that it comes before any subtest.
- test_name_last_part = test.split("/")[-1]
- result = "[%s]\n expected: %s\n" % (
- _escape_heading(test_name_last_part), wpt_actual_status)
- self.actual_metadata[test].insert(0, result)
- if message:
- self.messages[test].insert(0, "Harness: %s\n" % message)
-
- def _append_artifact(self, cur_dict, artifact_name, artifact_value):
- """
- Appends artifacts to the specified dictionary.
- :param dict cur_dict: the test leaf dictionary to append to
- :param str artifact_name: the name of the artifact
- :param str artifact_value: the value of the artifact
- """
- assert isinstance(artifact_value, str), "artifact_value must be a str"
- if "artifacts" not in cur_dict.keys():
- cur_dict["artifacts"] = defaultdict(list)
- cur_dict["artifacts"][artifact_name].append(artifact_value)
-
- def _store_test_result(self, name, actual, expected, actual_metadata,
- messages, wpt_actual, subtest_failure,
- duration=None, reftest_screenshots=None):
- """
- Stores the result of a single test in |self.tests|
-
- :param str name: name of the test.
- :param str actual: actual status of the test.
- :param str expected: expected statuses of the test.
- :param list actual_metadata: a list of metadata items.
- :param list messages: a list of test messages.
- :param str wpt_actual: actual status reported by wpt, may differ from |actual|.
- :param bool subtest_failure: whether this test failed because of subtests.
- :param Optional[float] duration: time it took in seconds to run this test.
- :param Optional[list] reftest_screenshots: see executors/base.py for definition.
- """
- # The test name can contain a leading / which will produce an empty
- # string in the first position of the list returned by split. We use
- # filter(None) to remove such entries.
- name_parts = filter(None, name.split("/"))
- cur_dict = self.tests
- for name_part in name_parts:
- cur_dict = cur_dict.setdefault(name_part, {})
- # Splitting and joining the list of statuses here avoids the need for
- # recursively postprocessing the |tests| trie at shutdown. We assume the
- # number of repetitions is typically small enough for the quadratic
- # runtime to not matter.
- statuses = cur_dict.get("actual", "").split()
- statuses.append(actual)
- cur_dict["actual"] = " ".join(statuses)
- cur_dict["expected"] = expected
- if duration is not None:
- # Record the time to run the first invocation only.
- cur_dict.setdefault("time", duration)
- durations = cur_dict.setdefault("times", [])
- durations.append(duration)
- if subtest_failure:
- self._append_artifact(cur_dict, "wpt_subtest_failure", "true")
- if wpt_actual != actual:
- self._append_artifact(cur_dict, "wpt_actual_status", wpt_actual)
- if wpt_actual == 'CRASH':
- for line in self.browser_log:
- self._append_artifact(cur_dict, "wpt_crash_log", line)
- for metadata in actual_metadata:
- self._append_artifact(cur_dict, "wpt_actual_metadata", metadata)
- for message in messages:
- self._append_artifact(cur_dict, "wpt_log", message)
-
- # Store screenshots (if any).
- for item in reftest_screenshots or []:
- if not isinstance(item, dict):
- # Skip the relation string.
- continue
- data = "%s: %s" % (item["url"], item["screenshot"])
- self._append_artifact(cur_dict, "screenshots", data)
-
- # Figure out if there was a regression, unexpected status, or flake.
- # This only happens for tests that were run
- if actual != "SKIP":
- if actual not in expected:
- cur_dict["is_unexpected"] = True
- if actual != "PASS":
- cur_dict["is_regression"] = True
- if len(set(statuses)) > 1:
- cur_dict["is_flaky"] = True
-
- # Update the count of how many tests ran with each status. Only includes
- # the first invocation's result in the totals.
- if len(statuses) == 1:
- self.num_failures_by_status[actual] += 1
-
- def _map_status_name(self, status):
- """
- Maps a WPT status to a Chromium status.
-
- Chromium has five main statuses that we have to map to:
- CRASH: the test harness crashed
- FAIL: the test did not run as expected
- PASS: the test ran as expected
- SKIP: the test was not run
- TIMEOUT: the did not finish in time and was aborted
-
- :param str status: the string status of a test from WPT
- :return: a corresponding string status for Chromium
- """
- if status == "OK":
- return "PASS"
- if status == "NOTRUN":
- return "SKIP"
- if status == "EXTERNAL-TIMEOUT":
- return "TIMEOUT"
- if status in ("ERROR", "PRECONDITION_FAILED"):
- return "FAIL"
- if status == "INTERNAL-ERROR":
- return "CRASH"
- # Any other status just gets returned as-is.
- return status
-
- def _get_expected_status_from_data(self, actual_status, data):
- """
- Gets the expected statuses from a |data| dictionary.
-
- If there is no expected status in data, the actual status is returned.
- This is because mozlog will delete "expected" from |data| if it is the
- same as "status". So the presence of "expected" implies that "status" is
- unexpected. Conversely, the absence of "expected" implies the "status"
- is expected. So we use the "expected" status if it's there or fall back
- to the actual status if it's not.
-
- If the test has multiple statuses, it will have other statuses listed as
- "known_intermittent" in |data|. If these exist, they will be added to
- the returned status with spaced in between.
-
- :param str actual_status: the actual status of the test
- :param data: a data dictionary to extract expected status from
- :return str: the expected statuses as a string
- """
- expected_statuses = self._map_status_name(data["expected"]) if "expected" in data else actual_status
- if data.get("known_intermittent"):
- all_statsues = {self._map_status_name(other_status) for other_status in data["known_intermittent"]}
- all_statsues.add(expected_statuses)
- expected_statuses = " ".join(sorted(all_statsues))
- return expected_statuses
-
- def _get_time(self, data):
- """Get the timestamp of a message in seconds since the UNIX epoch."""
- maybe_timestamp_millis = data.get("time")
- if maybe_timestamp_millis is not None:
- return float(maybe_timestamp_millis) / 1000
- return time.time()
-
- def _time_test(self, test_name, data):
- """Time how long a test took to run.
-
- :param str test_name: the name of the test to time
- :param data: a data dictionary to extract the test end timestamp from
- :return Optional[float]: a nonnegative duration in seconds or None if
- the measurement is unavailable or invalid
- """
- test_start = self.test_starts.pop(test_name, None)
- if test_start is not None:
- # The |data| dictionary only provides millisecond resolution
- # anyway, so further nonzero digits are unlikely to be meaningful.
- duration = round(self._get_time(data) - test_start, 3)
- if duration >= 0:
- return duration
- return None
-
- def suite_start(self, data):
- if self.start_timestamp_seconds is None:
- self.start_timestamp_seconds = self._get_time(data)
- if 'run_info' in data:
- self.flag_specific = data['run_info'].get('flag_specific', '')
-
- def test_start(self, data):
- test_name = data["test"]
- self.test_starts[test_name] = self._get_time(data)
-
- def test_status(self, data):
- test_name = data["test"]
- wpt_actual_status = data["status"]
- actual_status = self._map_status_name(wpt_actual_status)
- expected_statuses = self._get_expected_status_from_data(actual_status, data)
-
- is_unexpected = actual_status not in expected_statuses
- if is_unexpected and test_name not in self.tests_with_subtest_fails:
- self.tests_with_subtest_fails.add(test_name)
- # We should always get a subtest in the data dict, but it's technically
- # possible that it's missing. Be resilient here.
- subtest_name = data.get("subtest", "UNKNOWN SUBTEST")
- self._append_test_message(test_name, subtest_name,
- wpt_actual_status, data.get("message", ""))
-
- def test_end(self, data):
- test_name = data["test"]
- # Save the status reported by WPT since we might change it when
- # reporting to Chromium.
- wpt_actual_status = data["status"]
- actual_status = self._map_status_name(wpt_actual_status)
- expected_statuses = self._get_expected_status_from_data(actual_status, data)
- duration = self._time_test(test_name, data)
- subtest_failure = False
- if test_name in self.tests_with_subtest_fails:
- subtest_failure = True
- # Clean up the test list to avoid accumulating too many.
- self.tests_with_subtest_fails.remove(test_name)
- # This test passed but it has failing subtests. Since we can only
- # report a single status to Chromium, we choose FAIL to indicate
- # that something about this test did not run correctly.
- if actual_status == "PASS":
- actual_status = "FAIL"
-
- self._append_test_message(test_name, None, wpt_actual_status,
- data.get("message", ""))
- self._store_test_result(test_name,
- actual_status,
- expected_statuses,
- self.actual_metadata[test_name],
- self.messages[test_name],
- wpt_actual_status,
- subtest_failure,
- duration,
- data.get("extra", {}).get("reftest_screenshots"))
-
- # Remove the test from dicts to avoid accumulating too many.
- self.actual_metadata.pop(test_name)
- self.messages.pop(test_name)
-
- # New test, new browser logs.
- self.browser_log = []
-
- def shutdown(self, data):
- # Create the final result dictionary
- final_result = {
- # There are some required fields that we just hard-code.
- "interrupted": False,
- "path_delimiter": "/",
- "version": 3,
- "seconds_since_epoch": self.start_timestamp_seconds,
- "num_failures_by_type": self.num_failures_by_status,
- "flag_name": self.flag_specific,
- "tests": self.tests
- }
- return json.dumps(final_result)
-
- def process_output(self, data):
- cmd = data.get("command", "")
- if any(c in cmd for c in ["chromedriver", "logcat"]):
- self.browser_log.append(data['data'])
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/formatters/tests/test_chromium.py b/tests/wpt/tests/tools/wptrunner/wptrunner/formatters/tests/test_chromium.py
deleted file mode 100644
index bf815d5dc76..00000000000
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/formatters/tests/test_chromium.py
+++ /dev/null
@@ -1,828 +0,0 @@
-# mypy: ignore-errors
-
-import json
-import sys
-from os.path import dirname, join
-from io import StringIO
-
-from mozlog import handlers, structuredlog
-import pytest
-
-sys.path.insert(0, join(dirname(__file__), "..", ".."))
-from formatters.chromium import ChromiumFormatter
-
-
-@pytest.fixture
-def logger():
- test_logger = structuredlog.StructuredLogger("test_a")
- try:
- yield test_logger
- finally:
- # Loggers of the same name share state globally:
- # https://searchfox.org/mozilla-central/rev/1c54648c082efdeb08cf6a5e3a8187e83f7549b9/testing/mozbase/mozlog/mozlog/structuredlog.py#195-196
- #
- # Resetting the state here ensures the logger will not be shut down in
- # the next test.
- test_logger.reset_state()
-
-
-def test_chromium_required_fields(logger, capfd):
- # Test that the test results contain a handful of required fields.
-
- # Set up the handler.
- output = StringIO()
- logger.add_handler(handlers.StreamHandler(output, ChromiumFormatter()))
-
- # output a bunch of stuff
- logger.suite_start(["test-id-1"], run_info={}, time=123)
- logger.test_start("test-id-1")
- logger.test_end("test-id-1", status="PASS", expected="PASS")
- logger.suite_end()
- logger.shutdown()
-
- # check nothing got output to stdout/stderr
- # (note that mozlog outputs exceptions during handling to stderr!)
- captured = capfd.readouterr()
- assert captured.out == ""
- assert captured.err == ""
-
- # check the actual output of the formatter
- output.seek(0)
- output_obj = json.load(output)
-
- # Check for existence of required fields
- assert "interrupted" in output_obj
- assert "path_delimiter" in output_obj
- assert "version" in output_obj
- assert "num_failures_by_type" in output_obj
- assert "tests" in output_obj
-
- test_obj = output_obj["tests"]["test-id-1"]
- assert "actual" in test_obj
- assert "expected" in test_obj
-
-
-def test_time_per_test(logger, capfd):
- # Test that the formatter measures time per test correctly.
-
- # Set up the handler.
- output = StringIO()
- logger.add_handler(handlers.StreamHandler(output, ChromiumFormatter()))
-
- logger.suite_start(["test-id-1", "test-id-2"], run_info={}, time=50)
- logger.test_start("test-id-1", time=100)
- logger.test_start("test-id-2", time=200)
- logger.test_end("test-id-1", status="PASS", expected="PASS", time=300)
- logger.test_end("test-id-2", status="PASS", expected="PASS", time=199)
- logger.suite_end()
-
- logger.suite_start(["test-id-1"], run_info={}, time=400)
- logger.test_start("test-id-1", time=500)
- logger.test_end("test-id-1", status="PASS", expected="PASS", time=600)
- logger.suite_end()
-
- # Write the final results.
- logger.shutdown()
-
- # check nothing got output to stdout/stderr
- # (note that mozlog outputs exceptions during handling to stderr!)
- captured = capfd.readouterr()
- assert captured.out == ""
- assert captured.err == ""
-
- # check the actual output of the formatter
- output.seek(0)
- output_obj = json.load(output)
-
- test1_obj = output_obj["tests"]["test-id-1"]
- test2_obj = output_obj["tests"]["test-id-2"]
- # Test 1 run 1: 300ms - 100ms = 0.2s
- # Test 1 run 2: 600ms - 500ms = 0.1s
- assert test1_obj["time"] == pytest.approx(0.2)
- assert len(test1_obj["times"]) == 2
- assert test1_obj["times"][0] == pytest.approx(0.2)
- assert test1_obj["times"][1] == pytest.approx(0.1)
- assert "time" not in test2_obj
- assert "times" not in test2_obj
-
-
-def test_chromium_test_name_trie(logger, capfd):
- # Ensure test names are broken into directories and stored in a trie with
- # test results at the leaves.
-
- # Set up the handler.
- output = StringIO()
- logger.add_handler(handlers.StreamHandler(output, ChromiumFormatter()))
-
- # output a bunch of stuff
- logger.suite_start(["/foo/bar/test-id-1", "/foo/test-id-2"], run_info={},
- time=123)
- logger.test_start("/foo/bar/test-id-1")
- logger.test_end("/foo/bar/test-id-1", status="TIMEOUT", expected="FAIL")
- logger.test_start("/foo/test-id-2")
- logger.test_end("/foo/test-id-2", status="ERROR", expected="TIMEOUT")
- logger.suite_end()
- logger.shutdown()
-
- # check nothing got output to stdout/stderr
- # (note that mozlog outputs exceptions during handling to stderr!)
- captured = capfd.readouterr()
- assert captured.out == ""
- assert captured.err == ""
-
- # check the actual output of the formatter
- output.seek(0)
- output_obj = json.load(output)
-
- # Ensure that the test names are broken up by directory name and that the
- # results are stored at the leaves.
- test_obj = output_obj["tests"]["foo"]["bar"]["test-id-1"]
- assert test_obj["actual"] == "TIMEOUT"
- assert test_obj["expected"] == "FAIL"
-
- test_obj = output_obj["tests"]["foo"]["test-id-2"]
- # The ERROR status is mapped to FAIL for Chromium
- assert test_obj["actual"] == "FAIL"
- assert test_obj["expected"] == "TIMEOUT"
-
-
-def test_num_failures_by_type(logger, capfd):
- # Test that the number of failures by status type is correctly calculated.
-
- # Set up the handler.
- output = StringIO()
- logger.add_handler(handlers.StreamHandler(output, ChromiumFormatter()))
-
- # Run some tests with different statuses: 3 passes, 1 timeout
- logger.suite_start(["t1", "t2", "t3", "t4"], run_info={}, time=123)
- logger.test_start("t1")
- logger.test_end("t1", status="PASS", expected="PASS")
- logger.test_start("t2")
- logger.test_end("t2", status="PASS", expected="PASS")
- logger.test_start("t3")
- logger.test_end("t3", status="PASS", expected="FAIL")
- logger.test_start("t4")
- logger.test_end("t4", status="TIMEOUT", expected="CRASH")
- logger.suite_end()
- logger.shutdown()
-
- # check nothing got output to stdout/stderr
- # (note that mozlog outputs exceptions during handling to stderr!)
- captured = capfd.readouterr()
- assert captured.out == ""
- assert captured.err == ""
-
- # check the actual output of the formatter
- output.seek(0)
- num_failures_by_type = json.load(output)["num_failures_by_type"]
-
- # We expect 3 passes and 1 timeout, nothing else.
- assert sorted(num_failures_by_type.keys()) == ["PASS", "TIMEOUT"]
- assert num_failures_by_type["PASS"] == 3
- assert num_failures_by_type["TIMEOUT"] == 1
-
-
-def test_subtest_messages(logger, capfd):
- # Tests accumulation of test output
-
- # Set up the handler.
- output = StringIO()
- logger.add_handler(handlers.StreamHandler(output, ChromiumFormatter()))
-
- # Run two tests with subtest messages. The subtest name should be included
- # in the output. We should also tolerate missing messages and subtest names
- # with unusual characters.
- logger.suite_start(["t1", "t2"], run_info={}, time=123)
- logger.test_start("t1")
- logger.test_status("t1", status="FAIL", subtest="t1_a",
- message="t1_a_message")
- # Subtest name includes a backslash and two closing square brackets.
- logger.test_status("t1", status="PASS", subtest=r"t1_\[]]b",
- message="t1_b_message")
- logger.test_end("t1", status="PASS", expected="PASS")
- logger.test_start("t2")
- # Subtests with empty messages should not be ignored.
- logger.test_status("t2", status="PASS", subtest="t2_a")
- # A test-level message will also be appended
- logger.test_end("t2", status="TIMEOUT", expected="PASS",
- message="t2_message")
- logger.suite_end()
- logger.shutdown()
-
- # check nothing got output to stdout/stderr
- # (note that mozlog outputs exceptions during handling to stderr!)
- captured = capfd.readouterr()
- assert captured.out == ""
- assert captured.err == ""
-
- # check the actual output of the formatter
- output.seek(0)
- output_json = json.load(output)
-
- t1_artifacts = output_json["tests"]["t1"]["artifacts"]
- assert t1_artifacts["wpt_actual_metadata"] == [
- "[t1]\n expected: PASS\n",
- " [t1_a]\n expected: FAIL\n",
- " [t1_\\\\[\\]\\]b]\n expected: PASS\n",
- ]
- assert t1_artifacts["wpt_log"] == [
- "t1_a: t1_a_message\n",
- # Only humans will read the log, so there's no need to escape
- # characters here.
- "t1_\\[]]b: t1_b_message\n",
- ]
- assert t1_artifacts["wpt_subtest_failure"] == ["true"]
- t2_artifacts = output_json["tests"]["t2"]["artifacts"]
- assert t2_artifacts["wpt_actual_metadata"] == [
- "[t2]\n expected: TIMEOUT\n",
- " [t2_a]\n expected: PASS\n",
- ]
- assert t2_artifacts["wpt_log"] == [
- "Harness: t2_message\n"
- ]
- assert "wpt_subtest_failure" not in t2_artifacts.keys()
-
-
-def test_subtest_failure(logger, capfd):
- # Tests that a test fails if a subtest fails
-
- # Set up the handler.
- output = StringIO()
- formatter = ChromiumFormatter()
- logger.add_handler(handlers.StreamHandler(output, formatter))
-
- # Run a test with some subtest failures.
- logger.suite_start(["t1"], run_info={}, time=123)
- logger.test_start("t1")
- logger.test_status("t1", status="FAIL", subtest="t1_a",
- message="t1_a_message")
- logger.test_status("t1", status="PASS", subtest="t1_b",
- message="t1_b_message")
- logger.test_status("t1", status="TIMEOUT", subtest="t1_c",
- message="t1_c_message")
-
- # Make sure the test name was added to the set of tests with subtest fails
- assert "t1" in formatter.tests_with_subtest_fails
-
- # The test status is reported as a pass here because the harness was able to
- # run the test to completion.
- logger.test_end("t1", status="PASS", expected="PASS", message="top_message")
- logger.suite_end()
- logger.shutdown()
-
- # check nothing got output to stdout/stderr
- # (note that mozlog outputs exceptions during handling to stderr!)
- captured = capfd.readouterr()
- assert captured.out == ""
- assert captured.err == ""
-
- # check the actual output of the formatter
- output.seek(0)
- output_json = json.load(output)
-
- test_obj = output_json["tests"]["t1"]
- t1_artifacts = test_obj["artifacts"]
- assert t1_artifacts["wpt_actual_metadata"] == [
- "[t1]\n expected: PASS\n",
- " [t1_a]\n expected: FAIL\n",
- " [t1_b]\n expected: PASS\n",
- " [t1_c]\n expected: TIMEOUT\n",
- ]
- assert t1_artifacts["wpt_log"] == [
- "Harness: top_message\n",
- "t1_a: t1_a_message\n",
- "t1_b: t1_b_message\n",
- "t1_c: t1_c_message\n",
- ]
- assert t1_artifacts["wpt_subtest_failure"] == ["true"]
- # The status of the test in the output is a failure because subtests failed,
- # despite the harness reporting that the test passed. But the harness status
- # is logged as an artifact.
- assert t1_artifacts["wpt_actual_status"] == ["PASS"]
- assert test_obj["actual"] == "FAIL"
- assert test_obj["expected"] == "PASS"
- # Also ensure that the formatter cleaned up its internal state
- assert "t1" not in formatter.tests_with_subtest_fails
-
-
-def test_expected_subtest_failure(logger, capfd):
- # Tests that an expected subtest failure does not cause the test to fail
-
- # Set up the handler.
- output = StringIO()
- formatter = ChromiumFormatter()
- logger.add_handler(handlers.StreamHandler(output, formatter))
-
- # Run a test with some expected subtest failures.
- logger.suite_start(["t1"], run_info={}, time=123)
- logger.test_start("t1")
- logger.test_status("t1", status="FAIL", expected="FAIL", subtest="t1_a",
- message="t1_a_message")
- logger.test_status("t1", status="PASS", subtest="t1_b",
- message="t1_b_message")
- logger.test_status("t1", status="TIMEOUT", expected="TIMEOUT", subtest="t1_c",
- message="t1_c_message")
-
- # The subtest failures are all expected so this test should not be added to
- # the set of tests with subtest failures.
- assert "t1" not in formatter.tests_with_subtest_fails
-
- # The test status is reported as a pass here because the harness was able to
- # run the test to completion.
- logger.test_end("t1", status="OK", expected="OK")
- logger.suite_end()
- logger.shutdown()
-
- # check nothing got output to stdout/stderr
- # (note that mozlog outputs exceptions during handling to stderr!)
- captured = capfd.readouterr()
- assert captured.out == ""
- assert captured.err == ""
-
- # check the actual output of the formatter
- output.seek(0)
- output_json = json.load(output)
-
- test_obj = output_json["tests"]["t1"]
- assert test_obj["artifacts"]["wpt_actual_metadata"] == [
- "[t1]\n expected: OK\n",
- " [t1_a]\n expected: FAIL\n",
- " [t1_b]\n expected: PASS\n",
- " [t1_c]\n expected: TIMEOUT\n",
- ]
- assert test_obj["artifacts"]["wpt_log"] == [
- "t1_a: t1_a_message\n",
- "t1_b: t1_b_message\n",
- "t1_c: t1_c_message\n",
- ]
- # The status of the test in the output is a pass because the subtest
- # failures were all expected.
- assert test_obj["actual"] == "PASS"
- assert test_obj["expected"] == "PASS"
-
-
-def test_unexpected_subtest_pass(logger, capfd):
- # A subtest that unexpectedly passes is considered a failure condition.
-
- # Set up the handler.
- output = StringIO()
- formatter = ChromiumFormatter()
- logger.add_handler(handlers.StreamHandler(output, formatter))
-
- # Run a test with a subtest that is expected to fail but passes.
- logger.suite_start(["t1"], run_info={}, time=123)
- logger.test_start("t1")
- logger.test_status("t1", status="PASS", expected="FAIL", subtest="t1_a",
- message="t1_a_message")
-
- # Since the subtest behaviour is unexpected, it's considered a failure, so
- # the test should be added to the set of tests with subtest failures.
- assert "t1" in formatter.tests_with_subtest_fails
-
- # The test status is reported as a pass here because the harness was able to
- # run the test to completion.
- logger.test_end("t1", status="PASS", expected="PASS")
- logger.suite_end()
- logger.shutdown()
-
- # check nothing got output to stdout/stderr
- # (note that mozlog outputs exceptions during handling to stderr!)
- captured = capfd.readouterr()
- assert captured.out == ""
- assert captured.err == ""
-
- # check the actual output of the formatter
- output.seek(0)
- output_json = json.load(output)
-
- test_obj = output_json["tests"]["t1"]
- t1_artifacts = test_obj["artifacts"]
- assert t1_artifacts["wpt_actual_metadata"] == [
- "[t1]\n expected: PASS\n",
- " [t1_a]\n expected: PASS\n",
- ]
- assert t1_artifacts["wpt_log"] == [
- "t1_a: t1_a_message\n",
- ]
- assert t1_artifacts["wpt_subtest_failure"] == ["true"]
- # Since the subtest status is unexpected, we fail the test. But we report
- # wpt_actual_status as an artifact
- assert t1_artifacts["wpt_actual_status"] == ["PASS"]
- assert test_obj["actual"] == "FAIL"
- assert test_obj["expected"] == "PASS"
- # Also ensure that the formatter cleaned up its internal state
- assert "t1" not in formatter.tests_with_subtest_fails
-
-
-def test_expected_test_fail(logger, capfd):
- # Check that an expected test-level failure is treated as a Pass
-
- # Set up the handler.
- output = StringIO()
- logger.add_handler(handlers.StreamHandler(output, ChromiumFormatter()))
-
- # Run some tests with different statuses: 3 passes, 1 timeout
- logger.suite_start(["t1"], run_info={}, time=123)
- logger.test_start("t1")
- logger.test_end("t1", status="ERROR", expected="ERROR")
- logger.suite_end()
- logger.shutdown()
-
- # check nothing got output to stdout/stderr
- # (note that mozlog outputs exceptions during handling to stderr!)
- captured = capfd.readouterr()
- assert captured.out == ""
- assert captured.err == ""
-
- # check the actual output of the formatter
- output.seek(0)
- output_json = json.load(output)
-
- test_obj = output_json["tests"]["t1"]
- # The test's actual and expected status should map from "ERROR" to "FAIL"
- assert test_obj["actual"] == "FAIL"
- assert test_obj["expected"] == "FAIL"
- # ..and this test should not be a regression nor unexpected
- assert "is_regression" not in test_obj
- assert "is_unexpected" not in test_obj
-
-
-def test_unexpected_test_fail(logger, capfd):
- # Check that an unexpected test-level failure is marked as unexpected and
- # as a regression.
-
- # Set up the handler.
- output = StringIO()
- logger.add_handler(handlers.StreamHandler(output, ChromiumFormatter()))
-
- # Run some tests with different statuses: 3 passes, 1 timeout
- logger.suite_start(["t1"], run_info={}, time=123)
- logger.test_start("t1")
- logger.test_end("t1", status="ERROR", expected="OK")
- logger.suite_end()
- logger.shutdown()
-
- # check nothing got output to stdout/stderr
- # (note that mozlog outputs exceptions during handling to stderr!)
- captured = capfd.readouterr()
- assert captured.out == ""
- assert captured.err == ""
-
- # check the actual output of the formatter
- output.seek(0)
- output_json = json.load(output)
-
- test_obj = output_json["tests"]["t1"]
- # The test's actual and expected status should be mapped, ERROR->FAIL and
- # OK->PASS
- assert test_obj["actual"] == "FAIL"
- assert test_obj["expected"] == "PASS"
- # ..and this test should be a regression and unexpected
- assert test_obj["is_regression"] is True
- assert test_obj["is_unexpected"] is True
-
-
-def test_flaky_test_expected(logger, capfd):
- # Check that a flaky test with multiple possible statuses is seen as
- # expected if its actual status is one of the possible ones.
-
- # set up the handler.
- output = StringIO()
- logger.add_handler(handlers.StreamHandler(output, ChromiumFormatter()))
-
- # Run a test that is known to be flaky
- logger.suite_start(["t1"], run_info={}, time=123)
- logger.test_start("t1")
- logger.test_end("t1", status="ERROR", expected="OK", known_intermittent=["ERROR", "TIMEOUT"])
- logger.suite_end()
- logger.shutdown()
-
- # check nothing got output to stdout/stderr
- # (note that mozlog outputs exceptions during handling to stderr!)
- captured = capfd.readouterr()
- assert captured.out == ""
- assert captured.err == ""
-
- # check the actual output of the formatter
- output.seek(0)
- output_json = json.load(output)
-
- test_obj = output_json["tests"]["t1"]
- # The test's statuses are all mapped, changing ERROR->FAIL and OK->PASS
- assert test_obj["actual"] == "FAIL"
- # All the possible statuses are merged and sorted together into expected.
- assert test_obj["expected"] == "FAIL PASS TIMEOUT"
- # ...this is not a regression or unexpected because the actual status is one
- # of the expected ones
- assert "is_regression" not in test_obj
- assert "is_unexpected" not in test_obj
-
-
-def test_flaky_test_unexpected(logger, capfd):
- # Check that a flaky test with multiple possible statuses is seen as
- # unexpected if its actual status is NOT one of the possible ones.
-
- # set up the handler.
- output = StringIO()
- logger.add_handler(handlers.StreamHandler(output, ChromiumFormatter()))
-
- # Run a test that is known to be flaky
- logger.suite_start(["t1"], run_info={}, time=123)
- logger.test_start("t1")
- logger.test_end("t1", status="ERROR", expected="OK", known_intermittent=["TIMEOUT"])
- logger.suite_end()
- logger.shutdown()
-
- # check nothing got output to stdout/stderr
- # (note that mozlog outputs exceptions during handling to stderr!)
- captured = capfd.readouterr()
- assert captured.out == ""
- assert captured.err == ""
-
- # check the actual output of the formatter
- output.seek(0)
- output_json = json.load(output)
-
- test_obj = output_json["tests"]["t1"]
- # The test's statuses are all mapped, changing ERROR->FAIL and OK->PASS
- assert test_obj["actual"] == "FAIL"
- # All the possible statuses are merged and sorted together into expected.
- assert test_obj["expected"] == "PASS TIMEOUT"
- # ...this is a regression and unexpected because the actual status is not
- # one of the expected ones
- assert test_obj["is_regression"] is True
- assert test_obj["is_unexpected"] is True
-
-
-def test_precondition_failed(logger, capfd):
- # Check that a failed precondition gets properly handled.
-
- # set up the handler.
- output = StringIO()
- logger.add_handler(handlers.StreamHandler(output, ChromiumFormatter()))
-
- # Run a test with a precondition failure
- logger.suite_start(["t1"], run_info={}, time=123)
- logger.test_start("t1")
- logger.test_end("t1", status="PRECONDITION_FAILED", expected="OK")
- logger.suite_end()
- logger.shutdown()
-
- # check nothing got output to stdout/stderr
- # (note that mozlog outputs exceptions during handling to stderr!)
- captured = capfd.readouterr()
- assert captured.out == ""
- assert captured.err == ""
-
- # check the actual output of the formatter
- output.seek(0)
- output_json = json.load(output)
-
- test_obj = output_json["tests"]["t1"]
- # The precondition failure should map to FAIL status, but we should also
- # have an artifact containing the original PRECONDITION_FAILED status.
- assert test_obj["actual"] == "FAIL"
- assert test_obj["artifacts"]["wpt_actual_status"] == ["PRECONDITION_FAILED"]
- # ...this is an unexpected regression because we expected a pass but failed
- assert test_obj["is_regression"] is True
- assert test_obj["is_unexpected"] is True
-
-
-def test_repeated_test_statuses(logger, capfd):
- # Check that the logger outputs all statuses from multiple runs of a test.
-
- # Set up the handler.
- output = StringIO()
- logger.add_handler(handlers.StreamHandler(output, ChromiumFormatter()))
-
- # Run a test suite for the first time.
- logger.suite_start(["t1"], run_info={}, time=123)
- logger.test_start("t1")
- logger.test_end("t1", status="PASS", expected="PASS", known_intermittent=[])
- logger.suite_end()
-
- # Run the test suite for the second time.
- logger.suite_start(["t1"], run_info={}, time=456)
- logger.test_start("t1")
- logger.test_end("t1", status="FAIL", expected="PASS", known_intermittent=[])
- logger.suite_end()
-
- # Write the final results.
- logger.shutdown()
-
- # check nothing got output to stdout/stderr
- # (note that mozlog outputs exceptions during handling to stderr!)
- captured = capfd.readouterr()
- assert captured.out == ""
- assert captured.err == ""
-
- # check the actual output of the formatter
- output.seek(0)
- output_json = json.load(output)
-
- status_totals = output_json["num_failures_by_type"]
- assert status_totals["PASS"] == 1
- # A missing result type is the same as being present and set to zero (0).
- assert status_totals.get("FAIL", 0) == 0
-
- # The actual statuses are accumulated in a ordered space-separated list.
- test_obj = output_json["tests"]["t1"]
- assert test_obj["actual"] == "PASS FAIL"
- assert test_obj["expected"] == "PASS"
-
-
-def test_flaky_test_detection(logger, capfd):
- # Check that the logger detects flakiness for a test run multiple times.
-
- # Set up the handler.
- output = StringIO()
- logger.add_handler(handlers.StreamHandler(output, ChromiumFormatter()))
-
- logger.suite_start(["t1", "t2"], run_info={})
- logger.test_start("t1")
- logger.test_start("t2")
- logger.test_end("t1", status="FAIL", expected="PASS")
- logger.test_end("t2", status="FAIL", expected="FAIL")
- logger.suite_end()
-
- logger.suite_start(["t1", "t2"], run_info={})
- logger.test_start("t1")
- logger.test_start("t2")
- logger.test_end("t1", status="PASS", expected="PASS")
- logger.test_end("t2", status="FAIL", expected="FAIL")
- logger.suite_end()
-
- # Write the final results.
- logger.shutdown()
-
- # check nothing got output to stdout/stderr
- # (note that mozlog outputs exceptions during handling to stderr!)
- captured = capfd.readouterr()
- assert captured.out == ""
- assert captured.err == ""
-
- # check the actual output of the formatter
- output.seek(0)
- output_json = json.load(output)
-
- # We consider a test flaky if it runs multiple times and produces more than
- # one kind of result.
- test1_obj = output_json["tests"]["t1"]
- test2_obj = output_json["tests"]["t2"]
- assert test1_obj["is_flaky"] is True
- assert "is_flaky" not in test2_obj
-
-
-def test_known_intermittent_empty(logger, capfd):
- # If the known_intermittent list is empty, we want to ensure we don't append
- # any extraneous characters to the output.
-
- # set up the handler.
- output = StringIO()
- logger.add_handler(handlers.StreamHandler(output, ChromiumFormatter()))
-
- # Run a test and include an empty known_intermittent list
- logger.suite_start(["t1"], run_info={}, time=123)
- logger.test_start("t1")
- logger.test_end("t1", status="OK", expected="OK", known_intermittent=[])
- logger.suite_end()
- logger.shutdown()
-
- # check nothing got output to stdout/stderr
- # (note that mozlog outputs exceptions during handling to stderr!)
- captured = capfd.readouterr()
- assert captured.out == ""
- assert captured.err == ""
-
- # check the actual output of the formatter
- output.seek(0)
- output_json = json.load(output)
-
- test_obj = output_json["tests"]["t1"]
- # Both actual and expected statuses get mapped to Pass. No extra whitespace
- # anywhere.
- assert test_obj["actual"] == "PASS"
- assert test_obj["expected"] == "PASS"
-
-
-def test_known_intermittent_duplicate(logger, capfd):
- # We don't want to have duplicate statuses in the final "expected" field.
-
- # Set up the handler.
- output = StringIO()
- logger.add_handler(handlers.StreamHandler(output, ChromiumFormatter()))
-
- # There are two duplications in this input:
- # 1. known_intermittent already contains expected;
- # 2. both statuses in known_intermittent map to FAIL in Chromium.
- # In the end, we should only get one FAIL in Chromium "expected".
- logger.suite_start(["t1"], run_info={}, time=123)
- logger.test_start("t1")
- logger.test_end("t1", status="ERROR", expected="ERROR", known_intermittent=["FAIL", "ERROR"])
- logger.suite_end()
- logger.shutdown()
-
- # Check nothing got output to stdout/stderr.
- # (Note that mozlog outputs exceptions during handling to stderr!)
- captured = capfd.readouterr()
- assert captured.out == ""
- assert captured.err == ""
-
- # Check the actual output of the formatter.
- output.seek(0)
- output_json = json.load(output)
-
- test_obj = output_json["tests"]["t1"]
- assert test_obj["actual"] == "FAIL"
- # No duplicate "FAIL" in "expected".
- assert test_obj["expected"] == "FAIL"
-
-
-def test_reftest_screenshots(logger, capfd):
- # reftest_screenshots, if present, should be plumbed into artifacts.
-
- # Set up the handler.
- output = StringIO()
- logger.add_handler(handlers.StreamHandler(output, ChromiumFormatter()))
-
- # Run a reftest with reftest_screenshots.
- logger.suite_start(["t1"], run_info={}, time=123)
- logger.test_start("t1")
- logger.test_end("t1", status="FAIL", expected="PASS", extra={
- "reftest_screenshots": [
- {"url": "foo.html", "hash": "HASH1", "screenshot": "DATA1"},
- "!=",
- {"url": "foo-ref.html", "hash": "HASH2", "screenshot": "DATA2"},
- ]
- })
- logger.suite_end()
- logger.shutdown()
-
- # check nothing got output to stdout/stderr
- # (note that mozlog outputs exceptions during handling to stderr!)
- captured = capfd.readouterr()
- assert captured.out == ""
- assert captured.err == ""
-
- # check the actual output of the formatter
- output.seek(0)
- output_json = json.load(output)
-
- test_obj = output_json["tests"]["t1"]
- assert test_obj["artifacts"]["screenshots"] == [
- "foo.html: DATA1",
- "foo-ref.html: DATA2",
- ]
-
-
-def test_process_output_crashing_test(logger, capfd):
- """Test that chromedriver logs are preserved for crashing tests"""
-
- # Set up the handler.
- output = StringIO()
- logger.add_handler(handlers.StreamHandler(output, ChromiumFormatter()))
-
- logger.suite_start(["t1", "t2", "t3"], run_info={}, time=123)
-
- logger.test_start("t1")
- logger.process_output(100, "This message should be recorded", "/some/path/to/chromedriver --some-flag")
- logger.process_output(101, "This message should not be recorded", "/some/other/process --another-flag")
- logger.process_output(100, "This message should also be recorded", "/some/path/to/chromedriver --some-flag")
- logger.test_end("t1", status="CRASH", expected="CRASH")
-
- logger.test_start("t2")
- logger.process_output(100, "Another message for the second test", "/some/path/to/chromedriver --some-flag")
- logger.test_end("t2", status="CRASH", expected="PASS")
-
- logger.test_start("t3")
- logger.process_output(100, "This test fails", "/some/path/to/chromedriver --some-flag")
- logger.process_output(100, "But the output should not be captured", "/some/path/to/chromedriver --some-flag")
- logger.process_output(100, "Because it does not crash", "/some/path/to/chromedriver --some-flag")
- logger.test_end("t3", status="FAIL", expected="PASS")
-
- logger.suite_end()
- logger.shutdown()
-
- # check nothing got output to stdout/stderr
- # (note that mozlog outputs exceptions during handling to stderr!)
- captured = capfd.readouterr()
- assert captured.out == ""
- assert captured.err == ""
-
- # check the actual output of the formatter
- output.seek(0)
- output_json = json.load(output)
-
- test_obj = output_json["tests"]["t1"]
- assert test_obj["artifacts"]["wpt_crash_log"] == [
- "This message should be recorded",
- "This message should also be recorded"
- ]
-
- test_obj = output_json["tests"]["t2"]
- assert test_obj["artifacts"]["wpt_crash_log"] == [
- "Another message for the second test"
- ]
-
- test_obj = output_json["tests"]["t3"]
- assert "wpt_crash_log" not in test_obj["artifacts"]
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/testdriver-extra.js b/tests/wpt/tests/tools/wptrunner/wptrunner/testdriver-extra.js
index ab8b04ba3cd..3c2dd8b42dd 100644
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/testdriver-extra.js
+++ b/tests/wpt/tests/tools/wptrunner/wptrunner/testdriver-extra.js
@@ -263,11 +263,14 @@
window.test_driver_internal.bidi.emulation.set_geolocation_override =
function (params) {
+ if ('coordinates' in params && 'error' in params) {
+ throw new Error(
+ "`coordinates` and `error` are mutually exclusive in set_geolocation_override");
+ }
+
return create_action("bidi.emulation.set_geolocation_override", {
// Default to the current window.
contexts: [window],
- // Default to no coordinates.
- coordinates: null,
...(params ?? {})
});
}
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/testrunner.py b/tests/wpt/tests/tools/wptrunner/wptrunner/testrunner.py
index 64668fc470b..2c2da790e90 100644
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/testrunner.py
+++ b/tests/wpt/tests/tools/wptrunner/wptrunner/testrunner.py
@@ -601,7 +601,7 @@ class TestRunnerManager(threading.Thread):
assert self.browser.browser is not None
self.browser.browser.cleanup()
impl = self.test_implementations[(self.state.subsuite, self.state.test_type)]
- browser = impl.browser_cls(self.logger, remote_queue=self.command_queue,
+ browser = impl.browser_cls(self.logger,
**impl.browser_kwargs)
browser.setup()
self.browser = BrowserManager(self.logger,
diff --git a/tests/wpt/tests/tools/wptrunner/wptrunner/wptcommandline.py b/tests/wpt/tests/tools/wptrunner/wptrunner/wptcommandline.py
index 647cc7f21b1..7bfeef31717 100644
--- a/tests/wpt/tests/tools/wptrunner/wptrunner/wptcommandline.py
+++ b/tests/wpt/tests/tools/wptrunner/wptrunner/wptcommandline.py
@@ -11,7 +11,7 @@ from typing import Mapping, Optional
from . import config
from . import products
from . import wpttest
-from .formatters import chromium, wptreport, wptscreenshot
+from .formatters import wptreport, wptscreenshot
def abs_path(path):
@@ -418,7 +418,6 @@ scheme host and port.""")
"Cache API (default: %s)" % wptscreenshot.DEFAULT_API,
{"wptscreenshot"}, "store")
- commandline.log_formatters["chromium"] = (chromium.ChromiumFormatter, "Chromium Layout Tests format")
commandline.log_formatters["wptreport"] = (wptreport.WptreportFormatter, "wptreport format")
commandline.log_formatters["wptscreenshot"] = (wptscreenshot.WptscreenshotFormatter, "wpt.fyi screenshots")
diff --git a/tests/wpt/tests/wasm/core/js/simd/simd_select.wast.js b/tests/wpt/tests/wasm/core/js/simd/simd_select.wast.js
new file mode 100644
index 00000000000..e997d81f75a
--- /dev/null
+++ b/tests/wpt/tests/wasm/core/js/simd/simd_select.wast.js
@@ -0,0 +1,24 @@
+(function simd_select_wast_js() {
+
+// simd_select.wast:3
+let $1 = instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\x88\x80\x80\x80\x00\x01\x60\x03\x7b\x7b\x7f\x01\x7b\x03\x82\x80\x80\x80\x00\x01\x00\x07\x93\x80\x80\x80\x00\x01\x0f\x73\x65\x6c\x65\x63\x74\x5f\x76\x31\x32\x38\x5f\x69\x33\x32\x00\x00\x0a\x8f\x80\x80\x80\x00\x01\x89\x80\x80\x80\x00\x00\x20\x00\x20\x01\x20\x02\x1b\x0b");
+
+// simd_select.wast:9
+run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\xa6\x80\x80\x80\x00\x07\x60\x00\x00\x60\x01\x7f\x01\x6f\x60\x01\x6f\x01\x7f\x60\x01\x70\x01\x7f\x60\x02\x6f\x6f\x01\x7f\x60\x02\x70\x70\x01\x7f\x60\x03\x7b\x7b\x7f\x01\x7b\x02\x8b\x81\x80\x80\x00\x06\x06\x6d\x6f\x64\x75\x6c\x65\x0f\x73\x65\x6c\x65\x63\x74\x5f\x76\x31\x32\x38\x5f\x69\x33\x32\x00\x06\x08\x73\x70\x65\x63\x74\x65\x73\x74\x09\x65\x78\x74\x65\x72\x6e\x72\x65\x66\x00\x01\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0c\x69\x73\x5f\x65\x78\x74\x65\x72\x6e\x72\x65\x66\x00\x02\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0a\x69\x73\x5f\x66\x75\x6e\x63\x72\x65\x66\x00\x03\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0c\x65\x71\x5f\x65\x78\x74\x65\x72\x6e\x72\x65\x66\x00\x04\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0a\x65\x71\x5f\x66\x75\x6e\x63\x72\x65\x66\x00\x05\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x06\x0a\xe2\x80\x80\x80\x00\x01\xdc\x80\x80\x80\x00\x00\x02\x40\xfd\x0c\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\xfd\x0c\x05\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x08\x00\x00\x00\x41\x01\x10\x00\xfd\x0c\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfd\x4e\xfd\x0c\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\xfd\x23\xfd\x63\x45\x0d\x00\x0f\x0b\x00\x0b", exports($1)), "run", [])); // assert_return(() => call($1, "select_v128_i32", [v128("1 2 3 4"), v128("5 6 7 8"), 1]), v128("1 2 3 4"))
+
+// simd_select.wast:18
+run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\xa6\x80\x80\x80\x00\x07\x60\x00\x00\x60\x01\x7f\x01\x6f\x60\x01\x6f\x01\x7f\x60\x01\x70\x01\x7f\x60\x02\x6f\x6f\x01\x7f\x60\x02\x70\x70\x01\x7f\x60\x03\x7b\x7b\x7f\x01\x7b\x02\x8b\x81\x80\x80\x00\x06\x06\x6d\x6f\x64\x75\x6c\x65\x0f\x73\x65\x6c\x65\x63\x74\x5f\x76\x31\x32\x38\x5f\x69\x33\x32\x00\x06\x08\x73\x70\x65\x63\x74\x65\x73\x74\x09\x65\x78\x74\x65\x72\x6e\x72\x65\x66\x00\x01\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0c\x69\x73\x5f\x65\x78\x74\x65\x72\x6e\x72\x65\x66\x00\x02\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0a\x69\x73\x5f\x66\x75\x6e\x63\x72\x65\x66\x00\x03\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0c\x65\x71\x5f\x65\x78\x74\x65\x72\x6e\x72\x65\x66\x00\x04\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0a\x65\x71\x5f\x66\x75\x6e\x63\x72\x65\x66\x00\x05\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x06\x0a\xe2\x80\x80\x80\x00\x01\xdc\x80\x80\x80\x00\x00\x02\x40\xfd\x0c\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\xfd\x0c\x05\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x08\x00\x00\x00\x41\x00\x10\x00\xfd\x0c\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfd\x4e\xfd\x0c\x05\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x08\x00\x00\x00\xfd\x23\xfd\x63\x45\x0d\x00\x0f\x0b\x00\x0b", exports($1)), "run", [])); // assert_return(() => call($1, "select_v128_i32", [v128("1 2 3 4"), v128("5 6 7 8"), 0]), v128("5 6 7 8"))
+
+// simd_select.wast:27
+run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\xa6\x80\x80\x80\x00\x07\x60\x00\x00\x60\x01\x7f\x01\x6f\x60\x01\x6f\x01\x7f\x60\x01\x70\x01\x7f\x60\x02\x6f\x6f\x01\x7f\x60\x02\x70\x70\x01\x7f\x60\x03\x7b\x7b\x7f\x01\x7b\x02\x8b\x81\x80\x80\x00\x06\x06\x6d\x6f\x64\x75\x6c\x65\x0f\x73\x65\x6c\x65\x63\x74\x5f\x76\x31\x32\x38\x5f\x69\x33\x32\x00\x06\x08\x73\x70\x65\x63\x74\x65\x73\x74\x09\x65\x78\x74\x65\x72\x6e\x72\x65\x66\x00\x01\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0c\x69\x73\x5f\x65\x78\x74\x65\x72\x6e\x72\x65\x66\x00\x02\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0a\x69\x73\x5f\x66\x75\x6e\x63\x72\x65\x66\x00\x03\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0c\x65\x71\x5f\x65\x78\x74\x65\x72\x6e\x72\x65\x66\x00\x04\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0a\x65\x71\x5f\x66\x75\x6e\x63\x72\x65\x66\x00\x05\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x06\x0a\xe2\x80\x80\x80\x00\x01\xdc\x80\x80\x80\x00\x00\x02\x40\xfd\x0c\x00\x00\x80\x3f\x00\x00\x00\x40\x00\x00\x40\x40\x00\x00\x80\x40\xfd\x0c\x00\x00\xa0\x40\x00\x00\xc0\x40\x00\x00\xe0\x40\x00\x00\x00\x41\x41\x7f\x10\x00\xfd\x0c\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfd\x4e\xfd\x0c\x00\x00\x80\x3f\x00\x00\x00\x40\x00\x00\x40\x40\x00\x00\x80\x40\xfd\x23\xfd\x63\x45\x0d\x00\x0f\x0b\x00\x0b", exports($1)), "run", [])); // assert_return(() => call($1, "select_v128_i32", [v128("1_065_353_216 1_073_741_824 1_077_936_128 1_082_130_432"), v128("1_084_227_584 1_086_324_736 1_088_421_888 1_090_519_040"), -1]), v128("1. 2. 3. 4."))
+
+// simd_select.wast:36
+run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\xa6\x80\x80\x80\x00\x07\x60\x00\x00\x60\x01\x7f\x01\x6f\x60\x01\x6f\x01\x7f\x60\x01\x70\x01\x7f\x60\x02\x6f\x6f\x01\x7f\x60\x02\x70\x70\x01\x7f\x60\x03\x7b\x7b\x7f\x01\x7b\x02\x8b\x81\x80\x80\x00\x06\x06\x6d\x6f\x64\x75\x6c\x65\x0f\x73\x65\x6c\x65\x63\x74\x5f\x76\x31\x32\x38\x5f\x69\x33\x32\x00\x06\x08\x73\x70\x65\x63\x74\x65\x73\x74\x09\x65\x78\x74\x65\x72\x6e\x72\x65\x66\x00\x01\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0c\x69\x73\x5f\x65\x78\x74\x65\x72\x6e\x72\x65\x66\x00\x02\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0a\x69\x73\x5f\x66\x75\x6e\x63\x72\x65\x66\x00\x03\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0c\x65\x71\x5f\x65\x78\x74\x65\x72\x6e\x72\x65\x66\x00\x04\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0a\x65\x71\x5f\x66\x75\x6e\x63\x72\x65\x66\x00\x05\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x06\x0a\xe2\x80\x80\x80\x00\x01\xdc\x80\x80\x80\x00\x00\x02\x40\xfd\x0c\x00\x00\xc0\xbf\x00\x00\x20\xc0\x00\x00\x60\xc0\x00\x00\x90\xc0\xfd\x0c\x00\x00\x18\x41\x00\x00\x08\x41\x00\x00\xf0\x40\x00\x00\xd0\x40\x41\x00\x10\x00\xfd\x0c\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfd\x4e\xfd\x0c\x00\x00\x18\x41\x00\x00\x08\x41\x00\x00\xf0\x40\x00\x00\xd0\x40\xfd\x23\xfd\x63\x45\x0d\x00\x0f\x0b\x00\x0b", exports($1)), "run", [])); // assert_return(() => call($1, "select_v128_i32", [v128("-1_077_936_128 -1_071_644_672 -1_067_450_368 -1_064_304_640"), v128("1_092_091_904 1_091_043_328 1_089_470_464 1_087_373_312"), 0]), v128("9.5 8.5 7.5 6.5"))
+
+// simd_select.wast:45
+run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\xa6\x80\x80\x80\x00\x07\x60\x00\x00\x60\x01\x7f\x01\x6f\x60\x01\x6f\x01\x7f\x60\x01\x70\x01\x7f\x60\x02\x6f\x6f\x01\x7f\x60\x02\x70\x70\x01\x7f\x60\x03\x7b\x7b\x7f\x01\x7b\x02\x8b\x81\x80\x80\x00\x06\x06\x6d\x6f\x64\x75\x6c\x65\x0f\x73\x65\x6c\x65\x63\x74\x5f\x76\x31\x32\x38\x5f\x69\x33\x32\x00\x06\x08\x73\x70\x65\x63\x74\x65\x73\x74\x09\x65\x78\x74\x65\x72\x6e\x72\x65\x66\x00\x01\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0c\x69\x73\x5f\x65\x78\x74\x65\x72\x6e\x72\x65\x66\x00\x02\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0a\x69\x73\x5f\x66\x75\x6e\x63\x72\x65\x66\x00\x03\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0c\x65\x71\x5f\x65\x78\x74\x65\x72\x6e\x72\x65\x66\x00\x04\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0a\x65\x71\x5f\x66\x75\x6e\x63\x72\x65\x66\x00\x05\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x06\x0a\xe3\x80\x80\x80\x00\x01\xdd\x80\x80\x80\x00\x00\x02\x40\xfd\x0c\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\xfd\x0c\x10\x0f\x0e\x0d\x0c\x0b\x0a\x09\x08\x07\x06\x05\x04\x03\x02\x01\x41\xfb\x00\x10\x00\xfd\x0c\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfd\x4e\xfd\x0c\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\xfd\x23\xfd\x63\x45\x0d\x00\x0f\x0b\x00\x0b", exports($1)), "run", [])); // assert_return(() => call($1, "select_v128_i32", [v128("67_305_985 134_678_021 202_050_057 269_422_093"), v128("219_025_168 151_653_132 84_281_096 16_909_060"), 123]), v128("1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16"))
+
+// simd_select.wast:54
+run(() => call(instance("\x00\x61\x73\x6d\x01\x00\x00\x00\x01\xa6\x80\x80\x80\x00\x07\x60\x00\x00\x60\x01\x7f\x01\x6f\x60\x01\x6f\x01\x7f\x60\x01\x70\x01\x7f\x60\x02\x6f\x6f\x01\x7f\x60\x02\x70\x70\x01\x7f\x60\x03\x7b\x7b\x7f\x01\x7b\x02\x8b\x81\x80\x80\x00\x06\x06\x6d\x6f\x64\x75\x6c\x65\x0f\x73\x65\x6c\x65\x63\x74\x5f\x76\x31\x32\x38\x5f\x69\x33\x32\x00\x06\x08\x73\x70\x65\x63\x74\x65\x73\x74\x09\x65\x78\x74\x65\x72\x6e\x72\x65\x66\x00\x01\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0c\x69\x73\x5f\x65\x78\x74\x65\x72\x6e\x72\x65\x66\x00\x02\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0a\x69\x73\x5f\x66\x75\x6e\x63\x72\x65\x66\x00\x03\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0c\x65\x71\x5f\x65\x78\x74\x65\x72\x6e\x72\x65\x66\x00\x04\x08\x73\x70\x65\x63\x74\x65\x73\x74\x0a\x65\x71\x5f\x66\x75\x6e\x63\x72\x65\x66\x00\x05\x03\x82\x80\x80\x80\x00\x01\x00\x07\x87\x80\x80\x80\x00\x01\x03\x72\x75\x6e\x00\x06\x0a\xe2\x80\x80\x80\x00\x01\xdc\x80\x80\x80\x00\x00\x02\x40\xfd\x0c\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\xfd\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x41\x00\x10\x00\xfd\x0c\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfd\x4e\xfd\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfd\x23\xfd\x63\x45\x0d\x00\x0f\x0b\x00\x0b", exports($1)), "run", [])); // assert_return(() => call($1, "select_v128_i32", [v128("16_843_009 16_843_009 16_843_009 16_843_009"), v128("0 0 0 0"), 0]), v128("0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"))
+reinitializeRegistry();
+})();
diff --git a/tests/wpt/tests/wasm/core/simd/simd_select.wast.js.html b/tests/wpt/tests/wasm/core/simd/simd_select.wast.js.html
new file mode 100644
index 00000000000..5d50138374b
--- /dev/null
+++ b/tests/wpt/tests/wasm/core/simd/simd_select.wast.js.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="UTF-8">
+ <title>WebAssembly Web Platform Test</title>
+ </head>
+ <body>
+
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="../js/harness/async_index.js"></script>
+
+ <div id=log></div>
+ <script src="../js/simd/simd_select.wast.js"></script>
+ </body>
+</html>
diff --git a/tests/wpt/tests/webdriver/tests/bidi/__init__.py b/tests/wpt/tests/webdriver/tests/bidi/__init__.py
index 955335ea87e..587dc91a938 100644
--- a/tests/wpt/tests/webdriver/tests/bidi/__init__.py
+++ b/tests/wpt/tests/webdriver/tests/bidi/__init__.py
@@ -132,7 +132,7 @@ def assert_handle(obj: Mapping[str, Any], should_contain_handle: bool) -> None:
assert "handle" not in obj, f"Result should not contain `handle`. Actual: {obj}"
-async def create_console_api_message(bidi_session, context: str, text: str):
+async def create_console_api_message(bidi_session, context: Any, text: str):
await bidi_session.script.call_function(
function_declaration="""(text) => console.log(text)""",
arguments=[{"type": "string", "value": text}],
@@ -142,7 +142,7 @@ async def create_console_api_message(bidi_session, context: str, text: str):
return text
-async def get_device_pixel_ratio(bidi_session, context: str) -> float:
+async def get_device_pixel_ratio(bidi_session, context: Any) -> float:
result = await bidi_session.script.call_function(
function_declaration="""() => {
return window.devicePixelRatio;
@@ -167,7 +167,7 @@ async def get_element_dimensions(bidi_session, context, element):
return remote_mapping_to_dict(result["value"])
-async def get_viewport_dimensions(bidi_session, context: str,
+async def get_viewport_dimensions(bidi_session, context: Any,
with_scrollbar: bool = True, quirk_mode: bool = False):
if with_scrollbar:
expression = """
@@ -198,7 +198,7 @@ async def get_viewport_dimensions(bidi_session, context: str,
return remote_mapping_to_dict(result["value"])
-async def get_document_dimensions(bidi_session, context: str):
+async def get_document_dimensions(bidi_session, context: Any):
expression = """
({
height: document.documentElement.scrollHeight,
diff --git a/tests/wpt/tests/webdriver/tests/bidi/browsing_context/set_viewport/user_contexts.py b/tests/wpt/tests/webdriver/tests/bidi/browsing_context/set_viewport/user_contexts.py
index 64c26163734..fa1e748d6b2 100644
--- a/tests/wpt/tests/webdriver/tests/bidi/browsing_context/set_viewport/user_contexts.py
+++ b/tests/wpt/tests/webdriver/tests/bidi/browsing_context/set_viewport/user_contexts.py
@@ -1,11 +1,13 @@
import pytest
+from webdriver.bidi.modules.script import ContextTarget
from webdriver.bidi.undefined import UNDEFINED
from ... import get_device_pixel_ratio, get_viewport_dimensions
-
pytestmark = pytest.mark.asyncio
+CONTEXT_LOAD_EVENT = "browsingContext.load"
+
async def test_set_to_user_context(bidi_session, new_tab, create_user_context):
user_context = await create_user_context()
@@ -55,6 +57,48 @@ async def test_set_to_user_context(bidi_session, new_tab, create_user_context):
)
+async def test_set_to_user_context_window_open(
+ bidi_session,
+ new_tab,
+ create_user_context,
+ inline,
+ subscribe_events,
+ wait_for_event,
+ wait_for_future_safe,
+):
+ user_context = await create_user_context()
+ context_in_user_context_1 = await bidi_session.browsing_context.create(
+ user_context=user_context, type_hint="tab"
+ )
+
+ test_viewport = {"width": 250, "height": 300}
+ await bidi_session.browsing_context.set_viewport(
+ user_contexts=[user_context], viewport=test_viewport
+ )
+ assert (
+ await get_viewport_dimensions(bidi_session, context_in_user_context_1)
+ == test_viewport
+ )
+
+ await subscribe_events(events=[CONTEXT_LOAD_EVENT])
+
+ # Assert that tabs opened via window.open in the same user context
+ # successfully load and have the right viewport set.
+ on_load = wait_for_event(CONTEXT_LOAD_EVENT)
+ result = await bidi_session.script.evaluate(
+ await_promise=False,
+ expression=f"""window.open('{inline("popup")}')""",
+ target=ContextTarget(context_in_user_context_1["context"]),
+ )
+ event = await wait_for_future_safe(on_load)
+
+ contexts = await bidi_session.browsing_context.get_tree(root=event["context"])
+ assert len(contexts) == 1
+ popup_context = contexts[0]
+
+ assert await get_viewport_dimensions(bidi_session, popup_context) == test_viewport
+
+
async def test_set_to_default_user_context(bidi_session, new_tab, create_user_context):
user_context = await create_user_context()
context_in_user_context_1 = await bidi_session.browsing_context.create(
diff --git a/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/__init__.py b/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/__init__.py
index 33dcca90f04..5e1fd1aa863 100644
--- a/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/__init__.py
+++ b/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/__init__.py
@@ -1,28 +1 @@
-from webdriver.bidi.modules.script import ContextTarget
-
-from ... import remote_mapping_to_dict
-
-
TEST_COORDINATES = {"latitude": 10, "longitude": 15, "accuracy": 0.5}
-
-
-async def get_current_geolocation(bidi_session, context):
- # Per geolocation spec, the geolocation coordinates are returned
- # only for an active browsing context. It might be required to
- # re-activate the previously active tab in the test.
- await bidi_session.browsing_context.activate(context=context["context"])
-
- result = await bidi_session.script.call_function(
- function_declaration="""() =>
- new Promise(
- resolve => window.navigator.geolocation.getCurrentPosition(
- position => resolve(position.coords.toJSON()),
- error => resolve({code: error.code, message: error.message}),
- {timeout: 500}
- ))
- """,
- target=ContextTarget(context["context"]),
- await_promise=True,
- )
-
- return remote_mapping_to_dict(result["value"])
diff --git a/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/conftest.py b/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/conftest.py
index a90895c74ed..5fb9451f0a2 100644
--- a/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/conftest.py
+++ b/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/conftest.py
@@ -1,6 +1,35 @@
import pytest_asyncio
-from ... import get_context_origin
+from webdriver.bidi.modules.script import ContextTarget
+
+from ... import get_context_origin, remote_mapping_to_dict
+
+
+@pytest_asyncio.fixture
+async def get_current_geolocation(bidi_session, configuration):
+ async def get_current_geolocation(context):
+ # Per geolocation spec, the geolocation coordinates are returned
+ # only for an active browsing context. It might be required to
+ # re-activate the previously active tab in the test.
+ await bidi_session.browsing_context.activate(context=context["context"])
+
+ result = await bidi_session.script.call_function(
+ function_declaration="""(multiplier) =>
+ new Promise(
+ resolve => window.navigator.geolocation.getCurrentPosition(
+ position => resolve(position.coords.toJSON()),
+ error => resolve({code: error.code}),
+ {timeout: 500 * multiplier}
+ ))
+ """,
+ arguments=[{"type": "number", "value": configuration["timeout_multiplier"]}],
+ target=ContextTarget(context["context"]),
+ await_promise=True,
+ )
+
+ return remote_mapping_to_dict(result["value"])
+
+ return get_current_geolocation
@pytest_asyncio.fixture
diff --git a/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/contexts.py b/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/contexts.py
index 8a0e43475da..068bb804e73 100644
--- a/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/contexts.py
+++ b/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/contexts.py
@@ -2,14 +2,14 @@ import pytest
from webdriver.bidi.modules.emulation import CoordinatesOptions
-from . import get_current_geolocation, TEST_COORDINATES
+from . import TEST_COORDINATES
pytestmark = pytest.mark.asyncio
async def test_contexts(
- bidi_session, new_tab, top_context, url, set_geolocation_permission
+ bidi_session, new_tab, top_context, url, get_current_geolocation, set_geolocation_permission
):
test_url = url("/common/blank.html")
await bidi_session.browsing_context.navigate(
@@ -24,12 +24,10 @@ async def test_contexts(
)
await set_geolocation_permission(new_tab)
- default_coordinates = await get_current_geolocation(bidi_session, new_tab)
+ default_coordinates = await get_current_geolocation(new_tab)
assert default_coordinates != TEST_COORDINATES
- assert (
- await get_current_geolocation(bidi_session, top_context) == default_coordinates
- )
+ assert await get_current_geolocation(top_context) == default_coordinates
# Set geolocation override.
await bidi_session.emulation.set_geolocation_override(
@@ -41,24 +39,20 @@ async def test_contexts(
),
)
- assert await get_current_geolocation(bidi_session, new_tab) == TEST_COORDINATES
- assert (
- await get_current_geolocation(bidi_session, top_context) == default_coordinates
- )
+ assert await get_current_geolocation(new_tab) == TEST_COORDINATES
+ assert await get_current_geolocation(top_context) == default_coordinates
# Reset geolocation override.
await bidi_session.emulation.set_geolocation_override(
contexts=[new_tab["context"]], coordinates=None
)
- assert await get_current_geolocation(bidi_session, new_tab) == default_coordinates
- assert (
- await get_current_geolocation(bidi_session, top_context) == default_coordinates
- )
+ assert await get_current_geolocation(new_tab) == default_coordinates
+ assert await get_current_geolocation(top_context) == default_coordinates
async def test_multiple_contexts(
- bidi_session, new_tab, url, set_geolocation_permission
+ bidi_session, new_tab, url, get_current_geolocation, set_geolocation_permission
):
new_context = await bidi_session.browsing_context.create(type_hint="tab")
test_url = url("/common/blank.html")
@@ -74,12 +68,10 @@ async def test_multiple_contexts(
)
await set_geolocation_permission(new_tab)
- default_coordinates = await get_current_geolocation(bidi_session, new_tab)
+ default_coordinates = await get_current_geolocation(new_tab)
assert default_coordinates != TEST_COORDINATES
- assert (
- await get_current_geolocation(bidi_session, new_context) == default_coordinates
- )
+ assert await get_current_geolocation(new_context) == default_coordinates
# Set geolocation override.
await bidi_session.emulation.set_geolocation_override(
@@ -91,8 +83,8 @@ async def test_multiple_contexts(
),
)
- assert await get_current_geolocation(bidi_session, new_tab) == TEST_COORDINATES
- assert await get_current_geolocation(bidi_session, new_context) == TEST_COORDINATES
+ assert await get_current_geolocation(new_tab) == TEST_COORDINATES
+ assert await get_current_geolocation(new_context) == TEST_COORDINATES
# Reset geolocation override.
await bidi_session.emulation.set_geolocation_override(
@@ -101,7 +93,5 @@ async def test_multiple_contexts(
# The new coordinates can be different from the initial ones if the position
# was not available at the beginning.
- assert await get_current_geolocation(bidi_session, new_tab) != TEST_COORDINATES
- assert (
- await get_current_geolocation(bidi_session, new_context) != TEST_COORDINATES
- )
+ assert await get_current_geolocation(new_tab) != TEST_COORDINATES
+ assert await get_current_geolocation(new_context) != TEST_COORDINATES
diff --git a/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/coordinates.py b/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/coordinates.py
index b23354e9e42..ea4fe643c3e 100644
--- a/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/coordinates.py
+++ b/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/coordinates.py
@@ -4,7 +4,6 @@ from webdriver.bidi.modules.emulation import CoordinatesOptions
from webdriver.bidi.modules.script import ContextTarget
from ... import remote_mapping_to_dict
-from . import get_current_geolocation
pytestmark = pytest.mark.asyncio
@@ -22,7 +21,12 @@ pytestmark = pytest.mark.asyncio
],
)
async def test_get_current_position(
- bidi_session, new_tab, url, set_geolocation_permission, test_coordinates
+ bidi_session,
+ new_tab,
+ url,
+ get_current_geolocation,
+ set_geolocation_permission,
+ test_coordinates,
):
test_url = url("/common/blank.html")
await bidi_session.browsing_context.navigate(
@@ -32,7 +36,7 @@ async def test_get_current_position(
)
await set_geolocation_permission(new_tab)
- default_coordinates = await get_current_geolocation(bidi_session, new_tab)
+ default_coordinates = await get_current_geolocation(new_tab)
# Set default accuracy value.
if "accuracy" not in test_coordinates:
@@ -44,7 +48,7 @@ async def test_get_current_position(
contexts=[new_tab["context"]], coordinates=test_coordinates
)
- assert await get_current_geolocation(bidi_session, new_tab) == test_coordinates
+ assert await get_current_geolocation(new_tab) == test_coordinates
async def test_watch_position(
@@ -132,7 +136,7 @@ async def test_watch_position(
async def test_persists_on_reload(
- bidi_session, url, new_tab, set_geolocation_permission
+ bidi_session, url, new_tab, get_current_geolocation, set_geolocation_permission
):
test_url = url("/common/blank.html")
await bidi_session.browsing_context.navigate(
@@ -153,17 +157,17 @@ async def test_persists_on_reload(
),
)
- assert await get_current_geolocation(bidi_session, new_tab) == test_coordinates
+ assert await get_current_geolocation(new_tab) == test_coordinates
await bidi_session.browsing_context.reload(
context=new_tab["context"], wait="complete"
)
- assert await get_current_geolocation(bidi_session, new_tab) == test_coordinates
+ assert await get_current_geolocation(new_tab) == test_coordinates
async def test_persists_on_navigation(
- bidi_session, url, new_tab, set_geolocation_permission
+ bidi_session, url, new_tab, get_current_geolocation, set_geolocation_permission
):
test_url = url("/common/blank.html")
await bidi_session.browsing_context.navigate(
@@ -184,7 +188,7 @@ async def test_persists_on_navigation(
),
)
- assert await get_current_geolocation(bidi_session, new_tab) == test_coordinates
+ assert await get_current_geolocation(new_tab) == test_coordinates
await bidi_session.browsing_context.navigate(
context=new_tab["context"],
@@ -192,4 +196,24 @@ async def test_persists_on_navigation(
wait="complete",
)
- assert await get_current_geolocation(bidi_session, new_tab) == test_coordinates
+ assert await get_current_geolocation(new_tab) == test_coordinates
+
+
+async def test_reset_without_override(
+ bidi_session, new_tab, url, get_current_geolocation, set_geolocation_permission
+):
+ test_url = url("/common/blank.html")
+ await bidi_session.browsing_context.navigate(
+ context=new_tab["context"],
+ url=test_url,
+ wait="complete",
+ )
+ await set_geolocation_permission(new_tab)
+
+ default_coordinates = await get_current_geolocation(new_tab)
+
+ await bidi_session.emulation.set_geolocation_override(
+ contexts=[new_tab["context"]], coordinates=None
+ )
+
+ assert await get_current_geolocation(new_tab) == default_coordinates
diff --git a/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/error.py b/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/error.py
new file mode 100644
index 00000000000..27c23dc7811
--- /dev/null
+++ b/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/error.py
@@ -0,0 +1,123 @@
+import pytest
+
+from webdriver.bidi.modules.script import ContextTarget
+
+from ... import remote_mapping_to_dict
+
+pytestmark = pytest.mark.asyncio
+
+ERROR = {"type": "positionUnavailable"}
+EXPECTED_ERROR = {"code": 2}
+
+
+async def test_get_current_position(bidi_session, new_tab, url,
+ get_current_geolocation, set_geolocation_permission):
+ test_url = url("/common/blank.html")
+ await bidi_session.browsing_context.navigate(
+ context=new_tab["context"],
+ url=test_url,
+ wait="complete",
+ )
+ await set_geolocation_permission(new_tab)
+
+ await bidi_session.emulation.set_geolocation_override(
+ contexts=[new_tab["context"]], error=ERROR
+ )
+
+ assert await get_current_geolocation(new_tab) == EXPECTED_ERROR
+
+
+async def test_watch_position(
+ bidi_session,
+ new_tab,
+ url,
+ subscribe_events,
+ wait_for_event,
+ wait_for_future_safe,
+ set_geolocation_permission,
+):
+ await subscribe_events(["script.message"])
+
+ test_url = url("/common/blank.html")
+ await bidi_session.browsing_context.navigate(
+ context=new_tab["context"],
+ url=test_url,
+ wait="complete",
+ )
+ await set_geolocation_permission(new_tab)
+
+ await bidi_session.emulation.set_geolocation_override(
+ contexts=[new_tab["context"]],
+ error=ERROR
+ )
+
+ on_script_message = wait_for_event("script.message")
+ await bidi_session.browsing_context.activate(context=new_tab["context"])
+ await bidi_session.script.call_function(
+ arguments=[{"type": "channel", "value": {"channel": "channel_name"}}],
+ function_declaration="""(channel) =>
+ window.navigator.geolocation.watchPosition(
+ (result) => channel("unexpected result"),
+ (error) => channel({code: error.code})
+ )
+ """,
+ target=ContextTarget(new_tab["context"]),
+ await_promise=False,
+ )
+ event_data = await wait_for_future_safe(on_script_message)
+
+ assert remote_mapping_to_dict(event_data["data"]["value"]) == EXPECTED_ERROR
+
+
+async def test_persists_on_reload(
+ bidi_session, url, new_tab, get_current_geolocation, set_geolocation_permission
+):
+ test_url = url("/common/blank.html")
+ await bidi_session.browsing_context.navigate(
+ context=new_tab["context"],
+ url=test_url,
+ wait="complete",
+ )
+ await set_geolocation_permission(new_tab)
+
+ # Set geolocation override.
+ await bidi_session.emulation.set_geolocation_override(
+ contexts=[new_tab["context"]],
+ error=ERROR,
+ )
+
+ assert await get_current_geolocation(new_tab) == EXPECTED_ERROR
+
+ await bidi_session.browsing_context.reload(
+ context=new_tab["context"], wait="complete"
+ )
+
+ assert await get_current_geolocation(new_tab) == EXPECTED_ERROR
+
+
+async def test_persists_on_navigation(
+ bidi_session, url, new_tab, get_current_geolocation, set_geolocation_permission
+):
+ test_url = url("/common/blank.html")
+ await bidi_session.browsing_context.navigate(
+ context=new_tab["context"],
+ url=test_url,
+ wait="complete",
+ )
+ await set_geolocation_permission(new_tab)
+
+ # Set geolocation override.
+ await bidi_session.emulation.set_geolocation_override(
+ contexts=[new_tab["context"]],
+ error=ERROR,
+ )
+
+ assert await get_current_geolocation(new_tab) == EXPECTED_ERROR
+
+ await bidi_session.browsing_context.navigate(
+ context=new_tab["context"],
+ url=url("/webdriver/tests/support/html/default.html"),
+ wait="complete",
+ )
+
+ assert await get_current_geolocation(new_tab) == EXPECTED_ERROR
diff --git a/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/invalid.py b/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/invalid.py
index e804848e0be..2334d37517d 100644
--- a/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/invalid.py
+++ b/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/invalid.py
@@ -2,7 +2,6 @@ import pytest
import webdriver.bidi.error as error
from webdriver.bidi.modules.emulation import CoordinatesOptions
-from webdriver.bidi.undefined import UNDEFINED
pytestmark = pytest.mark.asyncio
@@ -75,7 +74,7 @@ async def test_params_contexts_iframe(bidi_session, new_tab, get_test_page):
)
-@pytest.mark.parametrize("value", [UNDEFINED, False, 42, "foo", []])
+@pytest.mark.parametrize("value", [False, 42, "foo", []])
async def test_params_coordinates_invalid_type(bidi_session, top_context, value):
with pytest.raises(error.InvalidArgumentException):
await bidi_session.emulation.set_geolocation_override(
@@ -333,3 +332,60 @@ async def test_params_user_contexts_entry_invalid_value(bidi_session, value):
},
user_contexts=[value],
)
+
+
+async def test_params_coordinates_and_error(bidi_session, top_context):
+ with pytest.raises(error.InvalidArgumentException):
+ await bidi_session.emulation.set_geolocation_override(
+ contexts=[top_context["context"]],
+ coordinates={
+ "latitude": 10,
+ "longitude": 10,
+ },
+ error={"type": "positionUnavailable"}
+ )
+
+
+async def test_params_no_coordinates_no_error(bidi_session, top_context):
+ with pytest.raises(error.InvalidArgumentException):
+ await bidi_session.emulation.set_geolocation_override(
+ contexts=[top_context["context"]],
+ )
+
+
+@pytest.mark.parametrize("value", [False, 42, "foo", []])
+async def test_params_error_invalid_type(bidi_session, top_context, value):
+ with pytest.raises(error.InvalidArgumentException):
+ await bidi_session.emulation.set_geolocation_override(
+ contexts=[top_context["context"]],
+ error=value,
+ )
+
+
+async def test_params_error_empty_object(bidi_session, top_context):
+ with pytest.raises(error.InvalidArgumentException):
+ await bidi_session.emulation.set_geolocation_override(
+ contexts=[top_context["context"]],
+ error={},
+ )
+
+
+@pytest.mark.parametrize("value", [None, False, 42, {}, []])
+async def test_params_error_type_invalid_type(bidi_session, top_context, value):
+ with pytest.raises(error.InvalidArgumentException):
+ await bidi_session.emulation.set_geolocation_override(
+ contexts=[top_context["context"]],
+ error={
+ "type": value
+ },
+ )
+
+
+async def test_params_error_type_invalid_value(bidi_session, top_context):
+ with pytest.raises(error.InvalidArgumentException):
+ await bidi_session.emulation.set_geolocation_override(
+ contexts=[top_context["context"]],
+ error={
+ "type": "unknownError",
+ },
+ )
diff --git a/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/user_contexts.py b/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/user_contexts.py
index 008dee5698d..b3038bb5958 100644
--- a/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/user_contexts.py
+++ b/tests/wpt/tests/webdriver/tests/bidi/emulation/set_geolocation_override/user_contexts.py
@@ -2,14 +2,19 @@ import pytest
from webdriver.bidi.modules.emulation import CoordinatesOptions
-from . import get_current_geolocation, TEST_COORDINATES
+from . import TEST_COORDINATES
pytestmark = pytest.mark.asyncio
async def test_user_contexts(
- bidi_session, url, create_user_context, new_tab, set_geolocation_permission
+ bidi_session,
+ url,
+ create_user_context,
+ new_tab,
+ get_current_geolocation,
+ set_geolocation_permission,
):
user_context = await create_user_context()
context_in_user_context_1 = await bidi_session.browsing_context.create(
@@ -29,12 +34,10 @@ async def test_user_contexts(
await set_geolocation_permission(new_tab)
await set_geolocation_permission(new_tab, user_context)
- default_coordinates = await get_current_geolocation(
- bidi_session, context_in_user_context_1
- )
+ default_coordinates = await get_current_geolocation(context_in_user_context_1)
assert default_coordinates != TEST_COORDINATES
- assert await get_current_geolocation(bidi_session, new_tab) == default_coordinates
+ assert await get_current_geolocation(new_tab) == default_coordinates
# Set geolocation override.
await bidi_session.emulation.set_geolocation_override(
@@ -46,11 +49,8 @@ async def test_user_contexts(
),
)
- assert (
- await get_current_geolocation(bidi_session, context_in_user_context_1)
- == TEST_COORDINATES
- )
- assert await get_current_geolocation(bidi_session, new_tab) == default_coordinates
+ assert await get_current_geolocation(context_in_user_context_1) == TEST_COORDINATES
+ assert await get_current_geolocation(new_tab) == default_coordinates
# Create a new context in the user context.
context_in_user_context_2 = await bidi_session.browsing_context.create(
@@ -62,14 +62,16 @@ async def test_user_contexts(
wait="complete",
)
- assert (
- await get_current_geolocation(bidi_session, context_in_user_context_2)
- == TEST_COORDINATES
- )
+ assert await get_current_geolocation(context_in_user_context_2) == TEST_COORDINATES
async def test_set_to_default_user_context(
- bidi_session, new_tab, create_user_context, url, set_geolocation_permission
+ bidi_session,
+ new_tab,
+ create_user_context,
+ url,
+ get_current_geolocation,
+ set_geolocation_permission,
):
user_context = await create_user_context()
context_in_user_context_1 = await bidi_session.browsing_context.create(
@@ -89,7 +91,7 @@ async def test_set_to_default_user_context(
await set_geolocation_permission(new_tab)
await set_geolocation_permission(new_tab, user_context)
- default_coordinates = await get_current_geolocation(bidi_session, new_tab)
+ default_coordinates = await get_current_geolocation(new_tab)
assert default_coordinates != TEST_COORDINATES
await bidi_session.emulation.set_geolocation_override(
@@ -103,10 +105,9 @@ async def test_set_to_default_user_context(
# Make sure that the geolocation changes are only applied to the context associated with default user context.
assert (
- await get_current_geolocation(bidi_session, context_in_user_context_1)
- == default_coordinates
+ await get_current_geolocation(context_in_user_context_1) == default_coordinates
)
- assert await get_current_geolocation(bidi_session, new_tab) == TEST_COORDINATES
+ assert await get_current_geolocation(new_tab) == TEST_COORDINATES
# Create a new context in the default context.
context_in_default_context_2 = await bidi_session.browsing_context.create(
@@ -120,8 +121,7 @@ async def test_set_to_default_user_context(
)
assert (
- await get_current_geolocation(bidi_session, context_in_default_context_2)
- == TEST_COORDINATES
+ await get_current_geolocation(context_in_default_context_2) == TEST_COORDINATES
)
# Reset geolocation override.
@@ -131,7 +131,11 @@ async def test_set_to_default_user_context(
async def test_set_to_multiple_user_contexts(
- bidi_session, create_user_context, url, set_geolocation_permission
+ bidi_session,
+ create_user_context,
+ url,
+ get_current_geolocation,
+ set_geolocation_permission,
):
user_context_1 = await create_user_context()
context_in_user_context_1 = await bidi_session.browsing_context.create(
@@ -164,18 +168,17 @@ async def test_set_to_multiple_user_contexts(
),
)
- assert (
- await get_current_geolocation(bidi_session, context_in_user_context_1)
- == TEST_COORDINATES
- )
- assert (
- await get_current_geolocation(bidi_session, context_in_user_context_2)
- == TEST_COORDINATES
- )
+ assert await get_current_geolocation(context_in_user_context_1) == TEST_COORDINATES
+ assert await get_current_geolocation(context_in_user_context_2) == TEST_COORDINATES
async def test_set_to_user_context_and_then_to_context(
- bidi_session, create_user_context, url, new_tab, set_geolocation_permission
+ bidi_session,
+ create_user_context,
+ url,
+ new_tab,
+ get_current_geolocation,
+ set_geolocation_permission,
):
user_context = await create_user_context()
context_in_user_context_1 = await bidi_session.browsing_context.create(
@@ -195,9 +198,7 @@ async def test_set_to_user_context_and_then_to_context(
await set_geolocation_permission(new_tab)
await set_geolocation_permission(new_tab, user_context)
- default_coordinates = await get_current_geolocation(
- bidi_session, context_in_user_context_1
- )
+ default_coordinates = await get_current_geolocation(context_in_user_context_1)
assert default_coordinates != TEST_COORDINATES
@@ -222,7 +223,7 @@ async def test_set_to_user_context_and_then_to_context(
),
)
assert (
- await get_current_geolocation(bidi_session, context_in_user_context_1)
+ await get_current_geolocation(context_in_user_context_1)
== new_geolocation_coordinates
)
@@ -232,7 +233,7 @@ async def test_set_to_user_context_and_then_to_context(
# Make sure that after reload the geolocation is still updated.
assert (
- await get_current_geolocation(bidi_session, context_in_user_context_1)
+ await get_current_geolocation(context_in_user_context_1)
== new_geolocation_coordinates
)
@@ -246,10 +247,7 @@ async def test_set_to_user_context_and_then_to_context(
wait="complete",
)
# Make sure that the geolocation override for the user context is applied.
- assert (
- await get_current_geolocation(bidi_session, context_in_user_context_2)
- == TEST_COORDINATES
- )
+ assert await get_current_geolocation(context_in_user_context_2) == TEST_COORDINATES
await bidi_session.emulation.set_geolocation_override(
contexts=[context_in_user_context_1["context"]],
@@ -258,6 +256,5 @@ async def test_set_to_user_context_and_then_to_context(
# Make sure that the geolocation override was reset.
assert (
- await get_current_geolocation(bidi_session, context_in_user_context_1)
- == default_coordinates
+ await get_current_geolocation(context_in_user_context_1) == default_coordinates
)
diff --git a/tests/wpt/tests/webnn/conformance_tests/conv2d.https.any.js b/tests/wpt/tests/webnn/conformance_tests/conv2d.https.any.js
index 9fe246c9395..45fecb2b40c 100644
--- a/tests/wpt/tests/webnn/conformance_tests/conv2d.https.any.js
+++ b/tests/wpt/tests/webnn/conformance_tests/conv2d.https.any.js
@@ -1105,6 +1105,1011 @@ const conv2dTests = [
}
}
}
+ },
+
+ // float16 tests
+ {
+ 'name':
+ 'conv2d float16 4D both input and filter non-constant tensors default options',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ 0.6123046875, 0.8857421875, 0.13671875,
+ 0.564453125, 0.896484375, 0.367919921875,
+ 0.68115234375, 0.047943115234375, 0.33349609375,
+ 0.1988525390625, 0.41162109375, 0.079345703125,
+ 0.42724609375, 0.53564453125, 0.59130859375,
+ 0.2841796875, 0.414794921875, 0.0269012451171875,
+ 0.362060546875, 0.99462890625, 0.07183837890625,
+ 0.1220703125, 0.84228515625, 0.453857421875,
+ 0.21533203125
+ ],
+ 'descriptor': {shape: [1, 1, 5, 5], dataType: 'float16'}
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.38037109375, 0.52783203125, 0.219482421875, 0.366943359375,
+ 0.33984375, 0.419921875, 0.380615234375, 0.1944580078125,
+ 0.56884765625
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'}
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [{'input': 'conv2dInput'}, {'filter': 'conv2dFilter'}],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [
+ 1.5322265625, 1.357421875, 1.3642578125, 1.0712890625, 1.1259765625,
+ 1.4716796875, 1.0791015625, 1.1552734375, 1.6572265625
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'conv2d float16 4D both input and filter constant tensors default options',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ 0.6123046875, 0.8857421875, 0.13671875,
+ 0.564453125, 0.896484375, 0.367919921875,
+ 0.68115234375, 0.047943115234375, 0.33349609375,
+ 0.1988525390625, 0.41162109375, 0.079345703125,
+ 0.42724609375, 0.53564453125, 0.59130859375,
+ 0.2841796875, 0.414794921875, 0.0269012451171875,
+ 0.362060546875, 0.99462890625, 0.07183837890625,
+ 0.1220703125, 0.84228515625, 0.453857421875,
+ 0.21533203125
+ ],
+ 'descriptor': {shape: [1, 1, 5, 5], dataType: 'float16'},
+ 'constant': true
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.38037109375, 0.52783203125, 0.219482421875, 0.366943359375,
+ 0.33984375, 0.419921875, 0.380615234375, 0.1944580078125,
+ 0.56884765625
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [{'input': 'conv2dInput'}, {'filter': 'conv2dFilter'}],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [
+ 1.5322265625, 1.357421875, 1.3642578125, 1.0712890625, 1.1259765625,
+ 1.4716796875, 1.0791015625, 1.1552734375, 1.6572265625
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name': 'conv2d float16 4D input and filter tensors default options',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ 0.6123046875, 0.8857421875, 0.13671875,
+ 0.564453125, 0.896484375, 0.367919921875,
+ 0.68115234375, 0.047943115234375, 0.33349609375,
+ 0.1988525390625, 0.41162109375, 0.079345703125,
+ 0.42724609375, 0.53564453125, 0.59130859375,
+ 0.2841796875, 0.414794921875, 0.0269012451171875,
+ 0.362060546875, 0.99462890625, 0.07183837890625,
+ 0.1220703125, 0.84228515625, 0.453857421875,
+ 0.21533203125
+ ],
+ 'descriptor': {shape: [1, 1, 5, 5], dataType: 'float16'}
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.38037109375, 0.52783203125, 0.219482421875, 0.366943359375,
+ 0.33984375, 0.419921875, 0.380615234375, 0.1944580078125,
+ 0.56884765625
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [{'input': 'conv2dInput'}, {'filter': 'conv2dFilter'}],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [
+ 1.5322265625, 1.357421875, 1.3642578125, 1.0712890625, 1.1259765625,
+ 1.4716796875, 1.0791015625, 1.1552734375, 1.6572265625
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name': 'conv2d float16 4D input and filter tensors options.padding',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ 0.6123046875, 0.8857421875, 0.13671875,
+ 0.564453125, 0.896484375, 0.367919921875,
+ 0.68115234375, 0.047943115234375, 0.33349609375,
+ 0.1988525390625, 0.41162109375, 0.079345703125,
+ 0.42724609375, 0.53564453125, 0.59130859375,
+ 0.2841796875, 0.414794921875, 0.0269012451171875,
+ 0.362060546875, 0.99462890625, 0.07183837890625,
+ 0.1220703125, 0.84228515625, 0.453857421875,
+ 0.21533203125
+ ],
+ 'descriptor': {shape: [1, 1, 5, 5], dataType: 'float16'}
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.38037109375, 0.52783203125, 0.219482421875, 0.366943359375,
+ 0.33984375, 0.419921875, 0.380615234375, 0.1944580078125,
+ 0.56884765625
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [
+ {'input': 'conv2dInput'}, {'filter': 'conv2dFilter'},
+ {'options': {'padding': [1, 1, 1, 1]}}
+ ],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [
+ 1.0390625, 0.8828125, 1.06640625, 0.814453125,
+ 0.67724609375, 1.0537109375, 1.5322265625, 1.357421875,
+ 1.3642578125, 1.1962890625, 0.80810546875, 1.0712890625,
+ 1.1259765625, 1.4716796875, 0.96044921875, 0.5888671875,
+ 1.0791015625, 1.1552734375, 1.6572265625, 1.201171875,
+ 0.316650390625, 0.75439453125, 0.77294921875, 0.97314453125,
+ 0.90234375
+ ],
+ 'descriptor': {shape: [1, 1, 5, 5], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name': 'conv2d float16 4D input and filter tensors options.strides',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ 0.6123046875, 0.8857421875, 0.13671875,
+ 0.564453125, 0.896484375, 0.367919921875,
+ 0.68115234375, 0.047943115234375, 0.33349609375,
+ 0.1988525390625, 0.41162109375, 0.079345703125,
+ 0.42724609375, 0.53564453125, 0.59130859375,
+ 0.2841796875, 0.414794921875, 0.0269012451171875,
+ 0.362060546875, 0.99462890625, 0.07183837890625,
+ 0.1220703125, 0.84228515625, 0.453857421875,
+ 0.21533203125
+ ],
+ 'descriptor': {shape: [1, 1, 5, 5], dataType: 'float16'}
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.38037109375, 0.52783203125, 0.219482421875, 0.366943359375,
+ 0.33984375, 0.419921875, 0.380615234375, 0.1944580078125,
+ 0.56884765625
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [
+ {'input': 'conv2dInput'}, {'filter': 'conv2dFilter'},
+ {'options': {'strides': [2, 2]}}
+ ],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [1.5322265625, 1.3642578125, 1.0791015625, 1.6572265625],
+ 'descriptor': {shape: [1, 1, 2, 2], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name': 'conv2d float16 4D input and filter tensors options.dilations',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ 0.6123046875, 0.8857421875, 0.13671875,
+ 0.564453125, 0.896484375, 0.367919921875,
+ 0.68115234375, 0.047943115234375, 0.33349609375,
+ 0.1988525390625, 0.41162109375, 0.079345703125,
+ 0.42724609375, 0.53564453125, 0.59130859375,
+ 0.2841796875, 0.414794921875, 0.0269012451171875,
+ 0.362060546875, 0.99462890625, 0.07183837890625,
+ 0.1220703125, 0.84228515625, 0.453857421875,
+ 0.21533203125
+ ],
+ 'descriptor': {shape: [1, 1, 5, 5], dataType: 'float16'}
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.38037109375, 0.52783203125, 0.219482421875, 0.366943359375,
+ 0.33984375, 0.419921875, 0.380615234375, 0.1944580078125,
+ 0.56884765625
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [
+ {'input': 'conv2dInput'}, {'filter': 'conv2dFilter'},
+ {'options': {'dilations': [2, 2]}}
+ ],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [1.3603515625],
+ 'descriptor': {shape: [1, 1, 1, 1], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'depthwise conv2d float16 4D input and filter tensors options.groups= input_channels',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ 0.8447265625, 0.943359375, 0.65576171875, 0.6982421875,
+ 0.99951171875, 0.2366943359375, 0.367431640625, 0.261962890625,
+ 0.62548828125, 0.84033203125, 0.37841796875, 0.454345703125,
+ 0.253173828125, 0.578125, 0.54150390625, 0.37841796875
+ ],
+ 'descriptor': {shape: [1, 4, 2, 2], dataType: 'float16'}
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.272216796875, 0.28125, 0.8544921875, 0.1796875, 0.7763671875,
+ 0.51416015625, 0.63720703125, 0.1280517578125, 0.83740234375,
+ 0.57275390625, 0.09857177734375, 0.5927734375, 0.58984375,
+ 0.96923828125, 0.2318115234375, 0.1480712890625
+ ],
+ 'descriptor': {shape: [4, 1, 2, 2], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [
+ {'input': 'conv2dInput'}, {'filter': 'conv2dFilter'},
+ {'options': {'groups': 4}}
+ ],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [1.1806640625, 1.1650390625, 1.3115234375, 0.89111328125],
+ 'descriptor': {shape: [1, 4, 1, 1], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'conv2d float16 4D input and filter tensors options.inputLayout=\'nchw\'',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ 0.7529296875, 0.751953125, 0.5947265625, 0.21630859375,
+ 0.07586669921875, 0.151123046875, 0.12127685546875, 0.53662109375,
+ 0.59375, 0.9912109375, 0.363037109375, 0.92919921875,
+ 0.227294921875, 0.54150390625, 0.08447265625, 0.6767578125,
+ 0.619140625, 0.392822265625
+ ],
+ 'descriptor': {shape: [2, 1, 3, 3], dataType: 'float16'}
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.1453857421875, 0.96728515625, 0.10833740234375, 0.3203125,
+ 0.6953125, 0.50732421875, 0.0814208984375, 0.5302734375,
+ 0.30712890625, 0.432373046875, 0.98486328125, 0.42822265625
+ ],
+ 'descriptor': {shape: [3, 1, 2, 2], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [
+ {'input': 'conv2dInput'}, {'filter': 'conv2dFilter'},
+ {'options': {'inputLayout': 'nchw'}}
+ ],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [
+ 0.884765625, 0.7412109375, 0.289794921875, 0.405517578125,
+ 0.962890625, 0.91064453125, 0.4833984375, 0.488037109375,
+ 0.8017578125, 0.62744140625, 0.448486328125, 0.87158203125,
+ 0.693359375, 1.037109375, 0.82861328125, 0.353271484375,
+ 1.1787109375, 0.8125, 0.81640625, 0.67822265625,
+ 0.9169921875, 1.0830078125, 1.2353515625, 0.98095703125
+ ],
+ 'descriptor': {shape: [2, 3, 2, 2], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'conv2d float16 4D input and filter tensors options.inputLayout=\'nhwc\'',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ 0.7529296875, 0.751953125, 0.5947265625, 0.21630859375,
+ 0.07586669921875, 0.151123046875, 0.12127685546875, 0.53662109375,
+ 0.59375, 0.9912109375, 0.363037109375, 0.92919921875,
+ 0.227294921875, 0.54150390625, 0.08447265625, 0.6767578125,
+ 0.619140625, 0.392822265625
+ ],
+ 'descriptor': {shape: [2, 3, 3, 1], dataType: 'float16'}
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.1453857421875, 0.96728515625, 0.10833740234375, 0.3203125,
+ 0.6953125, 0.50732421875, 0.0814208984375, 0.5302734375,
+ 0.30712890625, 0.432373046875, 0.98486328125, 0.42822265625
+ ],
+ 'descriptor': {shape: [3, 1, 2, 2], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [
+ {'input': 'conv2dInput'}, {'filter': 'conv2dFilter'},
+ {'options': {'inputLayout': 'nhwc'}}
+ ],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [
+ 0.884765625, 0.962890625, 0.8017578125, 0.7412109375,
+ 0.91064453125, 0.62744140625, 0.289794921875, 0.4833984375,
+ 0.448486328125, 0.405517578125, 0.488037109375, 0.87158203125,
+ 0.693359375, 1.1787109375, 0.9169921875, 1.037109375,
+ 0.8125, 1.0830078125, 0.82861328125, 0.81640625,
+ 1.2353515625, 0.353271484375, 0.67822265625, 0.98095703125
+ ],
+ 'descriptor': {shape: [2, 2, 2, 3], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'conv2d float16 4D input and filter tensors options.filterLayout=\'oihw\'',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ 0.7529296875, 0.751953125, 0.5947265625, 0.21630859375,
+ 0.07586669921875, 0.151123046875, 0.12127685546875, 0.53662109375,
+ 0.59375, 0.9912109375, 0.363037109375, 0.92919921875,
+ 0.227294921875, 0.54150390625, 0.08447265625, 0.6767578125,
+ 0.619140625, 0.392822265625
+ ],
+ 'descriptor': {shape: [2, 1, 3, 3], dataType: 'float16'}
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.1453857421875, 0.96728515625, 0.10833740234375, 0.3203125,
+ 0.6953125, 0.50732421875, 0.0814208984375, 0.5302734375,
+ 0.30712890625, 0.432373046875, 0.98486328125, 0.42822265625
+ ],
+ 'descriptor': {shape: [3, 1, 2, 2], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [
+ {'input': 'conv2dInput'}, {'filter': 'conv2dFilter'},
+ {'options': {'filterLayout': 'oihw'}}
+ ],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [
+ 0.884765625, 0.7412109375, 0.289794921875, 0.405517578125,
+ 0.962890625, 0.91064453125, 0.4833984375, 0.488037109375,
+ 0.8017578125, 0.62744140625, 0.448486328125, 0.87158203125,
+ 0.693359375, 1.037109375, 0.82861328125, 0.353271484375,
+ 1.1787109375, 0.8125, 0.81640625, 0.67822265625,
+ 0.9169921875, 1.0830078125, 1.2353515625, 0.98095703125
+ ],
+ 'descriptor': {shape: [2, 3, 2, 2], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'conv2d float16 4D input and filter tensors options.filterLayout=\'hwio\'',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ 0.7529296875, 0.751953125, 0.5947265625, 0.21630859375,
+ 0.07586669921875, 0.151123046875, 0.12127685546875, 0.53662109375,
+ 0.59375, 0.9912109375, 0.363037109375, 0.92919921875,
+ 0.227294921875, 0.54150390625, 0.08447265625, 0.6767578125,
+ 0.619140625, 0.392822265625
+ ],
+ 'descriptor': {shape: [2, 1, 3, 3], dataType: 'float16'}
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.1453857421875, 0.6953125, 0.30712890625, 0.96728515625,
+ 0.50732421875, 0.432373046875, 0.10833740234375, 0.0814208984375,
+ 0.98486328125, 0.3203125, 0.5302734375, 0.42822265625
+ ],
+ 'descriptor': {shape: [2, 2, 1, 3], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [
+ {'input': 'conv2dInput'}, {'filter': 'conv2dFilter'},
+ {'options': {'filterLayout': 'hwio'}}
+ ],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [
+ 0.884765625, 0.7412109375, 0.289794921875, 0.405517578125,
+ 0.962890625, 0.91064453125, 0.4833984375, 0.488037109375,
+ 0.8017578125, 0.62744140625, 0.448486328125, 0.87158203125,
+ 0.693359375, 1.037109375, 0.82861328125, 0.353271484375,
+ 1.1787109375, 0.8125, 0.81640625, 0.67822265625,
+ 0.9169921875, 1.0830078125, 1.2353515625, 0.98095703125
+ ],
+ 'descriptor': {shape: [2, 3, 2, 2], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'conv2d float16 4D input and filter tensors options.filterLayout=\'ohwi\'',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ 0.7529296875, 0.751953125, 0.5947265625, 0.21630859375,
+ 0.07586669921875, 0.151123046875, 0.12127685546875, 0.53662109375,
+ 0.59375, 0.9912109375, 0.363037109375, 0.92919921875,
+ 0.227294921875, 0.54150390625, 0.08447265625, 0.6767578125,
+ 0.619140625, 0.392822265625
+ ],
+ 'descriptor': {shape: [2, 1, 3, 3], dataType: 'float16'}
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.1453857421875, 0.96728515625, 0.10833740234375, 0.3203125,
+ 0.6953125, 0.50732421875, 0.0814208984375, 0.5302734375,
+ 0.30712890625, 0.432373046875, 0.98486328125, 0.42822265625
+ ],
+ 'descriptor': {shape: [3, 2, 2, 1], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [
+ {'input': 'conv2dInput'}, {'filter': 'conv2dFilter'},
+ {'options': {'filterLayout': 'ohwi'}}
+ ],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [
+ 0.884765625, 0.7412109375, 0.289794921875, 0.405517578125,
+ 0.962890625, 0.91064453125, 0.4833984375, 0.488037109375,
+ 0.8017578125, 0.62744140625, 0.448486328125, 0.87158203125,
+ 0.693359375, 1.037109375, 0.82861328125, 0.353271484375,
+ 1.1787109375, 0.8125, 0.81640625, 0.67822265625,
+ 0.9169921875, 1.0830078125, 1.2353515625, 0.98095703125
+ ],
+ 'descriptor': {shape: [2, 3, 2, 2], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'conv2d float16 4D input and filter tensors options.filterLayout=\'ihwo\'',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ 0.7529296875, 0.751953125, 0.5947265625, 0.21630859375,
+ 0.07586669921875, 0.151123046875, 0.12127685546875, 0.53662109375,
+ 0.59375, 0.9912109375, 0.363037109375, 0.92919921875,
+ 0.227294921875, 0.54150390625, 0.08447265625, 0.6767578125,
+ 0.619140625, 0.392822265625
+ ],
+ 'descriptor': {shape: [2, 1, 3, 3], dataType: 'float16'}
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.1453857421875, 0.6953125, 0.30712890625, 0.96728515625,
+ 0.50732421875, 0.432373046875, 0.10833740234375, 0.0814208984375,
+ 0.98486328125, 0.3203125, 0.5302734375, 0.42822265625
+ ],
+ 'descriptor': {shape: [1, 2, 2, 3], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [
+ {'input': 'conv2dInput'}, {'filter': 'conv2dFilter'},
+ {'options': {'filterLayout': 'ihwo'}}
+ ],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [
+ 0.884765625, 0.7412109375, 0.289794921875, 0.405517578125,
+ 0.962890625, 0.91064453125, 0.4833984375, 0.488037109375,
+ 0.8017578125, 0.62744140625, 0.448486328125, 0.87158203125,
+ 0.693359375, 1.037109375, 0.82861328125, 0.353271484375,
+ 1.1787109375, 0.8125, 0.81640625, 0.67822265625,
+ 0.9169921875, 1.0830078125, 1.2353515625, 0.98095703125
+ ],
+ 'descriptor': {shape: [2, 3, 2, 2], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'conv2d float16 4D input and filter tensors options.inputLayout=\'nhwc\' and options.filterLayout=\'oihw\'',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ 0.7529296875, 0.751953125, 0.5947265625, 0.21630859375,
+ 0.07586669921875, 0.151123046875, 0.12127685546875, 0.53662109375,
+ 0.59375, 0.9912109375, 0.363037109375, 0.92919921875,
+ 0.227294921875, 0.54150390625, 0.08447265625, 0.6767578125,
+ 0.619140625, 0.392822265625
+ ],
+ 'descriptor': {shape: [2, 3, 3, 1], dataType: 'float16'}
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.1453857421875, 0.96728515625, 0.10833740234375, 0.3203125,
+ 0.6953125, 0.50732421875, 0.0814208984375, 0.5302734375,
+ 0.30712890625, 0.432373046875, 0.98486328125, 0.42822265625
+ ],
+ 'descriptor': {shape: [3, 1, 2, 2], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [
+ {'input': 'conv2dInput'}, {'filter': 'conv2dFilter'},
+ {'options': {'inputLayout': 'nhwc', 'filterLayout': 'oihw'}}
+ ],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [
+ 0.884765625, 0.962890625, 0.8017578125, 0.7412109375,
+ 0.91064453125, 0.62744140625, 0.289794921875, 0.4833984375,
+ 0.448486328125, 0.405517578125, 0.488037109375, 0.87158203125,
+ 0.693359375, 1.1787109375, 0.9169921875, 1.037109375,
+ 0.8125, 1.0830078125, 0.82861328125, 0.81640625,
+ 1.2353515625, 0.353271484375, 0.67822265625, 0.98095703125
+ ],
+ 'descriptor': {shape: [2, 2, 2, 3], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'conv2d float16 4D input and filter tensors options.inputLayout=\'nhwc\' and options.filterLayout=\'hwio\'',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ 0.7529296875, 0.751953125, 0.5947265625, 0.21630859375,
+ 0.07586669921875, 0.151123046875, 0.12127685546875, 0.53662109375,
+ 0.59375, 0.9912109375, 0.363037109375, 0.92919921875,
+ 0.227294921875, 0.54150390625, 0.08447265625, 0.6767578125,
+ 0.619140625, 0.392822265625
+ ],
+ 'descriptor': {shape: [2, 3, 3, 1], dataType: 'float16'}
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.1453857421875, 0.6953125, 0.30712890625, 0.96728515625,
+ 0.50732421875, 0.432373046875, 0.10833740234375, 0.0814208984375,
+ 0.98486328125, 0.3203125, 0.5302734375, 0.42822265625
+ ],
+ 'descriptor': {shape: [2, 2, 1, 3], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [
+ {'input': 'conv2dInput'}, {'filter': 'conv2dFilter'},
+ {'options': {'inputLayout': 'nhwc', 'filterLayout': 'hwio'}}
+ ],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [
+ 0.884765625, 0.962890625, 0.8017578125, 0.7412109375,
+ 0.91064453125, 0.62744140625, 0.289794921875, 0.4833984375,
+ 0.448486328125, 0.405517578125, 0.488037109375, 0.87158203125,
+ 0.693359375, 1.1787109375, 0.9169921875, 1.037109375,
+ 0.8125, 1.0830078125, 0.82861328125, 0.81640625,
+ 1.2353515625, 0.353271484375, 0.67822265625, 0.98095703125
+ ],
+ 'descriptor': {shape: [2, 2, 2, 3], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'conv2d float16 4D input and filter tensors options.inputLayout=\'nhwc\' and options.filterLayout=\'ohwi\'',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ 0.7529296875, 0.751953125, 0.5947265625, 0.21630859375,
+ 0.07586669921875, 0.151123046875, 0.12127685546875, 0.53662109375,
+ 0.59375, 0.9912109375, 0.363037109375, 0.92919921875,
+ 0.227294921875, 0.54150390625, 0.08447265625, 0.6767578125,
+ 0.619140625, 0.392822265625
+ ],
+ 'descriptor': {shape: [2, 3, 3, 1], dataType: 'float16'}
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.1453857421875, 0.96728515625, 0.10833740234375, 0.3203125,
+ 0.6953125, 0.50732421875, 0.0814208984375, 0.5302734375,
+ 0.30712890625, 0.432373046875, 0.98486328125, 0.42822265625
+ ],
+ 'descriptor': {shape: [3, 2, 2, 1], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [
+ {'input': 'conv2dInput'}, {'filter': 'conv2dFilter'},
+ {'options': {'inputLayout': 'nhwc', 'filterLayout': 'ohwi'}}
+ ],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [
+ 0.884765625, 0.962890625, 0.8017578125, 0.7412109375,
+ 0.91064453125, 0.62744140625, 0.289794921875, 0.4833984375,
+ 0.448486328125, 0.405517578125, 0.488037109375, 0.87158203125,
+ 0.693359375, 1.1787109375, 0.9169921875, 1.037109375,
+ 0.8125, 1.0830078125, 0.82861328125, 0.81640625,
+ 1.2353515625, 0.353271484375, 0.67822265625, 0.98095703125
+ ],
+ 'descriptor': {shape: [2, 2, 2, 3], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'conv2d float16 4D input and filter tensors options.inputLayout=\'nhwc\' and options.filterLayout=\'ihwo\'',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ 0.7529296875, 0.751953125, 0.5947265625, 0.21630859375,
+ 0.07586669921875, 0.151123046875, 0.12127685546875, 0.53662109375,
+ 0.59375, 0.9912109375, 0.363037109375, 0.92919921875,
+ 0.227294921875, 0.54150390625, 0.08447265625, 0.6767578125,
+ 0.619140625, 0.392822265625
+ ],
+ 'descriptor': {shape: [2, 3, 3, 1], dataType: 'float16'}
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.1453857421875, 0.6953125, 0.30712890625, 0.96728515625,
+ 0.50732421875, 0.432373046875, 0.10833740234375, 0.0814208984375,
+ 0.98486328125, 0.3203125, 0.5302734375, 0.42822265625
+ ],
+ 'descriptor': {shape: [1, 2, 2, 3], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [
+ {'input': 'conv2dInput'}, {'filter': 'conv2dFilter'},
+ {'options': {'inputLayout': 'nhwc', 'filterLayout': 'ihwo'}}
+ ],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [
+ 0.884765625, 0.962890625, 0.8017578125, 0.7412109375,
+ 0.91064453125, 0.62744140625, 0.289794921875, 0.4833984375,
+ 0.448486328125, 0.405517578125, 0.488037109375, 0.87158203125,
+ 0.693359375, 1.1787109375, 0.9169921875, 1.037109375,
+ 0.8125, 1.0830078125, 0.82861328125, 0.81640625,
+ 1.2353515625, 0.353271484375, 0.67822265625, 0.98095703125
+ ],
+ 'descriptor': {shape: [2, 2, 2, 3], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name': 'conv2d float16 4D input and filter tensors 1D options.bias',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ 0.7529296875, 0.751953125, 0.5947265625, 0.21630859375,
+ 0.07586669921875, 0.151123046875, 0.12127685546875, 0.53662109375,
+ 0.59375, 0.9912109375, 0.363037109375, 0.92919921875,
+ 0.227294921875, 0.54150390625, 0.08447265625, 0.6767578125,
+ 0.619140625, 0.392822265625
+ ],
+ 'descriptor': {shape: [2, 1, 3, 3], dataType: 'float16'}
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.1453857421875, 0.96728515625, 0.10833740234375, 0.3203125,
+ 0.6953125, 0.50732421875, 0.0814208984375, 0.5302734375,
+ 0.30712890625, 0.432373046875, 0.98486328125, 0.42822265625
+ ],
+ 'descriptor': {shape: [3, 1, 2, 2], dataType: 'float16'},
+ 'constant': true
+ },
+ 'conv2dBias': {
+ 'data': [0.8134765625, 0.83935546875, 0.494384765625],
+ 'descriptor': {shape: [3], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [
+ {'input': 'conv2dInput'}, {'filter': 'conv2dFilter'},
+ {'options': {'bias': 'conv2dBias'}}
+ ],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [
+ 1.6982421875, 1.5546875, 1.103515625, 1.21875,
+ 1.8017578125, 1.75, 1.322265625, 1.3271484375,
+ 1.2958984375, 1.1220703125, 0.94287109375, 1.3662109375,
+ 1.5068359375, 1.8505859375, 1.6416015625, 1.1669921875,
+ 2.017578125, 1.65234375, 1.6552734375, 1.517578125,
+ 1.4111328125, 1.5771484375, 1.7294921875, 1.4755859375
+ ],
+ 'descriptor': {shape: [2, 3, 2, 2], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name': 'conv2d float16 4D input and filter tensors all options',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ 0.0997314453125, 0.53759765625, 0.3056640625,
+ 0.72216796875, 0.5068359375, 0.32373046875,
+ 0.8720703125, 0.2098388671875, 0.50537109375,
+ 0.0268707275390625, 0.5498046875, 0.0643310546875,
+ 0.156005859375, 0.11968994140625, 0.0361328125,
+ 0.414306640625, 0.24169921875, 0.67724609375,
+ 0.208740234375, 0.10980224609375, 0.7451171875,
+ 0.744140625, 0.787109375, 0.5888671875,
+ 0.11016845703125, 0.904296875, 0.11724853515625,
+ 0.85107421875, 0.424560546875, 0.0253753662109375,
+ 0.74755859375, 0.464599609375, 0.0304107666015625,
+ 0.42431640625, 0.384765625, 0.75830078125,
+ 0.990234375, 0.03717041015625, 0.014495849609375,
+ 0.826171875, 0.210693359375, 0.65673828125,
+ 0.258544921875, 0.480224609375, 0.97021484375,
+ 0.296875, 0.75244140625, 0.029632568359375,
+ 0.09027099609375, 0.7783203125
+ ],
+ 'descriptor': {shape: [1, 2, 5, 5], dataType: 'float16'}
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.638671875, 0.07763671875, 0.129150390625, 0.456298828125,
+ 0.404296875, 0.59423828125, 0.1424560546875, 0.90380859375
+ ],
+ 'descriptor': {shape: [2, 2, 1, 2], dataType: 'float16'},
+ 'constant': true
+ },
+ 'conv2dBias': {
+ 'data': [0.54248046875, 0.8408203125],
+ 'descriptor': {shape: [2], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [
+ {'input': 'conv2dInput'}, {'filter': 'conv2dFilter'}, {
+ 'options': {
+ 'padding': [1, 0, 0, 1],
+ 'strides': [1, 1],
+ 'dilations': [1, 1],
+ 'groups': 2,
+ 'inputLayout': 'nchw',
+ 'filterLayout': 'hwio',
+ 'bias': 'conv2dBias'
+ }
+ }
+ ],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [
+ 0.6591796875, 0.80322265625, 0.76904296875, 0.90673828125,
+ 0.74755859375, 0.9306640625, 1.3076171875, 0.98779296875,
+ 1.27734375, 0.876953125, 1.09375, 1.1748046875,
+ 0.82177734375, 0.92236328125, 0.57421875, 1.103515625,
+ 0.7978515625, 0.9609375, 0.7236328125, 0.60986328125,
+ 1.2451171875, 1.197265625, 1.404296875, 0.94384765625,
+ 0.6572265625, 1.484375, 1.6796875, 1.73046875,
+ 1.1162109375, 0.85595703125, 1.8291015625, 1.5419921875,
+ 1.501953125, 1.4853515625, 1.0712890625, 2.45703125,
+ 1.5126953125, 1.072265625, 1.8046875, 1.361328125,
+ 2.0703125, 1.55859375, 1.4384765625, 2.380859375,
+ 1.4814453125, 2.013671875, 1.4833984375, 1.1796875,
+ 2.078125, 1.37890625
+ ],
+ 'descriptor': {shape: [1, 2, 5, 5], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'conv2d float16 4D input and filter tensors, both negative input tensor and options.bias',
+ 'graph': {
+ 'inputs': {
+ 'conv2dInput': {
+ 'data': [
+ -0.80712890625, -0.8837890625, -0.77001953125,
+ -0.564453125, -0.397216796875, -0.1083984375,
+ -0.5517578125, -0.3955078125, -0.057586669921875,
+ -0.5146484375, -0.2132568359375, -0.95068359375,
+ -0.80419921875, -0.86474609375, -0.9609375,
+ -0.326416015625, -0.06884765625, -0.3203125,
+ -0.269287109375, -0.343017578125, -0.89892578125,
+ -0.90380859375, -0.63671875, -0.20068359375,
+ -0.787109375, -0.3466796875, -0.060028076171875,
+ -0.14990234375, -0.6484375, -0.8935546875,
+ -0.81494140625, -0.642578125, -0.032745361328125,
+ -0.6611328125, -0.58447265625, -0.09918212890625,
+ -0.166015625, -0.95068359375, -0.30517578125,
+ -0.62109375, -0.5400390625, -0.420166015625,
+ -0.188232421875, -0.35888671875, -0.71142578125,
+ -0.375, -0.7109375, -0.360595703125,
+ -0.546875, -0.032257080078125
+ ],
+ 'descriptor': {shape: [1, 2, 5, 5], dataType: 'float16'}
+ },
+ 'conv2dFilter': {
+ 'data': [
+ 0.638671875, 0.07763671875, 0.129150390625, 0.456298828125,
+ 0.404296875, 0.59423828125, 0.1424560546875, 0.90380859375
+ ],
+ 'descriptor': {shape: [2, 2, 1, 2], dataType: 'float16'},
+ 'constant': true
+ },
+ 'conv2dBias': {
+ 'data': [-0.375, -0.436279296875],
+ 'descriptor': {shape: [2], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'conv2d',
+ 'arguments': [
+ {'input': 'conv2dInput'}, {'filter': 'conv2dFilter'}, {
+ 'options': {
+ 'padding': [1, 0, 0, 1],
+ 'groups': 2,
+ 'filterLayout': 'hwio',
+ 'bias': 'conv2dBias'
+ }
+ }
+ ],
+ 'outputs': 'conv2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'conv2dOutput': {
+ 'data': [
+ -0.8271484375, -0.841796875, -0.7666015625, -0.65966796875,
+ -0.53564453125, -1.126953125, -1.318359375, -1.107421875,
+ -0.88330078125, -0.8369140625, -0.7373046875, -1.27734375,
+ -1.0830078125, -0.96484375, -1.091796875, -0.77587890625,
+ -1.1591796875, -1.16796875, -1.208984375, -1.126953125,
+ -1.0849609375, -0.91650390625, -0.900390625, -0.78466796875,
+ -0.912109375, -0.69677734375, -0.607421875, -1.111328125,
+ -1.62890625, -0.96728515625, -1.5556640625, -0.9208984375,
+ -1.3603515625, -1.8154296875, -0.85302734375, -1.001953125,
+ -1.458984375, -1.5810546875, -1.4970703125, -0.8505859375,
+ -1.220703125, -1.302734375, -1.0859375, -1.599609375,
+ -0.9072265625, -1.53515625, -1.302734375, -1.3232421875,
+ -1.142578125, -0.5107421875
+ ],
+ 'descriptor': {shape: [1, 2, 5, 5], dataType: 'float16'}
+ }
+ }
+ }
}
];
diff --git a/tests/wpt/tests/webnn/conformance_tests/conv_transpose2d.https.any.js b/tests/wpt/tests/webnn/conformance_tests/conv_transpose2d.https.any.js
index 67ce5dbaf65..a17df911e44 100644
--- a/tests/wpt/tests/webnn/conformance_tests/conv_transpose2d.https.any.js
+++ b/tests/wpt/tests/webnn/conformance_tests/conv_transpose2d.https.any.js
@@ -1578,6 +1578,1670 @@ const convTranspose2dTests = [
}
}
}
+ },
+
+ // float16 tests
+ {
+ 'name':
+ 'convTranspose2d float16 4D both input and filter non-constant tensors default options',
+ 'graph': {
+ 'inputs': {
+ 'convTranspose2dInput': {
+ 'data': [
+ 0.58740234375, 0.60791015625, 0.0172882080078125, 0.261474609375
+ ],
+ 'descriptor': {shape: [1, 1, 2, 2], dataType: 'float16'}
+ },
+ 'convTranspose2dFilter': {
+ 'data': [
+ 0.329345703125, 0.5869140625, 0.297119140625, 0.003337860107421875
+ ],
+ 'descriptor': {shape: [1, 1, 2, 2], dataType: 'float16'}
+ }
+ },
+ 'operators': [{
+ 'name': 'convTranspose2d',
+ 'arguments': [
+ {'input': 'convTranspose2dInput'}, {'filter': 'convTranspose2dFilter'}
+ ],
+ 'outputs': 'convTranspose2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'convTranspose2dOutput': {
+ 'data': [
+ 0.1934814453125, 0.544921875, 0.356689453125, 0.18017578125,
+ 0.27880859375, 0.155517578125, 0.005138397216796875,
+ 0.0777587890625, 0.0008726119995117188
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'convTranspose2d float16 4D both input and filter constant tensors default options',
+ 'graph': {
+ 'inputs': {
+ 'convTranspose2dInput': {
+ 'data': [
+ 0.58740234375, 0.60791015625, 0.0172882080078125, 0.261474609375
+ ],
+ 'descriptor': {shape: [1, 1, 2, 2], dataType: 'float16'},
+ 'constant': true
+ },
+ 'convTranspose2dFilter': {
+ 'data': [
+ 0.329345703125, 0.5869140625, 0.297119140625, 0.003337860107421875
+ ],
+ 'descriptor': {shape: [1, 1, 2, 2], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'convTranspose2d',
+ 'arguments': [
+ {'input': 'convTranspose2dInput'}, {'filter': 'convTranspose2dFilter'}
+ ],
+ 'outputs': 'convTranspose2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'convTranspose2dOutput': {
+ 'data': [
+ 0.1934814453125, 0.544921875, 0.356689453125, 0.18017578125,
+ 0.27880859375, 0.155517578125, 0.005138397216796875,
+ 0.0777587890625, 0.0008726119995117188
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'convTranspose2d float16 4D input and filter tensors default options',
+ 'graph': {
+ 'inputs': {
+ 'convTranspose2dInput': {
+ 'data': [
+ 0.58740234375, 0.60791015625, 0.0172882080078125, 0.261474609375
+ ],
+ 'descriptor': {shape: [1, 1, 2, 2], dataType: 'float16'}
+ },
+ 'convTranspose2dFilter': {
+ 'data': [
+ 0.329345703125, 0.5869140625, 0.297119140625, 0.003337860107421875
+ ],
+ 'descriptor': {shape: [1, 1, 2, 2], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'convTranspose2d',
+ 'arguments': [
+ {'input': 'convTranspose2dInput'}, {'filter': 'convTranspose2dFilter'}
+ ],
+ 'outputs': 'convTranspose2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'convTranspose2dOutput': {
+ 'data': [
+ 0.1934814453125, 0.544921875, 0.356689453125, 0.18017578125,
+ 0.27880859375, 0.155517578125, 0.005138397216796875,
+ 0.0777587890625, 0.0008726119995117188
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'convTranspose2d float16 4D input and filter tensors options.padding',
+ 'graph': {
+ 'inputs': {
+ 'convTranspose2dInput': {
+ 'data': [
+ 0.58740234375, 0.60791015625, 0.0172882080078125, 0.261474609375
+ ],
+ 'descriptor': {shape: [1, 1, 2, 2], dataType: 'float16'}
+ },
+ 'convTranspose2dFilter': {
+ 'data': [
+ 0.329345703125, 0.5869140625, 0.297119140625, 0.003337860107421875
+ ],
+ 'descriptor': {shape: [1, 1, 2, 2], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'convTranspose2d',
+ 'arguments': [
+ {'input': 'convTranspose2dInput'},
+ {'filter': 'convTranspose2dFilter'},
+ {'options': {'padding': [1, 1, 1, 1]}}
+ ],
+ 'outputs': 'convTranspose2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'convTranspose2dOutput': {
+ 'data': [0.27880859375],
+ 'descriptor': {shape: [1, 1, 1, 1], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'convTranspose2d float16 input tensors options.padding is the same upper padding',
+ 'graph': {
+ 'inputs': {
+ 'convTranspose2dInput': {
+ 'data': [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
+ 'descriptor': {shape: [1, 3, 3, 1], dataType: 'float16'}
+ },
+ 'convTranspose2dFilter': {
+ 'data': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
+ 'descriptor': {shape: [2, 3, 3, 1], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'convTranspose2d',
+ 'arguments': [
+ {'input': 'convTranspose2dInput'},
+ {'filter': 'convTranspose2dFilter'}, {
+ 'options': {
+ 'outputSizes': [6, 6],
+ 'groups': 1,
+ 'strides': [2, 2],
+ 'dilations': [1, 1],
+ 'padding': [0, 1, 0, 1],
+ 'filterLayout': 'ohwi',
+ 'inputLayout': 'nhwc'
+ }
+ }
+ ],
+ 'outputs': 'convTranspose2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'convTranspose2dOutput': {
+ 'data': [
+ 0.5, 0.5, 0.5, 0.5, 1, 1, 0.5, 0.5, 1, 1, 0.5, 0.5,
+ 0.5, 0.5, 0.5, 0.5, 1, 1, 0.5, 0.5, 1, 1, 0.5, 0.5,
+ 1, 1, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1,
+ 0.5, 0.5, 0.5, 0.5, 1, 1, 0.5, 0.5, 1, 1, 0.5, 0.5,
+ 1, 1, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1,
+ 0.5, 0.5, 0.5, 0.5, 1, 1, 0.5, 0.5, 1, 1, 0.5, 0.5
+ ],
+ 'descriptor': {shape: [1, 6, 6, 2], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'convTranspose2d float16 4D input and filter tensors options.strides',
+ 'graph': {
+ 'inputs': {
+ 'convTranspose2dInput': {
+ 'data': [
+ 0.056060791015625, 0.71142578125, 0.65283203125, 0.38623046875,
+ 0.386962890625, 0.9462890625, 0.095703125, 0.92333984375,
+ 0.63623046875
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'}
+ },
+ 'convTranspose2dFilter': {
+ 'data': [
+ 0.861328125, 0.626953125, 0.63671875, 0.83837890625,
+ 0.11883544921875, 0.9921875, 0.32861328125, 0.8740234375,
+ 0.720703125, 0.97998046875, 0.06170654296875, 0.322021484375, 0.75,
+ 0.39306640625, 0.1380615234375, 0.283935546875, 0.423583984375,
+ 0.1448974609375
+ ],
+ 'descriptor': {shape: [1, 2, 3, 3], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'convTranspose2d',
+ 'arguments': [
+ {'input': 'convTranspose2dInput'},
+ {'filter': 'convTranspose2dFilter'}, {'options': {'strides': [3, 2]}}
+ ],
+ 'outputs': 'convTranspose2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'convTranspose2dOutput': {
+ 'data': [
+ 0.04827880859375,
+ 0.03515625,
+ 0.6484375,
+ 0.446044921875,
+ 1.015625,
+ 0.4091796875,
+ 0.415771484375,
+ 0.0469970703125,
+ 0.00666046142578125,
+ 0.65185546875,
+ 0.08453369140625,
+ 1.2529296875,
+ 0.07757568359375,
+ 0.64794921875,
+ 0.0184173583984375,
+ 0.04901123046875,
+ 0.274169921875,
+ 0.62158203125,
+ 0.72705078125,
+ 0.57080078125,
+ 0.470458984375,
+ 0.332763671875,
+ 0.2421875,
+ 0.5791015625,
+ 0.2425537109375,
+ 1.0615234375,
+ 0.59326171875,
+ 0.6025390625,
+ 0.32373046875,
+ 0.0458984375,
+ 0.70751953125,
+ 0.045989990234375,
+ 1.177734375,
+ 0.1124267578125,
+ 0.93896484375,
+ 0.126953125,
+ 0.337646484375,
+ 0.405517578125,
+ 0.338134765625,
+ 0.58984375,
+ 0.8271484375,
+ 0.68212890625,
+ 0.08245849609375,
+ 0.05999755859375,
+ 0.8564453125,
+ 0.5791015625,
+ 1.1357421875,
+ 0.39892578125,
+ 0.405029296875,
+ 0.08026123046875,
+ 0.01137542724609375,
+ 0.869140625,
+ 0.1097412109375,
+ 1.44921875,
+ 0.07562255859375,
+ 0.63134765625,
+ 0.031463623046875,
+ 0.0836181640625,
+ 0.372314453125,
+ 0.80712890625,
+ 0.87451171875,
+ 0.55615234375,
+ 0.45849609375,
+ 0.054931640625,
+ 0.003459930419921875,
+ 0.71533203125,
+ 0.043914794921875,
+ 0.86865234375,
+ 0.040283203125,
+ 0.210205078125,
+ 0.04205322265625,
+ 0.02203369140625,
+ 0.54150390625,
+ 0.279541015625,
+ 0.587890625,
+ 0.256591796875,
+ 0.09014892578125,
+ 0.0159149169921875,
+ 0.02374267578125,
+ 0.2100830078125,
+ 0.30126953125,
+ 0.288330078125,
+ 0.276611328125,
+ 0.0946044921875,
+ 0.37841796875,
+ 0.023834228515625,
+ 0.50341796875,
+ 0.0238800048828125,
+ 1.0517578125,
+ 0.058380126953125,
+ 0.3046875,
+ 0.28955078125,
+ 0.15185546875,
+ 0.343505859375,
+ 0.152099609375,
+ 0.76318359375,
+ 0.3720703125,
+ 0.130615234375,
+ 0.10968017578125,
+ 0.16357421875,
+ 0.1658935546875,
+ 0.1639404296875,
+ 0.32470703125,
+ 0.40087890625,
+ 0.1370849609375,
+ 0.09381103515625,
+ 0.0059051513671875,
+ 0.935546875,
+ 0.056976318359375,
+ 0.9208984375,
+ 0.03924560546875,
+ 0.204833984375,
+ 0.07177734375,
+ 0.037628173828125,
+ 0.70556640625,
+ 0.363037109375,
+ 0.6044921875,
+ 0.25,
+ 0.08782958984375,
+ 0.0271759033203125,
+ 0.04052734375,
+ 0.276123046875,
+ 0.39111328125,
+ 0.314453125,
+ 0.26953125,
+ 0.0921630859375
+ ],
+ 'descriptor': {shape: [1, 2, 9, 7], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'convTranspose2d float16 4D input and filter tensors options.dilations',
+ 'graph': {
+ 'inputs': {
+ 'convTranspose2dInput': {
+ 'data': [
+ 0.319580078125, 0.97607421875, 0.4130859375, 0.479736328125,
+ 0.767578125, 0.908203125, 0.62060546875, 0.658203125, 0.6552734375
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'}
+ },
+ 'convTranspose2dFilter': {
+ 'data': [0.68359375, 0.96435546875, 0.8271484375, 0.5771484375],
+ 'descriptor': {shape: [1, 1, 2, 2], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'convTranspose2d',
+ 'arguments': [
+ {'input': 'convTranspose2dInput'},
+ {'filter': 'convTranspose2dFilter'},
+ {'options': {'dilations': [2, 2]}}
+ ],
+ 'outputs': 'convTranspose2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'convTranspose2dOutput': {
+ 'data': [
+ 0.218505859375, 0.66748046875, 0.59033203125, 0.94140625,
+ 0.3984375, 0.327880859375, 0.52490234375, 1.0830078125,
+ 0.740234375, 0.8759765625, 0.6884765625, 1.2568359375,
+ 1.572265625, 1.1982421875, 0.8701171875, 0.396728515625,
+ 0.634765625, 1.0283203125, 0.443115234375, 0.52392578125,
+ 0.51318359375, 0.54443359375, 0.900390625, 0.3798828125,
+ 0.378173828125
+ ],
+ 'descriptor': {shape: [1, 1, 5, 5], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'convTranspose2d float16 4D input and filter tensors options.outputPadding',
+ 'graph': {
+ 'inputs': {
+ 'convTranspose2dInput': {
+ 'data': [
+ 0.056060791015625, 0.71142578125, 0.65283203125, 0.38623046875,
+ 0.386962890625, 0.9462890625, 0.095703125, 0.92333984375,
+ 0.63623046875
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'}
+ },
+ 'convTranspose2dFilter': {
+ 'data': [
+ 0.861328125, 0.626953125, 0.63671875, 0.83837890625,
+ 0.11883544921875, 0.9921875, 0.32861328125, 0.8740234375,
+ 0.720703125, 0.97998046875, 0.06170654296875, 0.322021484375, 0.75,
+ 0.39306640625, 0.1380615234375, 0.283935546875, 0.423583984375,
+ 0.1448974609375
+ ],
+ 'descriptor': {shape: [1, 2, 3, 3], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'convTranspose2d',
+ 'arguments': [
+ {'input': 'convTranspose2dInput'},
+ {'filter': 'convTranspose2dFilter'},
+ {'options': {'strides': [3, 2], 'outputPadding': [1, 1]}}
+ ],
+ 'outputs': 'convTranspose2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'convTranspose2dOutput': {
+ 'data': [
+ 0.04827880859375,
+ 0.03515625,
+ 0.6484375,
+ 0.446044921875,
+ 1.015625,
+ 0.4091796875,
+ 0.415771484375,
+ 0,
+ 0.0469970703125,
+ 0.00666046142578125,
+ 0.65185546875,
+ 0.08453369140625,
+ 1.2529296875,
+ 0.07757568359375,
+ 0.64794921875,
+ 0,
+ 0.0184173583984375,
+ 0.04901123046875,
+ 0.274169921875,
+ 0.62158203125,
+ 0.72705078125,
+ 0.57080078125,
+ 0.470458984375,
+ 0,
+ 0.332763671875,
+ 0.2421875,
+ 0.5791015625,
+ 0.2425537109375,
+ 1.0615234375,
+ 0.59326171875,
+ 0.6025390625,
+ 0,
+ 0.32373046875,
+ 0.0458984375,
+ 0.70751953125,
+ 0.045989990234375,
+ 1.177734375,
+ 0.1124267578125,
+ 0.93896484375,
+ 0,
+ 0.126953125,
+ 0.337646484375,
+ 0.405517578125,
+ 0.338134765625,
+ 0.58984375,
+ 0.8271484375,
+ 0.68212890625,
+ 0,
+ 0.08245849609375,
+ 0.05999755859375,
+ 0.8564453125,
+ 0.5791015625,
+ 1.1357421875,
+ 0.39892578125,
+ 0.405029296875,
+ 0,
+ 0.08026123046875,
+ 0.01137542724609375,
+ 0.869140625,
+ 0.1097412109375,
+ 1.44921875,
+ 0.07562255859375,
+ 0.63134765625,
+ 0,
+ 0.031463623046875,
+ 0.0836181640625,
+ 0.372314453125,
+ 0.80712890625,
+ 0.87451171875,
+ 0.55615234375,
+ 0.45849609375,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0.054931640625,
+ 0.003459930419921875,
+ 0.71533203125,
+ 0.043914794921875,
+ 0.86865234375,
+ 0.040283203125,
+ 0.210205078125,
+ 0,
+ 0.04205322265625,
+ 0.02203369140625,
+ 0.54150390625,
+ 0.279541015625,
+ 0.587890625,
+ 0.256591796875,
+ 0.09014892578125,
+ 0,
+ 0.0159149169921875,
+ 0.02374267578125,
+ 0.2100830078125,
+ 0.30126953125,
+ 0.288330078125,
+ 0.276611328125,
+ 0.0946044921875,
+ 0,
+ 0.37841796875,
+ 0.023834228515625,
+ 0.50341796875,
+ 0.0238800048828125,
+ 1.0517578125,
+ 0.058380126953125,
+ 0.3046875,
+ 0,
+ 0.28955078125,
+ 0.15185546875,
+ 0.343505859375,
+ 0.152099609375,
+ 0.76318359375,
+ 0.3720703125,
+ 0.130615234375,
+ 0,
+ 0.10968017578125,
+ 0.16357421875,
+ 0.1658935546875,
+ 0.1639404296875,
+ 0.32470703125,
+ 0.40087890625,
+ 0.1370849609375,
+ 0,
+ 0.09381103515625,
+ 0.0059051513671875,
+ 0.935546875,
+ 0.056976318359375,
+ 0.9208984375,
+ 0.03924560546875,
+ 0.204833984375,
+ 0,
+ 0.07177734375,
+ 0.037628173828125,
+ 0.70556640625,
+ 0.363037109375,
+ 0.6044921875,
+ 0.25,
+ 0.08782958984375,
+ 0,
+ 0.0271759033203125,
+ 0.04052734375,
+ 0.276123046875,
+ 0.39111328125,
+ 0.314453125,
+ 0.26953125,
+ 0.0921630859375,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ 'descriptor': {shape: [1, 2, 10, 8], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'convTranspose2d float16 4D input and filter tensors options.outputSizes',
+ 'graph': {
+ 'inputs': {
+ 'convTranspose2dInput': {
+ 'data': [
+ 0.056060791015625, 0.71142578125, 0.65283203125, 0.38623046875,
+ 0.386962890625, 0.9462890625, 0.095703125, 0.92333984375,
+ 0.63623046875
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'}
+ },
+ 'convTranspose2dFilter': {
+ 'data': [
+ 0.861328125, 0.626953125, 0.63671875, 0.83837890625,
+ 0.11883544921875, 0.9921875, 0.32861328125, 0.8740234375,
+ 0.720703125, 0.97998046875, 0.06170654296875, 0.322021484375, 0.75,
+ 0.39306640625, 0.1380615234375, 0.283935546875, 0.423583984375,
+ 0.1448974609375
+ ],
+ 'descriptor': {shape: [1, 2, 3, 3], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'convTranspose2d',
+ 'arguments': [
+ {'input': 'convTranspose2dInput'},
+ {'filter': 'convTranspose2dFilter'},
+ {'options': {'strides': [3, 2], 'outputSizes': [10, 8]}}
+ ],
+ 'outputs': 'convTranspose2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'convTranspose2dOutput': {
+ 'data': [
+ 0.04827880859375,
+ 0.03515625,
+ 0.6484375,
+ 0.446044921875,
+ 1.015625,
+ 0.4091796875,
+ 0.415771484375,
+ 0,
+ 0.0469970703125,
+ 0.00666046142578125,
+ 0.65185546875,
+ 0.08453369140625,
+ 1.2529296875,
+ 0.07757568359375,
+ 0.64794921875,
+ 0,
+ 0.0184173583984375,
+ 0.04901123046875,
+ 0.274169921875,
+ 0.62158203125,
+ 0.72705078125,
+ 0.57080078125,
+ 0.470458984375,
+ 0,
+ 0.332763671875,
+ 0.2421875,
+ 0.5791015625,
+ 0.2425537109375,
+ 1.0615234375,
+ 0.59326171875,
+ 0.6025390625,
+ 0,
+ 0.32373046875,
+ 0.0458984375,
+ 0.70751953125,
+ 0.045989990234375,
+ 1.177734375,
+ 0.1124267578125,
+ 0.93896484375,
+ 0,
+ 0.126953125,
+ 0.337646484375,
+ 0.405517578125,
+ 0.338134765625,
+ 0.58984375,
+ 0.8271484375,
+ 0.68212890625,
+ 0,
+ 0.08245849609375,
+ 0.05999755859375,
+ 0.8564453125,
+ 0.5791015625,
+ 1.1357421875,
+ 0.39892578125,
+ 0.405029296875,
+ 0,
+ 0.08026123046875,
+ 0.01137542724609375,
+ 0.869140625,
+ 0.1097412109375,
+ 1.44921875,
+ 0.07562255859375,
+ 0.63134765625,
+ 0,
+ 0.031463623046875,
+ 0.0836181640625,
+ 0.372314453125,
+ 0.80712890625,
+ 0.87451171875,
+ 0.55615234375,
+ 0.45849609375,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0.054931640625,
+ 0.003459930419921875,
+ 0.71533203125,
+ 0.043914794921875,
+ 0.86865234375,
+ 0.040283203125,
+ 0.210205078125,
+ 0,
+ 0.04205322265625,
+ 0.02203369140625,
+ 0.54150390625,
+ 0.279541015625,
+ 0.587890625,
+ 0.256591796875,
+ 0.09014892578125,
+ 0,
+ 0.0159149169921875,
+ 0.02374267578125,
+ 0.2100830078125,
+ 0.30126953125,
+ 0.288330078125,
+ 0.276611328125,
+ 0.0946044921875,
+ 0,
+ 0.37841796875,
+ 0.023834228515625,
+ 0.50341796875,
+ 0.0238800048828125,
+ 1.0517578125,
+ 0.058380126953125,
+ 0.3046875,
+ 0,
+ 0.28955078125,
+ 0.15185546875,
+ 0.343505859375,
+ 0.152099609375,
+ 0.76318359375,
+ 0.3720703125,
+ 0.130615234375,
+ 0,
+ 0.10968017578125,
+ 0.16357421875,
+ 0.1658935546875,
+ 0.1639404296875,
+ 0.32470703125,
+ 0.40087890625,
+ 0.1370849609375,
+ 0,
+ 0.09381103515625,
+ 0.0059051513671875,
+ 0.935546875,
+ 0.056976318359375,
+ 0.9208984375,
+ 0.03924560546875,
+ 0.204833984375,
+ 0,
+ 0.07177734375,
+ 0.037628173828125,
+ 0.70556640625,
+ 0.363037109375,
+ 0.6044921875,
+ 0.25,
+ 0.08782958984375,
+ 0,
+ 0.0271759033203125,
+ 0.04052734375,
+ 0.276123046875,
+ 0.39111328125,
+ 0.314453125,
+ 0.26953125,
+ 0.0921630859375,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ 'descriptor': {shape: [1, 2, 10, 8], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'convTranspose2d float16 4D input and filter tensors options.inputLayout=nchw',
+ 'graph': {
+ 'inputs': {
+ 'convTranspose2dInput': {
+ 'data': [
+ 0.056060791015625, 0.71142578125, 0.65283203125, 0.38623046875,
+ 0.386962890625, 0.9462890625, 0.095703125, 0.92333984375,
+ 0.63623046875
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'}
+ },
+ 'convTranspose2dFilter': {
+ 'data': [
+ 0.861328125, 0.626953125, 0.63671875, 0.83837890625,
+ 0.11883544921875, 0.9921875, 0.32861328125, 0.8740234375,
+ 0.720703125, 0.97998046875, 0.06170654296875, 0.322021484375, 0.75,
+ 0.39306640625, 0.1380615234375, 0.283935546875, 0.423583984375,
+ 0.1448974609375
+ ],
+ 'descriptor': {shape: [1, 2, 3, 3], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'convTranspose2d',
+ 'arguments': [
+ {'input': 'convTranspose2dInput'},
+ {'filter': 'convTranspose2dFilter'},
+ {'options': {'inputLayout': 'nchw'}}
+ ],
+ 'outputs': 'convTranspose2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'convTranspose2dOutput': {
+ 'data': [
+ 0.04827880859375,
+ 0.64794921875,
+ 1.0439453125,
+ 0.8623046875,
+ 0.415771484375,
+ 0.379638671875,
+ 1.1787109375,
+ 1.9912109375,
+ 1.623046875,
+ 1.25,
+ 0.424560546875,
+ 1.5087890625,
+ 3.287109375,
+ 2.56640625,
+ 1.814453125,
+ 0.2071533203125,
+ 1.25,
+ 1.666015625,
+ 2.09765625,
+ 1.3134765625,
+ 0.031463623046875,
+ 0.386962890625,
+ 1.0849609375,
+ 1.2216796875,
+ 0.45849609375,
+ 0.054931640625,
+ 0.70068359375,
+ 0.70166015625,
+ 0.269287109375,
+ 0.210205078125,
+ 0.420654296875,
+ 0.95849609375,
+ 1.8525390625,
+ 0.53759765625,
+ 0.394775390625,
+ 0.3994140625,
+ 1.578125,
+ 2.12109375,
+ 1.1416015625,
+ 0.43017578125,
+ 0.181396484375,
+ 1.00390625,
+ 1.341796875,
+ 0.83447265625,
+ 0.2249755859375,
+ 0.0271759033203125,
+ 0.302734375,
+ 0.58544921875,
+ 0.4033203125,
+ 0.0921630859375
+ ],
+ 'descriptor': {shape: [1, 2, 5, 5], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'convTranspose2d float16 4D input and filter tensors options.inputLayout=nhwc',
+ 'graph': {
+ 'inputs': {
+ 'convTranspose2dInput': {
+ 'data': [
+ 0.056060791015625, 0.71142578125, 0.65283203125, 0.38623046875,
+ 0.386962890625, 0.9462890625, 0.095703125, 0.92333984375,
+ 0.63623046875
+ ],
+ 'descriptor': {shape: [1, 3, 3, 1], dataType: 'float16'}
+ },
+ 'convTranspose2dFilter': {
+ 'data': [
+ 0.861328125, 0.626953125, 0.63671875, 0.83837890625,
+ 0.11883544921875, 0.9921875, 0.32861328125, 0.8740234375,
+ 0.720703125, 0.97998046875, 0.06170654296875, 0.322021484375, 0.75,
+ 0.39306640625, 0.1380615234375, 0.283935546875, 0.423583984375,
+ 0.1448974609375
+ ],
+ 'descriptor': {shape: [1, 2, 3, 3], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'convTranspose2d',
+ 'arguments': [
+ {'input': 'convTranspose2dInput'},
+ {'filter': 'convTranspose2dFilter'},
+ {'options': {'inputLayout': 'nhwc'}}
+ ],
+ 'outputs': 'convTranspose2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'convTranspose2dOutput': {
+ 'data': [
+ 0.04827880859375,
+ 0.054931640625,
+ 0.64794921875,
+ 0.70068359375,
+ 1.0439453125,
+ 0.70166015625,
+ 0.8623046875,
+ 0.269287109375,
+ 0.415771484375,
+ 0.210205078125,
+ 0.379638671875,
+ 0.420654296875,
+ 1.1787109375,
+ 0.95849609375,
+ 1.9912109375,
+ 1.8525390625,
+ 1.623046875,
+ 0.53759765625,
+ 1.25,
+ 0.394775390625,
+ 0.424560546875,
+ 0.3994140625,
+ 1.5087890625,
+ 1.578125,
+ 3.287109375,
+ 2.12109375,
+ 2.56640625,
+ 1.1416015625,
+ 1.814453125,
+ 0.43017578125,
+ 0.2071533203125,
+ 0.181396484375,
+ 1.25,
+ 1.00390625,
+ 1.666015625,
+ 1.341796875,
+ 2.09765625,
+ 0.83447265625,
+ 1.3134765625,
+ 0.2249755859375,
+ 0.031463623046875,
+ 0.0271759033203125,
+ 0.386962890625,
+ 0.302734375,
+ 1.0849609375,
+ 0.58544921875,
+ 1.2216796875,
+ 0.4033203125,
+ 0.45849609375,
+ 0.0921630859375
+ ],
+ 'descriptor': {shape: [1, 5, 5, 2], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'convTranspose2d float16 4D input and filter tensors options.filterLayout=iohw',
+ 'graph': {
+ 'inputs': {
+ 'convTranspose2dInput': {
+ 'data': [
+ 0.056060791015625, 0.71142578125, 0.65283203125, 0.38623046875,
+ 0.386962890625, 0.9462890625, 0.095703125, 0.92333984375,
+ 0.63623046875
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'}
+ },
+ 'convTranspose2dFilter': {
+ 'data': [
+ 0.861328125, 0.626953125, 0.63671875, 0.83837890625,
+ 0.11883544921875, 0.9921875, 0.32861328125, 0.8740234375,
+ 0.720703125, 0.97998046875, 0.06170654296875, 0.322021484375, 0.75,
+ 0.39306640625, 0.1380615234375, 0.283935546875, 0.423583984375,
+ 0.1448974609375
+ ],
+ 'descriptor': {shape: [1, 2, 3, 3], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'convTranspose2d',
+ 'arguments': [
+ {'input': 'convTranspose2dInput'},
+ {'filter': 'convTranspose2dFilter'},
+ {'options': {'filterLayout': 'iohw'}}
+ ],
+ 'outputs': 'convTranspose2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'convTranspose2dOutput': {
+ 'data': [
+ 0.04827880859375,
+ 0.64794921875,
+ 1.0439453125,
+ 0.8623046875,
+ 0.415771484375,
+ 0.379638671875,
+ 1.1787109375,
+ 1.9912109375,
+ 1.623046875,
+ 1.25,
+ 0.424560546875,
+ 1.5087890625,
+ 3.287109375,
+ 2.56640625,
+ 1.814453125,
+ 0.2071533203125,
+ 1.25,
+ 1.666015625,
+ 2.09765625,
+ 1.3134765625,
+ 0.031463623046875,
+ 0.386962890625,
+ 1.0849609375,
+ 1.2216796875,
+ 0.45849609375,
+ 0.054931640625,
+ 0.70068359375,
+ 0.70166015625,
+ 0.269287109375,
+ 0.210205078125,
+ 0.420654296875,
+ 0.95849609375,
+ 1.8525390625,
+ 0.53759765625,
+ 0.394775390625,
+ 0.3994140625,
+ 1.578125,
+ 2.12109375,
+ 1.1416015625,
+ 0.43017578125,
+ 0.181396484375,
+ 1.00390625,
+ 1.341796875,
+ 0.83447265625,
+ 0.2249755859375,
+ 0.0271759033203125,
+ 0.302734375,
+ 0.58544921875,
+ 0.4033203125,
+ 0.0921630859375
+ ],
+ 'descriptor': {shape: [1, 2, 5, 5], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'convTranspose2d float16 4D input and filter tensors options.filterLayout=hwoi',
+ 'graph': {
+ 'inputs': {
+ 'convTranspose2dInput': {
+ 'data': [
+ 0.056060791015625, 0.71142578125, 0.65283203125, 0.38623046875,
+ 0.386962890625, 0.9462890625, 0.095703125, 0.92333984375,
+ 0.63623046875
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'}
+ },
+ 'convTranspose2dFilter': {
+ 'data': [
+ 0.861328125, 0.97998046875, 0.626953125, 0.06170654296875,
+ 0.63671875, 0.322021484375, 0.83837890625, 0.75, 0.11883544921875,
+ 0.39306640625, 0.9921875, 0.1380615234375, 0.32861328125,
+ 0.283935546875, 0.8740234375, 0.423583984375, 0.720703125,
+ 0.1448974609375
+ ],
+ 'descriptor': {shape: [3, 3, 2, 1], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'convTranspose2d',
+ 'arguments': [
+ {'input': 'convTranspose2dInput'},
+ {'filter': 'convTranspose2dFilter'},
+ {'options': {'filterLayout': 'hwoi'}}
+ ],
+ 'outputs': 'convTranspose2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'convTranspose2dOutput': {
+ 'data': [
+ 0.04827880859375,
+ 0.64794921875,
+ 1.0439453125,
+ 0.8623046875,
+ 0.415771484375,
+ 0.379638671875,
+ 1.1787109375,
+ 1.9912109375,
+ 1.623046875,
+ 1.25,
+ 0.424560546875,
+ 1.5087890625,
+ 3.287109375,
+ 2.56640625,
+ 1.814453125,
+ 0.2071533203125,
+ 1.25,
+ 1.666015625,
+ 2.09765625,
+ 1.3134765625,
+ 0.031463623046875,
+ 0.386962890625,
+ 1.0849609375,
+ 1.2216796875,
+ 0.45849609375,
+ 0.054931640625,
+ 0.70068359375,
+ 0.70166015625,
+ 0.269287109375,
+ 0.210205078125,
+ 0.420654296875,
+ 0.95849609375,
+ 1.8525390625,
+ 0.53759765625,
+ 0.394775390625,
+ 0.3994140625,
+ 1.578125,
+ 2.12109375,
+ 1.1416015625,
+ 0.43017578125,
+ 0.181396484375,
+ 1.00390625,
+ 1.341796875,
+ 0.83447265625,
+ 0.2249755859375,
+ 0.0271759033203125,
+ 0.302734375,
+ 0.58544921875,
+ 0.4033203125,
+ 0.0921630859375
+ ],
+ 'descriptor': {shape: [1, 2, 5, 5], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'convTranspose2d float16 4D input and filter tensors options.filterLayout=ohwi',
+ 'graph': {
+ 'inputs': {
+ 'convTranspose2dInput': {
+ 'data': [
+ 0.056060791015625, 0.71142578125, 0.65283203125, 0.38623046875,
+ 0.386962890625, 0.9462890625, 0.095703125, 0.92333984375,
+ 0.63623046875
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'}
+ },
+ 'convTranspose2dFilter': {
+ 'data': [
+ 0.861328125, 0.626953125, 0.63671875, 0.83837890625,
+ 0.11883544921875, 0.9921875, 0.32861328125, 0.8740234375,
+ 0.720703125, 0.97998046875, 0.06170654296875, 0.322021484375, 0.75,
+ 0.39306640625, 0.1380615234375, 0.283935546875, 0.423583984375,
+ 0.1448974609375
+ ],
+ 'descriptor': {shape: [2, 3, 3, 1], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'convTranspose2d',
+ 'arguments': [
+ {'input': 'convTranspose2dInput'},
+ {'filter': 'convTranspose2dFilter'},
+ {'options': {'filterLayout': 'ohwi'}}
+ ],
+ 'outputs': 'convTranspose2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'convTranspose2dOutput': {
+ 'data': [
+ 0.04827880859375,
+ 0.64794921875,
+ 1.0439453125,
+ 0.8623046875,
+ 0.415771484375,
+ 0.379638671875,
+ 1.1787109375,
+ 1.9912109375,
+ 1.623046875,
+ 1.25,
+ 0.424560546875,
+ 1.5087890625,
+ 3.287109375,
+ 2.56640625,
+ 1.814453125,
+ 0.2071533203125,
+ 1.25,
+ 1.666015625,
+ 2.09765625,
+ 1.3134765625,
+ 0.031463623046875,
+ 0.386962890625,
+ 1.0849609375,
+ 1.2216796875,
+ 0.45849609375,
+ 0.054931640625,
+ 0.70068359375,
+ 0.70166015625,
+ 0.269287109375,
+ 0.210205078125,
+ 0.420654296875,
+ 0.95849609375,
+ 1.8525390625,
+ 0.53759765625,
+ 0.394775390625,
+ 0.3994140625,
+ 1.578125,
+ 2.12109375,
+ 1.1416015625,
+ 0.43017578125,
+ 0.181396484375,
+ 1.00390625,
+ 1.341796875,
+ 0.83447265625,
+ 0.2249755859375,
+ 0.0271759033203125,
+ 0.302734375,
+ 0.58544921875,
+ 0.4033203125,
+ 0.0921630859375
+ ],
+ 'descriptor': {shape: [1, 2, 5, 5], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'convTranspose2d float16 4D input and filter tensors options.inputLayout=nhwc options.filterLayout=iohw',
+ 'graph': {
+ 'inputs': {
+ 'convTranspose2dInput': {
+ 'data': [
+ 0.056060791015625, 0.71142578125, 0.65283203125, 0.38623046875,
+ 0.386962890625, 0.9462890625, 0.095703125, 0.92333984375,
+ 0.63623046875
+ ],
+ 'descriptor': {shape: [1, 3, 3, 1], dataType: 'float16'}
+ },
+ 'convTranspose2dFilter': {
+ 'data': [
+ 0.861328125, 0.626953125, 0.63671875, 0.83837890625,
+ 0.11883544921875, 0.9921875, 0.32861328125, 0.8740234375,
+ 0.720703125, 0.97998046875, 0.06170654296875, 0.322021484375, 0.75,
+ 0.39306640625, 0.1380615234375, 0.283935546875, 0.423583984375,
+ 0.1448974609375
+ ],
+ 'descriptor': {shape: [1, 2, 3, 3], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'convTranspose2d',
+ 'arguments': [
+ {'input': 'convTranspose2dInput'},
+ {'filter': 'convTranspose2dFilter'},
+ {'options': {'inputLayout': 'nhwc', 'filterLayout': 'iohw'}}
+ ],
+ 'outputs': 'convTranspose2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'convTranspose2dOutput': {
+ 'data': [
+ 0.04827880859375,
+ 0.054931640625,
+ 0.64794921875,
+ 0.70068359375,
+ 1.0439453125,
+ 0.70166015625,
+ 0.8623046875,
+ 0.269287109375,
+ 0.415771484375,
+ 0.210205078125,
+ 0.379638671875,
+ 0.420654296875,
+ 1.1787109375,
+ 0.95849609375,
+ 1.9912109375,
+ 1.8525390625,
+ 1.623046875,
+ 0.53759765625,
+ 1.25,
+ 0.394775390625,
+ 0.424560546875,
+ 0.3994140625,
+ 1.5087890625,
+ 1.578125,
+ 3.287109375,
+ 2.12109375,
+ 2.56640625,
+ 1.1416015625,
+ 1.814453125,
+ 0.43017578125,
+ 0.2071533203125,
+ 0.181396484375,
+ 1.25,
+ 1.00390625,
+ 1.666015625,
+ 1.341796875,
+ 2.09765625,
+ 0.83447265625,
+ 1.3134765625,
+ 0.2249755859375,
+ 0.031463623046875,
+ 0.0271759033203125,
+ 0.386962890625,
+ 0.302734375,
+ 1.0849609375,
+ 0.58544921875,
+ 1.2216796875,
+ 0.4033203125,
+ 0.45849609375,
+ 0.0921630859375
+ ],
+ 'descriptor': {shape: [1, 5, 5, 2], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'convTranspose2d float16 4D input and filter tensors options.inputLayout=nhwc options.filterLayout=hwoi',
+ 'graph': {
+ 'inputs': {
+ 'convTranspose2dInput': {
+ 'data': [
+ 0.056060791015625, 0.71142578125, 0.65283203125, 0.38623046875,
+ 0.386962890625, 0.9462890625, 0.095703125, 0.92333984375,
+ 0.63623046875
+ ],
+ 'descriptor': {shape: [1, 3, 3, 1], dataType: 'float16'}
+ },
+ 'convTranspose2dFilter': {
+ 'data': [
+ 0.861328125, 0.97998046875, 0.626953125, 0.06170654296875,
+ 0.63671875, 0.322021484375, 0.83837890625, 0.75, 0.11883544921875,
+ 0.39306640625, 0.9921875, 0.1380615234375, 0.32861328125,
+ 0.283935546875, 0.8740234375, 0.423583984375, 0.720703125,
+ 0.1448974609375
+ ],
+ 'descriptor': {shape: [3, 3, 2, 1], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'convTranspose2d',
+ 'arguments': [
+ {'input': 'convTranspose2dInput'},
+ {'filter': 'convTranspose2dFilter'},
+ {'options': {'inputLayout': 'nhwc', 'filterLayout': 'hwoi'}}
+ ],
+ 'outputs': 'convTranspose2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'convTranspose2dOutput': {
+ 'data': [
+ 0.04827880859375,
+ 0.054931640625,
+ 0.64794921875,
+ 0.70068359375,
+ 1.0439453125,
+ 0.70166015625,
+ 0.8623046875,
+ 0.269287109375,
+ 0.415771484375,
+ 0.210205078125,
+ 0.379638671875,
+ 0.420654296875,
+ 1.1787109375,
+ 0.95849609375,
+ 1.9912109375,
+ 1.8525390625,
+ 1.623046875,
+ 0.53759765625,
+ 1.25,
+ 0.394775390625,
+ 0.424560546875,
+ 0.3994140625,
+ 1.5087890625,
+ 1.578125,
+ 3.287109375,
+ 2.12109375,
+ 2.56640625,
+ 1.1416015625,
+ 1.814453125,
+ 0.43017578125,
+ 0.2071533203125,
+ 0.181396484375,
+ 1.25,
+ 1.00390625,
+ 1.666015625,
+ 1.341796875,
+ 2.09765625,
+ 0.83447265625,
+ 1.3134765625,
+ 0.2249755859375,
+ 0.031463623046875,
+ 0.0271759033203125,
+ 0.386962890625,
+ 0.302734375,
+ 1.0849609375,
+ 0.58544921875,
+ 1.2216796875,
+ 0.4033203125,
+ 0.45849609375,
+ 0.0921630859375
+ ],
+ 'descriptor': {shape: [1, 5, 5, 2], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'convTranspose2d float16 4D input and filter tensors options.inputLayout=nhwc options.filterLayout=ohwi',
+ 'graph': {
+ 'inputs': {
+ 'convTranspose2dInput': {
+ 'data': [
+ 0.056060791015625, 0.71142578125, 0.65283203125, 0.38623046875,
+ 0.386962890625, 0.9462890625, 0.095703125, 0.92333984375,
+ 0.63623046875
+ ],
+ 'descriptor': {shape: [1, 3, 3, 1], dataType: 'float16'}
+ },
+ 'convTranspose2dFilter': {
+ 'data': [
+ 0.861328125, 0.626953125, 0.63671875, 0.83837890625,
+ 0.11883544921875, 0.9921875, 0.32861328125, 0.8740234375,
+ 0.720703125, 0.97998046875, 0.06170654296875, 0.322021484375, 0.75,
+ 0.39306640625, 0.1380615234375, 0.283935546875, 0.423583984375,
+ 0.1448974609375
+ ],
+ 'descriptor': {shape: [2, 3, 3, 1], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'convTranspose2d',
+ 'arguments': [
+ {'input': 'convTranspose2dInput'},
+ {'filter': 'convTranspose2dFilter'},
+ {'options': {'inputLayout': 'nhwc', 'filterLayout': 'ohwi'}}
+ ],
+ 'outputs': 'convTranspose2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'convTranspose2dOutput': {
+ 'data': [
+ 0.04827880859375,
+ 0.054931640625,
+ 0.64794921875,
+ 0.70068359375,
+ 1.0439453125,
+ 0.70166015625,
+ 0.8623046875,
+ 0.269287109375,
+ 0.415771484375,
+ 0.210205078125,
+ 0.379638671875,
+ 0.420654296875,
+ 1.1787109375,
+ 0.95849609375,
+ 1.9912109375,
+ 1.8525390625,
+ 1.623046875,
+ 0.53759765625,
+ 1.25,
+ 0.394775390625,
+ 0.424560546875,
+ 0.3994140625,
+ 1.5087890625,
+ 1.578125,
+ 3.287109375,
+ 2.12109375,
+ 2.56640625,
+ 1.1416015625,
+ 1.814453125,
+ 0.43017578125,
+ 0.2071533203125,
+ 0.181396484375,
+ 1.25,
+ 1.00390625,
+ 1.666015625,
+ 1.341796875,
+ 2.09765625,
+ 0.83447265625,
+ 1.3134765625,
+ 0.2249755859375,
+ 0.031463623046875,
+ 0.0271759033203125,
+ 0.386962890625,
+ 0.302734375,
+ 1.0849609375,
+ 0.58544921875,
+ 1.2216796875,
+ 0.4033203125,
+ 0.45849609375,
+ 0.0921630859375
+ ],
+ 'descriptor': {shape: [1, 5, 5, 2], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name': 'convTranspose2d float16 4D input and filter tensors options.bias',
+ 'graph': {
+ 'inputs': {
+ 'convTranspose2dInput': {
+ 'data': [
+ 0.1109619140625, 0.8681640625, 0.734375, 0.4306640625,
+ 0.59814453125, 0.12322998046875, 0.1611328125, 0.08837890625,
+ 0.291015625
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'}
+ },
+ 'convTranspose2dFilter': {
+ 'data': [
+ 0.6162109375, 0.26220703125, 0.794921875, 0.873046875, 0.8310546875,
+ 0.85498046875, 0.55517578125, 0.84033203125, 0.85302734375
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'},
+ 'constant': true
+ },
+ 'convTranspose2dBias': {
+ 'data': [0.45166015625],
+ 'descriptor': {shape: [1], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'convTranspose2d',
+ 'arguments': [
+ {'input': 'convTranspose2dInput'},
+ {'filter': 'convTranspose2dFilter'},
+ {'options': {'bias': 'convTranspose2dBias'}}
+ ],
+ 'outputs': 'convTranspose2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'convTranspose2dOutput': {
+ 'data': [
+ 0.52001953125, 1.015625, 1.2197265625, 1.333984375,
+ 1.03515625, 0.81396484375, 1.783203125, 2.484375,
+ 2.3125, 1.177734375, 0.98876953125, 2.00390625,
+ 2.986328125, 2.5703125, 1.4150390625, 0.83154296875,
+ 1.3564453125, 1.85546875, 1.3828125, 0.8056640625,
+ 0.541015625, 0.63623046875, 0.82470703125, 0.771484375,
+ 0.69970703125
+ ],
+ 'descriptor': {shape: [1, 1, 5, 5], dataType: 'float16'}
+ }
+ }
+ }
+ },
+ {
+ 'name':
+ 'convTranspose2d float16 4D input and filter tensors, both negative input tensor and options.bias',
+ 'graph': {
+ 'inputs': {
+ 'convTranspose2dInput': {
+ 'data': [
+ -0.10888671875, -0.298095703125, -0.390869140625, -0.5625,
+ -0.732421875, -0.84228515625, -0.305908203125, -0.9765625,
+ -0.01416015625
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'}
+ },
+ 'convTranspose2dFilter': {
+ 'data': [
+ 0.6162109375, 0.26220703125, 0.794921875, 0.873046875, 0.8310546875,
+ 0.85498046875, 0.55517578125, 0.84033203125, 0.85302734375
+ ],
+ 'descriptor': {shape: [1, 1, 3, 3], dataType: 'float16'},
+ 'constant': true
+ },
+ 'convTranspose2dBias': {
+ 'data': [-0.845703125],
+ 'descriptor': {shape: [1], dataType: 'float16'},
+ 'constant': true
+ }
+ },
+ 'operators': [{
+ 'name': 'convTranspose2d',
+ 'arguments': [
+ {'input': 'convTranspose2dInput'},
+ {'filter': 'convTranspose2dFilter'},
+ {'options': {'bias': 'convTranspose2dBias'}}
+ ],
+ 'outputs': 'convTranspose2dOutput'
+ }],
+ 'expectedOutputs': {
+ 'convTranspose2dOutput': {
+ 'data': [
+ -0.91259765625, -1.0576171875, -1.2509765625, -1.185546875,
+ -1.15625, -1.287109375, -1.794921875, -2.685546875,
+ -2.228515625, -1.849609375, -1.5859375, -2.890625,
+ -3.73828125, -3.53515625, -1.91015625, -1.4248046875,
+ -2.83203125, -3.494140625, -3.025390625, -1.576171875,
+ -1.015625, -1.64453125, -1.935546875, -1.6904296875,
+ -0.85791015625
+ ],
+ 'descriptor': {shape: [1, 1, 5, 5], dataType: 'float16'}
+ }
+ }
+ }
}
];
diff --git a/tests/wpt/tests/webnn/conformance_tests/log.https.any.js b/tests/wpt/tests/webnn/conformance_tests/log.https.any.js
index 011beef53ac..8ed807b3401 100644
--- a/tests/wpt/tests/webnn/conformance_tests/log.https.any.js
+++ b/tests/wpt/tests/webnn/conformance_tests/log.https.any.js
@@ -14,11 +14,8 @@
// MLOperand log(MLOperand input);
-const getLogPrecisionTolerance = (graphResources) => {
- const toleranceValueDict = {float32: 1 / 1024, float16: 1 / 1024};
- const expectedDataType =
- getExpectedDataTypeOfSingleOutput(graphResources.expectedOutputs);
- return {metricType: 'ATOL', value: toleranceValueDict[expectedDataType]};
+const getLogPrecisionTolerance = () => {
+ return {metricType: 'ULP', value: 8};
};
const logTests = [
diff --git a/tests/wpt/tests/webnn/conformance_tests/pooling.https.any.js b/tests/wpt/tests/webnn/conformance_tests/pooling.https.any.js
index f385aab1f15..8f81ff565d2 100644
--- a/tests/wpt/tests/webnn/conformance_tests/pooling.https.any.js
+++ b/tests/wpt/tests/webnn/conformance_tests/pooling.https.any.js
@@ -34,46 +34,6 @@
// MLOperand maxPool2d(
// MLOperand input, optional MLPool2dOptions options = {});
-
-const getPoolingOperatorsPrecisionTolerance = (graphResources) => {
- const args = graphResources.operators[0].arguments;
- const inputShape =
- graphResources.inputs[args[0][Object.keys(args[0])[0]]].descriptor.shape;
- const options =
- args.length === 2 ? {...args[1][Object.keys(args[1])[0]]} : {};
- let height;
- let width;
-
- if (options.windowDimensions) {
- height = options.windowDimensions[0];
- width = options.windowDimensions[1];
- } else {
- // If not present, the window dimensions are assumed to be the height and
- // width dimensions of the input shape
- if (options.layout && options.layout === 'nhwc') {
- height = inputShape[1];
- width = inputShape[2];
- } else {
- // nhwc layout of input
- height = inputShape[2];
- width = inputShape[3];
- }
- }
-
- const tolerance = height * width + 2;
- const toleranceDict = {
- averagePool2d: {float32: tolerance, float16: tolerance},
- l2Pool2d: {float32: tolerance, float16: tolerance},
- maxPool2d: {float32: 0, float16: 0},
- };
- const expectedDataType =
- getExpectedDataTypeOfSingleOutput(graphResources.expectedOutputs);
- return {
- metricType: 'ULP',
- value: toleranceDict[graphResources.operators[0].name][expectedDataType]
- };
-};
-
const poolingOperatorsTests = [
// averagePool2d tests
{
@@ -2345,8 +2305,7 @@ const poolingOperatorsTests = [
if (navigator.ml) {
poolingOperatorsTests.forEach((test) => {
- webnn_conformance_test(
- buildAndExecuteGraph, getPoolingOperatorsPrecisionTolerance, test);
+ webnn_conformance_test(buildAndExecuteGraph, getPrecisionTolerance, test);
});
} else {
test(() => assert_implements(navigator.ml, 'missing navigator.ml'));
diff --git a/tests/wpt/tests/webnn/conformance_tests/qdq_subgraph.https.any.js b/tests/wpt/tests/webnn/conformance_tests/qdq_subgraph.https.any.js
index 3b59c3bb49d..aa816cce7fb 100644
--- a/tests/wpt/tests/webnn/conformance_tests/qdq_subgraph.https.any.js
+++ b/tests/wpt/tests/webnn/conformance_tests/qdq_subgraph.https.any.js
@@ -535,12 +535,12 @@ const subgraphTests = [
{'input': 'transposeOutput'},
{'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
],
- 'outputs': 'quantizedtransposeOutput'
+ 'outputs': 'quantizedTransposeOutput'
},
{
'name': 'dequantizeLinear',
'arguments': [
- {'input': 'quantizedtransposeOutput'},
+ {'input': 'quantizedTransposeOutput'},
{'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
],
'outputs': 'output'
@@ -619,12 +619,12 @@ const subgraphTests = [
{'input': 'tanhOutput'},
{'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
],
- 'outputs': 'quantizedtanhOutput'
+ 'outputs': 'quantizedTanhOutput'
},
{
'name': 'dequantizeLinear',
'arguments': [
- {'input': 'quantizedtanhOutput'},
+ {'input': 'quantizedTanhOutput'},
{'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
],
'outputs': 'output'
@@ -702,12 +702,12 @@ const subgraphTests = [
{'input': 'sigmoidOutput'},
{'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
],
- 'outputs': 'quantizedsigmoidOutput'
+ 'outputs': 'quantizedSigmoidOutput'
},
{
'name': 'dequantizeLinear',
'arguments': [
- {'input': 'quantizedsigmoidOutput'},
+ {'input': 'quantizedSigmoidOutput'},
{'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
],
'outputs': 'output'
@@ -788,12 +788,12 @@ const subgraphTests = [
{'input': 'leakyReluOutput'},
{'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
],
- 'outputs': 'quantizedleakyReluOutput'
+ 'outputs': 'quantizedLeakyReluOutput'
},
{
'name': 'dequantizeLinear',
'arguments': [
- {'input': 'quantizedleakyReluOutput'},
+ {'input': 'quantizedLeakyReluOutput'},
{'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
],
'outputs': 'output'
@@ -924,6 +924,337 @@ const subgraphTests = [
}
}
},
+ {
+ 'name': 'quantized elu',
+ 'graph': {
+ 'inputs': {
+ 'input': {
+ 'data': [
+ 1.6811466217041016, 0.0479511022567749, 0.33355462551116943,
+ -0.1988269537687301, -0.0041167140007019, -0.0634240251779556,
+ ],
+ 'descriptor': {shape: [2, 3], dataType: 'float32'},
+ 'constant': false
+ },
+ 'inputScale': {
+ 'data': [0.003921568859368563],
+ 'descriptor': {shape: [1], dataType: 'float32'},
+ 'constant': true
+ },
+ 'inputZeroPoint': {
+ 'data': [0],
+ 'descriptor': {shape: [1], dataType: 'int8'},
+ 'constant': true
+ },
+ 'outputScale': {
+ 'data': [0.003921568859368563],
+ 'descriptor': {shape: [1], dataType: 'float32'},
+ 'constant': true
+ },
+ 'outputZeroPoint': {
+ 'data': [0],
+ 'descriptor': {shape: [1], dataType: 'int8'},
+ 'constant': true
+ },
+ },
+ 'operators': [
+ {
+ 'name': 'quantizeLinear',
+ 'arguments': [
+ {'input': 'input'},
+ {'scale': 'inputScale', 'zeroPoint': 'inputZeroPoint'}
+ ],
+ 'outputs': 'quantizedInput'
+ },
+ {
+ 'name': 'dequantizeLinear',
+ 'arguments': [
+ {'input': 'quantizedInput'},
+ {'scale': 'inputScale', 'zeroPoint': 'inputZeroPoint'}
+ ],
+ 'outputs': 'dequantizedInput'
+ },
+ {
+ 'name': 'elu',
+ 'arguments': [{'input': 'dequantizedInput'}],
+ 'outputs': 'eluOutput'
+ },
+ {
+ 'name': 'quantizeLinear',
+ 'arguments': [
+ {'input': 'eluOutput'},
+ {'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
+ ],
+ 'outputs': 'quantizedEluOutput'
+ },
+ {
+ 'name': 'dequantizeLinear',
+ 'arguments': [
+ {'input': 'quantizedEluOutput'},
+ {'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
+ ],
+ 'outputs': 'output'
+ }
+ ],
+ 'expectedOutputs': {
+ 'output': {
+ 'data': [
+ 0.49803924560546875, 0.0470588281750679, 0.3333333432674408,
+ -0.18039216101169586, -0.003921568859368563, -0.062745101749897,
+ ],
+ 'descriptor': {shape: [2, 3], dataType: 'float32'}
+ }
+ }
+ }
+ },
+ {
+ 'name': 'quantized averagePool2d',
+ 'graph': {
+ 'inputs': {
+ 'input': {
+ 'data': [
+ -2.549168109893799, -4.794857501983643,
+ 8.413617134094238, 6.108623504638672
+ ],
+ 'descriptor': {shape: [1, 2, 2, 1], dataType: 'float32'},
+ 'constant': false
+ },
+ 'inputScale': {
+ 'data': [0.343092918395996],
+ 'descriptor': {shape: [1], dataType: 'float32'},
+ 'constant': true
+ },
+ 'inputZeroPoint': {
+ 'data': [-128],
+ 'descriptor': {shape: [1], dataType: 'int8'},
+ 'constant': true
+ },
+ 'outputScale': {
+ 'data': [0.343092918395996],
+ 'descriptor': {shape: [1], dataType: 'float32'},
+ 'constant': true
+ },
+ 'outputZeroPoint': {
+ 'data': [-128],
+ 'descriptor': {shape: [1], dataType: 'int8'},
+ 'constant': true
+ },
+ },
+ 'operators': [
+ {
+ 'name': 'quantizeLinear',
+ 'arguments': [
+ {'input': 'input'},
+ {'scale': 'inputScale', 'zeroPoint': 'inputZeroPoint'}
+ ],
+ 'outputs': 'quantizedInput'
+ },
+ {
+ 'name': 'dequantizeLinear',
+ 'arguments': [
+ {'input': 'quantizedInput'},
+ {'scale': 'inputScale', 'zeroPoint': 'inputZeroPoint'}
+ ],
+ 'outputs': 'dequantizedInput'
+ },
+ {
+ 'name': 'averagePool2d',
+ 'arguments': [{'input': 'dequantizedInput'}, {'options': {'layout': 'nhwc'}}],
+ 'outputs': 'averagePool2dOutput'
+ },
+ {
+ 'name': 'quantizeLinear',
+ 'arguments': [
+ {'input': 'averagePool2dOutput'},
+ {'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
+ ],
+ 'outputs': 'quantizedAveragePool2dOutput'
+ },
+ {
+ 'name': 'dequantizeLinear',
+ 'arguments': [
+ {'input': 'quantizedAveragePool2dOutput'},
+ {'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
+ ],
+ 'outputs': 'output'
+ }
+ ],
+ 'expectedOutputs': {
+ 'output': {
+ 'data': [
+ 3.774022102355957,
+ ],
+ 'descriptor': {shape: [1, 1, 1, 1], dataType: 'float32'}
+ }
+ }
+ }
+ },
+ {
+ 'name': 'quantized maxPool2d',
+ 'graph': {
+ 'inputs': {
+ 'input': {
+ 'data': [
+ -2.549168109893799, -4.794857501983643,
+ 8.413617134094238, 6.108623504638672
+ ],
+ 'descriptor': {shape: [1, 2, 2, 1], dataType: 'float32'},
+ 'constant': false
+ },
+ 'inputScale': {
+ 'data': [0.343092918395996],
+ 'descriptor': {shape: [1], dataType: 'float32'},
+ 'constant': true
+ },
+ 'inputZeroPoint': {
+ 'data': [-128],
+ 'descriptor': {shape: [1], dataType: 'int8'},
+ 'constant': true
+ },
+ 'outputScale': {
+ 'data': [0.343092918395996],
+ 'descriptor': {shape: [1], dataType: 'float32'},
+ 'constant': true
+ },
+ 'outputZeroPoint': {
+ 'data': [-128],
+ 'descriptor': {shape: [1], dataType: 'int8'},
+ 'constant': true
+ },
+ },
+ 'operators': [
+ {
+ 'name': 'quantizeLinear',
+ 'arguments': [
+ {'input': 'input'},
+ {'scale': 'inputScale', 'zeroPoint': 'inputZeroPoint'}
+ ],
+ 'outputs': 'quantizedInput'
+ },
+ {
+ 'name': 'dequantizeLinear',
+ 'arguments': [
+ {'input': 'quantizedInput'},
+ {'scale': 'inputScale', 'zeroPoint': 'inputZeroPoint'}
+ ],
+ 'outputs': 'dequantizedInput'
+ },
+ {
+ 'name': 'maxPool2d',
+ 'arguments': [{'input': 'dequantizedInput'}, {'options': {'layout': 'nhwc'}}],
+ 'outputs': 'maxPool2dOutput'
+ },
+ {
+ 'name': 'quantizeLinear',
+ 'arguments': [
+ {'input': 'maxPool2dOutput'},
+ {'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
+ ],
+ 'outputs': 'quantizedMaxPool2dOutput'
+ },
+ {
+ 'name': 'dequantizeLinear',
+ 'arguments': [
+ {'input': 'quantizedMaxPool2dOutput'},
+ {'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
+ ],
+ 'outputs': 'output'
+ }
+ ],
+ 'expectedOutputs': {
+ 'output': {
+ 'data': [
+ 8.577322959899902,
+ ],
+ 'descriptor': {shape: [1, 1, 1, 1], dataType: 'float32'}
+ }
+ }
+ }
+ },
+ {
+ 'name': 'quantized reshape',
+ 'graph': {
+ 'inputs': {
+ 'input': {
+ 'data': [
+ 1.6811466217041016, 0.0479511022567749, 0.33355462551116943,
+ -0.1988269537687301, -0.0041167140007019, -0.0634240251779556,
+ ],
+ 'descriptor': {shape: [2, 3], dataType: 'float32'},
+ 'constant': false
+ },
+ 'inputScale': {
+ 'data': [0.003921568859368563],
+ 'descriptor': {shape: [1], dataType: 'float32'},
+ 'constant': true
+ },
+ 'inputZeroPoint': {
+ 'data': [16],
+ 'descriptor': {shape: [1], dataType: 'int8'},
+ 'constant': true
+ },
+ 'outputScale': {
+ 'data': [0.003921568859368563],
+ 'descriptor': {shape: [1], dataType: 'float32'},
+ 'constant': true
+ },
+ 'outputZeroPoint': {
+ 'data': [16],
+ 'descriptor': {shape: [1], dataType: 'int8'},
+ 'constant': true
+ },
+ },
+ 'operators': [
+ {
+ 'name': 'quantizeLinear',
+ 'arguments': [
+ {'input': 'input'},
+ {'scale': 'inputScale', 'zeroPoint': 'inputZeroPoint'}
+ ],
+ 'outputs': 'quantizedInput'
+ },
+ {
+ 'name': 'dequantizeLinear',
+ 'arguments': [
+ {'input': 'quantizedInput'},
+ {'scale': 'inputScale', 'zeroPoint': 'inputZeroPoint'}
+ ],
+ 'outputs': 'dequantizedInput'
+ },
+ {
+ 'name': 'reshape',
+ 'arguments': [{'input': 'dequantizedInput'}, {'newShape': [3, 2]}],
+ 'outputs': 'reshapeOutput'
+ },
+ {
+ 'name': 'quantizeLinear',
+ 'arguments': [
+ {'input': 'reshapeOutput'},
+ {'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
+ ],
+ 'outputs': 'quantizedReshapeOutput'
+ },
+ {
+ 'name': 'dequantizeLinear',
+ 'arguments': [
+ {'input': 'quantizedReshapeOutput'},
+ {'scale': 'outputScale', 'zeroPoint': 'outputZeroPoint'}
+ ],
+ 'outputs': 'output'
+ }
+ ],
+ 'expectedOutputs': {
+ 'output': {
+ 'data': [
+ 0.43529415130615234, 0.0470588281750679,
+ 0.3333333432674408, -0.20000001788139343,
+ -0.003921568859368563, -0.062745101749897,
+ ],
+ 'descriptor': {shape: [3, 2], dataType: 'float32'}
+ }
+ }
+ }
+ },
];
if (navigator.ml) {
diff --git a/tests/wpt/tests/webnn/conformance_tests/sign.https.any.js b/tests/wpt/tests/webnn/conformance_tests/sign.https.any.js
index 4c3a330f850..004c03bdf13 100644
--- a/tests/wpt/tests/webnn/conformance_tests/sign.https.any.js
+++ b/tests/wpt/tests/webnn/conformance_tests/sign.https.any.js
@@ -122,7 +122,8 @@ const signTests = [
'graph': {
'inputs': {
'signInput': {
- 'data': [-1, 0, 1, 2],
+ // int32 range: [/* -(2**31) */ -2147483648, /* 2**31 - 1 */ 2147483647]
+ 'data': [-2147483648, 0, 2147483646, 2147483647],
'descriptor': {shape: [2, 2], dataType: 'int32'}
}
},
@@ -166,7 +167,8 @@ const signTests = [
'graph': {
'inputs': {
'signInput': {
- 'data': [-1, 0, 1, 2, -2, -1, 0, 1],
+ // int8 range: [/* -(2**7) */ -128, /* 2**7 - 1 */ 127]
+ 'data': [-128, 0, 1, 2, -2, -1, 0, 127],
'descriptor': {shape: [1, 2, 2, 2], dataType: 'int8'}
}
},
diff --git a/tests/wpt/tests/webnn/resources/utils.js b/tests/wpt/tests/webnn/resources/utils.js
index 50d7911a918..9d5cfc70c10 100644
--- a/tests/wpt/tests/webnn/resources/utils.js
+++ b/tests/wpt/tests/webnn/resources/utils.js
@@ -59,6 +59,13 @@ const getPrecisionTolerance = (graphResources, intermediateOperands) => {
op, graphResources, intermediateOperands)
.value;
break;
+ case 'averagePool2d':
+ case 'maxPool2d':
+ case 'l2Pool2d':
+ toleranceValue += getPoolingOperatorsPrecisionTolerance(
+ op, graphResources, intermediateOperands)
+ .value;
+ break;
default:
const operatorTolerance =
operatorToleranceDict[op.name]?.[expectedDataType];
@@ -1006,6 +1013,53 @@ const getConv2dPrecisionTolerance =
return {metricType: 'ULP', value: toleranceValueDict[expectedDataType]};
};
+const getPoolingOperatorsPrecisionTolerance =
+ (op, graphResources, intermediateOperands) => {
+ const args = op.arguments;
+ const operatorName = op.name;
+ const {inputs} = graphResources;
+ let inputShape;
+ const inputIndex = args[0][Object.keys(args[0])[0]];
+ if (inputs[inputIndex]) {
+ inputShape = inputs[inputIndex].descriptor.shape;
+ } else {
+ inputShape = intermediateOperands[inputIndex].shape;
+ }
+ const options =
+ args.length === 2 ? {...args[1][Object.keys(args[1])[0]]} : {};
+ let height;
+ let width;
+
+ if (options.windowDimensions) {
+ height = options.windowDimensions[0];
+ width = options.windowDimensions[1];
+ } else {
+ // If not present, the window dimensions are assumed to be the height
+ // and width dimensions of the input shape
+ if (options.layout && options.layout === 'nhwc') {
+ height = inputShape[1];
+ width = inputShape[2];
+ } else {
+ // nhwc layout of input
+ height = inputShape[2];
+ width = inputShape[3];
+ }
+ }
+
+ const tolerance = height * width + 2;
+ const toleranceDict = {
+ averagePool2d: {float32: tolerance, float16: tolerance},
+ l2Pool2d: {float32: tolerance, float16: tolerance},
+ maxPool2d: {float32: 0, float16: 0},
+ };
+ const expectedDataType =
+ getExpectedDataTypeOfSingleOutput(graphResources.expectedOutputs);
+ return {
+ metricType: 'ULP',
+ value: toleranceDict[operatorName][expectedDataType]
+ };
+};
+
const getInstanceNormPrecisionTolerance = (graphResources) => {
// according to
// https://github.com/web-platform-tests/wpt/pull/43891#discussion_r1457026316
diff --git a/tests/wpt/tests/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-clone.https.html b/tests/wpt/tests/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-clone.https.html
index 9f07713d443..c93f8b3e541 100644
--- a/tests/wpt/tests/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-clone.https.html
+++ b/tests/wpt/tests/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-clone.https.html
@@ -37,7 +37,7 @@ promise_test(async t => {
const original = result.value;
let clone = structuredClone(original);
assert_equals(original.timestamp, clone.timestamp);
- assert_equals(original.getMetadata().absCaptureTime, clone.getMetadata().absCaptureTime);
+ assert_equals(original.getMetadata().captureTime, clone.getMetadata().captureTime);
assert_array_equals(Array.from(original.data), Array.from(clone.data));
await writer2.write(clone);
resolve();
diff --git a/tests/wpt/tests/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-metadata.https.html b/tests/wpt/tests/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-metadata.https.html
index 435e1c06783..df4577c5614 100644
--- a/tests/wpt/tests/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-metadata.https.html
+++ b/tests/wpt/tests/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-metadata.https.html
@@ -39,7 +39,7 @@ promise_test(async t => {
assert_true(original.getMetadata().hasOwnProperty('receiveTime'));
assert_true(original.getMetadata().receiveTime > 0);
assert_equals(original.getMetadata().rtpTimestamp, newFrame.getMetadata().rtpTimestamp);
- assert_equals(original.getMetadata().absCaptureTime, newFrame.getMetadata().absCaptureTime);
+ assert_equals(original.getMetadata().captureTime, newFrame.getMetadata().captureTime);
assert_equals(original.getMetadata().receiveTime, newFrame.getMetadata().receiveTime);
assert_array_equals(Array.from(original.data), Array.from(newFrame.data));
await writer2.write(newFrame);
@@ -83,7 +83,7 @@ promise_test(async t => {
assert_not_equals(original.getMetadata().rtpTimestamp, newFrame.getMetadata().rtpTimestamp);
assert_equals(newMetadata.rtpTimestamp, newFrame.getMetadata().rtpTimestamp);
assert_equals(original.getMetadata().receiveTime, newFrame.getMetadata().receiveTime);
- assert_equals(original.getMetadata().absCaptureTime, newFrame.getMetadata().absCaptureTime);
+ assert_equals(original.getMetadata().captureTime, newFrame.getMetadata().captureTime);
assert_array_equals(Array.from(original.data), Array.from(newFrame.data));
await writer2.write(newFrame);
resolve();
diff --git a/tests/wpt/tests/webrtc-encoded-transform/tentative/RTCPeerConnection-insertable-streams.js b/tests/wpt/tests/webrtc-encoded-transform/tentative/RTCPeerConnection-insertable-streams.js
index 0bf820acde4..f3873e1de4b 100644
--- a/tests/wpt/tests/webrtc-encoded-transform/tentative/RTCPeerConnection-insertable-streams.js
+++ b/tests/wpt/tests/webrtc-encoded-transform/tentative/RTCPeerConnection-insertable-streams.js
@@ -30,7 +30,7 @@ function areMetadataEqual(metadata1, metadata2, type) {
metadata1.payloadType == metadata2.payloadType &&
areArraysEqual(
metadata1.contributingSources, metadata2.contributingSources) &&
- metadata1.absCaptureTime == metadata2.absCaptureTime &&
+ metadata1.captureTime == metadata2.captureTime &&
metadata1.frameId === metadata2.frameId &&
areArraysEqual(metadata1.dependencies, metadata2.dependencies) &&
metadata1.spatialIndex === metadata2.spatialIndex &&
diff --git a/tests/wpt/webgl/meta/conformance/context/premultiplyalpha-test.html.ini b/tests/wpt/webgl/meta/conformance/context/premultiplyalpha-test.html.ini
deleted file mode 100644
index ffc971bc861..00000000000
--- a/tests/wpt/webgl/meta/conformance/context/premultiplyalpha-test.html.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[premultiplyalpha-test.html]
- bug: https://github.com/servo/servo/issues/21132
-
- [WebGL test #62]
- expected: FAIL
-
- [WebGL test #69]
- expected: FAIL